注解
这里主要涉及的是四个基础的元注解(meta-annotation)
Java中的注解,有四个基本的元注解target
Retention
Documented
Inherited
这四个元注解Documented
和Inherited
使用不多,主要为target
retention
两个注解
Target
描述注解的使用范围(即被修饰的注解可以用在什么地方).
public @interface Target {
/**
* Returns an array of the kinds of elements an annotation type
* can be applied to.
* @return an array of the kinds of elements an annotation type
* can be applied to
*/
ElementType[] value();
}
使用的范围也就是ElementType[]中的枚举类型数据
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
TYPE, // 类、接口、枚举
/** Field declaration (includes enum constants) */
FIELD, // 成员变量
/** Method declaration */
METHOD, // 成员方法
/** Formal parameter declaration */
PARAMETER, // 方法参数
/** Constructor declaration */
CONSTRUCTOR, // 构造方法
/** Local variable declaration */
LOCAL_VARIABLE, // 局部变量
/** Annotation type declaration */
ANNOTATION_TYPE, // 注解类
/** Package declaration */
PACKAGE, // 包
/**
* Type parameter declaration
*
* @since 1.8
*/
TYPE_PARAMETER, // 类型参数(泛型)
/**
* Use of a type
*
* @since 1.8
*/
TYPE_USE
}
Retention
描述注解保留的时间范围(即:被描述的注解在它所修饰的类中可以被保留到何时).
Reteniton注解用来限定那些被它所注解的注解类在注解到其他类上以后,可被保留到何时,一共有三种策略
public enum RetentionPolicy {
// 源文件保留
SOURCE,
// 编译期保留,默认值
CLASS,
// 运行期保留,可通过反射去获取注解信息
RUNTIME
}
反射
正常方式: 引入需要的包类名称
—-> 通过 new实例化
—-> 获取实例化对象
反射: 实例化对象
—-> getClass()方法
—-> 得到完整的包类名称
通过反射, 使得Java具有类似动态语言的特性
反射所提供的功能
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时调用任意一个对象的成员变量和方法
- 在运行时获取泛型信息
- 在运行时处理注解
- 生成动态代理
- ……
反射优点和缺点
优点:
可以动态创建和编译,有很大的灵活性
缺点:
慢于直接执行相同的操作
获取Class类的实例
已知某个类的实例
User user = new User(); Class<? extends User> aClass1 = user.getClass();
已知具体的类
Class<User> aClass2 = User.class;
已知类的全名称
Class<?> aClass3 = Class.forName("com.xxx.xxx");
类的加载过程
|—————————| |——————————| |————————————|
| 类的加载 | ------> | 类的链接 | -------> | 类的初始化 |
| Load | -----> | Link | -------> | Initialize |
|—————————| |——————————| |————————————|
获取Class对象之后可以做写什么
创建类的对象
aClass.newInstance(); // 本质是调用了无参构造器
若是没有无参构造器,可以通过明确调用对应的构造器,然后将参数传递进去即可
aClass.getDeclaredConstrutor(Class...parameterTypes)
可以调用类中的方法
User user = (User) aClass.newInstance(); Method setName = aClass.getDeclaredMethod("setName", String.class); // invoke: 激活 // (对象, "方法的值") setName.invoke(user, "维坤坤"); System.out.println(user.getName());
操作类中的属性
//反射操作属性 User user3 = (User) aClass.newInstance(); Field name = aClass.getDeclaredField("name"); // 不能操作私有属性 需要关闭安全检测 name.setAccessible(true); name.set(user3, "孔维坤"); System.out.println(user3.getName());
注解和反射的实际应用
编写一个注解,适用于成员方法,然后运行保留
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AAAA {
String value() default "";
}
通过反射获取注解中的内容
public class ReAno {
@AAAA("我是方法注解")
public void say(int a){
System.out.println("wtf " + a);
}
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
// 通过反射获取类
Class<ReAno> reAnoClass = ReAno.class;
Method say = reAnoClass.getDeclaredMethod("say", int.class);
AAAA annotation = say.getAnnotation(AAAA.class);
System.out.println(annotation.value());
// 输出: 我是方法注解
}
}
总结
通过反射获取注解的方式就是注解应用在什么范围, 就先通过反射获取对应的方法、或者成员变量,然后获取其注解即可。