什麼是注解?
對于很多初次接觸的開發者來說應該都有這個疑問?Annontation是Java5開始引入的新特征,中文名稱叫注解。它提供了一種安全的類似注釋的機制,用來将任何的資訊或中繼資料(metadata)與程式元素(類、方法、成員變量等)進行關聯。為程式的元素(類、方法、成員變量)加上更直覺更明了的說明,這些說明資訊是與程式的業務邏輯無關,并且供指定的工具或架構使用。Annontation像一種修飾符一樣,應用于包、類型、構造方法、方法、成員變量、參數及本地變量的聲明語句中。
Java注解是附加在代碼中的一些元資訊,用于一些工具在編譯、運作時進行解析和使用,起到說明、配置的功能。注解不會也不能影響代碼的實際邏輯,僅僅起到輔助性的作用。包含在 java.lang.annotation 包中。
注解的用處:
1、生成文檔。這是最常見的,也是java 最早提供的注解。常用的有@param @return 等
2、跟蹤代碼依賴性,運作時動态處理,擷取資訊,實作替代配置檔案功能。比如Dagger 2 依賴注入,未來java 開發,将大量注解配置,具有很大用處;
3、在編譯時進行格式檢查。如@override 放在方法前,如果你這個方法并不是覆寫了超類方法,則編譯時就能檢查出。
注解的原理:

Java也為我們提供了一些常用的注解:
@Override,代表某方法是重寫父類中的方法
@Deprecated,表示被标記的内容已經過時、不建議被使用,如果被使用編譯器會報警告,但程式也能正常運作。
@SuppressWarnings,由于内容被@Deprecated标記後,編譯器會有警告,如果想忽略警告可以使用@SuppressWarnings
自定義注解::
自定義注解類編寫的一些規則:
- Annotation 型定義為@interface, 所有的Annotation 會自動繼承java.lang.Annotation這一接口,并且不能再去繼承别的類或是接口.
- 參數成員隻能用public 或預設(default) 這兩個通路權修飾
- 參數成員隻能用基本類型byte、short、char、int、long、float、double、boolean八種基本資料類型和String、Enum、Class、annotations等資料類型,以及這一些類型的數組.
- 要擷取類方法和字段的注解資訊,必須通過Java的反射技術來擷取 Annotation 對象,因為你除此之外沒有别的擷取注解對象的方法
-
注解也可以沒有定義成員,,不過這樣注解就沒啥用了
PS:自定義注解需要使用到元注解
如何定義一個注解呢?可以先看看這些已有的注解是如何實作的,我們先以
@Override
為例:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
可以看到注解是通過
@interface
關鍵字來定義的,和接口的定義類似,但是又多了
@Target()
、
@Retention()
,這些是java中的元注解,元注解可以了解為内置的基礎注解,用來限定、說明自定義注解。除了這兩個元注解外,還有三個元注解
@Inherited
、
@Repeatable
、
@Documented
,後邊會詳細解釋這些元注解的作用!
再看看
@SuppressWarnings
注解的實作:
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}
和
@Override
相比最大的差別就是注解體中多了
String[] value();
,它代表注解的屬性!關于注解屬性也會在後邊詳細的介紹!
是以定義注解時,除了使用
@interface
還需要考慮元注解、注解屬性,一個自定義注解的僞碼如下:
@元注解0
@元注解1
@元注解2
public @interface 注解名稱 {
類型 attr0();
類型 attr1();
}
元注解:
java.lang.annotation 提供了四種元注解,專門注解其他的注解(在自定義注解的時候,需要使用到元注解):
@Documented – 注解是否将包含在JavaDoc中
@Retention – 什麼時候使用該注解
@Target – 注解用于什麼地方
@Inherited – 是否允許子類繼承該注解
1.)@Retention – 定義該注解的生命周期
● RetentionPolicy.SOURCE : 在編譯階段丢棄。這些注解在編譯結束之後就不再有任何意義,是以它們不會寫入位元組碼。@Override, @SuppressWarnings都屬于這類注解。
● RetentionPolicy.CLASS : 在類加載的時候丢棄。在位元組碼檔案的進行中有用。注解預設使用這種方式
● RetentionPolicy.RUNTIME : 始終不會丢棄,運作期也保留該注解,是以可以使用反射機制讀取該注解的資訊。我們自定義的注解通常使用這種方式。
2.)Target – 表示該注解用于什麼地方。預設值為任何元素,表示該注解用于什麼地方。可用的ElementType 參數包括
● ElementType.CONSTRUCTOR: 用于描述構造器
● ElementType.FIELD: 成員變量、對象、屬性(包括enum執行個體)
● ElementType.LOCAL_VARIABLE: 用于描述局部變量
● ElementType.METHOD: 用于描述方法
● ElementType.PACKAGE: 用于描述包
● ElementType.PARAMETER: 用于描述參數
● ElementType.TYPE: 用于描述類、接口(包括注解類型) 或enum聲明
3.)@Documented – 一個簡單的Annotations 标記注解,表示是否将注解資訊添加在java 文檔中。
4.)@Inherited – 定義該注釋和子類的關系
@Inherited 元注解是一個标記注解,@Inherited 闡述了某個被标注的類型是被繼承的。如果一個使用了@Inherited 修飾的annotation 類型被用于一個class,則這個annotation 将被用于該class 的子類。
例子:
@Retention(RetentionPolicy.RUNTIME) // 注解會在class位元組碼檔案中存在,在運作時可以通過反射擷取到
@Target({ElementType.FIELD,ElementType.METHOD})//定義注解的作用目标**作用範圍字段、枚舉的常量/方法
@Documented//說明該注解将被包含在javadoc中
@Inherited //這個注解将被用于該class 的子類
public @interface FieldMeta {
/**
* 注解屬性:下面介紹
* @return
*/
boolean id() default false;
/**
* 注解屬性:字段名稱,default 為預設
* @return
*/
String name() default "";
}
注解屬性
在注解中定義屬性和在接口中定義方法的格式類似,例如:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
String name();
int age() default 18;
String[] favour();
}
這樣就給Test注解定義了name、age兩個屬性,并用default關鍵字指定age的預設值為18。可以這樣使用定義好的注解:
@TestAnnotation(name = "Tom", age = 12, favour = {"music", "sports"})
public class Test {
}
由于age有預設值,可以在使用注解時不指定它的值。由于favour的類型為數組,是以當其有多個值時需要用{}包起來。
如果自定義注解沒有屬性或者屬性有預設值,則使用時可以直接寫@TestAnnotation,省略後邊的括号。
注解的屬性支援的資料類型如下:
- 基本類型(byte、short、int、float、double、long、char、boolean),不包括其對應的包裝類型
- String
- . Class,即Class<?>
- enum,例如enum staus {A, B, C}
- 注解,例如Override test();
及上述類型對應的數組
注解相關的文法糖就介紹到這裡了,接下來要關注的是當一個類、方法、屬性等使用了注解後,如何提取注解上的資訊。
注解與反射
要提取注解上的資訊,就要用到反射相關的知識了,下面看一個完整的例子,首先定義TestAnnotation注解,可以作用在類、字段、方法聲明的地方,并可以在運作時被擷取,以及三個屬性:
@Target(value = {ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
String name();
int age() default 18;
String[] favour();
}
在Test的類、字段、方法聲明的地方分别使用TestAnnotation注解:
@TestAnnotation(name = "Test", age = 20, favour = {"music", "sports"})
public class Test {
@TestAnnotation(name = "testField", favour = {"reading", "sports"})
private int testField;
@TestAnnotation(name = "testMethod", age = 10, favour = {"dancing", "music"})
public void testMethod() {
}
@TestAnnotation(name = "testMethod1", age = 12, favour = {"music"})
public void testMethod1() {
}
}
通過反射的方式提取注解資訊:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
resolve();
}
private void resolve() {
// 解析類上的注解
boolean isPresent = Test.class.isAnnotationPresent(TestAnnotation.class);
if (isPresent) {
TestAnnotation annotation = Test.class.getAnnotation(TestAnnotation.class);
showAnnotation(annotation);
}
// 解析字段上的注解
Field[] fields = Test.class.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(TestAnnotation.class)) {
TestAnnotation annotation = field.getAnnotation(TestAnnotation.class);
showAnnotation(annotation);
}
}
// 解析方法上的注解
Method[] methods = Test.class.getDeclaredMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(TestAnnotation.class)) {
TestAnnotation annotation = method.getAnnotation(TestAnnotation.class);
showAnnotation(annotation);
}
}
}
private void showAnnotation(TestAnnotation annotation) {
Log.e("Annotation", annotation.name() + "#" + annotation.age() + "#" + Arrays.toString(annotation.favour()));
}
}
運作後的效果如下:
其中涉及到了幾個關鍵的方法,Class、Method、Field等類都有這樣的方法:
boolean isAnnotationPresent(Class<? extends Annotation> annotation),用來判斷是否使用了某個注解。
public <A extends Annotation> A getAnnotation(Class<A> annotation),獲得指定名稱的注解對象。
public Annotation[] getAnnotations(),傳回對應元素的全部注解。
public Annotation[] getDeclaredAnnotations(),傳回直接在對應元素上使用的注解,不包括父類的注解。
boolean isAnnotationPresent(Class<? extends Annotation> annotation),用來判斷是否使用了某個注解。
public <A extends Annotation> A getAnnotation(Class<A> annotation),獲得指定名稱的注解對象。
public Annotation[] getAnnotations(),傳回對應元素的全部注解。
public Annotation[] getDeclaredAnnotations(),傳回直接在對應元素上使用的注解,不包括父類的注解。