基本概念
注解(Annotation),也叫元数据。一种代码级别的说明。JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明。
作用分类:
- 编写文档:通过代码里标识的注解生成文档(生成doc文档)
- 代码分析:通过代码里标识的注解对代码进行分析(使用反射)
- 编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查
注解的简单使用:
/**
* 注解的使用简单使用
* @author Administrator
* @version 1.0
*/
@SuppressWarnings("all") // 压制警告
public class AnnoDemo01 {
/**
* 计算两数的和
* @param a 整数
* @param b 整数
* @return 两数的和
*/
public int add(int a, int b) {
return a + b;
}
@Deprecated // 该注解标注的内容表示已过时
public void show() {
System.out.println("hi!");
}
public void show2() {
System.out.println("hello!");
}
}
在命令行下使用javadoc 源文件.java 即可生成doc文档
自定义注解:
格式:public @interface 注解名称{ }
本质:通过javap MyAnno.class反编译发现注解本质上就是一个接口,该接口默认继承Annotation接口
public interface MyAnno extends java.lang.annotation.Annotation {
}
属性:接口中的抽象方法
要求:
- 返回值类型:基本数据类型、String、枚举、注解或以上类型的数组
- 定义了属性,在使用注解时需要给属性赋值;或是在定义属性时,使用default关键字给属性默认初始化值,在使用注解时可以不进行属性的赋值。
public @interface MyAnno {
public abstract String name();
public abstract int age() default 16;
}
public class AnnoDemo02 {
@MyAnno(name="tom")
public void test() {
}
}
- 如果只有一个属性需要赋值,且属性的名称是value,则value可以省略,直接写值就可。
public @interface MyAnno {
/* public abstract String name();
public abstract int age() default 16;*/
public abstract int value();
}
public class AnnoDemo02 {
@MyAnno(123)
public void test() {
}
}
枚举类型的赋值
public enum Student {
stu1, stu2, stu4;
}
public @interface MyAnno {
public abstract String name();
public abstract int age();
public abstract Student stu();
}
public class AnnoDemo02 {
@MyAnno(name="tom", age=16, stu=Student.stu1)
public void test() {
}
}
注解类型赋值和数组类型赋值
public @interface MyAnno {
public abstract Anno anno(); // 注解类型
public abstract String[] str(); // 数组类型
}
@MyAnno(anno=@Anno, str="{111, 222}") // 如果数组中只有一个元素,可以省略大括号
public void test() {
}
元注解:
用于描述注解的注解
- @Target:描述注解能够作用的位置
- @Retention:描述注解被保留的阶段
- @Documented:描述注解是否被抽取到API文档中
- @Inherited:描述注解是否被子类继承
/**
* value为ElementType枚举数组类型,常用取值如下
* 1.TYPE:被描述的注解只能作用于类上
* 2.METHOD:被描述的注解只能作用于方法上
* 3.FIELD:被描述的注解只能作用于成员变量上
*/
/**
* value为RetentionPolicy为枚举类型,取值如下
* 1.RUNTIME(常用):被描述的注解,会保留到class字节码文件中,被JVM读取到
* 2.CLASS:被描述的注解,会保留到class字节码文件中,但不会被JVM读取到
* 3.SOURCE:被描述的注解,不会保留到class字节码文件中
*/
@Target(value = {ElementType.TYPE, ElementType.METHOD}) // 表示MyAnnotation注解只能作用于类和方法上
@Retention(RetentionPolicy.RUNTIME) // 当前被描述的注解,会保留到class字节码文件中,被JVM读取到
@Documented // 表示MyAnnotation注解会被抽取到javadoc文档中
@Inherited // 表示加过MyAnnotation注解的父类,其子类会自动继承MyAnnotation注解
public @interface MyAnnotation {
}
注解的解析:
在程序中获取注解中定义的属性值,可以代替配置文件中配置的信息。
@MyProperties(className="com.szly.annotation.Person", methodName="say")
public class ReflectTest {
public static void main(String[] args) throws Exception {
// 1. 解析注解
// 1.1 获取该类的字节码文件对象
Class<ReflectTest> reflectTestClass = ReflectTest.class;
// 1.2 获取注解对象
// 在内存中生成了一个该注解接口的子类实现对象
MyProperties annotation = reflectTestClass.getAnnotation(MyProperties.class);
// 1.3 调用注解对象中定义的抽象方法,获取返回值
String className = annotation.className();
String methodName = annotation.methodName();
System.out.println(className);
System.out.println(methodName);
// 2. 加载类进内存
Class clazz = Class.forName(className);
// 3. 创建对象
Object obj = clazz.newInstance();
// 4. 获取方法对象
Method method = clazz.getMethod(methodName);
// 5. 执行方法
method.invoke(obj);
}
}
getAnnotation(Class class)在内存中生成了一个该注解接口的子类实现对象,并返回。
该实现类重写了注解中定义的抽象方法,类似于如下写法:
public class MyPropertiesImpl implements Myproperties {
public String className() {
return "com.szly.annotation.Person";
}
public String methodName() {
return "say";
}
}