Java Annotation學習筆記
作為一個早期短暫從事過C++開發工作的程序員,我個人認為Annotation可能是Java與C++語言較大的不同點之一,這也是一個前C++程序員由衷認為Java可能、或許、maybe要比C++更好用的原因之一。二十多年來,Java一直保持著更新,不斷完善並與時俱進,這可能是其多年來獨領編程語言之風騷的重要原因。不多扯,入正題。(編程知識的學習,我一般會遵循這樣的一個過程:先熟悉基本概念,再來個小程序跑起來看看,最後理論與程序相結合,加深認識並總結記錄。)
1、什麼是Annotation
Annotation被譯作“注解”,標準的英譯漢,但是這個譯詞並沒有很好的反映Annotation在java中的意義,或許“標記”更好一些,也或許“標簽”。
注解(也被稱為元數據)為我們在代碼中添加信息提供了一種形式化的方法,使我們可以在稍後的某個時刻非常方便地使用這些數據。——《Java編程思想》
注解是那些插入到源代碼中使用其他工具可以對其進行處理的標簽。——《Java核心技術卷2》
從《Java核心技術卷2》的定義來看,注解包含兩點內容:其一,它是標簽、標記;此外,這個標簽(標記)可以使用其他工具進行處理。
那麼,其他工具在哪裏可以處理這些標簽呢?一個是源碼層,另一個是類文件。
注解不會改變程序的編譯方式,對於包含注解和不包含注解的代碼,Java編譯器會生成相同的虛擬機指令。——《Java核心技術卷2》
綜上,大致可以給出Java注解一個簡單通俗的描述使用過程:定義標簽,然後提供標簽處理工具,最後是應用標簽到其它的代碼中。當然,Java自帶的注解,我們就隻需要直接使用就可以了,因為定義和處理工具Java都幫我們做好了。
2、來個sample
2.1定義標簽
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UseCase {
public int id();
public String description() default "No description";
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
Target和Retention是java語言自帶的兩個元注解,Target表示用戶自定義的注解(UseCase)能應用在哪裏(注解類型聲明,包,類或接口、方法等);
Retention用來指明自定義注解應該保留多長的時間(取值為SOURCE/CLASS/RUNTIME)
public @interface UseCase
這是定義注解的語法形式,乍一看類似接口的定義,區別在於interface前加了一個@符號,後續使用該用戶自定義注解時語法就是@UseCase
public int id();
public String description() default "No description";
這是注解中定義字段的語法,()並不表示這是一個方法,再者,注解中不支持定義方法。
同時,可以設置字段的默認值,通過default關鍵字。沒有默認值的字段必須在使用時顯示指明。
2.2 標簽處理工具
import java.lang.reflect.*;
public class UseCaseTracker {
public static void trackUseCase(Class<?> cl) {
for (Method m : cl.getMethods()) {
UseCase useCase = m.getAnnotation(UseCase.class);
if (useCase != null) {
System.out.println("Found Use Case : " + useCase.id() + " " + useCase.description());
}
}
}
}
@Retention(RetentionPolicy.RUNTIME)中的RUNTIME使得注解可以保留到類文件中,並由虛擬機載入,這樣我們就可以通過反射API來獲得它們。接下來我就是通過反射API獲得這些注解。
public static void trackUseCase(Class<?> cl) {
for (Method m : cl.getMethods()) {
UseCase useCase = m.getAnnotation(UseCase.class);
if (useCase != null) {
System.out.println("Found Use Case : " + useCase.id() + " " + useCase.description());
}
}
}
trackUseCase方法通過傳入應用了UseCase注解的類,再通過反射API獲得這些注解並打印注解中的信息。
2.3 應用標簽
import java.util.List;
public class PasswordUtils {
@UseCase(id=47, description="Passwords must contain at least one numeric")
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="New passwords can't equal previously used ones")
public boolean checkForNewPassword(List<String> prePasswords, String password) {
return !prePasswords.contains(password);
}
}
@Target(ElementType.METHOD)規定了注解隻能是應用在方法上,因此,上述例子中便是如此。
@UseCase(id=47, description="Passwords must contain at least one numeric")
@UseCase(id=48)
@UseCase(id=49, description="New passwords can't equal previously used ones")
以上是三種使用應用自定義注解的例子。
2.4 測試
import java.lang.reflect.*;
public class UseCaseTracker {
public static void trackUseCase(Class<?> cl) {
for (Method m : cl.getMethods()) {
UseCase useCase = m.getAnnotation(UseCase.class);
if (useCase != null) {
System.out.println("Found Use Case : " + useCase.id() + " " + useCase.description());
}
}
}
public static void main(String[] args) {
trackUseCase(PasswordUtils.class);
}
}
直接在注解處理工具中加了個main方法來測試注解處理工具。輸出結果如下:
Found Use Case : 47 Passwords must contain at least one numeric
Found Use Case : 49 New passwords can't equal previously used ones
Found Use Case : 48 No description
注:以上代碼來自《Java編程思想》,部分有改動。
3、歸納整理
3.1 注解元素(屬性)的類型
基本類型(int/short/long/byte/char/double/float/boolean)
String
Class
enum
注解類型
以上五種類型組成的數組
注:注解元素值不能為null,默認值也不能為null。
3.2 標準注解
3.3 Target注解的元素類型
3.4 Retention注解的元素類型
最後更新:2017-07-02 21:32:27