必知必會之Java註解

必知必會之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(注釋的方法)

註解版本: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;
  }
}

@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);
    }
  }
}

@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 抑制沒被使用過的程式碼的警告

——————————————————————————————————————————————

原創:西狩

編寫日期 / 修訂日期:2021-02-24 / 2021-02-24

版權聲明:本文為部落客原創文章,遵循 CC BY-NC-SA-4.0 版權協議,轉載請附上原文出處鏈接和本聲明。