注解對于開發人員來講既熟悉又陌生,熟悉是因為隻要你是做開發,都會用到注解(常見的@Override);陌生是因為即使不使用注解也照常能夠進行開發;注解不是必須的,但了解注解有助于我們深入了解某些第三方架構(比如Spring,Hibernate,Mybatis等),提高工作效率。
Java注解又稱為标注,是Java從1.5開始支援加入源碼的特殊文法中繼資料;Java中的類、方法、變量、參數、包都可以被注解。這裡提到的中繼資料是描述資料的資料
1. 注解作用
格式檢查: 告訴編譯器資訊,比如被@Override标記的方法如果不是父類的某個方法,IDE會報錯;
減少配置: 運作時動态處理,得到注解資訊,實作代替配置檔案的功能;
減少重複工作:比如第三方架構xUtils,通過注解@ViewInject減少對findViewById的調用,
類似的還有(JUnit、ActiveAndroid等);
2. 注解分三類
1:JDK自帶的注解(Java目前隻内置了三種标準注解:@Override、@Deprecated、@SuppressWarnings,以及四種元注解:@Target、@Retention、@Documented、@Inherited)
2:第三方的注解——這一類注解是我們接觸最多和作用最大的一類
3:自定義注解——也可以看作是我們編寫的注解,其他的都是他人編寫注解
3. 自定義注解
JVM5.0定義了4個标準的元注解:
- @Target,
- @Retention,
- @Documented
- @Inherited
3.1 @Target
作用:用于描述注解的使用範圍
取值ElementType有:
- CONSTRUCTOR:用于描述構造器
- FIELD:用于描述域
- LOCAL_VARIABLE:用于描述局部變量
- METHOD:用于描述方法
- PACKAGE:用于描述包
- PARAMETER:用于描述參數
- TYPE:用于描述類、接口(包括注解類型) 或enum聲明
舉例
@Target(ElementType.TYPE)
public @interface Table {
/**
* 資料表名稱注解,預設值為類名稱
* @return
*/
public String tableName() default "className";
}
@Target(ElementType.FIELD)
public @interface NoDBColumn {
}
注解Table可以用于注解類、接口(包括注解類型)或enum聲明,而注解NoDBColumn僅用于注解類的成員變量。
3.2 @Retention
作用:用于描述注解的生命周期 ,取值RetentionPolicy有:
SOURCE:在源檔案中有效(即源檔案保留)
CLASS:在class檔案中有效(即class保留)
RUNTIME:在運作時有效(即運作時保留)
舉例:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
public String name() default "fieldName";
public String setFuncName() default "setField";
public String getFuncName() default "getField";
public boolean defaultDBValue() default false;
}
Column注解的的RetentionPolicy的屬性值是RUNTIME,這樣注解處理器可以通過反射,擷取到該注解的屬性值,進而去做一些運作時的邏輯處理
3.3 Documented
作用:用于描述其它類型的annotation應該被作為被标注的程式成員的公共API,是以可以被例如javadoc此類的工具文檔化。Documented是一個标記注解,沒有成員。
舉例:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Column {
public String name() default "fieldName";
public String setFuncName() default "setField";
public String getFuncName() default "getField";
public boolean defaultDBValue() default false;
}
3.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
public @interface Greeting {
public enum FontColor{ BULE,RED,GREEN};
String name();
FontColor fontColor() default FontColor.GREEN;
}
3.5 自定義注解
1. 使用interface自定義注解,自動繼承java.lang.annotation.Annotation接口。
2. 不能繼承其他的注解或接口。
3. 每一個方法實際上是聲明了一個配置參數。方法的名稱就是參數的名稱,傳回值類型就是參數的類型(隻能是基本類型、Class、String、enum)。可以通過default聲明參數的預設值。
定義注解格式:
public @interface 注解名(定義體)
注解參數的可支援資料類型:
所有基本資料類型(int,float,boolean,byte,double,char,long,short)
1. String類型
2. Class類型
3. enum類型
4.Annotation類型
以上所有類型的數組
3.6 代碼實踐
注解類代碼
@Retention(RetentionPolicy.RUNTIME) // 注解會在class位元組碼檔案中存在,在運作時可以通過反射擷取到
@Target({ElementType.FIELD,ElementType.METHOD})//定義注解的作用目标**作用範圍字段、枚舉的常量/方法
@Documented//說明該注解将被包含在javadoc中
public @interface FieldAnnotation {
String name() default "哈哈";
}
業務類
public class Student {
@FieldAnnotation(name="牛逼")
private String name;
private String id;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
測試類
public class StudentTest {
public static void main(String[] args) {
Field[] declaredFields = Student.class.getDeclaredFields();
for (Field f : declaredFields) {
System.out.println(f.isAnnotationPresent(FieldAnnotation.class));
if (f.isAnnotationPresent(FieldAnnotation.class)) {
//獲得注解的對象
FieldAnnotation fa = f.getAnnotation(FieldAnnotation.class);
if (fa != null) {
System.out.println(fa.name());
}
}
}
}
}