Java注釋Annotation從JDK 5開(kāi)始,Java增加了對(duì)元數(shù)據(jù)(MetaData)的支持,也就是Annotation(注釋?zhuān)nnotation提供了一種為程序元素設(shè)置元數(shù)據(jù)的方法,程序元素包括修飾包、類(lèi)、構(gòu)造器、方法、成員變量、參數(shù)、局部變量,從某些方面來(lái)看,Annotation就想修飾符一樣,可用于程序元素的聲明,這些信息被存儲(chǔ)在Annotation的”name = value”對(duì)中。需要注意的是, Annotation是一個(gè)接口,程序可以通過(guò)反射來(lái)獲取指定程序元素的Annotation對(duì)象,然后通過(guò)Annotation對(duì)象來(lái)取得注釋里的元數(shù)據(jù),且不會(huì)影響程序代碼的執(zhí)行,無(wú)論增加、刪除Annotation,代碼都始終如一地執(zhí)行。如果希望讓程序中的Annotation在運(yùn)行時(shí)起一定的作用,只有通過(guò)某種配套的工具對(duì)Annotation中的信息進(jìn)行訪問(wèn)和處理,訪問(wèn)和處理Annotation的工具統(tǒng)稱(chēng)為APT(Annotation ProcessingTool)。?一、Annotation基礎(chǔ)前面已經(jīng)講到APT負(fù)責(zé)提取Annotation里包含的元數(shù)據(jù),那么我們先了解一下4個(gè)基本Annotation的使用,需要在Annotation前面加上@符號(hào),以修飾符的形式放在程序元素的前面。4個(gè)基本的Annotation如下:@Override@Deprecated@SuppressWarnings@SafeVarargs1、@Override——限定重寫(xiě)父類(lèi)方法顧名思義,@Override就是用來(lái)重寫(xiě)方法的,它只能作用于方法,不能作用于其他程序元素,它可以強(qiáng)制一個(gè)子類(lèi)必須覆蓋父類(lèi)的方法。通常情況下會(huì)被省略,例如下面的程序指定了子類(lèi)Circle的draw()方法必須重寫(xiě)父類(lèi)方法:class?Shape{???void?draw(){??????System.out.println("畫(huà)一個(gè)形狀");???}}class?Circle?extends?Shape{???//將@Override省略效果相同???@Override???void?draw() {??????System.out.println("畫(huà)一個(gè)圓形");???}}但是@Override作為一個(gè)標(biāo)識(shí),可以告訴我們?cè)摲椒ㄔ谄涓割?lèi)中是不是存在,或者我們需要重寫(xiě)的方法是否跟父類(lèi)中的方法一致,例如上面的程序,如果我把draw()寫(xiě)成darw(),系統(tǒng)將會(huì)報(bào)編譯錯(cuò)誤。?2、@Deprecated——標(biāo)示已過(guò)時(shí)用于表示某個(gè)程序元素(類(lèi)、方法等)已過(guò)時(shí),當(dāng)其他程序使用已過(guò)時(shí)的類(lèi)、方法時(shí),編譯器將會(huì)給出警告。例如:public?classTest{???public?static?void?main(String[] args) {??????//下面調(diào)用shape的draw方法時(shí),編譯器會(huì)給出警告??????new?Shape().draw();???}}?class?Shape{???@Deprecated???void?draw(){??????System.out.println("畫(huà)一個(gè)形狀");???}}?3、@supressWarnings——抑制編譯器警告指示被該Annotation修飾的程序元素(以及該程序元素中的所有子元素)取消顯示指定的編譯器警告。@SupressWarnings會(huì)抑制作用于該程序元素的所有子元素,例如,使用@SupressWarnings修飾某個(gè)類(lèi)取消顯示某個(gè)編譯器警告,同時(shí)又修飾該類(lèi)里的某個(gè)方法取消顯示另一個(gè)編譯器警告,那么該方法將會(huì)同時(shí)取消顯示這兩個(gè)編譯器警告。在通常情況下,如果程序中使用沒(méi)有泛型限制的集合,將會(huì)引起編譯器的警告,為了避免編譯器的警告,可以使用@SupressWarnings修飾,例如://關(guān)閉整個(gè)類(lèi)里的編譯器警告@SuppressWarnings("unchecked")public?class?Test{???public?static?void?main(String[]args) {??????List<String>?tempList?=?new?ArrayList();???}}?4、Java7的“堆污染”警告與@SafeVarargs要理解堆污染,首先看一個(gè)例子:?????List list =?new?ArrayList<Integer>();??????list.add(10);//添加元素時(shí)引發(fā)unchecked異常??????//下面代碼引起“未經(jīng)檢查的轉(zhuǎn)換”的警告,但編譯、運(yùn)行時(shí)完全正常??????List<String> temp = list;??????//但只要訪問(wèn)temp里的元素,就會(huì)引起運(yùn)行時(shí)異常??????System.out.println(temp.get(0));如上所示,將list賦值給temp就造成了所謂的“堆污染”當(dāng)把一個(gè)不帶泛型的對(duì)象賦值給一個(gè)帶泛型的變量時(shí),往往就會(huì)引發(fā)“堆污染”。對(duì)于形參個(gè)數(shù)可變的方法,如果該形參的類(lèi)型是泛型,就更容易引發(fā)“堆污染”了,例如下面的方法中,相當(dāng)于把List<String>賦值給了List。該方法首先將listArray與listStrArray指向同一內(nèi)存地址,然后將整型集合賦值給listArray的第一個(gè)元素,再使用listStrArray取出第一個(gè)元素時(shí),將引發(fā)ClassCastException異常:??public?voidtemp(List<String>... listStrArray){??????List[] listArray = listStrArray;??????List<Integer> tempList = Arrays.asList(99);??????listArray[0] = tempList;??????String s = listStrArray[0].get(0);}而我們這里要講的@SafeVarargs,就是Java7專(zhuān)門(mén)用來(lái)一直“堆污染”警告而提供的Annotation。除此之外,另一種方法就是我們上面所將的,使用@SuppressWarning(“unchecked”)修飾。?二、JDK的元Annotation你可能會(huì)覺(jué)得這4個(gè)Annotation使用起來(lái)不太方便,認(rèn)為Annotation只有這么幾個(gè)功能。其實(shí),這只是Annotation的鳳毛麟角,如果我們想深入了解Annotation,那就得自定義Annotation。在講自定義Annotation之前,首先要說(shuō)的是元Annotation。因?yàn)锳nnotation并不是程序元素,甚至對(duì)其進(jìn)行增刪改也不會(huì)影響程序的正常運(yùn)行。那么如果我們想讓Annotation在程序運(yùn)行時(shí)也起作用,該怎么辦呢?我們要講的元Annotation就是來(lái)干這個(gè)的,它用來(lái)修飾其他的Annotation定義,使Annotation具有不同的作用域。1、使用@Retention我們?cè)趧?chuàng)建自定義Annotation時(shí),可以使用@Retention來(lái)修飾這個(gè)Annotation,用于指定被修飾的Annotation的作用域。@Retention包含一個(gè)RetentionPolicy類(lèi)型的value成員變量,其值可以使如下3個(gè):RetentionPolicy.Class:默認(rèn)值,編譯器將吧Annotation存儲(chǔ)于class文件中,但運(yùn)行Java程序時(shí),JVM不會(huì)保留Annotation,即只存儲(chǔ)但不參與程序運(yùn)行;RetentionPolicy.RUNTIME:編譯器將吧Annotation存儲(chǔ)于class文件中,運(yùn)行Java程序時(shí),JVM也會(huì)保留Annotation,即存儲(chǔ)且參與程序運(yùn)行;RetentionPolicy.SOURCE:編譯器不會(huì)存儲(chǔ)Annotation到class文件中,運(yùn)行Java程序時(shí)JVM也不會(huì)保留Annotation,即不存儲(chǔ)不保留。很多時(shí)候我們需要通過(guò)反射獲取注釋信息,所以就需要使用value屬性值為RetentionPolicy.RUNTIME的@Retention,例如下面我們定義了一個(gè)名為Param的Annotation,并且可以通過(guò)反射來(lái)獲取它的注釋信息:@Retention(RetentionPolicy.RUNTIME)public?@interface?Param?{???long?id();???String name();???int?sex()?default?1;}?2、使用@Target@Target也可以用來(lái)修飾Annotation定義,它用于指定被修飾的Annotation能用于修飾哪些程序單元,其value值有如下幾個(gè):ElementType.ANNOTATION_TYPE:指定該策略的Annotation只能修飾Annotation;ElementType.CONSTRUCTOR:只能修飾構(gòu)造器;ElementType.FIELD:只能修飾成員變量;ElementType.LOCAL_VARIABLE:只能修飾局部變量;ElementType.METHOD:只能修飾方法定義;ElementType.PACKAGE:只能修飾包定義;ElementType.PARAMETER:可以修飾參數(shù);ElementType.TYPE:可以修飾類(lèi)、接口(包括注釋類(lèi)型)或枚舉定義。下面的例子中,定義Param只能修飾方法:@Target(ElementType.METHOD)public?@interface?Param?{???long?id();???String name();???int?sex()?default?1;}3、使用@Document@Document用于指定被它修飾的Annotation類(lèi)將被javadoc工具提取成文檔。如果定義Annotation類(lèi)時(shí)使用了@Document修飾,則所有使用該Annotation修飾的程序元素的API文檔中將會(huì)包含該Annotation說(shuō)明。舉個(gè)例子來(lái)說(shuō)明:@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)@Documentedpublic?@interface?Param?{???long?id();???String name();???int?sex()?default?1;}public?class?Dulven {???public?static?void?main(String[]args) {?}???@Param(id=1001001,name="GISirFive")???public?void?toPerson() {?}}用javadoc提取API,打開(kāi)API后是這樣的:如果在定義Param時(shí)不加@Document,就不會(huì)顯示@Param()4、使用@Inherited@Inherited用于指定被其修飾的Annotation定義具有繼承性,被該Annotation修飾的類(lèi)如果有子類(lèi),則子類(lèi)將會(huì)自動(dòng)被該Annotation修飾。舉個(gè)例子:@Inheritedpublic?@interface?Param?{???long?id();???String name();???int?sex()?default?1;}@Param(id=110, name="singer")class?Base{??}?public?class?Dulven?extends?Base{???public?static?void?main(String[]args) {???System.out.println(Dulven.class.isAnnotationPresent(Param.class));???}}上面程序運(yùn)行會(huì)輸出true。???????????????????????????????????????????????三、自定義Annotation除了這4個(gè)基本的Annotation,細(xì)心的程序員可能會(huì)發(fā)現(xiàn)還有很多未知的Annotation。其實(shí)除了這4個(gè)基本的Annotation外,大多數(shù)都是我們自定義的,我們可以通過(guò)自定義Annotation來(lái)使代碼更通俗易懂。1、定義Annotation定義一個(gè)新的Annotation與定義一個(gè)接口類(lèi)似,需要使用@interface關(guān)鍵字,例如下面定義了一個(gè)名為Param的Annotation,并在Test類(lèi)中使用它:public?@interface?Param?{????}?@Parampublic?class?Test {???public?static?void?main(String[]args) {?????}}在默認(rèn)情況下,Annotation可用于修飾任何程序元素,包括類(lèi)、接口、方法等。如普通方法一樣,Annotation還可以帶成員變量,Annotation的成員變量在Annotation定義中以無(wú)形參的方法形式來(lái)聲明,其方法名和返回值定義了該成員變量的名字和類(lèi)型,例如:public?@interface?Param?{???long?id();???String name();???int?sex()?default?1;}一旦在Annotation里定義了成員變量,使用時(shí)就需要為其指定值,當(dāng)然我們也可以為成員變量指定默認(rèn)值,默認(rèn)值制定方法如同上面的sex,其默認(rèn)值為1。這樣我們?cè)谑褂脮r(shí)就不需要為其指定值了,例如:@Param(id=1001, name="旺旺")public?class?Animal {???public?static?void?main(String[]args) {?}}我們可以將Annotation按是否包含成員變量分為兩類(lèi):標(biāo)記Annotation:指沒(méi)有定義成員變量的Annotation。這種Annotation僅利用自身的存在與否來(lái)為我們提供信息,例如@Override、@Deprecated等。元數(shù)據(jù)Annotation:指包含成員變量的Annotation,因?yàn)樗鼈兛梢越邮芨嗟脑獢?shù)據(jù)。?2、提取Annotation信息當(dāng)開(kāi)發(fā)者使用Annotation修飾了類(lèi)、方法、Field等成員之后,正如Annotation的定義所言,Annotaion不能影響程序代碼的執(zhí)行,無(wú)論增加、刪除Annotation,代碼都始終如一的執(zhí)行。只有通過(guò)apt工具對(duì)Annotation中的信息進(jìn)行訪問(wèn)和處理,我們才能讓程序中的Annotation在程序運(yùn)行時(shí)起一定的作用。所以這些Annotation不會(huì)自己生效,必須由開(kāi)發(fā)者提供相應(yīng)的工具來(lái)提取并處理Annotation信息。Java使用Annotation接口來(lái)代表程序元素前面的注釋?zhuān)摻涌谑撬蠥nnotation類(lèi)型的付接口。Java5在
添加回答
舉報(bào)
0/150
提交
取消