注解
它可以用于創(chuàng)建文檔玛臂,跟蹤代碼中的依賴性迹冤,甚至執(zhí)行基本編譯時(shí)檢查泡徙。注解是以‘@注解名’在代碼中存在的,根據(jù)注解參數(shù)的個(gè)數(shù),我們可以將注解分為:標(biāo)記注解糖荒、單值注解捶朵、完整注解三類综看。它們都不會(huì)直接影響到程序的語(yǔ)義寓搬,只是作為注解(標(biāo)識(shí))存在,我們可以通過(guò)反射機(jī)制編程實(shí)現(xiàn)對(duì)這些元數(shù)據(jù)(用來(lái)描述數(shù)據(jù)的數(shù)據(jù))的訪問(wèn)镣典。另外,你可以在編譯時(shí)選擇代碼里的注解是否只存在于源代碼級(jí)锡溯,或者它也能在class文件祭饭、或者運(yùn)行時(shí)中出現(xiàn)(SOURCE/CLASS/RUNTIME)倡蝙。
元注解作用
如果要對(duì)于元數(shù)據(jù)的作用進(jìn)行分類,還沒(méi)有明確的定義猪钮,不過(guò)我們可以根據(jù)它所起的作用,大致可分為三類:
編寫文檔:通過(guò)代碼里標(biāo)識(shí)的元數(shù)據(jù)生成文檔笆载。
代碼分析:通過(guò)代碼里標(biāo)識(shí)的元數(shù)據(jù)對(duì)代碼進(jìn)行分析。
編譯檢查:通過(guò)代碼里標(biāo)識(shí)的元數(shù)據(jù)讓編譯器能實(shí)現(xiàn)基本的編譯檢查
1檐蚜、注解的作用分類
(1)生成文檔相關(guān)的注釋說(shuō)明:通過(guò)代碼里標(biāo)識(shí)的注解可以生成文檔相關(guān)的注釋說(shuō)明闯第。
下面我們就來(lái)演示一下咳短,首先我們編寫一個(gè)類咙好,添加相關(guān)的注解勾效。
/**
*@authorMr.wu
*@version1.0
*@since1.8
*/
publicclassAnnotationDemo{
/**
? ? *
*@parama
*@paramb
*@returna+b
? ? */
publicintsum(inta,intb){
returna+b;
}
}
然后使用javadoc指令把我們創(chuàng)建的類生成javadoc文檔层宫。
生成之后其监,如下圖所示:
打開(kāi)文檔,如下圖所示:
我們平時(shí)使用的JDK的文檔就是這樣生成的贮庞。
(2)分析運(yùn)行代碼:通過(guò)代碼里標(biāo)識(shí)的注解對(duì)代碼進(jìn)行分析運(yùn)行[使用反射]究西。 最后會(huì)進(jìn)行演示捉邢。
(3)編譯檢查:通過(guò)代碼里的注解讓編譯器實(shí)現(xiàn)編譯檢查伏伐。 例如:我們平時(shí)經(jīng)常使用的override注解晕拆,當(dāng)我們使用此注解時(shí),編譯器就會(huì)檢查該方法是否重寫了父類(或接口)中的方法吝镣。
2末贾、JDK內(nèi)置注解
JDK中內(nèi)置了三個(gè)基本的注解拱撵,下面就來(lái)介紹一下:
@Override: 限定重寫父類中方法, 該注解只能用于方法拴测;
@Deprecated: 用于表示所修飾的元素(類, 方法等)已過(guò)時(shí)集索,通常是因?yàn)樗揎椀慕Y(jié)構(gòu)危險(xiǎn)或存在更好的選擇务荆;
@SuppressWarnings: 抑制編譯器警告毅厚。
3、JDK中的元注解
元注解就是修飾注解的注解酷窥。JDK中有四個(gè)元注解:
@Target:表示注解能夠作用的位置。
(1) ElementType.TYPE :可以作用在類妆棒、接口和枚舉類上; (2) ElementType.METHOD :可以作用在方法上毅糟; (3) ElementType.FIELD :可以作用在成員變量上姆另; (4) ElementType.CONSTRUCTOR :可以作用在構(gòu)造器上; (5) ElementType.LOCAL_VARIABLE :可以作用在局部變量上甚侣。
@Retention:表示注解的生命周期殷费,即注解被保留的階段宗兼。
(1) RetentionPolicy.SOURCE :在源文件中有效(即源文件保留)氮采,編譯時(shí)編譯器會(huì)直接丟棄這種策略的注解鹊漠; (2) RetentionPolicy.CLASS : 在class文件中有效(即class保留),當(dāng)運(yùn)行Java程序時(shí), JVM不會(huì)保留注解畔师。這是默認(rèn)值 (3) RetentionPolicy.RUNTIME : 在運(yùn)行時(shí)有效(即運(yùn)行時(shí)保留),當(dāng)運(yùn)行 Java 程序時(shí), JVM會(huì)保留注解。程序可以通過(guò)反射獲取該注釋伯铣。
@Documented:表示該注解修飾的注解腔寡,可以被抽取到API文檔中忿磅。 注意:定義為Documented的注解必須設(shè)置Retention值為RetentionPolicy.RUNTIME 葱她。
@Inherited:表示該注解可以被人類繼承。
4览效、自定義注解
自定義注解很簡(jiǎn)單锤灿,格式為:
元注解
public@interface注解
自定義注解時(shí)但校,還有一些要求:
Annotation 的成員變量在 Annotation 定義中以無(wú)參數(shù)方法的形式來(lái)聲明术裸。其方法名和返回值定義了該成員的名字和類型袭艺。我們稱為配置參數(shù)。類型只能是八種基本數(shù)據(jù)類型升敲、String類型、Class類型驴党、enum類型、Annotation類型倔既、以及以上所有類型的數(shù)組攘轩;
可以在定義 Annotation 的成員變量時(shí)為其指定初始值, 指定成員變量的初始值時(shí)使用 default 關(guān)鍵字叉存。指定初始化值的注解歼捏,在使用時(shí)可以不對(duì)成員變量進(jìn)行賦值笨篷。
如果只有一個(gè)成員變量,建議使用參數(shù)名為value率翅;
如果定義的注解含有成員變量,那么使用時(shí)必須進(jìn)行賦值腺晾,除非它有默認(rèn)值,賦值的格式為“成員變量名 = 值”;如果只有一個(gè)成員變量悯蝉,且名稱為value,則可以省略“value=”直接賦值鼻由;
沒(méi)有定義成員變量的 Annotation 稱為標(biāo)記; 包含成員變量的 Annotation 稱為元數(shù)據(jù)。
下面我們就來(lái)自定義一個(gè)簡(jiǎn)單的注解:
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public@interfaceMyAnnotation {
Stringvalue();
}
那么注解的實(shí)質(zhì)是什么呢蔼紧?
我們把我們自定義的注解反編譯一下狠轻,如下圖所示:
publicinterfacezzuli.edu.annotation.MyAnnotationextendsjava.lang.annotation.Annotation{
publicabstractjava.lang.Stringvalue();
}
由反編譯后的代碼可知,注解的本質(zhì)實(shí)際上是一個(gè)接口哩至,并且該接口默認(rèn)繼承了 Annotation 接口蜜自。
5卢佣、JDK8中注解的新特性
Java 8對(duì)注解處理提供了兩點(diǎn)改進(jìn):可重復(fù)的注解及可用于類型的注解戈鲁。
@Repeatable:可重復(fù)注解
當(dāng)我們需要重復(fù)使用某個(gè)注解時(shí)婆殿,并且希望利用相同的注解來(lái)表現(xiàn)不同的形式時(shí)婆芦,我們可以借助@Repeatable注解消约。比如:我們?cè)谏钪幸粋€(gè)人往往是具有多種身份或粮,例如我是一家公司的員工渣锦,同時(shí)我還是我父母的孩子等等泡挺,此時(shí)我們就可以使用@Repeatable注解來(lái)完成娄猫。
在Java 8之前沒(méi)有@Repeatable時(shí)媳溺,我們都是通過(guò)定義注解的數(shù)組來(lái)實(shí)現(xiàn)可重復(fù)注解的悬蔽,如下所示:
//定義一個(gè)表示角色的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Role {
String value();
}
//定義一個(gè)角色數(shù)組的注解,表示可重復(fù)的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Roles {
Role[] value();
}
//使用表示可重復(fù)注解的Roles注解來(lái)表示人所扮演的不同的角色
@Roles({@Role("employee") ,@Role("son")})
publicclassPeople{
}
在Java 8中出現(xiàn)了@Repeatable可重復(fù)注解之后,變得簡(jiǎn)單了很多虽缕。下面我們就來(lái)演示一下伍派,還是使用上面的例子诉植,方便我們進(jìn)行對(duì)比晾腔。
//定義一個(gè)Role注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Roles.class) //表示Role注解是一個(gè)可重復(fù)注解
public @interface Role {
String value();
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Roles {
Role[] value();
}
@Role("employee")
@Role("son")
public class People {
}
類型注解
JDK1.8之后,關(guān)于元注解@Target的參數(shù)類型ElementType枚舉值多了兩個(gè):TYPE_PARAMETER缤至、TYPE_USE嫉到。 (1) ElementType.TYPE_PARAMETER:表示該注解能寫在類型變量的聲明語(yǔ)句中何恶,如:參數(shù)聲明细层、泛型聲明等疫赎; (2) ElementType.TYPE_USE:表示該注解能寫在使用類型的任何語(yǔ)句中捧搞。
6、注解的底層實(shí)現(xiàn)原理
我們?cè)谑褂每蚣軙r(shí)經(jīng)常會(huì)使用注解受葛,那注解底層到底是怎樣執(zhí)行的呢闰渔?下面我們通過(guò)一個(gè)例子來(lái)演示一下:
例子:自定義一個(gè)注解,注解標(biāo)注在哪里就讓哪個(gè)方法執(zhí)行。
//自定義一個(gè)注解
@Retention(RetentionPolicy.RUNTIME)
public @interface MyJunit {
}
public class PrintNumber {
public void showOdd(){//打印奇數(shù)
for(inti =0; i <10; i++) {
if(i %2!=0) {
System.out.print(i+" ");
}
}
}
@MyJunit
public void showEven(){//打印偶數(shù)
for(inti =0; i <10; i++) {
if(i %2==0) {
System.out.print(i+" ");
}
}
}
}
public class JunitTest {
public static void main(String[] args) throws Exception {
//1.創(chuàng)建PrintNumber對(duì)象
PrintNumber printNumber = new PrintNumber();
//2.獲取該類的字節(jié)碼文件對(duì)象
Class clazz = printNumber.getClass();
//3.獲取對(duì)象中的所有方法
Method[] methods = clazz.getMethods();
for(Method method:methods) {
//4.判斷方法上是否有@MyJunit注解
boolean flag = method.isAnnotationPresent(MyJunit.class);
//5.如果方法上有@MyJunit注解狂塘,則執(zhí)行
if(flag){
method.invoke(printNumber);
}
}
}
}
運(yùn)行結(jié)果:
由上述代碼可知廊营,注解底層是通過(guò)反射實(shí)現(xiàn)的。
易錯(cuò)點(diǎn):由于注解默認(rèn)的生命周期是在class文件中有效假哎,當(dāng)運(yùn)行Java程序時(shí), 注解就會(huì)失效惧蛹。 所以我們?cè)谧远x注解時(shí),要想在運(yùn)行時(shí)使用沧烈,則應(yīng)該把注解的生命周期設(shè)置為運(yùn)行時(shí)有效(RetentionPolicy.RUNTIME),否則會(huì)報(bào)錯(cuò)惩歉。
總結(jié)
java提供的Documented元注解跟Javadoc的作用是差不多的锨并,其實(shí)它存在的好處是開(kāi)發(fā)人員可以定制Javadoc不支持的文檔屬性包警,并在開(kāi)發(fā)中應(yīng)用壹瘟。如果對(duì)技術(shù)文章殴俱,以及干貨內(nèi)容,程序員生活類文章,感興趣的朋友們可以關(guān)注收藏轉(zhuǎn)發(fā)一下踱讨,期待下一次的文章嗎,那就抓緊關(guān)注我吧。