由于本人能力有限,文中若有錯(cuò)誤之處,歡迎指正。
轉(zhuǎn)載請(qǐng)注明出處:http://www.lxweimin.com/p/e3949dedbe48
寫在前面
本篇主要講解注解的基本概念、基本使用,以及容易產(chǎn)生迷惑的地方。實(shí)例應(yīng)用將在后續(xù)的簡(jiǎn)文中繼續(xù)分析。
什么是注解?
官方解釋:能夠添加到 Java 源代碼的語(yǔ)法元數(shù)據(jù)。類、方法、變量、參數(shù)、包都可以被注解,可用來將信息元數(shù)據(jù)與程序元素進(jìn)行關(guān)聯(lián)。
注解(也被成為元數(shù)據(jù))為我們?cè)诖a中添加信息提供了一種形式化的方法,使我們可以在稍后某個(gè)時(shí)刻非常方便地使用這些數(shù)據(jù)。
通俗來說:其實(shí)注解也是一種注釋,只不過,注解可以指定保留的時(shí)間,以及攜帶一些相關(guān)的數(shù)據(jù)。而注釋只能在源碼中存在。
元注解
注解當(dāng)中也會(huì)存在注解,這種給自定義注解使用的注解稱為---元注解。
- @Rentention
用來標(biāo)記自定義注解的有效范圍。有三種:
有效范圍 | |
---|---|
RetentionPolicy.SOURCE | 只在源代碼中保留,編譯器會(huì)忽略,一般都是用來增加代碼的理解性或者幫助代碼檢查之類的,比如@Override
|
RetentionPolicy.CLASS |
默認(rèn)選擇,能把注解保留到編譯后的字節(jié)碼class文件中,僅僅到字節(jié)碼文件中,運(yùn)行時(shí)是無法得到的。一般用在動(dòng)態(tài)生成模板代碼的類庫(kù)中。比如ButterKnife
|
RetentionPolicy.RUNTIME | 注解不僅 能保留到class字節(jié)碼文件中,還能在運(yùn)行通過反射獲取到,這是最常用的一種。 |
- @Target
規(guī)定注解所修飾的對(duì)象范圍。
修飾范圍 | |
---|---|
ElementType.CONSTRUCTOR | 構(gòu)造器聲明 |
ElementType.FIELD | 成員變量、對(duì)象、屬性(包括enum實(shí)例) |
ElementType.LOCAL_VARIABLE | 局部變量聲明 |
ElementType.METHOD | 方法聲明 |
ElementType.PACKAGE | 包聲明 |
ElementType.PARAMETER | 參數(shù)聲明 |
ElementType.TYPE | 類、接口(包括注解類型)或enum聲明 |
@Documented
標(biāo)記著此注解會(huì)被javadoc工具提取成文檔。@Interited
允許子類繼承父類中的注解。將在最后以示例的形式講解。
自定義注解
我們可以在使用元注解的基礎(chǔ)上,使用
@interface
關(guān)鍵字實(shí)現(xiàn)自定義注解。其默認(rèn)繼承自:java.lang.annotation.Annotation
。如下所示:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
@Inherited
public @interface MyAnnotation {
int value() default 1;
boolean canBeNull() default false;
}
上面的自定義注解可以這樣使用:
@MyAnnotation @MyAnnotation() @MyAnnotation(1) @MyAnnotation(value=1) @MyAnnotation(canBuNull = true) @MyAnnotation(value = 1,canBuNull = true)
但不能這樣使用:
@MyAnnotation(true)
因?yàn)楫?dāng)括號(hào)中的內(nèi)容不以鍵值對(duì)的形式出現(xiàn)時(shí),默認(rèn)使用的是value =
。
注解元素
注解元素名來自于注解類的方法名,且有以下特點(diǎn):
- 方法返回值只能是
基本數(shù)據(jù)類型
,String
,Class
,annotation
,enum
或它們的一維數(shù)組
。 - 所有方法沒有方法體,沒有參數(shù),沒有修飾符,默認(rèn)為
public abstract
修飾。不允許拋異常。 - 若只有一個(gè)默認(rèn)元素,可直接用
value()
函數(shù)。一個(gè)元素都沒有表示該注解為標(biāo)記注解(Mark Annotation)
。
默認(rèn)值
注解元素必須有確定的值,要么在定義注解的默認(rèn)值中指定,要么在使用注解時(shí)指定。
注解元素的值不能為null
。
@Retention(RetentionPolicy.RUNTIME)
@Target({FIELD, TYPE, METHOD})
public @interface MyAnnotation {
int value() default 1;
boolean[] canWrite() default {true, false};
Class test(); // 定義的時(shí)候沒有默認(rèn)值,在使用的時(shí)候必須指定
}
// 使用示例
@MyAnnotation(value = 23, test = Integer.class)
int age;
@Interited元注解
允許子類繼承父類中的注解。
當(dāng)被@Inherited
標(biāo)注的注解的Retention
為RetentionPolicy.RUNTIME
,則反射API增強(qiáng)了這種繼承性。如果我們使用反射去查詢一個(gè)@Inherited
注解類型的注解時(shí),反射代碼檢查將展開工作:檢查class和其父類
,直到指定的注解類型被發(fā)現(xiàn),或者到達(dá)類繼承結(jié)構(gòu)的頂層Object
。
相對(duì)于其它元注解,@Interited元注解
比較特殊,下面將以示例的形式幫助大家更好的理解它的作用:
首先我們定義一個(gè)自定義注解@MyAnnotation
,并使用@Interited標(biāo)記
:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
@Inherited
public @interface MyAnnotation {
String value() default "";
}
定義一個(gè)類SuperClass
:
@MyAnnotation("SuperClass")
public class SuperClass {
@MyAnnotation("SuperClass::testMethod")
public void testMethod(){
}
}
定義一個(gè)類SubClass
繼承自SuperClass
:
public class SubClass extends SuperClass {
}
測(cè)試主程序:
public class Main {
public static void main(String[] args) throws Exception{
testSuper();
testSub();
}
private static void testSub() {
MyAnnotation myAnnotation = SubClass.class.getAnnotation(MyAnnotation.class);
if(myAnnotation == null){
System.out.println("注解不存在");
return;
}
String value = myAnnotation.value();
System.out.println(value);
}
private static void testSuper() {
MyAnnotation myAnnotation = SuperClass.class.getAnnotation(MyAnnotation.class);
if(myAnnotation == null){
System.out.println("注解不存在");
return;
}
String value = myAnnotation.value();
System.out.println(value);
}
}
運(yùn)行以上程序,testSub()
testSuper()
都打印的是在SuperClass類
標(biāo)記的注解值SuperClass
。而SubClass類
中沒有使用注解,所以,可以確定是從父類中繼承過來的。這里面起到關(guān)鍵作用的就是@Interited元注解
。
還有很多種情況,大家可以自行編碼嘗試,下面給出總結(jié):
注: 父類使用的注解被@Inherited標(biāo)注的情況下
- 如果父類的注解是作用在類上面,那么子類是可以繼承過來。
- 如果父類的注解作用在方法上面,那么子類也可以繼承過來。
- 如果子類重寫了父類中使用了注解的方法,那么子類將無法繼承該方法的注解。
- 實(shí)現(xiàn)類不能繼承接口中的注解。