• 必知必会面试题之 Java 注解



    title: 必知必会面试题之 Java 注解
    date: 2021-02-24
    updated: 2021-03-10
    categories:

    • Java
    • 必知必会
      tags:
    • Java
    • 面试
    • 必知必会

    不定期更新中……



    元注解

    @Documented

    仅用在注解类上,表示在使用 javadoc 工具生成帮助文档时,使用该注解的类会在 API 文档中展示该注解。

    注解版本:1.5+

    场景举例:

    • 创建一个注解类 TestAnnotation

      package com.xs.annotation;
      
      import java.lang.annotation.Documented;
      import java.lang.annotation.ElementType;
      import java.lang.annotation.Target;
      
      @Documented
      @Target(ElementType.TYPE)
      public @interface TestAnnotation {
      
        public String value() default "javadoc";
      }
      
    • 创建一个使用该注解的类 DocumentTest

      package com.xs.annotation;
      
      @TestAnnotation
      public class DocumentedTest {
      }
      
    • 生成 javadoc(使用 javadoc 命令 或 使用 eclipse、IDEA 等 IDE 提供的 javadoc 生成工具)

    • 打开生成的 API 文档(/doc/index.html),如下:

      图1

    • 若删除注解类 TestAnnotation 中的 @Documented 注解,再次生成 javadoc,注解消失

      图2

    @Inherited

    仅用在注解类上,被它修饰的注解具有继承性。也就是说,在一个类上使用被 @Inherited 标注的注解,其子类也会继承这些被 @Inherited 标注的注解。

    注解版本:1.5+

    场景举例:

    • 创建一个带有 @Inherited 的注解类 InheritedAnnotation

      package com.xs.annotation;
      
      import java.lang.annotation.ElementType;
      import java.lang.annotation.Inherited;
      import java.lang.annotation.Target;
      
      @Inherited
      @Target(ElementType.TYPE)
      @Retention(RetentionPolicy.RUNTIME)
      public @interface InheritedAnnotation {
      
        public String value() default "Inherited";
      }
      
    • 创建一个使用该注解的类 InheritedParent

      package com.xs.annotation;
      
      import com.xs.annotation.TestInheritedAnnotation;
      
      @InheritedAnnotation(value="parent")
      public class InheritedParent {
        
      }
      
    • 为 InheritedParent 类创建子类 InheritedChild

      package com.xs.annotation;
      
      import com.xs.annotation.InheritedParent;
      
      public class InheritedChild extends InheritedParent {
        
        public static void main(String[] args) {
      		Class<InheritedChild> child = InheritedChild.class;
      		InheritedAnnotation annotation = child.getAnnotation(InheritedAnnotation.class);
      		System.out.println(annotation.value());
      	}
      }
      
    • 运行 main 方法,输出如下。

      parent
      

    @Retention

    仅用在注解类上,用来描述注解保留的时间范围。一共有三种策略,定义在 RetentionPolicy 枚举中,分别是:源文件保留、编译期保留、运行期保留,默认值为编译期保留。运行期保留可以用来获取注解信息。

    注解版本:1.5+

    场景举例:

    • 分别实现三种策略

      package com.xs.annotation.meta;
      
      import java.lang.annotation.Retention;
      import java.lang.annotation.RetentionPolicy;
      
      @Retention(RetentionPolicy.SOURCE)
      public @interface SourcePolicy {
       
      }
      
      @Retention(RetentionPolicy.CLASS)
      public @interface ClassPolicy {
       
      }
      
      @Retention(RetentionPolicy.RUNTIME)
      public @interface RuntimePolicy {
       
      }
      
    • 创建一个类,并使用以上三种注解去注解三个方法

      package com.xs.annotation.meta;
      
      public class RetentionTest {
       
      	@SourcePolicy
      	public void sourcePolicy() {
      	}
       
      	@ClassPolicy
      	public void classPolicy() {
      	}
       
      	@RuntimePolicy
      	public void runtimePolicy() {
      	}
      }
      
    • 生成字节码文件

      javap -verbose RetentionClass
      ### 以下为输出结果 ###
      警告: 二进制文件RetentionClass包含com.xs.annotation.meta.RetentionClass
      Classfile /Users/lihuiming/git/xs/xs-technology/xs-learning-annotation/target/classes/com/xs/annotation/meta/RetentionClass.class
        Last modified 2021-2-20; size 709 bytes
        MD5 checksum 88516f888e7e83d00ffe708e32d852a0
        Compiled from "RetentionClass.java"
      public class com.xs.annotation.meta.RetentionClass
        minor version: 0
        major version: 52
        flags: ACC_PUBLIC, ACC_SUPER
      Constant pool:
         #1 = Methodref          #3.#20         // java/lang/Object."<init>":()V
         #2 = Class              #21            // com/xs/annotation/meta/RetentionClass
         #3 = Class              #22            // java/lang/Object
         #4 = Utf8               <init>
         #5 = Utf8               ()V
         #6 = Utf8               Code
         #7 = Utf8               LineNumberTable
         #8 = Utf8               LocalVariableTable
         #9 = Utf8               this
        #10 = Utf8               Lcom/xs/annotation/meta/RetentionClass;
        #11 = Utf8               sourcePolicy
        #12 = Utf8               classPolicy
        #13 = Utf8               RuntimeInvisibleAnnotations
        #14 = Utf8               Lcom/xs/annotation/meta/RetentionClassPolicy;
        #15 = Utf8               runtimePolicy
        #16 = Utf8               RuntimeVisibleAnnotations
        #17 = Utf8               Lcom/xs/annotation/meta/RetentionRuntimePolicy;
        #18 = Utf8               SourceFile
        #19 = Utf8               RetentionClass.java
        #20 = NameAndType        #4:#5          // "<init>":()V
        #21 = Utf8               com/xs/annotation/meta/RetentionClass
        #22 = Utf8               java/lang/Object
      {
        public com.xs.annotation.meta.RetentionClass();
          descriptor: ()V
          flags: ACC_PUBLIC
          Code:
            stack=1, locals=1, args_size=1
               0: aload_0
               1: invokespecial #1                  // Method java/lang/Object."<init>":()V
               4: return
            LineNumberTable:
              line 8: 0
            LocalVariableTable:
              Start  Length  Slot  Name   Signature
                  0       5     0  this   Lcom/xs/annotation/meta/RetentionClass;
      
        public void sourcePolicy();
          descriptor: ()V
          flags: ACC_PUBLIC
          Code:
            stack=0, locals=1, args_size=1
               0: return
            LineNumberTable:
              line 12: 0
            LocalVariableTable:
              Start  Length  Slot  Name   Signature
                  0       1     0  this   Lcom/xs/annotation/meta/RetentionClass;
      
        public void classPolicy();
          descriptor: ()V
          flags: ACC_PUBLIC
          Code:
            stack=0, locals=1, args_size=1
               0: return
            LineNumberTable:
              line 16: 0
            LocalVariableTable:
              Start  Length  Slot  Name   Signature
                  0       1     0  this   Lcom/xs/annotation/meta/RetentionClass;
          RuntimeInvisibleAnnotations:
            0: #14()
      
        public void runtimePolicy();
          descriptor: ()V
          flags: ACC_PUBLIC
          Code:
            stack=0, locals=1, args_size=1
               0: return
            LineNumberTable:
              line 20: 0
            LocalVariableTable:
              Start  Length  Slot  Name   Signature
                  0       1     0  this   Lcom/xs/annotation/meta/RetentionClass;
          RuntimeVisibleAnnotations:
            0: #17()
      }
      SourceFile: "RetentionClass.java"
      
    • 从字节码可以看出,编译器没有记录下 sourcePolicy() 方法的注解信息,分别使用了 RuntimeInvisibleAnnotations 和 RuntimeVisibleAnnotations 属性去记录了classPolicy()方法 和 runtimePolicy()方法 的注解信息。

    @Target

    仅用在注解类上,用来标注注解的元素类型(ElementType),即设置注解的适用范围。如果没有标注 @Target,那么该注解可以作用在任何地方。

    注解版本:1.5+

    场景举例:

    package javax.validation;
    
    import static java.lang.annotation.ElementType.CONSTRUCTOR;
    import static java.lang.annotation.ElementType.FIELD;
    import static java.lang.annotation.ElementType.METHOD;
    import static java.lang.annotation.ElementType.PARAMETER;
    import static java.lang.annotation.ElementType.TYPE_USE;
    import static java.lang.annotation.RetentionPolicy.RUNTIME;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.Retention;
    import java.lang.annotation.Target;
    
    @Target({ METHOD, FIELD, CONSTRUCTOR, PARAMETER, TYPE_USE })
    @Retention(RUNTIME)
    @Documented
    public @interface Valid {
    }
    

    常用注解

    @Deprecated

    标注在类、接口、成员方法和成员变量上,表示某个元素(类、方法等)已过时。当其他程序使用已过时的元素时,编译器将会给出警告。

    注解版本:1.5+

    场景举例:

    @Deprecated
    public class DeprecatedClass {
      
      @Deprecated
      public int value;
      
      @Deprecated
      public void m1() {
        System.out.println("Deprecated");
      }
    }
    

    @FunctionalInterface

    Java 8 版本后,Java引入函数式编程。@FunctionalInterface 就是 Java 8 版本新增的注解,用来标注函数式接口。

    什么是函数式接口?如果接口中只有一个抽象方法(可以包含多个默认方法或多个 static 方法),那么该接口就是函数式接口。函数式接口是为 Java 8 的 Lambda 表达式准备的。

    @FunctionalInterface 本身只起到标注作用,用来告诉编译器检查这个接口是否符合函数式接口的规范(只能包含一个抽象方法)。

    注解版本:1.8+

    场景举例:

    package org.springframework.boot;
    
    @FunctionalInterface
    public interface ApplicationRunner {
      void run(ApplicationArguments args) throws Exception;
    }
    

    @Override

    标注在方法上,用来标注方法为重写方法。

    注解版本:1.5+

    场景举例:

    public class Parent {
      
      public void m1() {
        System.out.println("Parent");
      }
    }
    
    public class Child extends Parent {
      
      @Override
      public void m1() {
        System.out.println("Child");
      }
    }
    

    @PostConstruct

    @PostConstruct 该注解被用来修饰一个非静态的 void() 方法。被 @PostConstruct 修饰的方法会在服务器加载 Servlet 的时候运行,并且只会被服务器执行一次。PostConstruct 在构造函数之后执行,init() 方法之前执行。

    该注解的方法在 Spring 整个 Bean 初始化中的执行顺序:Constructor(构造方法) -> @Autowired(依赖注入) -> @PostConstruct(注释的方法)

    注解版本:Common Annotations 1.0+

    场景举例:

    public class Test {
      private static Test test = new Test();
      
      @Autowired
      private OtherService otherService;
      
      @PostConstruct
      public void init() {
        System.out.println("init");
        test.otherService = otherService;
      }
    }
    

    @PreDestroy

    注解版本:Common Annotations 1.0+

    场景举例:

    @Qualifier

    注解版本:

    场景举例:

    @SafeVarargs

    标注在 static 或 final 方法上,表示被该注解修饰的方法取消显示指定的编译器警告。

    注解版本:1.7+

    场景举例:

    public class SafeVarargsClass {
      
      public static void main(String[] args) {
        // 没有 @SafeVarargs 会有编译警告
        display("10", 20, 30);
      }
      
      @SafeVarargs
      public static <T> void m1(T... array) {
        for (T arg : array) {
          System.out.println(arg.getClass().getName() + ":" + arg);
        }
      }
    }
    

    @Scope

    注解版本:

    场景举例:

    @Singleton

    注解版本:

    场景举例:

    @SuppressWarnings

    标注在类或方法上,表示被该注解修饰的程序元素(以及该程序元素中的所有子元素)取消显示指定的编译器警告,且会一直作用于该程序元素的所有子元素。

    注解的使用有以下三种:

    • 抑制单类型的警告:@SuppressWarnings("unchecked")

    • 抑制多类型的警告:@SuppressWarnings("unchecked","rawtypes")

    • 抑制所有类型的警告:@SuppressWarnings("unchecked")

    全部关键字请参考附录:[@SuppressWarnings 关键字](#@SuppressWarnings 关键字)

    注解版本:1.5+

    场景举例:

    @SuppressWarnings("unchecked","rawtypes")
    public class SuppressWarningsClass {
      
      @SuppressWarnings("unchecked")
      public void m1() {
        System.out.println("unchecked");
      }
    }
    

    附录

    ElementType

    The constants of this enumerated type provide a simple classification of the syntactic locations where annotations may appear in a Java program. These constants are used in {@link Target java.lang.annotation.Target} meta-annotations to specify where it is legal to write annotations of a given type.

    版本:1.5+

    取值 释义
    TYPE 用于描述类、接口(包括注解类型)、枚举
    FIELD 用于描述字段(包括枚举、常量)
    METHOD 用于描述方法
    PARAMETER 用于描述形参
    CONSTRUCTOR 用于描述构造器
    LOCAL_VARIABLE 用于描述局部变量
    ANNOTATION_TYPE 用于描述注解类型
    PACKAGE 用于描述包
    TYPE_PARAMETER JAVA 8 新增,作用在泛型上
    TYPE_USE JAVA 8 新增,用于描述任何类型

    RetentionPolicy

    Annotation retention policy. The constants of this enumerated type describe the various policies for retaining annotations. They are used in conjunction with the {@link Retention} meta-annotation type to specify how long annotations are to be retained.

    版本:1.5+

    取值 释义
    SOURCE 注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
    CLASS 注解被保留到 class文件,但jvm加载class文件时候被遗弃,默认为该级别;
    RUNTIME 注解不仅被保存到 class文件中,jvm加载class文件之后,仍然存在;

    @SuppressWarnings 关键字

    关键字 用途
    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 抑制没被使用过的代码的警告
  • 相关阅读:
    Visual Studio DSL 入门 11为状态机设计器添加规则
    不平静的2009,期待更不平静的2010
    ASP.NET MVC 2 正式发布
    [翻译] DSL和模型驱动开发的最佳实践(2/4)
    Visual Studio DSL 入门 9创建状态机的图形符号
    Visual Studio DSL 入门 6DSL的图形表示1
    智诚B2C1.31正式发
    一个程序员的创业尝试
    Visual Studio DSL 入门 13结合T4生成代码
    Visual Studio DSL 入门 10完善状态机案例
  • 原文地址:https://www.cnblogs.com/lihuimingxs/p/must-know-java-annotation.html
Copyright © 2020-2023  润新知