目录
- 注解
- 反射
注解
注解可以标记在包、类、属性、方法,方法参数以及局部变量上,且同一个地方可以同时标记多个注解。注解最常见的应用是可以通过反射技术去得到类里面的注解,以决定怎么去运行类。
负责注解其他注解的注解,java的四个元注解:
1.@Target:描述注解的使用范围,即用在类、方法、变量,它的取值范围定义在ElementType 枚举中。
public enum ElementType {
// 类、接口、枚举类
TYPE,
// 成员变量(包括:枚举常量)
FIELD,
// 成员方法
METHOD,
// 方法参数
PARAMETER,
// 构造方法
CONSTRUCTOR,
// 局部变量
LOCAL_VARIABLE,
// 注解类
ANNOTATION_TYPE,
// 可用于修饰:包
PACKAGE,
// 类型参数,JDK 1.8 新增
TYPE_PARAMETER,
// 使用类型的任何地方,JDK 1.8 新增
TYPE_USE
}
2.@Retention:表示在什么级别保存该注释信息,用于描述注解的生命周期,定义在RetentionPolicy枚举中。(SOURCE<CLASS<RUNTIME)
public enum RetentionPolicy {
// 源文件保留
SOURCE,
// 编译期保留,默认值
CLASS,
// 运行期保留,可通过反射去获取注解信息
RUNTIME
}
3.@Documented:说明该注解将被包含在javadoc帮助文档中,没有此注解则不会保留。
4.@Inherited:说明子类可以继承父类中的该注解,比方一个类使用了被@Inherited修饰的注解,则其子类将自动具有该注解。
自定义注解测试:
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnoation {
//参数类型+参数名
String name() default "";
int age() default 0;
//如果值为默认值-1,代表不存在
int id() default -1;
//value才可以省略
String[] schools() default {"北京大学"};
}
反射
利用反射读取注解,可以理解为逆着来,以前是从类创建对象,现在从对象获取到类的结构,甚至方法,属性。
reflection是java被视为动态语言的关键,反射机制允许程序在执行期间,借助Reflection API取得任何类的内部信息,并能直接操作任意对象的的内部属性及方法。
类加载器在加载类的时候,是将.class文件里的二进制数据读取到内存中,将其放在方法区中,然后再在堆内存中创建.Class对象,用来封装类的数据结构,加载类的最终产物是堆内存中的Class对象,我们可以通过这个.Class对象看到类的结构,这个对象就像一面镜子,透过这个镜子可以看到类的结构,所以称之为反射。
打印的结果为true,可以知道只有一个class对象加载到内存中
测试User类:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private String name;
private int id;
private int age;
}
测试字节码文件是否一致:
public static void main(String[] args) throws ClassNotFoundException {
Class c1 = Class.forName("com.mytest.test.model.User");
Class c2 = Class.forName("com.mytest.test.model.User");
System.out.println(c1==c2);
}
打印结果为true。
测试获取类的属性,getFields方法只能获取public修饰的属性,getDeclaredFields可以获取所有的
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
Class<?> c = Class.forName("com.mytest.test.model.User");
Field[] fields = c.getFields();
fields = c.getDeclaredFields();
for (Field field : fields) {
System.out.println(field);
}
Method getName = c.getMethod("getName", null);
Method setName = c.getMethod("setName", String.class);
System.out.println(getName);
System.out.println(setName);
Constructor[] constructors = c.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
}
打印结果:
通过反射获取注解及里面的参数
注意事项:
- 如果获取的是类的注解,直接从类的class对象中获取注解,再通过注解获取里面的参数
- 如果是获取方法或者属性的注解,先中类的class对象中获取方法或者属性,再获取里面的参数。
- getDeclaredFields:获取属性
- getAnnotation:获取注解
@Slf4j
public class Test {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
Class<?> c = Class.forName("com.mytest.test.User2");
Annotation[] annotations = c.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
TableKuang tableKuang = c.getAnnotation(TableKuang.class);
String value = tableKuang.value();
System.out.println(value);
Field[] declaredFields = c.getDeclaredFields();
for (Field declaredField : declaredFields) {
FieldKuang annotation = declaredField.getAnnotation(FieldKuang.class);
System.out.println(annotation);
System.out.println(annotation.columnName());
System.out.println(annotation.length());
System.out.println(annotation.type());
}
}
}
//类名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TableKuang {
String value();
}
//属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldKuang {
String columnName();
String type();
int length();
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableKuang("测试user")
class User2 {
@FieldKuang(columnName = "name注解", type = "String注解",length = 11)
private String name;
@FieldKuang(columnName = "id注解", type = "int注解",length = 12)
private int id;
@FieldKuang(columnName = "age注解", type = "int注解",length = 13)
private int age;
}
打印结果: