深入理解Java:註解
註解作用:每當你創建描述符性質的類或者接口時,一旦其中包含重復性的工作,就可以考慮使用註解來簡化與自動化該過程。
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:用於描述局部變量
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:註解