玩轉(zhuǎn)Java注解:元注解、內(nèi)置注解、自定義注解的原理和實(shí)現(xiàn)
點(diǎn)擊關(guān)注公眾號(hào),Java干貨及時(shí)送達(dá)

來(lái)源:www.jianshu.com/p/ddd0b880641a
前言
Java 注解(Annotation)又稱 Java 標(biāo)注,是 JDK5.0 引入的一種注釋機(jī)制。重點(diǎn):和 Javadoc 不同,Java 標(biāo)注可以通過(guò)反射獲取標(biāo)注內(nèi)容。
大話空話不用說(shuō)太多,簡(jiǎn)而言之,在編譯器生成類文件時(shí),標(biāo)注 可以被嵌入到字節(jié)碼中。Java 虛擬機(jī)可以保留標(biāo)注內(nèi)容,在運(yùn)行時(shí)可以獲取到標(biāo)注內(nèi)容 。當(dāng)然它也支持自定義 Java 標(biāo)注。反射+注解,是不是感覺(jué)Java變成一種動(dòng)態(tài)語(yǔ)言?哈哈哈!
我覺(jué)得注解可以分為三個(gè)部分來(lái)講:內(nèi)置注解,元注解,自定義注解。
一、內(nèi)置注解
1、@Override 重寫(xiě)
概念:檢查該方法是否是重寫(xiě)方法。如果發(fā)現(xiàn)其父類,或者是引用的接口中并沒(méi)有該方法時(shí),會(huì)報(bào)編譯錯(cuò)誤。
//這個(gè)extends 不要在意,我寫(xiě)上去只是為了更加方便直觀的去理解,Object是萬(wàn)物之源,不寫(xiě)也會(huì)默認(rèn)是其子類,不用解釋過(guò)多吧?
public?class?Annotation1?extends?Object{
????@Override
????public?String?toString?(){
????????return?"我是重新定義過(guò)的toString方法";
????}
}
@Override(重寫(xiě)),這個(gè)大家應(yīng)該很熟悉,重寫(xiě)父類的方法。我們可以看下Object類中toString()是什么樣子的。

那么顯而易見(jiàn),使用了@Override(重寫(xiě))注解,方法名、方法參數(shù)必須得和父類保持一致,否則會(huì)報(bào)錯(cuò)。如下圖所示:

如果不加@Override(重寫(xiě))注解,則正常編譯。

2、@Deprecated 過(guò)期警告
概念:標(biāo)記過(guò)時(shí)方法。如果使用該方法,會(huì)報(bào)編譯警告。在開(kāi)發(fā)中,我們經(jīng)常能遇到這樣的情況,如下圖:

在jdk中有大量這樣的方法,我就不舉例了,自己寫(xiě)一個(gè)可能會(huì)更加方便理解。
public?class?Annotation1?extends?Object{
????public?static?void?main(String[]?args)?{
????????testDeprecated.toString1();
????}
}
class?testDeprecated?{
????@Deprecated
????public?static?String?toString1(){
????????return?"我是重新定義過(guò)的toString方法";
????}
}

注意點(diǎn):這個(gè)不是報(bào)錯(cuò),只是警告,提醒我們這個(gè)方法可能會(huì)有問(wèn)題,可能有更好的方法來(lái)實(shí)現(xiàn)!
3、@SuppressWarnings 忽略警告
概念:指示編譯器去忽略注解中聲明的警告。
平時(shí)開(kāi)發(fā)中,我們會(huì)遇到這樣的情況,如下圖:

這也不是錯(cuò)誤,這是提醒我們,該方法沒(méi)有使用到,警告提醒的作用。加上@SuppressWarnings注解后。
public?class?Annotation1?extends?Object{
????public?static?void?main(String[]?args)?{
????}
????@SuppressWarnings("all")
????public?static?void?testSuppressWarnings(){
????????System.out.println("測(cè)試+testSuppressWarnings忽略警告!");
????}
}
方法成功高亮起來(lái),并且沒(méi)有警告提示了!

我們可以點(diǎn)進(jìn)去看下這個(gè)注解為什么需要參數(shù)?

看這里,這個(gè)不是方法哦,這是參數(shù)。
在注解中的參數(shù)格式:calss + 參數(shù)名 + ()!這個(gè)需要強(qiáng)行記憶哦,回頭我們自定義注解時(shí)也需要用到。換一種寫(xiě)法加深理解!如下圖:

注意點(diǎn):當(dāng)注解中只有一個(gè)參數(shù)時(shí),我們無(wú)需加上參數(shù)名,注解會(huì)自動(dòng)幫我們匹配的。
二、元注解
概念:顧名思義,元注解就是給注解使用的注解!
1、@Retention 作用域-(常用)
概念:表示在什么級(jí)別保存該注解信息。在實(shí)際開(kāi)發(fā)中,我們一般都寫(xiě)RUNTIME,除非項(xiàng)目有特殊需求!我們看下@Retention的源碼。

可以看到,需要一個(gè)參數(shù),進(jìn)參數(shù)瞅瞅。

SOURCE:源代碼時(shí)有用。
CLASS:class文件中有用,但會(huì)被jvm丟棄。
RUNTIME:運(yùn)行時(shí)有用。
關(guān)系:RUNTIME>CLASS>SOURCE
后面我們自定義注解時(shí),每個(gè)都需要用該注解!
2、@Documented 作用文檔
概念:將此注解包含在 javadoc 中 ,它代表著此注解會(huì)被javadoc工具提取成文檔。
老規(guī)矩看下源碼:

無(wú)參的注解,作用域?yàn)?code style="margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;background: rgb(248, 245, 236);color: rgb(255, 53, 2);line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">RetentionPolicy.RUNTIME,運(yùn)行時(shí)有用!這個(gè)只是用來(lái)作為標(biāo)記,了解即可,在實(shí)際運(yùn)行后會(huì)將該注解寫(xiě)入javadoc中,方便查看。
3、@Target 目標(biāo)-(常用)
概念:標(biāo)記這個(gè)注解應(yīng)該是使用在哪種 Java 成員上面!

參數(shù)源碼:

注意這里是數(shù)組格式的參數(shù),證明可以傳多個(gè)值。
@Target(ElementType.TYPE)——接口、類、枚舉、注解@Target(ElementType.FIELD)——字段、枚舉的常量@Target(ElementType.METHOD)——方法@Target(ElementType.PARAMETER)——方法參數(shù)@Target(ElementType.CONSTRUCTOR)——構(gòu)造函數(shù)@Target(ElementType.LOCAL_VARIABLE)——局部變量@Target(ElementType.ANNOTATION_TYPE)——注解@Target(ElementType.PACKAGE)——包
我們來(lái)試一下:

目標(biāo)不對(duì)會(huì)報(bào)錯(cuò)的哦!我們將其改成方法上!編譯即正常通過(guò)。

其他的作用域大家可以去自行嘗試,篇幅問(wèn)題,無(wú)法做到每個(gè)都去試一遍!
4、@Inherited 繼承
概念:標(biāo)記這個(gè)注解是繼承于哪個(gè)注解類(默認(rèn) 注解并沒(méi)有繼承于任何子類)。

這個(gè)很簡(jiǎn)單,就是當(dāng)@InheritedAnno注解加在某個(gè)類A上時(shí),假如類B繼承了A,則B也會(huì)帶上該注解。
5、新注解-(了解即可)
從 Java 7 開(kāi)始,額外添加了 3 個(gè)注解:
@SafeVarargs- Java 7 開(kāi)始支持,忽略任何使用參數(shù)為泛型變量的方法或構(gòu)造函數(shù)調(diào)用產(chǎn)生的警告。@FunctionalInterface- Java 8 開(kāi)始支持,標(biāo)識(shí)一個(gè)匿名函數(shù)或函數(shù)式接口。@Repeatable- Java 8 開(kāi)始支持,標(biāo)識(shí)某注解可以在同一個(gè)聲明上使用多次。
三、自定義注解
我們來(lái)定義一個(gè)屬于自己的注解。
@Retention(value?=?RetentionPolicy.RUNTIME)
@Target(value?=?ElementType.METHOD)
@Inherited
@interface?myAnnotation?{
????String?name()?default?"";
????int?age()?default?18;
????String?like();
????String?IDCard()?default?"";
}
格式:修飾符(pulic)+ @interface +注解名+ {參數(shù)等}
可利用default 設(shè)置默認(rèn)值,設(shè)定了默認(rèn)值后使用注解時(shí)不傳值也不會(huì)報(bào)錯(cuò),反之報(bào)錯(cuò)!

我們只需要傳沒(méi)有默認(rèn)值的參數(shù)即可。
如果不傳則報(bào)錯(cuò):

總結(jié)
主要就是要注意元注解的使用,因?yàn)槲覀冏远x注解時(shí)必須得用到!其實(shí)注解主要配合反射來(lái)用,在此就不展開(kāi)來(lái)敘述了。
往 期 推 薦
1、致歉!抖音Semi Design承認(rèn)參考阿里Ant Design
2、對(duì)比7種分布式事務(wù)方案,還是偏愛(ài)阿里開(kāi)源的Seata,真香!
點(diǎn)分享
點(diǎn)收藏
點(diǎn)點(diǎn)贊
點(diǎn)在看





