註解使用入門(一)

本篇博客要講解主要分爲如下幾個問題

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

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

註解的相關知識點

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

1)什麼是註解(Annotation):

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

2)註解的分類:

根據註解參數的個數,咱們能夠將註解分爲三類:segmentfault

  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 等,未標註則表示可修飾全部ide

    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開發相關知識,同時還分享技術人成長曆程,包括我的總結,職場經驗,面試經驗等,但願能讓你少走一點彎路。

相關文章
相關標籤/搜索