前言

最近温习到注解这一块,于是想写篇文章,把看到的注解知识梳理一下。

注解的分类

注解主要分为三大类:普通注解、元注解、自定义注解,下面来详细梳理一下这三大类注解。

一、普通注解

普通注解,常见的主要有三个 :@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;
}

注解类型java 注解类型包含注解_Java

(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等,所以我们学习注解还是很有必要的。