1. 程式人生 > >深入理解Java:註解

深入理解Java:註解

註釋 element 每一個 gree arc res 參數名稱 生命周期 水果

註解作用:每當你創建描述符性質的類或者接口時,一旦其中包含重復性的工作,就可以考慮使用註解來簡化與自動化該過程。

Java提供了四種元註解,專門負責新註解的創建工作。‘

元註解

元註解的作用就是負責註解其他註解。Java5.0定義了4個標準的meta-annotation類型,它們被用來提供對其它 annotation類型作說明。Java5.0定義的元註解:
    [email protected],
    [email protected],
    [email protected],
    [email protected]
  這些類型和它們所支持的類在java.lang.annotation包中可以找到。下面我們看一下每個元註解的作用和相應分參數的使用說明。

@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聲明

使用示例:

  1 /***
  2  *
  3  * 實體註解接口
  4  */
  5 @Target(value = {ElementType.TYPE})
  6 @Retention(value = RetentionPolicy.RUNTIME)
  7 public @interface Entity {
  8     /***
  9      * 實體默認firstLevelCache屬性為false
 10
* @return boolean 11 */
12 boolean firstLevelCache() default false; 13 /*** 14 * 實體默認secondLevelCache屬性為false 15 * @return boolean 16 */ 17 boolean secondLevelCache() default true; 18 /*** 19 * 表名默認為空 20 * @return String 21 */ 22 String tableName() default ""; 23 /*** 24 * 默認以""分割註解 25 */ 26 String split() default ""; 27 }

@Retention

 @Retention定義了該Annotation被保留的時間長短:某些Annotation僅出現在源代碼中,而被編譯器丟棄;而另一些卻被編譯在class文件中;編譯在class文件中的Annotation可能會被虛擬機忽略,而另一些在class被裝載時將被讀取(請註意並不影響class的執行,因為Annotation與class在使用上是被分離的)。使用這個meta-Annotation可以對 Annotation的“生命周期”限制。

作用:表示需要在什麽級別保存該註釋信息,用於描述註解的生命周期(即:被描述的註解在什麽範圍內有效)

 取值(RetentionPoicy)有:

    1.SOURCE:在源文件中有效(即源文件保留)
    2.CLASS:在class文件中有效(即class保留)
    3.RUNTIME:在運行時有效(即運行時保留)

使用示例:

  1 /***
  2  * 字段註解接口
  3  */
  4 @Target(value = {ElementType.FIELD})//註解可以被添加在屬性上
  5 @Retention(value = RetentionPolicy.RUNTIME)//註解保存在JVM運行時刻,能夠在運行時刻通過反射API來獲取到註解的信息
  6 public @interface Column {
  7     String name();//註解的name屬性
  8 }

Column註解的的RetentionPolicy的屬性值是RUTIME,這樣註解處理器可以通過反射,獲取到該註解的屬性值,從而去做一些運行時的邏輯處理

@Documented

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

@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類型被發現,或者到達類繼承結構的頂層。

自定義註解

  使用@interface自定義註解時,自動繼承了java.lang.annotation.Annotation接口,由編譯程序自動完成其他細節。在定義註解時,不能繼承其他的註解或接口。@interface用來聲明一個註解,其中的每一個方法實際上是聲明了一個配置參數。方法的名稱就是參數的名稱,返回值類型就是參數的類型(返回值類型只能是基本類型、Class、String、enum)。可以通過default來聲明參數的默認值。

定義註解格式:
  public @interface 註解名 {定義體}

註解參數的可支持數據類型:

    1.所有基本數據類型(int,float,boolean,byte,double,char,long,short)
    2.String類型
    3.Class類型
    4.enum類型
    5.Annotation類型
    6.以上所有類型的數組

  Annotation類型裏面的參數該怎麽設定:
  第一,只能用public或默認(default)這兩個訪問權修飾.例如,String value();這裏把方法設為defaul默認類型;   
  第二,參數成員只能用基本類型byte,short,char,int,long,float,double,boolean八種基本數據類型和 String,Enum,Class,annotations等數據類型,以及這一些類型的數組.例如,String value();這裏的參數成員就為String;  
  第三,如果只有一個參數成員,最好把參數名稱設為"value",後加小括號.例:下面的例子FruitName註解就只有一個參數成員。

  簡單的自定義註解和使用註解實例:

示例1:

  1 /***
  2  *主鍵註解接口
  3  */
  4 @Target(value = {ElementType.FIELD})
  5 @Retention(value = RetentionPolicy.RUNTIME)
  6 public @interface Id {
  7 }

示例2:

  1 /**屬性不需要被持久化註解**/
  2 @Target(value = {ElementType.FIELD})
  3 @Retention(value = RetentionPolicy.RUNTIME)
  4 @Documented
  5 public @interface Transient {
  6 }

註解元素的默認值:

註解元素必須有確定的值,要麽在定義註解的默認值中指定,要麽在使用註解時指定,非基本類型的註解元素的值不可為null。因此, 使用空字符串或0作為默認值是一種常用的做法。這個約束使得處理器很難表現一個元素的存在或缺失的狀態,因為每個註解的聲明中,所有元素都存在,並且都具有相應的值,為了繞開這個約束,我們只能定義一些特殊的值,例如空字符串或者負數,一次表示某個元素不存在,在定義註解時,這已經成為一個習慣用法。

定義了註解,並在需要的時候給相關類,類屬性加上註解信息,如果沒有響應的註解信息處理流程,註解可以說是沒有實用價值。如何讓註解真真的發揮作用,主要就在於註解處理方法,下一步我們將學習註解信息的獲取和處理!

如果沒有用來讀取註解的方法和工作,那麽註解也就不會比註釋更有用處了。使用註解的過程中,很重要的一部分就是創建於使用註解處理器。Java SE5擴展了反射機制的API,以幫助程序員快速的構造自定義註解處理器。

註解處理器類庫(java.lang.reflect.AnnotatedElement):

  Java使用Annotation接口來代表程序元素前面的註解,該接口是所有Annotation類型的父接口。除此之外,Java在java.lang.reflect 包下新增了AnnotatedElement接口,該接口代表程序中可以接受註解的程序元素,該接口主要有如下幾個實現類:

  Class:類定義
  Constructor:構造器定義
  Field:累的成員變量定義
  Method:類的方法定義
  Package:類的包定義

  java.lang.reflect 包下主要包含一些實現反射功能的工具類,實際上,java.lang.reflect 包所有提供的反射API擴充了讀取運行時Annotation信息的能力。當一個Annotation類型被定義為運行時的Annotation後,該註解才能是運行時可見,當class文件被裝載時被保存在class文件中的Annotation才會被虛擬機讀取。
  AnnotatedElement 接口是所有程序元素(Class、Method和Constructor)的父接口,所以程序通過反射獲取了某個類的AnnotatedElement對象之後,程序就可以調用該對象的如下四個個方法來訪問Annotation信息:

  方法1:<T extends Annotation> T getAnnotation(Class<T> annotationClass): 返回改程序元素上存在的、指定類型的註解,如果該類型註解不存在,則返回null。
  方法2:Annotation[] getAnnotations():返回該程序元素上存在的所有註解。
  方法3:boolean is AnnotationPresent(Class<?extends Annotation> annotationClass):判斷該程序元素上是否包含指定類型的註解,存在則返回true,否則返回false.
  方法4:Annotation[] getDeclaredAnnotations():
返回直接存在於此元素上的所有註釋。與此接口中的其他方法不同,該方法將忽略繼承的註釋。(如果沒有註釋直接存在於此元素上,則返回長度為零的一個數組。)該方法的調用者可以隨意修改返回的數組;這不會對其他調用者返回的數組產生任何影響。

  一個簡單的註解處理器:

  1 /***********註解聲明***************/
  2 
  3 /**
  4  * 水果名稱註解
  5  * @author peida
  6  *
  7  */
  8 @Target(ElementType.FIELD)
  9 @Retention(RetentionPolicy.RUNTIME)
 10 @Documented
 11 public @interface FruitName {
 12     String value() default "";
 13 }
 14 
 15 /**
 16  * 水果顏色註解
 17  * @author peida
 18  *
 19  */
 20 @Target(ElementType.FIELD)
 21 @Retention(RetentionPolicy.RUNTIME)
 22 @Documented
 23 public @interface FruitColor {
 24     /**
 25      * 顏色枚舉
 26      * @author peida
 27      *
 28      */
 29     public enum Color{ BULE,RED,GREEN};
 30 
 31     /**
 32      * 顏色屬性
 33      * @return
 34      */
 35     Color fruitColor() default Color.GREEN;
 36 
 37 }
 38 
 39 /**
 40  * 水果供應者註解
 41  * @author peida
 42  *
 43  */
 44 @Target(ElementType.FIELD)
 45 @Retention(RetentionPolicy.RUNTIME)
 46 @Documented
 47 public @interface FruitProvider {
 48     /**
 49      * 供應商編號
 50      * @return
 51      */
 52     public int id() default -1;
 53 
 54     /**
 55      * 供應商名稱
 56      * @return
 57      */
 58     public String name() default "";
 59 
 60     /**
 61      * 供應商地址
 62      * @return
 63      */
 64     public String address() default "";
 65 }
 66 
 67 /***********註解使用***************/
 68 
 69 public class Apple {
 70 
 71     @FruitName("Apple")
 72     private String appleName;
 73 
 74     @FruitColor(fruitColor=Color.RED)
 75     private String appleColor;
 76 
 77     @FruitProvider(id=1,name="陜西紅富士集團",address="陜西省西安市延安路89號紅富士大廈")
 78     private String appleProvider;
 79 
 80     public void setAppleColor(String appleColor) {
 81         this.appleColor = appleColor;
 82     }
 83     public String getAppleColor() {
 84         return appleColor;
 85     }
 86 
 87     public void setAppleName(String appleName) {
 88         this.appleName = appleName;
 89     }
 90     public String getAppleName() {
 91         return appleName;
 92     }
 93 
 94     public void setAppleProvider(String appleProvider) {
 95         this.appleProvider = appleProvider;
 96     }
 97     public String getAppleProvider() {
 98         return appleProvider;
 99     }
100 
101     public void displayName(){
102         System.out.println("水果的名字是:蘋果");
103     }
104 }
105 
106 /***********註解處理器***************/
107 
108 public class FruitInfoUtil {
109     public static void getFruitInfo(Class<?> clazz){
110 
111         String strFruitName=" 水果名稱:";
112         String strFruitColor=" 水果顏色:";
113         String strFruitProvicer="供應商信息:";
114 
115         Field[] fields = clazz.getDeclaredFields();
116 
117         for(Field field :fields){
118             if(field.isAnnotationPresent(FruitName.class)){
119                 FruitName fruitName = (FruitName) field.getAnnotation(FruitName.class);
120                 strFruitName=strFruitName+fruitName.value();
121                 System.out.println(strFruitName);
122             }
123             else if(field.isAnnotationPresent(FruitColor.class)){
124                 FruitColor fruitColor= (FruitColor) field.getAnnotation(FruitColor.class);
125                 strFruitColor=strFruitColor+fruitColor.fruitColor().toString();
126                 System.out.println(strFruitColor);
127             }
128             else if(field.isAnnotationPresent(FruitProvider.class)){
129                 FruitProvider fruitProvider= (FruitProvider) field.getAnnotation(FruitProvider.class);
130                 strFruitProvicer=" 供應商編號:"+fruitProvider.id()+" 供應商名稱:"+fruitProvider.name()+" 供應商地址:"+fruitProvider.address();
131                 System.out.println(strFruitProvicer);
132             }
133         }
134     }
135 }
136 
137 /***********輸出結果***************/
138 public class FruitRun {
139 
140     /**
141      * @param args
142      */
143     public static void main(String[] args) {
144 
145         FruitInfoUtil.getFruitInfo(Apple.class);
146 
147     }
148 
149 }
150 
151 ====================================
152  水果名稱:Apple
153  水果顏色:RED
154  供應商編號:1 供應商名稱:陜西紅富士集團 供應商地址:陜西省西安市延安路89號紅富士大廈

Java註解的基礎知識點(見下面導圖)基本都過了一遍,下一篇我們通過設計一個基於註解的簡單的ORM框架,來綜合應用和進一步加深對註解的各個知識點的理解和運用。

技術分享

參考:

深入理解Java:註解(Annotation)--註解處理器

內容來自:cnblogs:牛奶、不加糖

深入理解Java:註解