Java注解
注解是对代码的一种增强,可以在代码编译或者程序运行期间获取注解的信息,然后根据这 些信息做各种牛逼的事情。
定义注解
在定义注解前可以先问自己几个问题。
这个注解用来做什么 ?
这个注解需要什么参数 ?
注解需要可以用在哪里 ?
注解会被保留到什么时候 ?
可以直接加在后面
语法
public @interface MyAnnotation {}
注解中的参数
public @interface 注解名称{
[public] 参数类型 参数名称1() [default 参数默认值];
[public] 参数类型 参数名称2() [default 参数默认值];
[public] 参数类型 参数名称n() [default 参数默认值];
}
注解中可以定义多个参数,参数的定义有一下的特点:
访问修饰符必须为 public,默认为 public 。
参数类型只能是基本数据类型、String、Class、枚举类型、注解类型。
参数名称后面的 () 不是定义方法参数的地方,也不能在括号始终定义如何参数
default 代表默认值。
如果没有默认值,在使用注解的时候必须给该参数赋值。
@Target 指定使用范围
使用 @Target 注解定义注解的使用范围
@Target(value = {ElementType.TYPE,ElementType.METHOD}) public @interface MyAnnotation {}
上面这种定义说明可以使用在 类、接口、注解类型、枚举类型以及方法上面。
如果在指定要的注解上面不使用 @Target 表示可以使用在如何地方
全部范围
public enum ElementType {
/* 类、接口、枚举、注解上面 */
TYPE,
/* 字段上 */
FIELD,
/* 方法上 */
METHOD,
/* 方法的参数上 */
PARAMETER,
/* 构造函数上 */
CONSTRUCTOR,
/* 本地变量上 */
LOCAL_VARIABLE,
/* 注解上 */
ANNOTATION_TYPE,
/* 包上 */
PACKAGE,
/* 类型参数上 */
TYPE_PARAMETER,
/* 类型名称上 */
TYPE_USE
}
@Retention 保留策略
我们先来看一下 JAVA 程序的 3 个过程
源码阶段
源码被编译为字节码之后变成 class 文件
字节码被虚拟机加载然后运行
public enum RetentionPolicy {
/* 注解只保留在源码中,编译为字节码之后就丢失了,也就是 class 文件中就不存在了 */
SOURCE,
/* 注解只保留在源码和字节码中,运行阶段会丢失 */
CLASS,
/* 源码、字节码、运行期间都存在 */
RUNTIME
}
@Inherit 注解继承
让子类可以继承父类中被 @Inherited 修饰的注解,注意是继承父类中的,如果接口中的注解也使 用 @Inherited 修饰了,那么接口的实现类是无法继承这个注解的
实验
public class InheritTest {
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@interface A {}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@interface B {}
@A
interface I {};
@B
static class C{}
class TestClass extends C implements I { }
public static void main(String[] args) {
for (Annotation annotation : TestClass.class.getAnnotations()) {
System.out.println(annotation);
}
}
}
输出
@InheritTest$B()
从输出中可以看出类可以继承父类上被 @Inherited 修饰的注解,而不能继承接口上被 @Inherited 修饰 的注解
@Repeatable 重复使用注解
定义
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(As1.class) // 2
@interface A {
};
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface As1 {
A[] value(); // 1
}
容器注解中必须有个 value 类型的参数,参数类型为子注解类型的数组。
在注解上加上 @Repeatable 注解,@Repeatable 中 value 的值为容器注解。**注意这里容器注解的 Repeatable 一定要大于或等于原注解**
@AliasFor 注解进行增强
实验
public class AliasForTest {
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface A {
String value() default "a";
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@A
@interface B {
String value() default "b";
@AliasFor(annotation = A.class, value = "value")
String aValue();
}
@B(value = "xiaou", aValue = "123")
class C {}
@Test
public void test1() {
System.out.println(AnnotatedElementUtils.getMergedAnnotation(C.class, B.class));
System.out.println(AnnotatedElementUtils.getMergedAnnotation(C.class, A.class));
}
}
输出结果
@org.test.AliasForTest.B(aValue="123", value="xiaou")
@org.test.AliasForTest.A(value="123")
从结果看出来 B 注解的 aValue = A 注解的 value 值为什么出现了这种情况关键在于这行代码。
// 指定 B 注解中 aValue 参数作为 A 中 value 参数的别名
@AliasFor(annotation = A.class, value = "value")
String aValue();
@AliasFor 注解的 annotation 参数指定的注解需要加载当前注解上面。