Java基礎知識整理之註解

Wayfreem發表於2019-01-19

什麼是註解?

Annotation 是 Java5 之後開始引入的新特性,中文為註解。註解提供了一種安全的類似註釋的機制,用來將任何的資訊或後設資料(metadata)與程式元素(類、方法、成員變數等)進行關聯。為程式的元素(類、方法、成員變數)加上更直觀更明瞭的說明,這些說明與程式的業務邏輯無關,並且提供給指定的工具或框架使用。

Java註解是附加在程式碼中的一些元資訊,用於一些工具在編譯、執行時進行解析和使用,起到說明、配置的功能。註解不會也不能影響程式碼的實際邏輯,僅僅起到輔助性的作用。包含在 java.lang.annotation 包中。

總結

==註解(Annotation)相當於一種標記,在程式中加入註解就等於為程式打上某種標記==,沒有加,則等於沒有任何標記,以後,javac編譯器、開發工具和其他程式可以通過反射來了解你的類及各種元素上有無何種標記,看你的程式有什麼標記,就去幹相應的事,==標記可以加在包、類,屬性、方法,方法的引數以及區域性變數上。==

一個註解準確意義上來說,只不過是一種特殊的註釋而已,如果沒有解析它的程式碼,它可能連註釋都不如。

元註解(meta-annotation)

元註解的作用就是負責註解其他註解。 Java5.0定義了4個標準的meta-annotation型別,它們被用來提供對其它 annotation型別作說明。他們分別是 @Target、@Retetion、@Documented、@Inherited

@Target 註解

@Target 的作用:就是說明註解可以用在什麼地方。

@Target說明了Annotation所修飾的物件範圍:Annotation可被用於 packages、types(類、介面、列舉、Annotation型別)、型別成員(方法、構造方法、成員變數、列舉值)、方法引數和本地變數(如迴圈變數、catch引數)。在Annotation型別的宣告中使用了target可更加明晰其修飾的目標。

ElementType 的值有以下幾種:

1.CONSTRUCTOR: 用於描述構造器
2.FIELD: 用於描述域
3.LOCAL_VARIABLE: 用於描述區域性變數
4.METHOD: 用於描述方法
5.PACKAGE: 用於描述包
6.PARAMETER: 用於描述引數
7.TYPE: 用於描述類、介面(包括註解型別) 或enum宣告

程式碼示例

// 下面的註解可以作用在欄位上面
@Target(ElementType.FIELD)
public @interface MyAnnotation {
}

@Retention 註解

@Retention 註解決定 註解的生命週期。我們的java程式執行的順序: Java原始檔(x.java檔案) —> .class(編譯為 class 檔案) —> 記憶體中的位元組碼檔案(執行時)。

對應於 @Retention 註解的三個值:

  • RetentionPolicy.SOURCE:在原始檔中有效(即原始檔保留)。在編譯階段丟棄,這些註解在編譯結束之後就沒有任何意義了。
  • RetentionPolicy.CLASS:在class檔案中有效(即class保留)。在類載入的時候丟棄。註解預設是使用這種方式。
  • RetentionPolicy.RUNTIME:在執行時有效(即執行時保留)。始終不丟棄,執行期也保留該註解。因此可以使用反射機制讀取該註解的資訊。我們自定義的註解通常使用這種方式。

@Documented 註解

一個簡單的Annotations標記註解,表示是否將註解資訊新增在java文件中。

@Inherited 註解

 @Inherited 元註解是一個標記註解,@Inherited闡述了某個被標註的型別是被繼承的。如果一個使用了@Inherited修飾的annotation型別被用於一個class,則這個annotation將被用於該class的子類。

  注意:@Inherited annotation型別是被標註過的class的子類所繼承。類並不從它所實現的介面繼承annotation,方法並不從它所過載的方法繼承annotation。

  當@Inherited annotation型別標註的annotation的Retention是RetentionPolicy.RUNTIME,則反射API增強了這種繼承性。如果我們使用java.lang.reflect去查詢一個@Inherited annotation型別的annotation時,反射程式碼檢查將展開工作:檢查class和其父類,直到發現指定的annotation型別被發現,或者到達類繼承結構的頂層。
  

為註解增加屬性

value 屬性

value 屬性是一個特殊的屬性。當註解中使用的屬性名為value時,對其賦值時可以不指定屬性名稱而直接寫上屬性值介面;除了value以外的變數名都需要使用 name = value 的方式賦值。

示例

@SuppressWarnings("deprecation")    // 在使用的地方

給屬性設定初始值

即在定義註解的時候,就給某些欄位附上初始值。

示例

String value() default "config";    // 在自定義註解的地方

陣列型別的屬性

int[] intArray() default {1,2,3};    // 在自定義註解的地方

CustomAnnotation(ingArray = {4,5,6})    // 在使用註解的地方

CustomAnnotation(ingArray = 7)          // 如果陣列屬性中只有一個元素時,屬性值部分可以省略大括號。

java 8 對註解的增強

@Repeatable註解,關於型別註解的宣告,函式式介面註解@FunctionalInterface(與Lambdas結合使用)。@Repeatable:說明該註解標識的註解可以多次使用到同一個元素的宣告上。

註解的重複機制

@Repeatable(Annotations.class) 
public @interface MyAnnotation {  
     String role();  
}  
 
public @interface Annotations {  
    MyAnnotation[] value();  
}  
 
public class RepeatAnnotationUseOldVersion {  
    @MyAnnotation(role="Admin")  
    @MyAnnotation(role="Manager")
    public void doSomeThing(){  
    }  
} 

自定義註解

註解的宣告方式

@Retention(value=RUNTIME)    // value的值是可以改變的, RUNTIME 只是作為demo
@Target(value=TYPE)          // value的值是可以改變的, TYPE 只是作為demo
public @interface CustomAnnotationClass{}

對於註解的宣告,由於 java 沒有引入新的關鍵字,而是使用 @interface 關鍵字,來宣告一個註解。我們需要為自定義的註解增加強制性的屬性,保留策略和作用的目標。這樣子宣告的直接才有意義。

例項說明

CustomAnnotation.java

import java.lang.annotation.*;

import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;


@Retention(value=RUNTIME)     // RUNTIME 告訴程式這個註解不會被丟棄,可以通過反射來獲取
@Target(value=METHOD)   // 通過 METHOD 屬性,可以讓註解在方法上面
public @interface CustomAnnotation {

    /* value:是一個特殊的屬性,若在設定值時只有一個value屬性需要設定或者其他屬性都採用預設值時 ,
    那麼value=可以省略,直接寫所設定的值即可。 */
    String value(); // 註解只要一個變數時,變數名必須為 value

    /* 我們所有的方法宣告都不能有引數和throw 子句 */
    public String name() default "Wayfreem";

    public String description();
}

獲取註解

我們獲取註解一般通過反射的方式來獲取。java 反射API提供了許多方法,在執行時從類、方法獲取其他元素上面獲取註解資訊。下面列出常用的方法:

  • getAnnotations(): 返回該元素的所有註解,包括沒有顯式定義該元素上的註解。
  • isAnnotationPresent(annotation): 檢查傳入的註解是否存在於當前元素。
  • getAnnotation(class): 按照傳入的引數獲取指定型別的註解。返回null說明當前元素不帶有此註解。返回值為Annotation[]。
  • getMethod(String name, Class<?>… parameterTypes):獲取某個方法
public class TestAnnotation {

    @CustomAnnotation(value = "test1", name = "測試1", description = "測試方法1")
    private void method1(){}

    @CustomAnnotation(value = "test2", name = "測試2", description = "測試方法2")
    public void method2(){}

    @CustomAnnotation(value = "test3", name = "測試3", description = "測試方法3")
    public void method3(){}

    public void menthod4(){}

    public static void main(String[] args) {

        // 通過反射來回去所有宣告的方法(包含還有加上了註解了的和未註解的 )
        Method[] methods = TestAnnotation.class.getDeclaredMethods();

        for (Method method: methods){

            // 傳入指定的註解,判斷是否為傳入的註解型別
            boolean flag = method.isAnnotationPresent(CustomAnnotation.class);
            if(!flag){
                continue;
            }

            // 獲取到指定的註解型別,並且將其例項化
            CustomAnnotation customAnnotation = method.getAnnotation(CustomAnnotation.class);
            // 獲取到自定義的註解中的值
            System.out.println("value : "+customAnnotation.value() + 
                    ", name:" + customAnnotation.name() + 
                    ", description:"+ customAnnotation.description());
        }
    }
}

控制檯輸出

value : test1, name:測試1, description:測試方法1
value : test2, name:測試2, description:測試方法2
value : test3, name:測試3, description:測試方法3

相關文章