1. 注解概述
注解就是 Java 代码里的特殊标记,是给当前程序的开发者提供必要的信息和标记,是给 Java 编译或者 JVM 提供的必要的数据支持和标记。比如 @Override、@Test 等,作用是让其他程序根据注解信息来决定怎么执行该程序。注解可以用在类、构造方法、成员变量、参数等位置处。注解本质是一个接口,java 中所有注解都是继承了 Annotation 接口。
2. 注解的基本格式
自定义注解格式
public @interface 注解名 {
public 属性类型 属性名() default 默认值;
}
例如
public @interface MyAnnotation {
String test1();
boolean test2() default true;
String[] test3();
}
Java中的注解是在 JDK5 的时候引入的一种新特性。注解以@符号开头,紧接着是注解名称。注解名称后面的括号中可以包含一些参数或值。
@Documented
@Retention(RetentionPolicy.SOURCE)
@Target(value = {TYPE})
@Inherited
public @interface MyAnnotation {
}
3. 元注解
3.1 元注解概念
元注解指的是修饰注解的注解,注解的对象是注解
3.2 三种常用元注解
3.2.1 @Documented
@Documented
标记当前注解参与 Javadoc 操作,可以生成 JavaDoc 文档
3.2.2 @Retention
@Retention:指注解的作用范围,有三种 SOURCE,CLASS,RUNTIME 分别对应代码的编译阶段、生成 .class 字节码文件、运行阶段。
RetentionPolicy.SOURCE
只参与编译阶段,如@Override .注解可以参与代码的编译过程,提供相对应的标记,提示 Java 编译器在编程过程中,检查相对应的代码格式是否满足当前注解的需求。即 SOURCE 只作用在源码阶段,字节码文件中不存在
RetentionPolicy.CLASS
对应注解可以在 Class 文件中存在,但是不会参与程序运行
RetentionPolicy.RUNTIME
对应注解参与编译过程,生成的 .class 字节码文件,并且在程序运行过程中存在,可以利用反射操作获取对应的注解信息,从注解中得到相应的数据内容。
3.2.3 @Target
用于修饰 Annotation 定义,指定被修饰的 Annotation 能用于哪些程序元素,即决定当前注解可以作用于 Java 代码中的哪一部分。@Target 包含一个名为 value 的成员变量,通过枚举来决定当前注解可以使用范围。
枚举类型如下:
ElementType | 功能概述 |
---|---|
ElementType.TYPE | 表示当前注解可以用于类和接口,以及枚举 |
ElementType.FIELD | 表示当前注解可以用于成员变量 |
ElementType.METHOD | 表示当前注解可以用于成员方法 |
ElementType.PARAMETER | 表示当前注解可以用于成员方法参数 @NotNull |
ElementType.CONSTRUCTOR | 表示当前注解可以用于构造方法 |
ElementType.ANNOTATION_TYPE | 表示当前注解可以用于注解 |
ElementType.LOCAL_VARIABLE | 表示当前注解可以用于局部变量 |
ElementType.PACKAGE | 表示当前注解可以用于包 |
一个注解可以用于多种类型
public @interface Target { ElementType[] value(); }
import static java.lang.annotation.ElementType.*;
// 表示当前注解可以用于方法
@Target({METHOD})
// 表示当前注解可以用于枚举,成员变量,成员方法,构造方法
@Target({TYPE, FIELD, METHOD, CONSTRUCTOR})
4. 注解属性
注解属性支持的数据类型:基本数据类型,String,Enum ,注解,数组
注解属性就是注解中的成员变量,注解中只有成员变量,没有方法。
- 注解中如果需要使用属性,属性列表中必须有一个 value 反馈类型任意
- 注解属性可以给予默认数据,如果在用户使用当前注解时,没有对指定属性
进行数据提供,当前注解属性反馈数据为默认数据
MyAnnotation 注解中有 value 和 msg 两个属性,使用时候应该进行赋值
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyAnnotation {
int value;
String msg;
}
@MyAnnotation(value=1,msg="MyAnnotation")
public class Test{
}
如果注解中只有一个 value 属性,使用注解时,value 名称可以不写,
其他属性都有 default 默认值时,value 名称也可以不写
对没有 default 默认值的属性,在使用注解时,没有提供对应数据,会直接报错
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyAnnotation {
int value;
}
@MyAnnotation("Java")
public class Test{
}
5. 模拟 Junit 框架
自定义注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyTest1 {
}
在 Annotation1 类中定义若干方法,部分方法加上 @MyTest1 注解修饰,部分注解不添加。模拟 Junit,执行添加 @MyTest1 的方法
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Annotation1 {
@MyTest1
public void test1() {
System.out.println("test1 running");
}
public void test2() {
System.out.println("test2 running");
}
public void test3() {
System.out.println("test3 running");
}
@MyTest1
public void test4() {
System.out.println("test4 running");
}
public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {
Annotation1 a = new Annotation1();
Class cls = Annotation1.class;
Method[] declaredMethods = cls.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
if (declaredMethod.isAnnotationPresent(MyTest1.class)) {
declaredMethod.invoke(a);
}
}
}
}
执行结果如下: