Java 注解
注解的本质就是给载体打一个Tag,我们查找到这个tag后就可以对我们打Tag的载体进行一些特殊处理
Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。 注解是元数据的一种形式,提供有关 于程序但不属于程序本身的数据。注解对它们注解的代码的操作没有直接影响。
package java.lang.annotation;
public interface Annotation {
boolean equals(Object obj);
int hashCode();
String toString();
Class<? extends Annotation> annotationType();
}
与声明一个"Class"不同的是,注解的声明使用 @interface 关键字。一个注解的声明如下:
public @interface FirstAnnotation {
String name();
int age();
}
元注解
在定义注解时,注解类也能够使用其他的注解声明。对注解类型进行注解的注解类,我们称之为 metaannotation(元注解)。一般的,我们在定义自定义注解时,需要指定的元注解有两个 :
@Retention
注解指定标记注解的存储方式:
@Retention(RetentionPolicy.SOURCE) //只保留在java源码中
@Target({ElementType.FIELD}) //只能作用于属性
public @interface FirstAnnotation {
String name(); //无默认值
int age() default 18; // 有默认值
}
注意:在使用注解时,如果定义的注解中的类型元素无默认值,则必须进行传值。
public class AnnotationTest {
@FirstAnnotation("那路多") //如果只存在value元素需要传值的情况,则可以省略:value =
Student student; //注意只有命名为value才能省略 value = 直接写
@FirstAnnotation(value = "萨斯给",age = 18)
Student student1;
}
在把java文件编译成Class文件后source级别的注解就会消失:
可以看到编译后source级别的注解消失了
可以用于 IDE检查和APT等场景使用
@Retention(SOURCE) //源码级别注解
@Target({ANNOTATION_TYPE})
public @interface IntDef {
int[] value() default {};
boolean flag() default false;
boolean open() default false;
}
@IntDef(value = {1,2}) // 限定只能传 1 和 2
@Retention(RetentionPolicy.SOURCE) //只保留在java源码中
@Target({ElementType.PARAMETER}) //只能作用于方法参数之上
public @interface IntDefAnnotationTest {
int value();
}
public class AnnotationTest {
public void test(@IntDefAnnotationTest(value = 1) int a){
System.out.println(a);
}
}
如果传1或者2编译器可以通过

IDE报警了但是还是能编译通过和运行
可以修改此类语法检查级别:
可以看到AS没有再报错,本身IDEA/AS 就是由Java开发的,Lint工具实现了对Java语法的检查,借助注解能对被注解的特定语法进行额外检查。
//Java源码
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface Login {
}
@Login
public void jumpA() {
startActivity(new Intent(this, AActivity.class));
}
public void jumpB() {
startActivity(new Intent(this, BActivity.class));
}
//Class字节码
@Login
public void jumpA() {
if (this.isLogin) {
this.startActivity(new Intent(this, LoginActivity.class));
} else {
this.startActivity(new Intent(this, AActivity.class));
}
}
public void jumpB() {
startActivity(new Intent(this, BActivity.class));
}
实现:用反射技术和RUNTIME级别注解,实现ButterKnife功能
@Inherited
作用:如果一个类用上了@Inherited修饰的注解,那么其子类也会继承这个注解
注意是作用在类上的注解切必须是继承关系。
注意:
- 接口用上个@Inherited修饰的注解,其实现类不会继承这个注解
- 父类的方法用了@Inherited修饰的注解,子类也不会继承这个注解
例:
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.FIELD})
public @interface FirstAnnotation {
String value();
int age() default 18;
}
@FirstAnnotation("继承")
public class AnnotationTest {
@FirstAnnotation("那路多")
Student student;
@FirstAnnotation(value = "萨斯给",age = 18)
Student student1;
public void test(@IntDefAnnotationTest(value = 1) int a){
System.out.println(a);
}
}
public class AnnotationTest1 extends AnnotationTest {
}
public class AnnotationMainTest {
public static void main(String[] args) {
System.out.println(Arrays.toString(AnnotationTest1.class.getAnnotations()));
}
}
执行结果:
只有作用在类上的继承过来了。
@Repeatable
@Repeatable
是jdk8中新增的注解。在没有@Repeatable
注解的的注解中,在同一个地方使用相同的注解会报错,有了此元注解注解的注解,就可以在同一个地方使用相同的注解。
官方解释:
The annotation type {@code java.lang.annotation.Repeatable} is used to indicate that the annotation type whose declaration it (meta-)annotates is repeatable. The value of @Repeatable indicates the containing annotation type for the repeatable annotation type.
@Repeatable 注解是用于声明其它类型注解的元注解,来表示这个声明的注解是可重复的。@Repeatable的值是另一个注解,其可以通过这个另一个注解的值来包含这个可重复的注解。
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.FIELD})
@Repeatable(SecondAnnotation.class)
public @interface FirstAnnotation {
String value();
int age() default 18;
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.FIELD})
public @interface SecondAnnotation {
FirstAnnotation[] value();
}
其中,@
FirstAnnotation 注解上的元注解@Repeatable
中的值,使用了@
SecondAnnotation注解,@
SecondAnnotation注解中包含的值类型是一个@
FirstAnnotation注解的数组!
这就解释了官方文档中@Repeatable
中值的使用。
测试:
public class AnnotationTest {
@FirstAnnotation("1")
@FirstAnnotation("2")
@FirstAnnotation("3")
int a;
}
public class AnnotationMainTest {
public static void main(String[] args) {
Class annotationTestClass= AnnotationTest.class;
try {
Field field = annotationTestClass.getDeclaredField("a");
Annotation[] annotations = field.getAnnotations();
System.out.println(annotations.length);
System.out.println(Arrays.toString(annotations));
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
}
执行:
因为int a属性上使用了3个@FirstAnnotation注解,所以猜测打印注解长度为3,然后打印详情,可是结果并不同。
1
[@com.example.fragmentadaptertest.annotation.SecondAnnotation(value=[@com.example.fragmentadaptertest.annotation.FirstAnnotation(age=18, value=1), @com.example.fragmentadaptertest.annotation.FirstAnnotation(age=18, value=2), @com.example.fragmentadaptertest.annotation.FirstAnnotation(age=18, value=3)])]
结果显示,int a属性
上的注解长度为 1 , 且打印信息为@SecondAnnontation
注解,它的值包含了使用的两个注解。因此可知在jdk8中,相同注解只是以集合的方式进行了保存,原理并没有变化。
注意事项
一些约束
@Repeatable 所声明的注解,其元注解@Target的使用范围要比@Repeatable的值声明的注解中的@Target的范围要大或相同,否则编译器错误,显示@Repeatable值所声明的注解的元注解@Target不是@Repeatable声明的注解的@Target的子集
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.FIELD})
@Repeatable(SecondAnnotation.class)
public @interface FirstAnnotation {
String value();
int age() default 18;
}
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE,ElementType.FIELD})
public @interface SecondAnnotation {
FirstAnnotation[] value();
}
简单说就是如果想让一个自定义注解A可以在一个地方使用,就打上@Repeatable标签,标签的值是一个注解B,注解B的
value的类型必须是A的数组类型。
最后使用多个A注解的实质,就是一个B注解类型包裹多个A注解类型的值。
想一想供需关系,A需要B来包裹实现注解数组,我们使用的是A在一个地方多次使用,就知道A在B必须在,A不在B可以在也可以不在。
所以B的存在级别要大于等于A。
RUNTIME > CLASS > SOURCE