一.注解基礎(chǔ)知識
1.注解的格式
??最簡單的注解就像下面這樣:
@Entity
??@符號指示編譯器其后面的內(nèi)容是注解。在下面的例子中,注解的名稱為Override:
@OverridevoidsuperMethod(){...}
??注解可以有若干個屬性??梢栽谑褂米⒔鈺r指定屬性的值:
@Auther(name ="maconn")classMyClass{? ? ...}
??如果注解只有一個屬性,則可以省略屬性的名稱:
@Auther("maconn")classMyClass{? ? ...}
如果注解沒有屬性,或不需要指定屬性的值,則可以省略括號,就像上面的@Override注解。
可以在一個聲明上使用多個注解:
@Auther("maconn")@EntityclassMyClass{? ? ...}
??有些一個注解可以在一個聲明上使用多次,這種注解稱為重復注解:
@Author("maconn")@Author("anotherPerson")classMyClass{ ... }
重復注解時Java8引入的特性。稍后我們將會講解更多有關(guān)重復注解的內(nèi)容。
可以使用java.lang或java.lang.annotation包中的注解,例如前面的Override就是Java中預(yù)定義的注解;也可以定義自己的注解類型,前面的例子中,Entity和Author就是自定義的注解。
2.在什么地方使用注解
??注解可以用在類型的聲明上面,例如類的聲明,域的聲明,方法的聲明等。從Java8開始,注解還可以用在使用這些類型的地方,下面是幾個例子:
創(chuàng)建對象時:
new@InternedMyObject();
類型轉(zhuǎn)換:
myString = (@NotNullString) str;
實現(xiàn)接口時:
classUnmodifiableListimplements@ReadonlyList<@ReadonlyT>{ ... }
拋出異常時:
voidmonitorTemperature()throws@Critical TemperatureException{ ... }
??這種注解稱為類型注解,稍后將進行深入討論。
二.自定義注解
??注解可以用來替換代碼中的部分注釋。假設(shè)一個項目中的每個類都需要一些包含重要信息的注釋:
publicclassGeneration3ListextendsGeneration2List{// Author: maconn// Date: 2018/12/23// Current revision: 1.0// Last modified: 2018/12/23...}
??要使用注解添加相同的元數(shù)據(jù),必須先定義注解:
@interfaceClassPreamble {Stringauthor();Stringdate();doublecurrentRevision()default1;StringlastModified()default"N/A";}
注解的定義看上去有點像接口的定義,只不過interface關(guān)鍵字前面多了一個@符號。注解實際上是一種特殊的接口,有關(guān)這部分的內(nèi)容會在后面介紹到。
上面的注解定義中包含了注解屬性的聲明,看上去就像定義了一個方法一樣??梢詾閷傩远x默認值。
定義好注解之后,就可以向下面這樣使用了:
@ClassPreamble(? author ="maconn",? date ="2018/12/23",? currentRevision =1.0,? lastModified ="2018/12/23",)publicclassGeneration3ListextendsGeneration2List{? ? ...}
三.預(yù)定義注解
??在Java API中已經(jīng)為我們預(yù)先定義了幾個注解。這其中有幾個是供Java編譯器使用的,還有些注解是用在別的注解上的。
給Java編譯器使用的注解
@Deprecated:@Deprecated注解表示被標記的元素已經(jīng)被棄用或者說不再推薦使用。如果在程序中使用帶有@Deprecated注解的方法、類或域,編譯器就會給出警告。當一個元素被棄用時,也應(yīng)該在同時在Javadoc中使用@deprecated(注意Javadoc中的@deprcated首字母是小寫)標記,就像下面這樣:
/**? *@deprecated* explanation of why it was deprecated? */@DeprecatedstaticvoiddeprecatedMethod(){}
@Override:@Override注解告訴編譯器該元素旨在覆蓋超類中聲明的元素。假設(shè)超類中聲明了一個int overriddenMethod()方法,當在子類中重寫這個方法時,可以加上@Override注解,就像下面這樣:
@OverrideintoverriddenMethod(){? ? ...}
雖然在重寫方法時不需要使用此批注,但使用它有助于防止出錯。如果@Override標記的方法無法正確覆蓋其超類中的方法,則編譯器會給出錯誤。
@SuppressWarnings:@SuppressWarnings注解可以讓編譯器忽略它指定的警告。在以下示例中,使用了不推薦使用的方法,編譯器通常會生成警告。但是,在添加@SuppressWarnings注解后,編譯器將不再給出警告。
@SuppressWarnings("deprecation")voiduseDeprecatedMethod(){? ? objectOne.deprecatedMethod();}
??每個警告都屬于一個類別。在Java中警告有兩個類別:deprecation和unchecked。如果要同時忽略這兩種警告,可以使用以下語法:
@SuppressWarnings({"unchecked","deprecation"})
@SafeVarargs:當應(yīng)用于方法或構(gòu)造函數(shù)時,@SafeVarargs注解斷言代碼不會對可變參數(shù)列表執(zhí)行潛在的不安全操作。使用此注注解時,有關(guān)可變參數(shù)列表的unchecked警告將會被忽略。
@SafeVarargspublicstaticTvoiduseVarargs(T... args){returnargs.length >0? args[0] :null;? }
@FunctionalInterface:@FunctionalInterface注解是Java8引入的注解,作用在接口上以表明該接口是函數(shù)式接口(函數(shù)式接口是指只有一個抽象方法的接口)。
作用于其他注解的注解
作用于其他注解的注解被稱為元注解。在java.lang.annotation包中定義了以下幾個元注解:
@Retention:@Retention注解用于定義注解的保留策略:
RetentionPolicy.SOURCE - 注解僅存在于源碼中,編譯時將會被忽略。
RetentionPolicy.CLASS - 注解會在class字節(jié)碼文件中存在,但會被JVM忽略。
RetentionPolicy.RUNTIME - 注解會被JVM保留,因此可以在運行時環(huán)境使用。
@Documented:@Documented注解用來定義被標注的注解在使用時是否會出現(xiàn)在Javadoc文檔中??紤]下面的例子:
importjava.lang.annotation.Documented ;@MyAnnotation(name="maconn")publicclassAnnotationDemo{publicvoidfoo(){}}@Documented@interfaceMyAnnotation{publicStringname();}
??在上面的例子中,我們自定義了一個注解MyAnnotation,注意它的定義上有一個@Documented注解。我們在AnnotationDemo類上使用了@MyAnnotation,然后這個源文件使用javadoc命令提取文檔,結(jié)果如下:

可以看到,AnnotationDemo類上的注解@MyAnnotation出現(xiàn)在了文檔中。正常情況下,沒有@Documented注解的注解是不會出現(xiàn)在文檔中的。下面的例子中,我們?nèi)サ鬗yAnnotation注解上的@Documented:
importjava.lang.annotation.Documented ;@MyAnnotation(name="maconn")publicclassAnnotationDemo{publicvoidfoo(){}}@interfaceMyAnnotation{publicStringname();}
??然后重新生成文檔:

可以看到,@MyAnnotation注解并沒有出現(xiàn)在文檔里。
@Target:@Target注解作用在另外一個注解上用來限制這個注解可以用在哪些類型上:
ElementType.ANNOTATION_TYPE 可以應(yīng)用于注解類型。
ElementType.CONSTRUCTOR 可以應(yīng)用于構(gòu)造函數(shù)。
ElementType.FIELD 可以應(yīng)用于域。
ElementType.LOCAL_VARIABLE 可以應(yīng)用于局部變量。
ElementType.METHOD 可以應(yīng)用于方法。
ElementType.PACKAGE 可以應(yīng)用于包聲明。
ElementType.PARAMETER 可以應(yīng)用于方法的參數(shù)。
ElementType.TYPE 可以應(yīng)用于類,接口或枚舉類型。
@Inherited:@Inherited注解表明子類可以繼承此注解,如果一個類使用此注解,則它的子類也繼承此注解。此注解僅適用于類聲明。
@Repeatable:@Repeatable是Java8中引入的注解。@Repeatable注解標記的注解可以在一個類型上使用多次。
四.重復注解
??有些時候,可能需要將多個相同的注解用在一個類型上。從Java8開始,可以使用重讀注解做到這一點。例如,假設(shè)我們要編寫一個定時任務(wù)?,F(xiàn)在要設(shè)置定時器在每個月的最后一天和每個周五的23:00運行方法doSomething。要設(shè)置定時器,需要創(chuàng)建一個@Schedule注解并將其應(yīng)用于doSomething方法兩次。如下面的代碼所示:
@Schedule(dayOfMonth =“l(fā)ast”)@Schedule(dayOfWeek =“Fri”,hour =“23”)publicvoiddoSomeThing(){? ? ...}
??出于兼容性的原因,重復注解被存儲在由Java編譯器生成的容器注解內(nèi)。為了使編譯器執(zhí)行此操作,需要以下兩個步驟:
第1步:聲明一個可重復的注解
??要重復的注解上一定要使用元注解@Repeatable標記。下面的例子定義了重復注解@Schedule:
importjava.lang.annotation.Repeatable;@Repeatable(Schedules.class)public@interfaceSchedule {StringdayOfMonth()default"first";StringdayOfWeek()default"Mon";inthour()default12;}
@Repeatable注解的值,是用來存儲這個重復注解的容器注解。在這個例子中,@Repeatable注解的值是Schedules.class,因此重復注解@Schedule都存儲在@Schedules中。
如果在同一個類型上使用多個相同的注解并且這個注解不是重復注解時,將會產(chǎn)生編譯時錯誤。
第2步:聲明容器注解
??容器注解必須有一個數(shù)組類型的value元素,且數(shù)組元素的類型必須是一個可重復注解。下面聲明了容器注解@Schedules:
public@interfaceSchedules {? ? Schedule[] value();}