[Java复习] Java注解

156 阅读6分钟

image.png

1. 注解简介

注解(Annotation)也称为元数据(MetaData)。可以用于修饰包、类、方法、属性、构造器、局部变量等数据元素。可以再原有逻辑不变的情况下,在源文件中嵌入一些补充信息。

注解可以在编译类加载运行时被读取。

2. 基本注解

2.1 @Override

被@Override修饰的方法必须要重写父类的方法。起到说明作用

查看该注解的源码可知:

 @Target(ElementType.METHOD)
 @Retention(RetentionPolicy.SOURCE)
 public @interface Override {
 }
  • 该注解只能用于修饰方法
  • 该注解只能保留在源代码中,编译器将会直接丢弃该注解。

通过一个案例来加加深理解:

 public class MyOverride {
     public static void main(String[] args) {
         Dog dog = new Dog();
         dog.calls();
     }
 }
 ​
 class Animal{
     public void calls(){
         System.out.println("Animal发出了叫声");
     }
 }
 ​
 class Dog extends Animal{
     @Override // 使用Override注解说明该方法重写了父类的方法
     public void calls(){
         System.out.println("汪汪~");
     }
 }

运行程序,可以发现在子类Dog中是否添加@Override都可以正常运行,这说明是否添加@Override注解,子类会重写父类的方法。那么添加@Override注解的作用是什么?再看如下案例:

 class Animal{
 ​
     public void calls(){
         System.out.println("Animal发出了叫声");
     }
 }
 ​
 class Dog extends Animal{
     @Override  // 使用@Override修饰了一个在父类中不存在的方法。
     public void run(){
         System.out.println("汪汪~");
     }
 }

运行程序,发现抛出异常。因此,通过以上案例,我们可以总结出@Overide的作用之一:在程序运行时,告诉编译器要检测该方法是否在父类中存在,起到语法校验的作用。防止在子类中想重写父类方法但却写错的这些低级错误。

📓 小结

  1. 用于修饰方法
  2. 只能保留在源代码,无法通过反射获取该注解信息。
  3. 说明作用
  4. 语法校验作用

2.2 @Deprecated

被注解@Deprecated修饰的元素表示已经过时,在不久的将来会去掉。

 public class MyDeprecated {
     public static void main(String[] args) {
         Cat.run();
 ​
     }
 }
 ​
 @Deprecated // 使用@Deprecated注解修饰该类,表示该类及其子元素已经弃用
 class Cat{
     public static void run(){
         System.out.println("run run run ~");
     }
 }

image-20220323162041150.png

可以发现当在其他地方调用被@Deprecated注解修饰的类时,该类会被中划线划掉。即不推荐使用,但能够使用。 起到提示尽快更新过渡的作用

查看该基本注解的源码可知:

 @Documented
 @Retention(RetentionPolicy.RUNTIME)
 @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
 public @interface Deprecated {
 }
  • 该注解可以用于修饰:构造器、成员变量、局部变量、方法、包、参数、类等所有元素
  • 该注解能够存在于字节码文件中,并且JVM可以获得注解信息,也可以通过反射获取注解信息

📓小结

  1. 可以用于修饰所有元素。
  2. 表示已经弃用,起到提示尽快更新过渡的作用。
  3. JVM可以获得注解信息,也可以通过反射获取注解信息。

2.3 @SuppressWarnings

被该注解修饰的元素及其子元素将会被取消显示指定的编译器警告。使用参数value来指定要被抑制的类型,常用的值如下:

关键字用途
all抑制所有警告
boxing抑制装箱、拆箱操作时候的警告
cast抑制映射相关的警告
dep-ann抑制启用注释的警告
deprecation抑制过期方法警告
fallthrough抑制在 switch 中缺失 breaks 的警告
finally抑制 finally 模块没有返回的警告
hiding抑制相对于隐藏变量的局部变量的警告
incomplete-switch忽略不完整的 switch 语句
nls忽略非 nls 格式的字符
null忽略对 null 的操作
rawtypes使用 generics 时忽略没有指定相应的类型
restriction抑制禁止使用劝阻或禁止引用的警告
serial忽略在 serializable 类中没有声明 serialVersionUID 变量
static-access抑制不正确的静态访问方式警告
synthetic-access抑制子类没有按最优方法访问内部类的警告
unchecked抑制没有进行类型检查操作的警告
unqualified-field-access抑制没有权限访问的域的警告
unused抑制没被使用过的代码的警告
 @SuppressWarnings(value = "{all}") // 去掉该类中的所有警告
 public class MySupperWarnings {
     public static void main(String[] args) {
 ​
         List<String> myList = new ArrayList();
     }
 }

分析源代码可知:

 @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
 @Retention(RetentionPolicy.SOURCE)
 public @interface SuppressWarnings {
     String[] value();
 }
  • 该注解可以用于修饰除注解外的其他元素。
  • 该注解只能保留在源代码中。

2.4 @FunctionalInterface

该注解用于指定某个接口必须时函数式接口,否则编译器则会抛出异常。

函数式接口:接口中只有一个抽象方法,但可以包含多个默认方法(default)和多个静态方法(static),这样的接口就是函数式接口。函数式接口是为了lambda表达式准备的。

 @FunctionalInterface
 interface ins{
 ​
     
     default void bar1(){
         System.out.println("这是第一个默认方法");
     }
     default void bar2(){
         System.out.println("这是第一个默认方法");
     }
     static void bb(){
         System.out.println("这是一个static方法");
     }
     // 只有一个抽象方法
     void test();
     // void test2();
     
 }

当接口中含有多个抽象方法时:

 Error:(14, 1) java: 意外的 @FunctionalInterface 注释
   annotation.ins 不是函数接口
     在 接口 annotation.ins 中找到多个非覆盖抽象方法

查看源码可知:

 @Documented
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.TYPE)
 public @interface FunctionalInterface {}
  • 该注解信息可以通过反射获得。
  • 该注解只能用于修饰接口。

3. 元注解

3.1 @Retention

@Retention 用于描述注解的生命周期,也就是该注解被保留的时间长短。通过value参数来设置生命周期:

序号value作用
1RetentionPolicy.SOURCE在源文件中有效(即源文件保留)
2RetentionPolicy.SOURCE在 class 文件中有效(即 class 保留)
3RetentionPolicy.RUNTIME在运行时有效(即运行时保留)

生命周期大小排序为 SOURCE < CLASS < RUNTIME前者能使用的地方后者一定也能使用。如果需要在运行时去动态获取注解信息,那只能用 RUNTIME 注解;如果要在编译时进行一些预处理操作,就用 CLASS注解;如果只是做一些检查性的操作,比如 @Override 和 @SuppressWarnings,则可选用 SOURCE 注解。

3.2 @Target

@Target 注解用来指定一个注解的使用范围,即被 @Target 修饰的注解可以用在什么地方。通过value参数来设置使用范围:

序号value作用
1ElementType.ANNOTATION_TYPE用于注解
2ElementType.CONSTRUCTOR用于构造方法
3ElementType.FIELD用于成员变量(包括枚举常量)
4ElementType.LOCAL_VARIABLE用于局部变量
5ElementType.METHOD用于方法
6ElementType.PACKAGE用于包
7ElementType.PARAMETER用于类型参数(JDK 1.8新增)
8ElementType.TYPE用于类、接口(包括注解类型)或 enum 声明

3.3 @Documented

用 @Documented 注解修饰的注解类会被 JavaDoc 工具提取成文档。默认情况下,JavaDoc 是不包括注解的。

3.4 Inherited

@Inherited 是一个标记注解,用来指定该注解可以被继承。使用 @Inherited 注解的 Class 类,表示这个注解会被子类自动继承。