註解使用入門(一)

  • 2019 年 10 月 10 日
  • 筆記

本篇博客要講解主要分為以下幾個問題

  1. 註解的相關知識點
  2. 基於運行時的註解的例子解析說明

至於關於編譯時的註解,待下篇博客的時候會結合例子講解一下,目前我也正在學習當中

註解的相關知識點

提到註解,大多數人應該都不默認,在我們程序中見到的@Override,@Deprected,@SupressWarnings等等,這些都是註解,只不過是系統自己封裝好的,而我們平時比較少去深入理解是怎樣實現的?

1)什麼是註解(Annotation):

Annotation(註解)就是Java提供了一種元程序中的元素關聯任何信息和着任何元數據(metadata)的途徑和方法。Annotion(註解)是一個接口,程序可以通過反射來獲取指定程序元素的Annotion對象,然後通過Annotion對象來獲取註解裏面的元數據。

2)註解的分類:

根據註解參數的個數,我們可以將註解分為三類:

  1. 標記註解:一個沒有成員定義的Annotation類型被稱為標記註解。這種Annotation類型僅使用自身的存在與否來為我們提供信息。比如後面的系統註解@Override;
  2. 單值註解
  3. 完整註解 

根據註解使用方法和用途,我們可以將Annotation分為三類:

  1. JDK內置系統註解
  2. 元註解
  3. 自定義註解

3)元註解:

元註解的作用就是負責註解其他註解。Java5.0定義了4個標準的meta-annotation類型,它們被用來提供對其它annotation類型作說明。Java5.0定義的元註解:

  1. @Target,
  2. @Retention,
  3. @Documented,
  4. @Inherited

4)元註解解析說明

  • @Documented 是否會保存到 Javadoc 文檔中

  • @Retention 保留時間,可選值

    SOURCE(源碼時),CLASS(編譯時),RUNTIME(運行時),默認為 CLASS,SOURCE 大都為 Mark Annotation,這類 Annotation 大都用來校驗,比如 Override, SuppressWarnings

  • @Target 可以用來修飾哪些程序元素,如 TYPE, METHOD, CONSTRUCTOR, FIELD, PARAMETER 等,未標註則表示可修飾所有

    ANONOTATION_TYPE(註解類型聲明),
    PACKAGE(包)
    TYPE (類,包括enum及接口,註解類型)
    METHOD (方法)
    CONSTRUCTOR (構造方法)
    FIFLD (成員變量)
    PARAMATER (參數)
    LOCAL_VARIABLE (局部 變量)

  • @Inherited 是否可以被繼承,默認為 false

5)什麼是metadata(元數據):

元數據從metadata一詞譯來,就是「關於數據的數據」的意思。
 
 元數據的功能作用有很多,比如:你可能用過Javadoc的注釋自動生成文檔。這就是元數據功能的一種。總的來說,元數據可以用來創建文檔,跟蹤代碼的依賴性,執行編譯時格式檢查,代替已有的配置文件。如果要對於元數據的作用進行分類,目前還沒有明確的定義,不過我們可以根據它所起的作用,大致可分為三類:

  1. 編寫文檔:通過代碼里標識的元數據生成文檔
  2. 代碼分析:通過代碼里標識的元數據對代碼進行分析
  3. 編譯檢查:通過代碼里標識的元數據讓編譯器能實現基本的編譯檢查

其他知識點暫時不介紹,個人覺得一下子介紹太多概念很難消化。下面讓我們一起結合例子來使用它。


下面我們來看一下我們要怎樣寫一個基於編譯時的自定義註解的例子

自定義註解大概可分為以下三個步驟:

  1. 自定義一個註解
  2. 在其他類使用我們的註解
  3. 在運行的時候解析我們的註解

解析運行流程圖

1)首先我們我們來看一下我們是怎樣自定義一個註解的

這些類型和它們所支持的類在java.lang.annotation包中可以找到。

/*   * 定義註解 MethodInfo   * 為方便測試:註解目標為類 方法,屬性及構造方法   * 註解中含有三個元素 id ,name和 gid;   * id 元素 有默認值 0   */    @Documented  @Target({ElementType.TYPE,ElementType.METHOD,      ElementType.FIELD,ElementType.CONSTRUCTOR})  @Retention(RetentionPolicy.RUNTIME)  @Inherited  public @interface MethodInfo {      String name() default "xujunTest";      int id() default 0;      Class<Long> gid();  }

解析說明

  • (1). 通過 @interface 定義,註解名即為自定義註解名,這裡註解名為MethodInfo
  • (2). 註解配置參數名為註解類的方法名,且:

    a. 所有方法沒有方法體,沒有參數沒有修飾符,實際只允許 public & abstract 修飾符,默認為 public,不允許拋異常

    b. 方法返回值只能是基本類型,String, Class, annotation, enumeration 或者是他們的一維數組

    c. 若只有一個默認屬性,可直接用 value() 函數。一個屬性都沒有表示該 Annotation 為 Mark Annotation

  • (3). 可以加 default 表示默認值,如

String name() default "xujunTest";

2)接着我們來看一下我們要怎樣使用我們自定義的註解

/**   * 這個類專門用來測試註解使用   * @author xujun   */   //類成員註解  @MethodInfo (name="type",gid=Long.class)  public class UserAnnotation {      //類成員註解      @TestA(name="param",id=1,gid=Long.class)      private Integer age;        //構造方法註解      @TestA (name="construct",id=2,gid=Long.class)      public UserAnnotation(){        }        //類方法註解      @TestA(name="public method",id=3,gid=Long.class)      public void a(){          Map<String,String> m = new HashMap<String,String>(0);      }        //類方法註解      @TestA(name="protected method",id=4,gid=Long.class)      protected void b(){          Map<String,String> m = new HashMap<String,String>(0);      }        //類方法註解      @TestA(name="private method",id=5,gid=Long.class)      private void c(){          Map<String,String> m = new HashMap<String,String>(0);      }        public void b(Integer a){        }  }

3)最後我們一起來看一下我們怎樣在運行的時候解析我們的Annotation註解

運行時 Annotation 解析

(1) 運行時 Annotation 指 @Retention 為 RUNTIME 的 Annotation,可手動調用下面常用 API 解析

method.getAnnotation(AnnotationName.class);  method.getAnnotations();  method.isAnnotationPresent(AnnotationName.class);

其他 @Target 如 Field,Class 方法類似

  • getAnnotation(AnnotationName.class) 表示得到該 Target 某個 Annotation 的信息,因為一個 Target 可以被多個 Annotation 修飾
/*  * 根據註解類型返回方法的指定類型註解  */  MethodInfo annotation = (MethodInfo) constructor                       .getAnnotation(MethodInfo.class);
  • getAnnotations() 則表示得到該 Target 所有 Annotation
Annotation[] annotations = clazz.getAnnotations();  for (Annotation annotation : annotations) {      MethodInfo methodInfo = (MethodInfo) annotation  }
  • isAnnotationPresent(AnnotationName.class) 表示該 Target 是否被某個 Annotation 修飾
/*  * 判斷構造方法中是否有指定註解類型的註解  */  boolean hasAnnotation = constructor  .isAnnotationPresent(MethodInfo.class);  if (hasAnnotation) {      /*      * 根據註解類型返回方法的指定類型註解      */      MethodInfo annotation = (MethodInfo) constructor      .getAnnotation(MethodInfo.class);  }    

測試代碼

public class ParseAnnotation {        static String className="com.xujun.animation.test.UserAnnotation";      /**       * 簡單打印出UserAnnotation 類中所使用到的類註解 該方法只打印了 Type 類型的註解       *       * @throws ClassNotFoundException       */      public static void parseTypeAnnotation() throws ClassNotFoundException {          Class clazz = Class.forName(className);            Annotation[] annotations = clazz.getAnnotations();          for (Annotation annotation : annotations) {              MethodInfo testA = (MethodInfo) annotation;              System.out.println("id= "" + testA.id() + ""; name= ""                      + testA.name() + ""; gid = " + testA.gid());          }      }        /**       * 簡單打印出UserAnnotation 類中所使用到的方法註解 該方法只打印了 Method 類型的註解       *       * @throws ClassNotFoundException       */      public static void parseMethodAnnotation() {          Method[] methods = UserAnnotation.class.getDeclaredMethods();          for (Method method : methods) {              /*               * 判斷方法中是否有指定註解類型的註解               */              boolean hasAnnotation = method.isAnnotationPresent(MethodInfo.class);              if (hasAnnotation) {                  /*                   * 根據註解類型返回方法的指定類型註解                   */                  MethodInfo annotation = method.getAnnotation(MethodInfo.class);                  System.out.println("method = " + method.getName() + " ; id = "                          + annotation.id() + " ; description = "                          + annotation.name() + "; gid= " + annotation.gid());              }          }      }        /**       * 簡單打印出UserAnnotation 類中所使用到的方法註解 該方法只打印了 Method 類型的註解       *       * @throws ClassNotFoundException       */      public static void parseConstructAnnotation() {          Constructor[] constructors = UserAnnotation.class.getConstructors();          for (Constructor constructor : constructors) {              /*               * 判斷構造方法中是否有指定註解類型的註解               */              boolean hasAnnotation = constructor                      .isAnnotationPresent(MethodInfo.class);              if (hasAnnotation) {                  /*                   * 根據註解類型返回方法的指定類型註解                   */                  MethodInfo annotation = (MethodInfo) constructor                          .getAnnotation(MethodInfo.class);                  System.out.println("constructor = " + constructor.getName()                          + " ; id = " + annotation.id() + " ; description = "                          + annotation.name() + "; gid= " + annotation.gid());              }          }      }        public static void main(String[] args) throws ClassNotFoundException {          parseTypeAnnotation();          parseMethodAnnotation();          parseConstructAnnotation();      }  }

運行以上測試程序,將可以看到以下輸出結果

id= "0"; name= "type"; gid = class java.lang.Long
method = c ; id = 5 ; description = private method; gid= class java.lang.Long
method = b ; id = 4 ; description = protected method; gid= class java.lang.Long
method = a ; id = 3 ; description = public method; gid= class java.lang.Long
constructor = com.xujun.animationdemo.UserAnnotation ; id = 2 ; description = construct; gid= class java.lang.Long


轉載請註明原博客地址:

源碼下載地址:

相關博客推薦

java Type 詳解

java 反射機制詳解

註解使用入門(一)

Android 自定義編譯時註解1 – 簡單的例子

Android 編譯時註解 —— 語法詳解

帶你讀懂 ButterKnife 的源碼

掃一掃,歡迎關注我的微信公眾號 stormjun94(徐公碼字), 目前是一名程序員,不僅分享 Android開發相關知識,同時還分享技術人成長曆程,包括個人總結,職場經驗,面試經驗等,希望能讓你少走一點彎路。

Exit mobile version