1. 注解的概念
注解(Annotation),也叫元数据。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
2. 注解的作用
- 编写文档:通过代码里标识的注解生成 doc 文档
- 编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查
- 代码分析:通过代码里标识的注解对代码进行分析
3. JDK 内置注解
-
@Override:检测该注解标注的方法是否继承自父类(接口)
-
@Deprecated:该注解标注的内容,表示已过时
-
@SuppressWarnings:压制警告
如:@SuppressWarnings(“all”):压制所有警告
4. 自定义注解
4.1 注解的格式
元注解
public @interface 注解名称{
属性列表;
}
4.2 注解的本质
-
自定义一个注解,如下:
public @interface 注解名称 { }
-
将这个注解先进行编译,再将其反编译,得到如下代码:
public interface 注解名称 extends java.lang.annotation.Annotation { }
-
于是得出结论:注解本质上就是一个接口,该接口默认继承Annotation接口。
4.3 注解的属性
由注解的格式我们可以知道,注解由元注解和接口两部分组成。
注解的属性就是接口中的方法。
使用注解时,有以下要求:
- 属性的返回值类型只能是以下几种:
- 基本数据类型
- String
- 枚举
- 注解
- 以上类型的数组
- 定义了属性,在使用时需要给属性赋值
- 如果定义属性时,使用 default 关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值。
- 如果只有一个属性需要赋值,并且属性的名称是 value,则 value 可以省略,直接定义值即可。
- 数组赋值时,值使用 {} 包裹。如果数组中只有一个值,则 {} 可以省略。
4.4 元注解
元注解是用于描述注解的注解
以下是常用的元注解:
- @Target:描述注解能够作用的位置
- ElementType.TYPE:可以作用于类上
- ElementType.METHOD:可以作用于方法上
- ElementType.FIELD:可以作用于成员变量上
- @Retention:描述注解被保留的阶段
- RetentionPolicy.SOURCE:当前被描述的注解,会被编译器弃用
- RetentionPolicy.CLASS:当前被描述的注解,会保留到class字节码文件中
- RetentionPolicy.RUNTIME:当前被描述的注解,会保留到class字节码文件中,并被 JVM 读取到(自定义的注解一般使用这个)
- @Documented:描述注解是否被抽取到 API 文档中
- @Inherited:描述注解是否被子类继承
5. 使用自定义注解
-
获取注解定义位置(Class,Method,Field)的对象
-
由对象获取指定的注解
对象名.getAnnotation(注解名.Class)
-
调用注解中的抽象方法获取属性值
6. 注解的案例
-
需求分析
写一个“框架",在不改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法。
-
概要设计
在类的前面声明自定义注解,并给属性赋予全类名和方法名,然后在类中使用注解获取属性值,然后加载该类进内存,之后创建对象,再利用反射获取需要执行的方法,最后执行方法。
-
详细设计
- 创建自定义注解 @pro,其中有属性:className(),methodName()
- 在测试类的前面添加自定义注解 @pro 并赋值
- 使用注解获取属性值
- 根据全类名加载该类进内存,获取对应的 Class 对象
- 创建对象
- 利用反射获取需要执行的方法
- 执行该方法
-
编码实现
-
@pro
/** * 描述需要执行的类名,方法名 */ @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface pro { String className(); String methodName(); }
-
AnnotationTest
@pro(className = "com.zt.annotation.Student",methodName = "study") public class AnnotationTest { public static void main(String[] args) throws Exception { //1. 使用注解 //1.1 获取该类的类对象 Class<AnnotationTest> reflectTestClass = AnnotationTest.class; //1.2 获取上边的注解对象 // 其实就是在内存中生成了一个该注解接口的子类实现对象 pro annotation = reflectTestClass.getAnnotation(pro.class); //1.3 调用注解对象中的抽象方法,获取返回值 String className = annotation.className(); String methodName = annotation.methodName(); //2. 根据全类名加载该类进内存,获取对应的 Class 对象 Class<?> aClass = Class.forName(className); //3. 创建对象 Object o = aClass.newInstance(); //4. 利用反射获取方法 Method method1 = aClass.getMethod(methodName); //5. 执行该方法 method1.invoke(o); } }
-
执行结果
study... Process finished with exit code 0
-