Java之註解解析
一、註解的基本概念
Java 註解就像修飾符一樣,可以用於從java程式碼中抽取文件、跟蹤程式碼中的依賴性或者在編譯時做檢查。註解可以被應用在包、類、方法、成員變數、引數和本地變數的宣告中。我們大多數人最先接觸的註解就是@Override。
註解的工作原理就是,先使用註解修飾java程式碼,然後另一塊叫做註解處理器的程式碼會解析這段註解和被修飾的程式碼並做相應的處理。
二、JDK內建的標準註解
JavaSE中內建了三個標準註解,都是定義在java.lang中,它們是:
- @Override:用於修飾子類的方法覆蓋了父類中的方法;
- @Deprecated:用於修飾已經過時了的方法,不推薦使用的方法;
- @SuppressWarnnings:告訴java編譯器禁止編譯警告。
2.1 @Override
@Override很簡單,只是一個標記,用於標註一個方法。它表示,被它標註的方法覆蓋了父類的方法。如果一不小心,子類的方法名寫錯了,有了@Override之後,編譯時會報錯。也就是說被@Override標註的方法如果沒有覆蓋父類的方法,編譯時報錯。
2.2 @Deprecated
@Deprecated也是一個標記註解,用於修飾一個方法。它表示此方法不推薦使用。無論是繼承、覆蓋或直接使用此方法,編譯器都會給出警告。
2.3 @SuppressWarnings
字面翻譯就是抑制警告,它用於告訴編譯器,對被標註的這句程式碼不要給出特定的警告。
@SuppressWarnings有一些引數用於表示特定的警告:
- deprecation:不要給出“不贊成使用的類或方法的警告”;
- unchecked:不要給出“型別轉換時警告”;
- fallthrough:不要給出”switch語句塊沒有break的警告”;
- path:不要給出“不存在的路徑”的警告;
- serial:不要給出“可序列化類缺少serialVersionUID”的警告;
- finally:不要給出“finally語句塊不能正常完成”的警告;
- all:不要給出以上所有情況的警告。
三、 自定義註解
java允許我們自己定義註解,它提供了元註解用於自定義註解。
3.1 元註解
java提供元註解的目的就是讓開發者自定義註解,元註解負責註解自定義註解。
- @Target;
- @Retention;
- @Documented;
- @Inherited。
Java5.0定義了4個元註解。接下來,分別分析這四個元註解:
3.1.1 @Target
@Target用來說明自定義註解可以用在什麼地方,其ElementType取值有:
1. CONSTRUCTOR:用於描述構造器
2. FIELD:用於描述域
3. LOCAL_VARIABLE:用於描述區域性變數
4. METHOD:用於描述方法
5. PACKAGE:用於描述包
6. PARAMETER:用於描述引數
7. TYPE:用於描述類、介面(包括註解型別) 或enum宣告
使用示例:@Target(ElementType.FIELD)
3.1.2 @Retention
@Retention用來描述自定義註解的生命週期,其RetentionPoicy取值有:
1. SOURCE:在原始檔中有效
2. CLASS:在class檔案中有效
3. RUNTIME:在執行時有效
使用示例:@Retention(RetentionPolicy.RUNTIME)
3.1.3 @Documented
@Documented用於表示自定義註解可以被javadoc之類的工具文件化,沒有成員。
使用示例:@Documented
3.1.4 @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型別被發現,或者到達類繼承結構的頂層。
使用示例:@Inherited
3.2 開始自定義註解
定義註解格式:
public @interface 註解名 {定義體}
註解引數的可支援資料型別:
- 所有基本資料型別(int,float,boolean,byte,double,char,long,short)
- String型別
- Class型別
- enum型別
- Annotation型別
- 以上所有型別的陣列
使用@interface自定義註解時,自動繼承了java.lang.annotation.Annotation介面,由編譯程式自動完成其他細節。在定義註解時,不能繼承其他的註解或介面。
@interface用來宣告一個註解,其中的每一個方法實際上是聲明瞭一個配置引數。方法的名稱就是引數的名稱,返回值型別就是引數的型別(返回值型別只能是基本型別、Class、String、enum)。可以通過default來宣告引數的預設值。
注意:
只能用public或預設(default)這兩個訪問權修飾.例如,String value();這裡把方法設為defaul預設型別;
引數成員只能用基本型別byte,short,char,int,long,float,double,boolean八種基本資料型別和 String,Enum,Class,annotations等資料型別,以及這一些型別的陣列.例如,String value();這裡的引數成員就為String;
- 如果只有一個引數成員,最好把引數名稱設為”value”,後加小括號.
3.3 例子
定義一個註解@Info
- package io.mykit.annotation.jdk.provider;
- import java.lang.annotation.Documented;
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- import java.lang.annotation.Target;
- /**
- * 定義基本資訊Info註解
- * @author liuyazhuang
- *
- */
- @Target(ElementType.TYPE)
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- public@interface Info {
- String name() default"liuyazhuang";
- int age() default18;
- String[] hobby() default {"basketball", "football"};
- }
再定義一個註解@Gender
- package io.mykit.annotation.jdk.provider;
- import java.lang.annotation.Documented;
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- import java.lang.annotation.Target;
- /**
- * 定義性別註解
- * @author liuyazhuang
- *
- */
- @Target(ElementType.FIELD)
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- public@interface Gender {
- publicenum GenderEnum{BOY, GIRL}
- GenderEnum gender() default GenderEnum.BOY;
- }
定義一個User類,使用剛才定義的兩個註解:
- package io.mykit.annotation.jdk.provider.entity;
- import io.mykit.annotation.jdk.provider.Gender;
- import io.mykit.annotation.jdk.provider.Info;
- import io.mykit.annotation.jdk.provider.Gender.GenderEnum;
- /**
- * 以註解標識的類
- * @author liuyazhuang
- *
- */
- @Info(name="liuyazhuang", age = 18, hobby = {"Java", "C", "Python", "Go"})
- publicclass User {
- @Gender(gender = GenderEnum.BOY)
- private String sex;
- public String getSex() {
- return sex;
- }
- publicvoid setSex(String sex) {
- this.sex = sex;
- }
- }
- package io.mykit.annotation.jdk.provider.parser;
- import java.lang.reflect.Field;
- import io.mykit.annotation.jdk.provider.Gender;
- import io.mykit.annotation.jdk.provider.Info;
- /**
- * 解析Info註解和Gender註解
- * @author liuyazhuang
- *
- */
- publicclass AnnotationProcessor {
- /**
- * 解析clazz例項中的指定註解
- * @param clazz:要傳入的clazz物件
- */
- publicstaticvoid parseAnnotation(Class<?> clazz){
- //clazz是以Info註解修飾
- if(clazz.isAnnotationPresent(Info.class)){
- //獲取Info註解
- Info annotation = clazz.getAnnotation(Info.class);
- System.out.println(annotation);
- System.out.println(annotation.name());
- System.out.println(annotation.age());
- String[] bobby = annotation.hobby();
- for(String str : bobby){
- System.out.println(str);
- }
- }
- Field[] fields = clazz.getDeclaredFields();
- if(fields != null && fields.length > 0){
- for(Field field : fields){
- if(field.isAnnotationPresent(Gender.class)){
- Gender annotation = field.getAnnotation(Gender.class);
- System.out.println(annotation);
- System.out.println(annotation.gender());
- }
- }
- }
- }
- }