java編程思想之註解
註解 (元數據) 為我們在代碼中添加信息提供了一種形式化的方法,使我們可以在稍後的某個時刻非常方便的使用這些數據。
註解在一定程度上是在把元數據與源代碼文件結合在一起,而不是保存在外部文檔中。註解是眾多引入 javaSE5 中的重要語言變化之一。他們可以提供用來完整地描述程序所需的信息,而這些信息是無法用 Java 來表達的。註解可以用來生成描述文件,甚至或是新的類定義,並且有助於編寫減輕樣板代碼的負擔。通過使用註解,我們可以將這些元數據保存在 Java 源代碼中,並利用 annotation API 為自己的註解構造處理工具,同時,註解的優點還包括:更加幹凈易讀的代碼以及編譯期類型檢查等。雖然 Java SE5 預先定義了一些元數據,但一般來說,主要還是需要程序員自己添加新的註解,並且按照自己的方式使用它們。
註解的語法比較簡單,除了 @
符號的使用外,它基本與 Java 固有的語法一樣。Java SE5 內置了三種,定義在 java.lang 中的註解:
@Override
,表示當前的方法定義將覆蓋超類中的方法。@Deprecated
,如果程序員使用了註解為它的元素,那麽編譯器會發出警告信息。@SuppressWarnings
,關閉不當的編譯器警告信息。Java SE5 之前的版本也可以使用這個註解,不過會被忽略不起作用。
每當你創建了描述性質的類或接口時,一旦其中包含了重復性的工作,那就可以考慮使用註解來簡化與自動化該過程。註解是在實際的源代碼級別保存所有的信息,而不是某種註釋性的文字,這使得代碼更整潔,且便於維護。通過使用擴展的 annotation API,或外部的字節碼工具類庫,程序員擁有對源代碼以及字節碼強大的檢查與操作能力。
基本語法
下面示例中,使用 @Test
對 TestExecute() 方法進行註解。這個註解本身並不做任何事情,但是編譯器要確保在其構造路徑上必須有 @Test
註解的定義。
public @interface Test {
}
public class Testble {
public void execute() {
System.out.println("Executing..");
}
@Test
void testExecute(){
execute();
}
}
被註解的方法與其他的方法沒有區別。 @Test
可以與任何修飾符共同作用域方法。
定義註解
上面的例子註解的定義我們已經看到了。註解的定義看起來很像接口的定義。事實上與任何 Java 文件一樣,註解也會被編譯為 class 文件。除了 @
@Test
的定義很像一個空的接口。定義註解時會需要一些元註解,如 @Target
和 @Retention
。 @Target
用來定義你的註解將應用於什麽地方。 @Deprecated
用來定義應該用於哪一個級別可用,在源代碼中、類文件中或者運行時。
在註解中一般都會包含某些元素用以表示某些值。當分析出來註解時,程序和工具可以利用這些值。註解的元素看起來就像接口的方法,唯一的區別是你可以為他指定默認值。沒有元素的註解被稱為標記註解。
下面是一個簡單的註解,它可以跟蹤一個項目中的用例。程序員可以在該方法上添加註解,我們就可以計算有多少已經實現了該用例。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UseCase {
public int id();
public String description() default "沒有描述";
}
註意:id 和 description 類似方法的定義。description 元素有一個 default 值,如果在註解某個方法時沒有給出 description 的值,則就會使用這個默認值。
下面的三個方法被註解:
public class PasswordUtils {
@UseCase(id =47,description = "password 哈哈哈防止破解")
public boolean validatePassword(String password) {
return (password.matches("\\w*\\d\\w*"));
}
@UseCase(id = 48)
public String encryptPassword(String password) {
return new StringBuilder(password).reverse().toString();
}
@UseCase(id = 49,description = "是否包含在這個密碼庫中")
public boolean checkForNewPassword(List<String> prevPassword,String password) {
return !prevPassword.contains(password);
}
}
註解的元素在使用時是名值對的形式放入註解的括號內。
元註解
Java 目前只內置了三種標準註解,以及四種元註解。元註解就是註解的註解:
@Target | 表示註解可以用在什麽地方。ElementType 的參數包括: CONSTRUCTOR:構造器的聲明 FIELD:域聲明 LOCAL_VARIABLE:局部變量聲明 METHOD:方法聲明 PACKAGE:包聲明 PARAMETER:參數聲明 TYPE:類、接口或enum聲明 |
|||||
@Retention | 表示需要在什麽級別保存註解信息。可選的RententionPolicy參數: SOURCE:註解將被編譯器丟失 CLASS:註解在class文件中可用,但會被 vm 丟失 RUNTIME:vm 在運行期也保留註解,因此可以通過反射機制讀取註解的信息。 |
|||||
@Documented | 將此註解包含在 javaDoc 中 | |||||
@Inherited | 允許子類繼承父類中的註解 |
大多數時候我麽都是編寫字節的註解,並編寫自己的處理器處理他們。
編寫註解處理器
如果沒有用來讀取註解的工具,那麽註解就不會這有用。使用註解很重要的就是創建和使用註解處理器。Java SE5 擴展了反射機制的 API,方便我們構造這種工具。同時還提供了一個外部工具 apt 幫助我們解析帶有註解的 Java 源代碼。
下面我們就用反射來做一個簡單的註解處理器。我們用它來讀取上面的 PasswordUtils 類。
public class UseCaseTracker {
public static void trackUseCase(List<Integer> useCase,Class<?> cl) {
for (Method method : cl.getDeclaredMethods()) {
UseCase uCase = method.getAnnotation(UseCase.class);
if (uCase != null) {
System.out.println("方法上的註解信息:"+uCase.id()+" "+uCase.description());
}
}
for (Integer integer : useCase) {
System.out.println("參數:"+integer);
}
}
public static void main(String[] args) {
List<Integer> uList = new ArrayList<>();
Collections.addAll(uList, 47,48,49,50);
trackUseCase(uList, PasswordUtils.class);
}
}
測試結果:
方法上的註解信息:49 是否包含在這個密碼庫中 方法上的註解信息:48 沒有描述 方法上的註解信息:47 password 哈哈哈防止破解 參數:47 參數:48 參數:49 參數:50
上面用到了兩個反射的方法:getDeclaredMethods() 和 getAnnotation(),getAnnotation() 方法返回指定類型的註解對象,在這裏使用 UseCse。如果被註解的方法上沒有改類型的註解,則返回 null 值。然後我們從返回的 UseCase 對象中提取元素的值。