註解(一種元數據形式)提供有關不屬於程序自己的程序的數據,註解對它們註解的代碼的操做沒有直接影響。html
註解有許多用途,其中包括:java
本課程介紹了可使用註解的位置,以及如何應用註解,Java平臺標準版(Java SE API)中提供了哪些預約義註解類型,類型註解如何與可插拔類型系統結合使用來編寫具備更強類型檢查的代碼,以及如何實現重複註解。程序員
在最簡單的形式中,註解以下所示:正則表達式
@Entity
符號字符(@
)向編譯器指示後面的內容是註解,在如下示例中,註解的名稱爲Override
:segmentfault
@Override void mySuperMethod() { ... }
註解能夠包含元素,這些元素能夠是命名的,也能夠是未命名的,這些元素的值以下:api
@Author( name = "Benjamin Franklin", date = "3/27/2003" ) class MyClass() { ... }
或:數組
@SuppressWarnings(value = "unchecked") void myMethod() { ... }
若是隻有一個名爲value
的元素,則能夠省略該名稱,如:安全
@SuppressWarnings("unchecked") void myMethod() { ... }
若是註解沒有元素,則能夠省略括號,如前面的@Override
示例所示。oracle
也能夠在同一聲明上使用多個註解:框架
@Author(name = "Jane Doe") @EBook class MyClass { ... }
若是註解具備相同的類型,則稱爲重複註解:
@Author(name = "Jane Doe") @Author(name = "John Smith") class MyClass { ... }
從Java SE 8發行版開始,支持重複註解,有關更多信息,請參閱重複註解。
註解類型能夠是Java SE API的java.lang
或java.lang.annotation
包中定義的類型之一,在前面的示例中,Override
和SuppressWarnings
是預約義的Java註解,也能夠定義本身的註解類型,上一個示例中的Author
和Ebook
註解是自定義註解類型。
註解能夠應用於聲明:類、字段、方法和其餘程序元素的聲明,當在聲明中使用時,按照慣例,每一個註解一般出如今它本身的行上。
從Java SE 8發行版開始,註解也能夠應用於類型的使用,這裏有些例子:
類實例建立表達式:
new @Interned MyObject();
輸入:
myString = (@NonNull String) str;
implements
子句:
class UnmodifiableList<T> implements @Readonly List<@Readonly T> { ... }
拋出的異常聲明:
void monitorTemperature() throws @Critical TemperatureException { ... }
這種形式的註解稱爲類型註解,有關更多信息,請參閱類型註解和可插拔類型系統。
許多註解替換代碼中的註釋。
假設一個軟件組一般在每一個類的開頭都帶有註釋,這些註釋提供了重要的信息:
public class Generation3List extends Generation2List { // Author: John Doe // Date: 3/17/2002 // Current revision: 6 // Last modified: 4/12/2004 // By: Jane Doe // Reviewers: Alice, Bill, Cindy // class code goes here }
要使用註解添加相同的元數據,必須先定義註解類型,這樣作的語法是:
@interface ClassPreamble { String author(); String date(); int currentRevision() default 1; String lastModified() default "N/A"; String lastModifiedBy() default "N/A"; // Note use of array String[] reviewers(); }
註解類型定義相似於接口定義,其中關鍵字interface
前面帶有at符號(@
)(@ = AT
,如在註解類型中),註解類型是一種接口形式,將在後面的課程中介紹,目前,你不須要了解接口。
前一個註解定義的主體包含註解類型元素聲明,它看起來很像方法,請注意,他們能夠定義可選的默認值。
定義註解類型後,你可使用該類型的註解,並填入值,以下所示:
@ClassPreamble ( author = "John Doe", date = "3/17/2002", currentRevision = 6, lastModified = "4/12/2004", lastModifiedBy = "Jane Doe", // Note array notation reviewers = {"Alice", "Bob", "Cindy"} ) public class Generation3List extends Generation2List { // class code goes here }
注意:要使@ClassPreamble
中的信息出如今Javadoc
生成的文檔中,必須使用@Documented
註解來註解@ClassPreamble
定義:
// import this to use @Documented import java.lang.annotation.*; @Documented @interface ClassPreamble { // Annotation element definitions }
Java SE API中預約義了一組註解類型,某些註解類型用於Java編譯器使用,有些註解類型應用於其餘註解。
java.lang
中定義的預約義註解類型是@Deprecated
、@Override
和@SuppressWarnings
。
@Deprecated:@Deprecated註解表示標記已棄用和不該該再使用的元素,只要程序使用帶有@Deprecated
註解的方法、類或字段,編譯器就會生成警告。當棄用元素時,也應使用Javadoc @deprecated
標記對其進行記錄,如如下示例所示。在Javadoc註釋和註解中使用at符號(@
)並不是巧合:它們在概念上是相關的,另請注意,Javadoc標記以小寫d
開頭,註解以大寫D
開頭。
// Javadoc comment follows /** * @deprecated * explanation of why it was deprecated */ @Deprecated static void deprecatedMethod() { }
@Override:@Override註解通知編譯器該元素旨在覆蓋超類中聲明的元素,將在接口和繼承中討論重寫方法。
// mark method as a superclass method // that has been overridden @Override int overriddenMethod() { }
雖然在重寫方法時不須要使用此註解,但它有助於防止出錯,若是使用@Override
標記的方法沒法正確覆蓋其某個超類中的方法,則編譯器會生成錯誤。
@SuppressWarnings:@SuppressWarnings註解告訴編譯器抑制它將生成的特定警告,在如下示例中,使用了棄用的方法,編譯器一般會生成警告,可是,在這種狀況下,註解會致使警告被抑制。
// use a deprecated method and tell // compiler not to generate a warning @SuppressWarnings("deprecation") void useDeprecatedMethod() { // deprecation warning // - suppressed objectOne.deprecatedMethod(); }
每一個編譯器警告都屬於一個類別,Java語言規範列出了兩個類別:deprecation
和unchecked
,當在泛型出現以前編寫的遺留代碼接口時,可能會發生unchecked
警告,要禁止多種類別的警告,請使用如下語法:
@SuppressWarnings({"unchecked", "deprecation"})
@SafeVarargs:@SafeVarargs註解在應用於方法或構造函數時斷言代碼不對其varargs
參數執行可能不安全的操做,使用此註解類型時,與varargs
使用相關的未經檢查的警告被抑制。
@FunctionalInterface:Java SE 8中引入的@FunctionalInterface註解,表示類型聲明旨在成爲Java語言規範定義的功能接口。
應用於其餘註解的註解稱爲元註解,java.lang.annotation
中定義了幾種元註解類型。
@Retention:@Retention註解指定標記的註解的存儲方式:
RetentionPolicy.SOURCE
— 標記的註解僅保留在源級別中,並被編譯器忽略。RetentionPolicy.CLASS
— 標記的註解在編譯時由編譯器保留,但Java虛擬機(JVM)會忽略。RetentionPolicy.RUNTIME
— 標記的註解由JVM保留,所以運行時環境可使用它。@Documented:@Documented註解代表,不管什麼時候使用指定的註解,都應使用Javadoc工具記錄這些元素(默認狀況下,註解不包含在Javadoc中),有關更多信息,請參閱Javadoc工具頁面。
@Target:@Target註解標記另外一個註解,以限制能夠應用註解的Java元素類型,目標註解指定如下元素類型之一做爲其值:
ElementType.ANNOTATION_TYPE
能夠應用於註解類型。ElementType.CONSTRUCTOR
能夠應用於構造函數。ElementType.FIELD
能夠應用於字段或屬性。ElementType.LOCAL_VARIABLE
能夠應用於局部變量。ElementType.METHOD
能夠應用於方法級註解。ElementType.PACKAGE
能夠應用於包聲明。ElementType.PARAMETER
能夠應用於方法的參數。ElementType.TYPE
能夠應用於類的任何元素。@Inherited:@Inherited註解代表註解類型能夠從超類繼承(默認狀況下不是這樣),當用戶查詢註解類型而且該類沒有此類型的註解時,將查詢類的超類以獲取註解類型,此註解僅適用於類聲明。
@Repeatable:Java SE 8中引入的@Repeatable註解代表標記的註解能夠屢次應用於相同的聲明或類型使用,有關更多信息,請參閱重複註解。
在Java SE 8發行版以前,註解只能應用於聲明,從Java SE 8發行版開始,註解也能夠應用於任何類型的使用,這意味着能夠在任何使用類型的地方使用註解。使用類型的一些示例包括類實例建立表達式(new
)、類型轉換、implements
子句和throws
子句,這種註解形式稱爲類型註解,註解基礎知識中提供了幾個示例。
建立類型註解是爲了支持改進的Java程序分析,以確保更強的類型檢查,Java SE 8版本不提供類型檢查框架,但它容許你編寫(或下載)類型檢查框架,該框架實現爲與Java編譯器結合使用的一個或多個可插拔模塊。
例如,你但願確保程序中的特定變量永遠不會分配給null
,你想避免觸發NullPointerException
,你能夠編寫自定義插件來檢查此問題,而後,你將修改代碼以註解該特定變量,代表它永遠不會被賦值爲null
,變量聲明可能以下所示:
@NonNull String str;
當你編譯代碼(包括命令行中的NonNull
模塊)時,編譯器會在檢測到潛在問題時輸出警告,容許你修改代碼以免錯誤,在更正代碼以移除全部警告後,程序運行時不會發生此特定錯誤。
你可使用多個類型檢查模塊,其中每一個模塊檢查不一樣類型的錯誤,經過這種方式,你能夠在Java類型系統的基礎上構建,在你但願的時間和位置添加特定的檢查。
經過明智地使用類型註解和可插拔類型檢查器,你能夠編寫更強大且更不容易出錯的代碼。
在許多狀況下,你沒必要編寫本身的類型檢查模塊,有第三方爲你完成了這項工做,例如,你可能但願利用華盛頓大學建立的Checker Framework,該框架包括NonNull
模塊、正則表達式模塊和互斥鎖模塊,有關更多信息,請參閱Checker Framework。
在某些狀況下,你但願將相同的註解應用於聲明或類型用途,從Java SE 8發行版開始,重複註解使你能夠執行此操做。
例如,你正在編寫代碼以使用計時器服務,該服務使你可以在給定時間或某個計劃上運行方法,相似於UNIX cron服務,如今你要設置一個計時器來運行一個方法doPeriodicCleanup
,在該月的最後一天和每一個星期五晚上11點運行,要設置要運行的計時器,請建立一個@Schedule
註解並將其應用於doPeriodicCleanup
方法兩次,第一次使用指定月份的最後一天,第二次使用指定星期五晚上11點,以下面的代碼示例所示:
@Schedule(dayOfMonth="last") @Schedule(dayOfWeek="Fri", hour="23") public void doPeriodicCleanup() { ... }
前面的示例將註解應用於方法,你能夠在使用標準註解的任何位置重複註解,例如,你有一個用於處理未受權訪問異常的類,你可使用一個@Alert
註解爲管理者註解該類,爲管理員註解另外一個註解:
@Alert(role="Manager") @Alert(role="Administrator") public class UnauthorizedAccessException extends SecurityException { ... }
出於兼容性緣由,重複註解存儲在由Java編譯器自動生成的容器註解中,爲了使編譯器執行此操做,代碼中須要兩個聲明。
註解類型必須使用@Repeatable
元註解進行標記,如下示例定義自定義@Schedule
可重複註解類型:
@Repeatable(Schedules.class) public @interface Schedule { String dayOfMonth() default "first"; String dayOfWeek() default "Mon"; int hour() default 12; }
@Repeatable
元註解的值(在括號中)是Java編譯器生成的用於存儲重複註解的容器註解的類型,在此示例中,包含註解類型是Schedules
,所以重複@Schedule
註解存儲在@Schedules
註解中。
將相同的註解應用於聲明而不首先聲明它是可重複的,這會致使編譯時錯誤。
包含註解類型必須具備帶數組類型的value
元素,數組類型的組件類型必須是可重複的註解類型,包含註解類型的Schedules
的聲明以下:
public @interface Schedules { Schedule[] value(); }
Reflection API中有幾種可用於檢索註解的方法,返回單個註解的方法(例如AnnotatedElement.getAnnotation(Class<T>))的行爲未更改,由於若是存在所請求類型的一個註解,它們僅返回單個註解,若是存在多個所請求類型的註解,則能夠經過首先獲取其容器註解來獲取它們,經過這種方式,遺留代碼繼續工做。Java SE 8中引入了其餘方法,它們掃描容器註解以一次返回多個註解,例如AnnotatedElement.getAnnotationsByType(Class<T>),有關全部可用方法的信息,請參閱AnnotatedElement類規範。
設計註解類型時,必須考慮該類型註解的基數,如今可使用註解零次、一次,或者,若是註解的類型標記爲@Repeatable
,則不止一次,經過使用@Target
元註解,還能夠限制註解類型的使用位置。例如,你能夠建立只能在方法和字段上使用的可重複註解類型,仔細設計註解類型很是重要,以確保使用註解的程序員發現它儘量靈活和強大。