定義
Java注解又稱Java標(biāo)注,是Java語言5.0版本開始支持加入源代碼的特殊語法元數(shù)據(jù)(元數(shù)據(jù) Metadata:描述數(shù)據(jù)的數(shù)據(jù))。
Java語言中的類、方法、變量、參數(shù)和包等都可以被標(biāo)注。和Javadoc不同,Java標(biāo)注可以通過反射獲取標(biāo)注內(nèi)容。在編譯器生成類文件時(shí),標(biāo)注可以被嵌入到字節(jié)碼中。Java虛擬機(jī)可以保留標(biāo)注內(nèi)容,在運(yùn)行時(shí)可以獲取到標(biāo)注內(nèi)容。 當(dāng)然它也支持自定義Java標(biāo)注
內(nèi)置的注解
Java 5 定義了一套注解,共有 7 個(gè),3 個(gè)在 java.lang 中,剩下 4 個(gè)在 java.lang.annotation 中。
作用在代碼的注解(java.lang包中)
- @Override - 檢查該方法是否是重載方法。如果發(fā)現(xiàn)其父類,或者是引用的接口中并沒有該方法時(shí),會(huì)報(bào)編譯錯(cuò)誤。
- @Deprecated - 標(biāo)記過時(shí)方法。如果使用該方法,會(huì)報(bào)編譯警告。
- @SuppressWarnings - 指示編譯器去忽略注解中聲明的警告。
作用在其他注解的注解(或者說 元注解,java.lang.annotation包中)
- @Retention - 標(biāo)識這個(gè)注解怎么保存,是只在代碼中,還是編入class文件中,或者是在運(yùn)行時(shí)可以通過反射訪問。
- @Documented - 標(biāo)記這些注解是否包含在用戶文檔中。
- @Target - 標(biāo)記這個(gè)注解應(yīng)該是哪種 Java 成員。
- @Inherited - 標(biāo)記這個(gè)注解是繼承于哪個(gè)注解類(默認(rèn) 注解并沒有繼承于任何子類)
從 Java 7 開始,額外添加了 3 個(gè)注解
- @SafeVarargs - Java 7 開始支持,忽略任何使用參數(shù)為泛型變量的方法或構(gòu)造函數(shù)調(diào)用產(chǎn)生的警告。
- @FunctionalInterface - Java 8 開始支持,標(biāo)識一個(gè)匿名函數(shù)或函數(shù)式接口。
- @Repeatable - Java 8 開始支持,標(biāo)識某注解可以在同一個(gè)聲明上使用多次。
功能
注解可以用于許多不同的目的,最常見的是:
- 給編譯器提供信息:編譯器可以使用注解來產(chǎn)生警告,甚至根據(jù)不同的規(guī)則產(chǎn)生錯(cuò)誤。 這種用法的一個(gè)例子是Java 8 的 @FunctionalInterface 注解。 這使編譯器驗(yàn)證注釋類,并檢查它是否是一個(gè)正確的功能接口。
- 文檔化:軟件應(yīng)用程序可以使用注解來測量代碼的質(zhì)量,如FindBugs或PMD,或者像Jenkins,Jira或Teamcity一樣自動(dòng)生成報(bào)告。
- 代碼生成:使用注解可以使用代碼中存在的元數(shù)據(jù)信息自動(dòng)生成代碼或XML文件。 JAXB庫就是一個(gè)很好的例子。
- 運(yùn)行時(shí)處理:在運(yùn)行時(shí)檢查的注解可用于不同的目標(biāo),如單元測試(Junit),依賴注入(Spring),驗(yàn)證,日志記錄(Log4J),數(shù)據(jù)訪問(Hibernate)等。
實(shí)際例子
講個(gè) Java 官方提供的例子,用注解實(shí)現(xiàn)簡單的測試框架。
@MyTest 注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyTest {
}
FooTest 測試類
public class FooTest {
@MyTest
public static void m1() {
}
public static void m2() {
}
@MyTest
public static void m3() {
throw new RuntimeException("Boom");
}
public static void m4() {
}
@MyTest
public static void m5() {
}
public static void m6() {
}
@MyTest
public static void m7() {
throw new RuntimeException("Crash");
}
public static void m8() {
}
}
RunMyTests 測試執(zhí)行類
import java.lang.reflect.Method;
public class RunMyTests {
public static void main(String[] args) throws Exception {
int passed = 0;
int failed = 0;
if (args.length < 1) {
System.out.println("please give test class.");
System.exit(1);
}
for (Method method : Class.forName(args[0]).getMethods()) {
if (method.isAnnotationPresent(MyTest.class)) {
try {
method.invoke(null);
passed++;
} catch (Throwable ex) {
System.out.printf("Test %s failed: %s %n", method, ex.getCause());
failed++;
}
}
}
System.out.printf("Passed: %d, Failed %d%n", passed, failed);
}
}
編譯、運(yùn)行測試執(zhí)行類
> javac FooTest.java MyTest.java RunMyTests.java
> java RunMyTests FooTest
Test public static void FooTest.m3() failed: java.lang.RuntimeException: Boom
Test public static void FooTest.m7() failed: java.lang.RuntimeException: Crash
Passed: 2, Failed 2