Java 註解Annotation研究

wongstar發表於2017-11-16

目前主流框架都支援註解。比如Android平臺上的 Retrofit,EventBus,Butterknife等
Java Web: Spring MVC ,MyBatis等.註解是從Java SDK 1.5 開始啟動使用的,使用註解到底有什麼好處呢?

對於配置檔案需要新增較多邏輯程式碼來處理,而註解只需要註解元 資料就可以區分,程式碼整潔,閱讀性好,減少配置檔案等。

annotation.png
annotation.png

如上圖所示:
Java的註解分為元註解和標準註解。

  • 標準註解:系統提供的註解

    @Deprecated :表示已經過期,不推薦使用.比如你有一個方法現在不想用了,但是其他人呼叫了,刪掉會影響其他程式碼。這樣用這個標記來說明.

    @Documented
    @Retention(RetentionPolicy.RUNTIME)//執行期runtime
    @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})//範圍
    public @interface Deprecated {
    }複製程式碼
    @Deprecated
    public void test(){
    }複製程式碼

@Override 主要作用是覆蓋父類方法

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)//在原始檔中出現,在編譯後會被編譯器丟棄
public @interface Override {
}複製程式碼

比如android onCreate方法,覆蓋父類方法

 @Override
 protected void onCreate(Bundle savedInstanceState) {
}複製程式碼

比如java toString()方法,覆蓋java lang toString方法

 @Override
  public String toString() {
        return super.toString();
   }複製程式碼

@SupressWarning 關閉不當的編譯器警告資訊
比如

  • 元註解:可以自己定義註解

    @Retention 表示需要在什麼級別儲存該註解資訊,標記有下面3個引數可選
    ```
    public enum RetentionPolicy {
    /**

    • Annotations are to be discarded by the compiler.
      */
      SOURCE,//註解將被編譯器丟棄

    /**

    • Annotations are to be recorded in the class file by the compiler
    • but need not be retained by the VM at run time. This is the default
    • behavior.
      */
      CLASS,//註解在class檔案可用,但是會VM丟棄

      /**

    • Annotations are to be recorded in the class file by the compiler and
    • retained by the VM at run time, so they may be read reflectively.
      *
    • @see java.lang.reflect.AnnotatedElement
      */
      RUNTIME//VM執行期也會保留註解,可用用反射機制可用讀取對應的資料
      }
      >@Target 表示被描述的註解用於什麼地方,可用運用到地方如下複製程式碼
      @Documented
      @Retention(RetentionPolicy.RUNTIME)
      @Target(ElementType.ANNOTATION_TYPE)
      public @interface Target {
      /**
    • Returns an array of the kinds of elements an annotation type
    • can be applied to.
    • @return an array of the kinds of elements an annotation type
    • can be applied to
      */
      ElementType[] value();//這邊是一個ElementType陣列
      }
複製程式碼

public enum ElementType {
/* Class, interface (including annotation type), or enum declaration /
TYPE,//用於描述類,介面 或列舉宣告

/** Field declaration (includes enum constants) */
FIELD,//用於描述域

/** Method declaration */
METHOD,//用於描述方法

/** Formal parameter declaration */
PARAMETER,//用於描述引數

/** Constructor declaration */
CONSTRUCTOR,//用於描述構造器

/** Local variable declaration */
LOCAL_VARIABLE,//用於描述區域性變數

/** Annotation type declaration */
ANNOTATION_TYPE,//用於描述Annotation type

/** Package declaration */
PACKAGE,//描述包

/**
 * Type parameter declaration
 *
 * @since 1.8
 */
TYPE_PARAMETER,

/**
 * Use of a type
 *
 * @since 1.8
 */
TYPE_USE複製程式碼

}


>@Documented用於描述其它型別的annotation應該被作為被標註的程式成員的公共API,因此可以被例如javadoc此類的工具文件化。Documented是一個標記註解,沒有成員.複製程式碼

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}

>@Inherited 元註解是一個標記註解,@Inherited闡述了某個被標註的型別是被繼承的,則這個annotation將被用於該class的子類。複製程式碼

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}

自定義自己的Annotation 格式如下
public @interface 註解名 {定義體}

定義體裡面一般如下圖所示:

![anntion2.png](http://upload-images.jianshu.io/upload_images/2155810-4b26c1633260ed4b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

舉個栗子寫個Demo吧:
定義一個Person Annotation 裡面定義了2個方法 say 和move 方法.複製程式碼

@Target({ElementType.METHOD,ElementType.TYPE,ElementType.LOCAL_VARIABLE,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface PersonAnnotation {
public String say() default "hello";
public String move() default "walk";
Sex sex() default Sex.MAN;//列舉型別
}

定義一個UserAnnotation 定義name, age 和 PersonAnnotation annotation複製程式碼

@Target({ElementType.METHOD , ElementType.TYPE,ElementType.FIELD,ElementType.LOCAL_VARIABLE})//可以修飾方法,例項變數,local 變數
@Retention(RetentionPolicy.RUNTIME)
public @interface UserAnnotation {
String name() default "";
String age() default "";
PersonAnnotation person();//使用了Annotation型別
}

定義性別enum.複製程式碼

public enum Sex {
MAN,WOMEN,OTHER
}

上面的程式碼定義的不錯,那怎麼使用呢?答案是反射,直接上程式碼吧?
定義User類複製程式碼

@UserAnnotation//因為在Target定義了ElementType.TYPE可以修飾類
public class User {
@UserAnnotation(name = "Star")//因為在Target中定義了ElementType.FIELD就可以定義在例項變數中拉
protected String name;
@UserAnnotation(age = 100)
int age;
@UserAnnotation(name = "wong" , age = 1000)
protected String value;
@UserAnnotation(name = "GEAK",age = 90)//因為在Target中定義了ElementType.METHOD
public void getUserInfo(String name,int age){
System.out.println("the name is:"+name+"\tthe age is:"+age);
}
public void getInfo(){
System.out.println("=======getInfo start=====");
System.out.println("the name is:"+this.name+"\tthe age is:"+this.age);
System.out.println("=======getInfo end=====");
}

}

定義了自己的後設資料,下面是測試怎麼使用複製程式碼
@Test
public void test() {
    //User user = new User();
    System.out.println("startTest");
    Class<User> u = User.class;
    try {
        Method method = u.getMethod("getUserInfo", new Class[]{String.class, int.class});
        UserAnnotation userAnnotation = method.getAnnotation(UserAnnotation.class);
        System.out.println("the name is:" + userAnnotation.name() + "the age is:" + userAnnotation.age());
        if (method != null) {
            method.invoke(u.newInstance(), userAnnotation.name(), userAnnotation.age());
        }
        Field fields[] = u.getDeclaredFields();
        Field f = u.getDeclaredField("value");
        UserAnnotation userAnnotation1 = f.getAnnotation(UserAnnotation.class);
        System.out.println("the f name is:" + userAnnotation1.name() + "the f age is:" + userAnnotation1.age());
        for (Field field : fields) {
            if (field.isAnnotationPresent(UserAnnotation.class)) {
                System.out.println("the name is:" + field.getName());
                UserAnnotation annotation = field.getAnnotation(UserAnnotation.class);
                if (annotation != null) {
                    System.out.println("the name is:" + annotation.name() + "the age is:" + annotation.age());
                }
            }
        }

    } catch (Exception e) {
        e.printStackTrace();
    }

}複製程式碼
獲取對應的User Class ,有下面兩種方式
- 獲取Class相關
1.Class<User> u = User.class; //通過Class直接獲取
2.Class<User> u = Class.forName("User");//通過name獲取

- Class中主要用到的方法
Method[] getMethods() //獲取所有的方法包括父類的方法
Field[] getFields() //獲取所有例項變數包括父類的例項
Field[] getDeclaredFields()//獲取所有的方法不包括父類的方法
Method[] getDeclaredMethods()//獲取所有例項變數不包括父類的例項
- 獲取Annotation
Method.getAnnotation(Annatation.class)//獲取方法上面的Annotation
Field.getAnnotation(Annatation.class)//獲取filed上的Annotation

獲取結果:複製程式碼

the name is:GEAKthe age is:90 //獲取到方法上的Annotation
the name is:GEAK the age is:90//獲取到方法上的Annotation
the f name is:wongthe f age is:1000//利用反射的機制來呼叫方法
the name is:name
the name is:Starthe age is:10//獲取對應例項的name上的Annotation
the name is:age
the name is:the age is:100
the name is:value
the name is:wongthe age is:1000
```
總結:
通過Annotation和反射機制來進行合理的配置你的原始碼.這樣就可以省去大部分的if else或者初始化等工作.

相關文章