前言
最近温习到注解这一块,于是想写篇文章,把看到的注解知识梳理一下。
注解的分类
注解主要分为三大类:普通注解、元注解、自定义注解,下面来详细梳理一下这三大类注解。
一、普通注解
普通注解,常见的主要有三个 :@Override、@Deprecated、@SuppressWarnings
(1)@Override
@Override注解我们可能见到的比较多,主要用于子类对父类方法的重写。
public interface Observer {
void update(String message);
}
public class Boy implements Observer {
@Override
public void update(String message) {
}
}
(2)@Deprecated
@Deprecated主要用于注解过期、废弃的方法或者类等。若某类或某方法加上该注解之后,表示此方法或类不再建议使用,调用时也会出现删除线,但并不代表不能用,只是说,不推荐使用,因为还有更好的方法可以调用。
如我们常见的ViewPager中的setOnPageChangeListener:
@Deprecated
public void setOnPageChangeListener(OnPageChangeListener listener) {
mOnPageChangeListener = listener;
}
(3)@SuppressWarnings
该批注的作用是给编译器一条指令,告诉它对被批注的代码元素内部的某些警告保持静默,可以忽略编译器的警告信息。
二、元注解
元注解说白了就是用来注解其他注解的注解。
Java中总共有5种元注解:@Retention,@Documented,@Target,@Inherited,@Repeatable
(1)@Retention
用来说明注解的存活时间,有三种取值:
- RetentionPolicy.SOURCE:注解只在源码阶段保留,编译器开始编译时它将被丢弃忽视
- RetentionPolicy.CLASS:注解会保留到编译期,但运行时不会把它加载到JVM中
- RetentionPolicy.RUNTIME:注解可以保留到程序运行时,它会被加载到JVM中,所以程序运行过程中可以获取到它们
(2) @Documented
这个注解跟文档相关,它的作用是能够将注解中的元素包含到Javadoc中去,应该被JavaDoc所记录。
(3)@Target
用来说明注解的目标,有以下取值:
- ElementType.PACKAGE:可作用在包上
- ElementType.TYPE:可作用在类、接口、枚举上
- ElementType.ANNOTATION_TYPE:可以作用在注解上
- ElementType.FIELD:可作用在属性上
- ElementType.CONSTRUCTOR:可作用在构造方法上
- ElementType.METHOD:可作用在方法上
- ElementType.PARAMETER:可作用在方法参数上
- ElementType.LOCAL_VARIABLE:可作用在局部变量上,例如方法中定义的变量
- ElementType.TYPE_PARAMETER:可作用在类参数的声明上 Since jdk1.8以后添加的
- ElementType.TYPE_USE:可作用在使用的类型 Since jdk1.8以后添加的
具体可以参考:@Target注解的取值
(4)@Inherited
一个父类被该类注解修饰,那么它的子类如果没有任何注解修饰,就会继承父类的这个注解。
例如:
@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Person {
}
@Person
public class Teacher {
}
public class TeacherA extends Teacher{
}
注解Person被@Inherited修饰,Teacher被Person修饰,TeacherA继承Teacher(TeacherA上又无其他注解),那么TeacherA就会拥有Person这个注解。
(5)@Repeatable
它是java1.8引入的,标记的注解可以多次应用于相同的声明或类型。
例如:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Persons {
Person[] value();
}
@Repeatable(Persons.class)
public @interface Person{
String role() default "";
}
@Person(role="Teacher")
@Person(role="Student")
@Person(role="Doctor")
public class Man {
String name="";
}
(6)注解中的属性:
注解的属性也叫做成员变量,注解只有成员变量,没有方法。注解的成员变量在注解的定义中以“无形参的方法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型。
例如:
@Repeatable(Persons.class)
public @interface Person{
String role() default "";
}
注解Person里面的role属性,role是一个成员变量,只不过书写形式为 String role() default ""; 即所谓的“无形参的方法”形式书写,给role设置了默认值为空字符串 default ""。
三、自定义注解
自定义运行是注解分为两步:声明注解、解析注解。
自定义注解,是以元注解为基础注解自己定义的注解,现在我们来一起来写一个自定义注解的例子:
(1)声明注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Animal {
int value() default 5;
}
上面是自己定义了一个注解Animal,Animal含有一个int类型的属性,该属性的名称为value,默认值为5。该注解主要用来注解成员变量,作用于运行期。
(2)解析注解
public class TestMain {
@Animal(value = 12)
public int age;
public static void main(String[] args) {
TestMain testMain=new TestMain();
Class clazz=testMain.getClass();
try {
Field fieldAge=clazz.getField("age");
Animal animal=fieldAge.getAnnotation(Animal.class);
System.out.println(animal.value()+"===岁===");
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
}
上面通过类的反射机制解析注解,先拿到TestMain类里面的age成员变量,再通过成员变量调用getAnnotation方法拿到该成员变量的注解。
打印结果:
12===岁===
总结
通过以上的回顾,我们对注解有了一个基本的认识,目前的好多开源框架都使用了注解。例如ButterKnife、ARouter、Dagger2等,所以我们学习注解还是很有必要的。