java註解的強大

什麼是註解(Annotation):

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

  Annotation(註解)是JDK5.0及以後版本引入的。它可以用於創建文檔,跟蹤代碼中的依賴性,甚至執行基本編譯時檢查。從某些方面看,annotation就像修飾符一樣被使用,並應用於包、類型、構造方法、方法、成員變量、參數、本地變量的聲明中。這些信息被存儲在Annotation的“name=value”結構對中。

  Annotation的成員在Annotation類型中以無參數的方法的形式被聲明。其方法名和返回值定義了該成員的名字和類型。在此有一個特定的默認語法:允許聲明任何Annotation成員的默認值。一個Annotation可以將name=value對作爲沒有定義默認值的Annotation成員的值,當然也可以使用name=value對來覆蓋其它成員默認值。這一點有些近似類的繼承特性,父類的構造函數可以作爲子類的默認構造函數,但是也可以被子類覆蓋。

  Annotation能被用來爲某個程序元素(類、方法、成員變量等)關聯任何的信息。需要注意的是,這裏存在着一個基本的規則:Annotation不能影響程序代碼的執行,無論增加、刪除 Annotation,代碼都始終如一的執行。另外,儘管一些annotation通過java的反射api方法在運行時被訪問,而java語言解釋器在工作時忽略了這些annotation。正是由於java虛擬機忽略了Annotation,導致了annotation類型在代碼中是“不起作用”的; 只有通過某種配套的工具纔會對annotation類型中的信息進行訪問和處理。本文中將涵蓋標準的Annotation和meta-annotation類型,陪伴這些annotation類型的工具是java編譯器(當然要以某種特殊的方式處理它們)。


什麼是metadata(元數據):

  元數據從metadata一詞譯來,就是“關於數據的數據”的意思。
  元數據的功能作用有很多,比如:你可能用過Javadoc的註釋自動生成文檔。這就是元數據功能的一種。總的來說,元數據可以用來創建文檔,跟蹤代碼的依賴性,執行編譯時格式檢查,代替已有的配置文件。如果要對於元數據的作用進行分類,目前還沒有明確的定義,不過我們可以根據它所起的作用,大致可分爲三類:
    1. 編寫文檔:通過代碼裏標識的元數據生成文檔
    2. 代碼分析:通過代碼裏標識的元數據對代碼進行分析
    3. 編譯檢查:通過代碼裏標識的元數據讓編譯器能實現基本的編譯檢查
  在Java中元數據以標籤的形式存在於Java代碼中,元數據標籤的存在並不影響程序代碼的編譯和執行,它只是被用來生成其它的文件或在運行時獲取被運行代碼的描述信息。
  綜上所述:
    第一,元數據以標籤的形式存在於Java代碼中。
    第二,元數據描述的信息是類型安全的,即元數據內部的字段都是有明確類型的。
    第三,元數據需要編譯器之外的工具額外的處理用來生成其它的程序部件。
    第四,元數據可以只存在於Java源代碼級別,也可以存在於編譯之後的Class文件內部。


 Annotation和Annotation類型:

  Annotation:

  Annotation使用了在java5.0所帶來的新語法,它的行爲十分類似public、final這樣的修飾符。每個Annotation具有一個名字和成員個數>=0。每個Annotation的成員具有被稱爲name=value對的名字和值(就像javabean一樣),name=value裝載了Annotation的信息。

  Annotation類型:

  Annotation類型定義了Annotation的名字、類型、成員默認值。一個Annotation類型可以說是一個特殊的java接口,它的成員變量是受限制的,而聲明Annotation類型時需要使用新語法。當我們通過java反射api訪問Annotation時,返回值將是一個實現了該annotation類型接口的對象,通過訪問這個對象我們能方便的訪問到其Annotation成員。後面的章節將提到在java5.0的 java.lang包裏包含的3個標準Annotation類型。


註解的分類:

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

  根據註解使用方法和用途,我們可以將Annotation分爲三類:
    1.JDK內置系統註解
    2.元註解
    3.自定義註解


 系統內置標準註解:

  註解的語法比較簡單,除了@符號的使用外,他基本與Java固有的語法一致,JavaSE中內置三個標準註解,定義在java.lang中:
    @Override:用於修飾此方法覆蓋了父類的方法;
    @Deprecated:用於修飾已經過時的方法;
    @SuppressWarnnings:用於通知java編譯器禁止特定的編譯警告。

  下面我們依次看看三個內置標準註解的作用和使用場景。


@Override,限定重寫父類方法

  @Override 是一個標記註解類型,它被用作標註方法。它說明了被標註的方法重載了父類的方法,起到了斷言的作用。如果我們使用了這種Annotation在一個沒有覆蓋父類方法的方法時,java編譯器將以一個編譯錯誤來警示。這個annotaton常常在我們試圖覆蓋父類方法而又寫錯了方法名時發揮威力。使用方法極其簡單:在使用此annotation時只要在被修飾的方法前面加上@Override即可。下面的代碼是一個使用@Override修飾一個企圖重載父類的displayName()方法,而又存在拼寫錯誤的實例:

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. public class Fruit {  
  2.   
  3.     public void displayName(){  
  4.         System.out.println("水果的名字是:*****");  
  5.     }  
  6. }  
  7.   
  8. class Orange extends Fruit {  
  9.     @Override  
  10.     public void displayName(){  
  11.         System.out.println("水果的名字是:桔子");  
  12.     }  
  13. }  
  14.   
  15. class Apple extends Fruit {  
  16.     @Override  
  17.     public void displayname(){  
  18.         System.out.println("水果的名字是:蘋果");  
  19.     }  
  20. }  

  Orange類編譯不會有任何問題,Apple類在編譯的時候會提示相應的錯誤。@Override註解只能用於方法,不能用於其他程序元素。


@Deprecated,標記已過時:

  同 樣Deprecated也是一個標記註解。當一個類型或者類型成員使用@Deprecated修飾的話,編譯器將不鼓勵使用這個被標註的程序元素。而且這種修飾具有一定的 “延續性”:如果我們在代碼中通過繼承或者覆蓋的方式使用了這個過時的類型或者成員,雖然繼承或者覆蓋後的類型或者成員並不是被聲明爲 @Deprecated,但編譯器仍然要報警。

  值得注意,@Deprecated這個annotation類型和javadoc中的 @deprecated這個tag是有區別的:前者是java編譯器識別的,而後者是被javadoc工具所識別用來生成文檔(包含程序成員爲什麼已經過 時、它應當如何被禁止或者替代的描述)。

  在java5.0,java編譯器仍然象其從前版本那樣尋找@deprecated這個javadoc tag,並使用它們產生警告信息。但是這種狀況將在後續版本中改變,我們應在現在就開始使用@Deprecated來修飾過時的方法而不是 @deprecated javadoc tag。

  下面一段程序中使用了@Deprecated註解標示方法過期,同時在方法註釋中用@deprecated tag 標示該方法已經過時,代碼如下:

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. class AppleService {  
  2.     public void displayName(){  
  3.         System.out.println("水果的名字是:蘋果");  
  4.     }  
  5.       
  6.     /** 
  7.      * @deprecated 該方法已經過期,不推薦使用 
  8.      */  
  9.     @Deprecated  
  10.     public void showTaste(){  
  11.         System.out.println("水果的蘋果的口感是:脆甜");  
  12.     }  
  13.       
  14.     public void showTaste(int typeId){  
  15.         if(typeId==1){  
  16.             System.out.println("水果的蘋果的口感是:酸澀");  
  17.         }  
  18.         else if(typeId==2){  
  19.             System.out.println("水果的蘋果的口感是:綿甜");  
  20.         }  
  21.         else{  
  22.             System.out.println("水果的蘋果的口感是:脆甜");  
  23.         }  
  24.     }  
  25. }  
  26.   
  27. public class FruitRun {  
  28.   
  29.     /** 
  30.      * @param args 
  31.      */  
  32.     public static void main(String[] args) {  
  33.         Apple apple=new Apple();  
  34.         apple.displayName();      
  35.           
  36.         AppleService appleService=new AppleService();  
  37.         appleService.showTaste();  
  38.         appleService.showTaste(0);  
  39.         appleService.showTaste(2);  
  40.     }  
  41.   
  42. }  
  AppleService類的showTaste() 方法被@Deprecated標註爲過時方法,在FruitRun類中使用的時候,編譯器會給出該方法已過期,不推薦使用的提示。

@SuppressWarnnings,抑制編譯器警告:

  @SuppressWarnings 被用於有選擇的關閉編譯器對類、方法、成員變量、變量初始化的警告。在java5.0,sun提供的javac編譯器爲我們提供了-Xlint選項來使編譯器對合法的程序代碼提出警告,此種警告從某種程度上代表了程序錯誤。例如當我們使用一個generic collection類而又沒有提供它的類型時,編譯器將提示出"unchecked warning"的警告。通常當這種情況發生時,我們就需要查找引起警告的代碼。如果它真的表示錯誤,我們就需要糾正它。例如如果警告信息表明我們代碼中的switch語句沒有覆蓋所有可能的case,那麼我們就應增加一個默認的case來避免這種警告。
  有時我們無法避免這種警告,例如,我們使用必須和非generic的舊代碼交互的generic collection類時,我們不能避免這個unchecked warning。此時@SuppressWarning就要派上用場了,在調用的方法前增加@SuppressWarnings修飾,告訴編譯器停止對此方法的警告。
  @SuppressWarning不是一個標記註解。它有一個類型爲String[]的成員,這個成員的值爲被禁止的警告名。對於javac編譯器來講,被-Xlint選項有效的警告名也同樣對@SuppressWarings有效,同時編譯器忽略掉無法識別的警告名。
  annotation語法允許在annotation名後跟括號,括號中是使用逗號分割的name=value對用於爲annotation的成員賦值。實例如下:

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. public class FruitService {  
  2.       
  3.     @SuppressWarnings(value={ "rawtypes""unchecked" })  
  4.     public static  List<Fruit> getFruitList(){  
  5.         List<Fruit> fruitList=new ArrayList();  
  6.         return fruitList;  
  7.     }  
  8.       
  9.     @SuppressWarnings({ "rawtypes""unchecked" })  
  10.     public static  List<Fruit> getFruit(){  
  11.         List<Fruit> fruitList=new ArrayList();  
  12.         return fruitList;  
  13.     }  
  14.   
  15.     @SuppressWarnings("unused")  
  16.     public static void main(String[] args){  
  17.         List<String> strList=new ArrayList<String>();  
  18.     }  
  19. }  

  在這個例子中SuppressWarnings annotation類型只定義了一個單一的成員,所以只有一個簡單的value={...}作爲name=value對。又由於成員值是一個數組,故使用大括號來聲明數組值。注意:我們可以在下面的情況中縮寫annotation:當annotation只有單一成員,併成員命名爲"value="。這時可以省去"value="。比如將上面方法getFruit()的SuppressWarnings annotation就是縮寫的。

   SuppressWarnings註解的常見參數值的簡單說明:

    1. deprecation:使用了不贊成使用的類或方法時的警告;
    2. unchecked:執行了未檢查的轉換時的警告,例如當使用集合時沒有用泛型 (Generics) 來指定集合保存的類型; 
    3. fallthrough:當switch程序塊直接通往下一種情況而沒有 Break 時的警告;
    4. path:在類路徑、源文件路徑等中有不存在的路徑時的警告; 
    5. serial:當在可序列化的類上缺少serialVersionUID定義時的警告; 
    6. finally:任何finally子句不能正常完成時的警告; 
    7. all:關於以上所有情況的警告。

  要深入學習註解,我們就必須能定義自己的註解,並使用註解,在定義自己的註解之前,我們就必須要了解Java爲我們提供的元註解和相關定義註解的語法。

元註解:

  元註解的作用就是負責註解其他註解。Java5.0定義了4個標準的meta-annotation類型,它們被用來提供對其它annotation類型作說明。Java5.0定義的元註解:
    1.@Target,
    2.@Retention,
    3.@Documented,
    4.@Inherited
  這些類型和它們所支持的類在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聲明

  使用實例:

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. @Target(ElementType.TYPE)  
  2. public @interface Table {  
  3.     /** 
  4.      * 數據表名稱註解,默認值爲類名稱 
  5.      * @return 
  6.      */  
  7.     public String tableName() default "className";  
  8. }  
  9.   
  10. @Target(ElementType.FIELD)  
  11. public @interface NoDBColumn {  
  12.   
  13. }  
  註解Table 可以用於註解類、接口(包括註解類型) 或enum聲明,而註解NoDBColumn僅可用於註解類的成員變量。

  @Retention:

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

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

  取值(RetentionPoicy)有:

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

  Retention meta-annotation類型有唯一的value作爲成員,它的取值來自java.lang.annotation.RetentionPolicy的枚舉類型值。具體實例如下:

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. @Target(ElementType.FIELD)  
  2. @Retention(RetentionPolicy.RUNTIME)  
  3. public @interface Column {  
  4.     public String name() default "fieldName";  
  5.     public String setFuncName() default "setField";  
  6.     public String getFuncName() default "getField";   
  7.     public boolean defaultDBValue() default false;  
  8. }  
  Column註解的的RetentionPolicy的屬性值是RUTIME,這樣註解處理器可以通過反射,獲取到該註解的屬性值,從而去做一些運行時的邏輯處理

  @Documented:

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

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. @Target(ElementType.FIELD)  
  2. @Retention(RetentionPolicy.RUNTIME)  
  3. @Documented  
  4. public @interface Column {  
  5.     public String name() default "fieldName";  
  6.     public String setFuncName() default "setField";  
  7.     public String getFuncName() default "getField";   
  8.     public boolean defaultDBValue() default false;  
  9. }  
@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類型被發現,或者到達類繼承結構的頂層。

  實例代碼:

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. /** 
  2.  *  
  3.  * @author peida 
  4.  * 
  5.  */  
  6. @Inherited  
  7. public @interface Greeting {  
  8.     public enum FontColor{ BULE,RED,GREEN};  
  9.     String name();  
  10.     FontColor fontColor() default FontColor.GREEN;  
  11. }  

自定義註解:

  使用@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註解就只有一個參數成員。

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

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. package annotation;  
  2.   
  3. import java.lang.annotation.Documented;  
  4. import java.lang.annotation.ElementType;  
  5. import java.lang.annotation.Retention;  
  6. import java.lang.annotation.RetentionPolicy;  
  7. import java.lang.annotation.Target;  
  8.   
  9. /** 
  10.  * 水果名稱註解 
  11.  * @author peida 
  12.  * 
  13.  */  
  14. @Target(ElementType.FIELD)  
  15. @Retention(RetentionPolicy.RUNTIME)  
  16. @Documented  
  17. public @interface FruitName {  
  18.     String value() default "";  
  19. }  
[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. package annotation;  
  2.   
  3. import java.lang.annotation.Documented;  
  4. import java.lang.annotation.ElementType;  
  5. import java.lang.annotation.Retention;  
  6. import java.lang.annotation.RetentionPolicy;  
  7. import java.lang.annotation.Target;  
  8.   
  9. /** 
  10.  * 水果顏色註解 
  11.  * @author peida 
  12.  * 
  13.  */  
  14. @Target(ElementType.FIELD)  
  15. @Retention(RetentionPolicy.RUNTIME)  
  16. @Documented  
  17. public @interface FruitColor {  
  18.     /** 
  19.      * 顏色枚舉 
  20.      * @author peida 
  21.      * 
  22.      */  
  23.     public enum Color{ BULE,RED,GREEN};  
  24.       
  25.     /** 
  26.      * 顏色屬性 
  27.      * @return 
  28.      */  
  29.     Color fruitColor() default Color.GREEN;  
  30.   
  31. }  
[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. package annotation;  
  2.   
  3. import annotation.FruitColor.Color;  
  4.   
  5. public class Apple {  
  6.       
  7.     @FruitName("Apple")  
  8.     private String appleName;  
  9.       
  10.     @FruitColor(fruitColor=Color.RED)  
  11.     private String appleColor;  
  12.       
  13.       
  14.       
  15.       
  16.     public void setAppleColor(String appleColor) {  
  17.         this.appleColor = appleColor;  
  18.     }  
  19.     public String getAppleColor() {  
  20.         return appleColor;  
  21.     }  
  22.       
  23.       
  24.     public void setAppleName(String appleName) {  
  25.         this.appleName = appleName;  
  26.     }  
  27.     public String getAppleName() {  
  28.         return appleName;  
  29.     }  
  30.       
  31.     public void displayName(){  
  32.         System.out.println("水果的名字是:蘋果");  
  33.     }  
  34. }  

註解元素的默認值:

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

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. package annotation;  
  2.   
  3. import java.lang.annotation.Documented;  
  4. import java.lang.annotation.ElementType;  
  5. import java.lang.annotation.Retention;  
  6. import java.lang.annotation.RetentionPolicy;  
  7. import java.lang.annotation.Target;  
  8.   
  9. /** 
  10.  * 水果供應者註解 
  11.  * @author peida 
  12.  * 
  13.  */  
  14. @Target(ElementType.FIELD)  
  15. @Retention(RetentionPolicy.RUNTIME)  
  16. @Documented  
  17. public @interface FruitProvider {  
  18.     /** 
  19.      * 供應商編號 
  20.      * @return 
  21.      */  
  22.     public int id() default -1;  
  23.       
  24.     /** 
  25.      * 供應商名稱 
  26.      * @return 
  27.      */  
  28.     public String name() default "";  
  29.       
  30.     /** 
  31.      * 供應商地址 
  32.      * @return 
  33.      */  
  34.     public String address() default "";  
  35. }  
  定義了註解,並在需要的時候給相關類,類屬性加上註解信息,如果沒有響應的註解信息處理流程,註解可以說是沒有實用價值。如何讓註解真真的發揮作用,主要就在於註解處理方法,下一步我們將學習註解信息的獲取和處理!
如果沒有用來讀取註解的方法和工作,那麼註解也就不會比註釋更有用處了。使用註解的過程中,很重要的一部分就是創建於使用註解處理器。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 isAnnotationPresent(Class<? extends Annotation> annotationClass),判斷該程序元素上是否包含指定類型的註解,存在則返回true,否則返回false。
  方法4:Annotation[] getDeclaredAnnotations(),返回直接存在於此元素上的所有註釋。與此接口中的其他方法不同,該方法將忽略繼承的註釋。(如果沒有註釋直接存在於此元素上,則返回長度爲零的一個數組。)該方法的調用者可以隨意修改返回的數組;這不會對其他調用者返回的數組產生任何影響。

  一個簡單的註解處理器:  

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. package annotation;  
  2.   
  3. import annotationexample.FruitColor.Color;  
  4. import java.lang.reflect.Field;  
  5.   
  6. /** 
  7.  * *********註解使用************** 
  8.  */  
  9. class Apple {  
  10.   
  11.     @FruitName("Apple")  
  12.     private String appleName;  
  13.   
  14.     @FruitColor(fruitColor = Color.RED)  
  15.     private String appleColor;  
  16.   
  17.     @FruitProvider(id = 1, name = "陝西紅富士集團", address = "陝西省西安市延安路89號紅富士大廈")  
  18.     private String appleProvider;  
  19.   
  20.     public void setAppleColor(String appleColor) {  
  21.         this.appleColor = appleColor;  
  22.     }  
  23.   
  24.     public String getAppleColor() {  
  25.         return appleColor;  
  26.     }  
  27.   
  28.     public void setAppleName(String appleName) {  
  29.         this.appleName = appleName;  
  30.     }  
  31.   
  32.     public String getAppleName() {  
  33.         return appleName;  
  34.     }  
  35.   
  36.     public void setAppleProvider(String appleProvider) {  
  37.         this.appleProvider = appleProvider;  
  38.     }  
  39.   
  40.     public String getAppleProvider() {  
  41.         return appleProvider;  
  42.     }  
  43.   
  44.     public void displayName() {  
  45.         System.out.println("水果的名字是:蘋果");  
  46.     }  
  47. }  
  48.   
  49. /** 
  50.  * *********註解處理器************** 
  51.  */  
  52. class FruitInfoUtil {  
  53.   
  54.     public static void getFruitInfo(Class<?> clazz) {  
  55.         String strFruitName = " 水果名稱:";  
  56.         String strFruitColor = " 水果顏色:";  
  57.         String strFruitProvicer = "供應商信息:";  
  58.         Field[] fields = clazz.getDeclaredFields();  // 得到類的所有成員變量  
  59.   
  60.         for (Field field : fields) {              
  61.             if (field.isAnnotationPresent(FruitName.class)) {  // 如果成員變量上有FruitName註解  
  62.                 FruitName fruitName = (FruitName) field.getAnnotation(FruitName.class);  // 得到註解  
  63.                 strFruitName = strFruitName + fruitName.value();  // 得到註解的參數值  
  64.                 System.out.println(strFruitName);  
  65.             } else if (field.isAnnotationPresent(FruitColor.class)) {  // 獲取FruitColor註解  
  66.                 FruitColor fruitColor = (FruitColor) field.getAnnotation(FruitColor.class);  
  67.                 strFruitColor = strFruitColor + fruitColor.fruitColor().toString();  
  68.                 System.out.println(strFruitColor);  
  69.             } else if (field.isAnnotationPresent(FruitProvider.class)) {  // 獲取FruitProvider註解  
  70.                 FruitProvider fruitProvider = (FruitProvider) field.getAnnotation(FruitProvider.class);  
  71.                 strFruitProvicer = " 供應商編號:" + fruitProvider.id() + " 供應商名稱:" + fruitProvider.name() +   
  72.                         " 供應商地址:" + fruitProvider.address();  
  73.                 System.out.println(strFruitProvicer);  
  74.             }  
  75.         }  
  76.     }  
  77. }  
  78.   
  79. /** 
  80.  * *********輸出結果************** 
  81.  */  
  82. public class FruitRun {  
  83.   
  84.     /** 
  85.      * @param args 
  86.      */  
  87.     public static void main(String[] args) {  
  88.         // 從使用註解的Apple類中獲取註解信息  
  89.         FruitInfoUtil.getFruitInfo(Apple.class);  
  90.     }  
  91. }  

  輸出結果:

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. 水果名稱:Apple  
  2.  水果顏色:RED  
  3.  供應商編號:1 供應商名稱:陝西紅富士集團 供應商地址:陝西省西安市延安路89號紅富士大廈  
  Java註解的基礎知識點基本都過了一遍,見下面導圖。

圖1 Java註解基礎知識

參考文獻:

http://www.cnblogs.com/peida/archive/2013/04/23/3036035.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章