JAVA基础之注解和反射
JAVA基础之注解和反射
注解
基础语法
定义注解
public @interface Table {
}
添加属性
public @interface Table {
public String name();
}
增加默认值
public @interface Column {
public String name() default "";
}
元注解
@Retention:设置注解保存策略。
@Retention(RetentionPolicy.RUNTIME)
public @interface Author {
String name() default "";
}
- SOURCE:注解将被编译器丢弃,编译后的class文件不包含注解内容。
- CLASS:默认策略,注解将被编译保留在class文件中,但VM运行时不会保留。
- RUNTIME:注解将被编译保留在class文件中,VM加载时也会保留,所以可以通过反射方式进行读取。
@Documented:标记注解是否包含在javadoc等工具生成的文档中。
@Documented
public @interface Author {
String name() default "";
}
@Target:设置注解可以用在Java那些成员上,限制注解的使用范围预防造成滥用(例如:@Override只能在方法上使用)。
@Target({ElementType.TYPE})
public @interface Author {
String name() default "";
}
- TYPE :Class、interface(包括注解接口)、enum、record。
- FIELD:字段(包含枚举常量)。
- METHOD:方法。
- PARAMETER:方法参数。
- CONSTRUCTOR:构造函数。
- LOCAL_VARIABLE:局部变量。
- ANNOTATION_TYPE:注释接口(以前称为注释类型)。
- PACKAGE:包。
- TYPE_PARAMETER:泛型类型参数(1.8版本添加)。
- TYPE_USE:使用类型(1.8版本添加)。
- MODULE:模块(9版本添加)。
- RECORD_COMPONENT:Record组件(16版本添加)。
**@Inherited **:如果类上的注解是@Inherited修饰的,那么类的子类也会继承这个注解。
@Inherited
public @interface Author {
String name() default "";
}
- 接口上的注解即使被@Inherited修饰,其实现类也不会继承注解。
- 类方法上的注解即使被@Inherited修饰,子类不会继承注解。
内置注解
@Override
public class User {
@Override
public String toString() {
return super.toString();
}
}
- 检查方法是否为重写方法,如果发现其父类或引用的接口中没有该方法时,IDE会报错。
@Deprecated
@Deprecated
public Date(int year, int month, int date) {
this(year, month, date, 0, 0, 0);
}
- 标记方法已过时,使用该方法时IDE会给出警告。
**@SuppressWarnings **
public static void main(String[] args) {
@SuppressWarnings("unused")
Date date = new Date(10000l);
}
- 告诉IDE忽略注解中声明的警告。
@SafeVarargs
public class User<T> {
@SafeVarargs
public User(T...ts) {
}
@SuppressWarnings("unchecked")
public void test3( T...ts) {
}
@SafeVarargs
public final void test1(T...ts) {
}
@SafeVarargs
public static <V> void test2(V...ts) {
}
}
- 该注解是在Java 7版本添加,向IDE声明,忽略泛型可变参数的
Type safety: Potential heap pollution via varargs parameter ts
警告(可以用在构造函数、静态方法、不可变方法、其它方法需要使用@SuppressWarnings
处理)。
@FunctionalInterface
@FunctionalInterface
interface Calculate {
public int t();
}
- 该注解是Java8版本添加,标识匿名函数或函数式接口,使用
@FunctionalInterface
注解的接口只能有一个方法(函数式接口定义)。
@Repeatable
public @interface Authors {
Author[] value();
}
@Repeatable(Authors.class)
public @interface Author {
String name() default "";
int level();
}
public class User {
@Author(name = "赵又廷",level = 0)
@Author(name = "高圆圆",level = 1)
public void run() {
}
}
- Java8版本添加,标识注解可以同时使用多次。
反射
Class API
测试文件
/**
* @ClassName: Animal
* @Description: 动物基类
*/
public class Animal {
// 名称
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
/**
* @ClassName: Vertebrate
* @Description: 脊椎动物
*/
public class Vertebrate extends Animal {
// 生存环境
private String env;
public String getEnv() {
return env;
}
public void setEnv(String env) {
this.env = env;
}
}
获取类字段
- 找不到字段时会报异常
java.lang.NoSuchFieldException
。
Class API | 继承 | 私有 |
---|---|---|
getDeclaredField() | × | √ |
getField() | √ | × |
getDeclaredFields() | × | √ |
getFields() | √ | × |
public static void main(String[] args) throws NoSuchFieldException, SecurityException{
Class<Vertebrate> cls = Vertebrate.class;
// 获取env成功,获取name会报异常
Field f_env = cls.getDeclaredField("env");
System.out.println(f_env.getName());
}
public static void main(String[] args) throws NoSuchFieldException, SecurityException{
Class<Vertebrate> cls = Vertebrate.class;
// 获取name、env都会报异常,将两个字段的修饰符改为public可以获取。
Field f_env = cls.getField("name");
System.out.println(f_env.getName());
}
public static void main(String[] args) throws NoSuchFieldException, SecurityException{
Class<Vertebrate> cls = Vertebrate.class;
// f数组只有一个元素(env字段)
Field[] f = cls.getDeclaredFields();
System.out.println(f.length);
}
public static void main(String[] args) throws NoSuchFieldException, SecurityException{
Class<Vertebrate> cls = Vertebrate.class;
// f数组是空的(将env和name字段的修饰符改为public可以获取)
Field[] f = cls.getFields();
System.out.println(f.length);
}
获取类方法
Class API | 继承 | 私有 |
---|---|---|
getDeclaredMethod(String name, Class<?>… parameterTypes) | × | √ |
getMethod(String name, Class<?>… parameterTypes) | √ | × |
getDeclaredMethods() | × | √ |
getMethods() | √ | × |
// 测试之前我们先向Vertebrate类中增加下列方法
private void breed() {
System.out.println(super.getName()+"无配偶");
}
public static void main(String[] args) throws NoSuchMethodException{
Class<Vertebrate> cls = Vertebrate.class;
// 可以获取到方法
Method m = cls.getDeclaredMethod("breed");
System.out.println(m.getName());
}
public static void main(String[] args) throws NoSuchMethodException{
Class<Vertebrate> cls = Vertebrate.class;
// 因为breed方法是私有方法,固报错
Method m = cls.getMethod("breed");
System.out.println(m.getName());
}
- **注意:**getDeclaredMethod、getMethod函数只有name参数时表示匹配无参的方法,如果没有则报错。
public static void main(String[] args) throws NoSuchMethodException{
Class<Vertebrate> cls = Vertebrate.class;
// 获取到getEnv、setEnv、breed三个方法,getName、setName由于是基类方法无法获取
Method[] m = cls.getDeclaredMethods();
for(int i=0;i<m.length;i++) {
System.out.println(m[i].getName());
}
}
public static void main(String[] args) throws NoSuchMethodException{
Class<Vertebrate> cls = Vertebrate.class;
// 无法获取breed方法,其它都可以获取
Method[] m = cls.getMethods();
for(int i=0;i<m.length;i++) {
System.out.println(m[i].getName());
}
}
获取类构造函数
Class API | 私有 |
---|---|
getDeclaredConstructor(Class<?>… parameterTypes) | √ |
getConstructor(Class<?>… parameterTypes) | × |
getDeclaredConstructors() | √ |
getConstructors() | × |
// 测试之前,我们先将Vertebrate的默认构造函数改为私有的
private Vertebrate() {
}
public static void main(String[] args) throws NoSuchMethodException{
Class<Vertebrate> cls = Vertebrate.class;
// 可以获取到无参的构造函数
Constructor<Vertebrate> c = cls.getDeclaredConstructor();
System.out.println(c.getName());
}
public static void main(String[] args) throws NoSuchMethodException{
Class<Vertebrate> cls = Vertebrate.class;
// 无法获取默认无参的构造函数
Constructor<Vertebrate> c = cls.getConstructor();
System.out.println(c.getName());
}
public static void main(String[] args) throws NoSuchMethodException{
Class<Vertebrate> cls = Vertebrate.class;
// 可以获取到无参的构造函数
Constructor<?>[] c = cls.getDeclaredConstructors();
System.out.println(c.length);
}
public static void main(String[] args) throws NoSuchMethodException{
Class<Vertebrate> cls = Vertebrate.class;
// 无法获取默认无参的构造函数
Constructor<?>[] c = cls.getConstructors();
System.out.println(c.length);
}
Field API
字段类型
public static void main(String[] args) throws NoSuchFieldException {
Class<Vertebrate> cls = Vertebrate.class;
Field f = cls.getDeclaredField("env");
Class<?> type = f.getType();
System.out.println(type.getCanonicalName());
}
字段修饰符
public static void main(String[] args) throws NoSuchFieldException {
Class<Vertebrate> cls = Vertebrate.class;
Field f = cls.getDeclaredField("env");
int modifiers = f.getModifiers();
System.out.println(Modifier.PRIVATE == modifiers);
System.out.println(Modifier.FINAL == modifiers);
}
修改字段值
public static void main(String[] args) throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException{
Class<Vertebrate> cls = Vertebrate.class;
Field f = cls.getDeclaredField("env");
Vertebrate obj = new Vertebrate();
f.setAccessible(true);
f.set(obj, "海洋");
System.out.println(obj.getEnv());
}
- 当字段的访问修饰符是私有的,我们需要通过
setAccessible(true)
打开访问限制。
面试题(奇葩)
public static void main(String[] args) {
String a = new String("a");
String b = a;
// TODO 将字符串的值由a改为b,仍打印true
System.out.println(a == b);
}
public static void main(String[] args) throws NoSuchFieldException,
IllegalArgumentException, IllegalAccessException{
String a = new String("a");
String b = a;
System.out.println("a:"+a);
// 通过反射从新设置a对象的value字段值
Class<String> cls = String.class;
Field f = cls.getDeclaredField("value");
f.setAccessible(true);
char[] c = new char[1];
c[0] = 'b';
f.set(a, c);
System.out.println(a == b);
System.out.println("a:"+a);
}
- 这段代码必须限制在1.8版本之下,不然会报错
Unable to make field private final byte[] java.lang.String.value accessible: module java.base does not "opens java.lang" to unnamed module
。
字段注解
public static void main(String[] args) throws NoSuchFieldException {
Class<Vertebrate> cls = Vertebrate.class;
Field f = cls.getDeclaredField("env");
Annotation[] a = f.getAnnotations();
for(int i=0;i<a.length;i++) {
System.out.println(a[i].toString());
}
}
- 前置修改:给env字段增加注解(注意自定义注解的元注解
@Retention
需要设置为RetentionPolicy.RUNTIME
。)
Method API
返回类型
public static void main(String[] args) throws NoSuchMethodException, SecurityException {
Class<Vertebrate> cls = Vertebrate.class;
Method method = cls.getDeclaredMethod("breed");
Class<?> returnType = method.getReturnType();
System.out.println(returnType.getName());
}
参数列表
public static void main(String[] args) throws NoSuchMethodException, SecurityException {
Class<Vertebrate> cls = Vertebrate.class;
Method method = cls.getDeclaredMethod("setEnv",String.class);
Parameter[] parameters = method.getParameters();
for(int i=0;i<parameters.length;i++) {
System.out.println(parameters[i].getName());
System.out.println(parameters[i].getType());
System.out.println(parameters[i].getAnnotations().length);
}
}
-
正常情况下,基于节省资源的考虑,类文件是不保留原始参数名称的(可以通过增加编译参数
javac -parameters
的方式保留形参名称,或者Eclipse将下图中的选项选中)。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ViFgkHgV-1673947516797)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\image-20230116172625760.png)]
访问修饰符
public static void main(String[] args) throws NoSuchMethodException, SecurityException {
Class<Vertebrate> cls = Vertebrate.class;
Method method = cls.getDeclaredMethod("setEnv",String.class);
int modifiers = method.getModifiers();
System.out.println(Modifier.PUBLIC == modifiers);
}
调用方法
public static void main(String[] args) throws NoSuchMethodException, SecurityException,
IllegalAccessException, IllegalArgumentException, InvocationTargetException {
Class<Vertebrate> cls = Vertebrate.class;
Method method = cls.getDeclaredMethod("setEnv",String.class);
Vertebrate obj = new Vertebrate();
method.setAccessible(true);
Object ret = method.invoke(obj, "海洋");
System.out.println(ret);
System.out.println(obj.getEnv());
}
Constructor API
访问修饰符
public static void main(String[] args){
Class<Vertebrate> cls = Vertebrate.class;
Constructor<?>[] constructors = cls.getConstructors();
for(int i=0;i<constructors.length;i++) {
System.out.println(Modifier.PUBLIC == constructors[i].getModifiers());
}
}
创建实例
public static void main(String[] args) throws NoSuchMethodException, SecurityException,
InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException{
Class<Vertebrate> cls = Vertebrate.class;
Constructor<Vertebrate> constructor = cls.getConstructor(String.class);
Vertebrate obj = constructor.newInstance("海洋");
System.out.println(obj.getEnv());
}
-
Constructor.newInstance()
与Class.newInstance()
相比:-
Class.newInstance()
只能调用无参构造函数。 -
当构造函数报错时,
Class.newInstance()
会抛出原始异常,Constructor.newInstance()
会使用java.lang.IllegalArgumentException
进行包装。 -
Class.newInstance()
要求构造函数是公共的,Constructor.newInstance()
可以调用私有构造方法。public static void main(String[] args){ Class<Vertebrate> cls = Vertebrate.class; Constructor<Vertebrate> constructor = cls.getDeclaredConstructor(String.class); constructor.setAccessible(true); Vertebrate obj = constructor.newInstance("海洋"); System.out.println(obj.getEnv()); }
-