什么是注解
注解是 Java 5 的一個新特性。注解是插入你代碼中的一種注釋或者說是一種元數(shù)據(jù)(meta data)。這些注解信息可以在編譯期使用預(yù)編譯工具進(jìn)行處理(pre-compiler tools)佳吞,也可以在運(yùn)行期使用 Java 反射機(jī)制進(jìn)行處理。
那么通俗來說俯逾,注解就是我們所說的標(biāo)簽昆淡,我們可以把一些類,方法盒件,變量蹬碧,參數(shù)添加注解,即加上標(biāo)簽來表明這些是干啥的炒刁,以便有需要的時候查看恩沽。
注解的聲明
使用@interface
聲明:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {
}
使用
@Target
注解傳入ElementType.METHOD
參數(shù)來標(biāo)明@Test
只能用于方法上。使用
@Retention(RetentionPolicy.TRUNTIME)
來表示該注解生存期是運(yùn)行時翔始。@Target
和@Retention
是由Java提供的元注解罗心,即可以標(biāo)記自定義注解的注解。
元注解
@Target
@Target
用來約束注解可以應(yīng)用的地方(如類绽昏、方法协屡、方法參數(shù)、字段)全谤,ElementType
是枚舉類型肤晓,其定義如下:
public enum ElementType {
/**標(biāo)明該注解可以用于類、接口(包括注解類型)或enum聲明*/
TYPE,
/** 標(biāo)明該注解可以用于字段(域)聲明认然,包括enum實(shí)例 */
FIELD,
/** 標(biāo)明該注解可以用于方法聲明 */
METHOD,
/** 標(biāo)明該注解可以用于參數(shù)聲明 */
PARAMETER,
/** 標(biāo)明注解可以用于構(gòu)造函數(shù)聲明 */
CONSTRUCTOR,
/** 標(biāo)明注解可以用于局部變量聲明 */
LOCAL_VARIABLE,
/** 標(biāo)明注解可以用于注解聲明(應(yīng)用于另一個注解上)*/
ANNOTATION_TYPE,
/** 標(biāo)明注解可以用于包聲明 */
PACKAGE,
/**
* 標(biāo)明注解可以用于類型參數(shù)聲明(1.8新加入)
* @since 1.8
*/
TYPE_PARAMETER,
/**
* 類型使用聲明(1.8新加入)
* @since 1.8
*/
TYPE_USE
}
當(dāng)注解未指定Target
值時补憾,則此注解可以用于任何元素上,多個值使用{}包含并用逗號隔開卷员,如下:
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE,
METHOD, PACKAGE, PARAMETER, TYPE})
@Retention
@Retention
用來約束注解的生命周期盈匾,有三個級別:源碼級別(SOURCE)、類文件級別(CLASS)毕骡、運(yùn)行時級別(RUNTIME),
SOURCE: 注解將被編譯器丟棄(該類型的注解信息只會保留在源碼里削饵,源碼經(jīng)過編譯后,注解信息會被丟棄未巫,不會保留在編譯好的class文件里)
CLASS:注解在class文件中可用窿撬,但會被VM丟棄(該類型的注解信息會保留在源碼里和class文件里,在執(zhí)行的時候叙凡,不會加載到虛擬機(jī)中)劈伴,請注意,當(dāng)注解未定義Retention時握爷,默認(rèn)值是CLASS跛璧,如Java內(nèi)置注解严里,@Override、@Deprecated追城、@SuppressWarnning等
- RUNTIME:注解信息將在運(yùn)行期(JVM)也保留刹碾,因此可以通過反射機(jī)制讀取注解的信息(源碼、class文件和執(zhí)行的時候都有注解的信息)座柱,如SpringMvc中的@Controller教硫、@Autowired、@RequestMapping等辆布。
@Inherited
@Inherited 可以讓注解被繼承瞬矩,但這并不是真的繼承,只是通過使用@Inherited锋玲,可以讓子類Class對象使用getAnnotations()獲取父類被@Inherited修飾的注解景用。
/** 使用@Inherited注解會被子類所繼承 */
@Retention(Retention.RUNTIME)
@Inherited
public @interface InheritedTest {
String value();
}
/** 未聲明@Inherited, 不會被子類繼承*/
@Retention(RetentionPolicy.RUNTIME)
public @interface NoInheritedTest {
String value();
}
/**父類*/
@InheritedTest("InheritedTest:使用@Inherited的class")
@NoInheritedTest("NoInheritedTest:未使用@Inherited的class")
public class Parent {
@InheritedTest("InheritedTest:使用@Inherited method")
@NoInheritedTest("NoInheritedTest:未使用@Inherited method")
public void method(){
}
@InheritedTest("InheritedTest:使用@Inherited method2")
@NoInheritedTest("NoInheritedTest:未使用@Inherited method2")
public void method2(){
}
@InheritedTest("InheritedTest:使用@Inherited field")
@NoInheritedTest("NoInheritedTest:未使用@Inherited field")
public String a;
}
/**子類 只繼承了一個method方法 */
public class Child extends Parent {
@Override
public void method() {
}
}
/**使用反射進(jìn)行測試 */
public class Test {
public static void main(String[] args) throws NoSuchMethodException, NoSuchFieldException {
Class<Child> childClass = Child.class;
//對類測試
System.out.println("-------------------------------------");
System.out.println("對類測試");
if (childClass.isAnnotationPresent(InheritedTest.class)) {
System.out.println(childClass.getAnnotation(InheritedTest.class).value());
}
if (childClass.isAnnotationPresent(NoInheritedTest.class)) {
System.out.println(childClass.getAnnotation(NoInheritedTest.class).value());
}
//對方法1測試
System.out.println("-------------------------------------");
System.out.println("對方法1測試");
Method method = childClass.getMethod("method", null);
if (method.isAnnotationPresent(InheritedTest.class)) {
System.out.println(method.getAnnotation(InheritedTest.class).value());
}
if (method.isAnnotationPresent(NoInheritedTest.class)) {
System.out.println(method.getAnnotation(NoInheritedTest.class).value());
}
//對方法2測試
System.out.println("-------------------------------------");
System.out.println("對方法2測試");
Method method2 = childClass.getMethod("method2");
if (method2.isAnnotationPresent(InheritedTest.class)) {
System.out.println(method2.getAnnotation(InheritedTest.class).value());
}
if (method2.isAnnotationPresent(NoInheritedTest.class)) {
System.out.println(method2.getAnnotation(NoInheritedTest.class).value());
}
//對變量測試
System.out.println("-------------------------------------");
System.out.println("對變量測試");
Field field = childClass.getField("a");
if (field.isAnnotationPresent(InheritedTest.class)) {
System.out.println(field.getAnnotation(InheritedTest.class).value());
}
if (field.isAnnotationPresent(NoInheritedTest.class)) {
System.out.println(field.getAnnotation(NoInheritedTest.class).value());
}
}
}
/**輸出結(jié)果*/
-------------------------------------
對類測試
InheritedTest:使用@Inherited的class
-------------------------------------
對方法1測試
-------------------------------------
對方法2測試
InheritedTest:使用@Inherited method2
NoInheritedTest:未使用@Inherited method2
-------------------------------------
對變量測試
InheritedTest:使用@Inherited field
NoInheritedTest:未使用@Inherited field
我們可以得出結(jié)論惭蹂,使用`@Inherited`注解類的時候伞插,子類繼承了父類的注解,但是第一個方法沒有繼承到注解盾碗,說明`@Inherited`對方法無效媚污,但方法2和域拿到的是父類的注解,所以有輸出廷雅。
@Repeatable
在Java SE 8中引入的 @Repeatable 注解表明標(biāo)記的注解可以多次應(yīng)用于相同的聲明或類型使用耗美。
可以這么理解:有個人可以音樂家,畫家航缀,程序員商架,那我們就用@Repeatable給這個人貼上這三個標(biāo)簽。
@Retention(RetentionPolicy.RUNTIME)
public @interface Persons {
Person[] value();
}
@Repeatable(Persons.class)
public @interface Person {
String role default "";
}
@Person(role="Painter")
@Person(role="Musician")
@Person(role="Lion")
public class KUN {}
注解的成員變量及其數(shù)據(jù)類型
我們定義注解芥玉,也可以定義一些成員變量蛇摸,而且還可以用default指定默認(rèn)值。
@Retention(RetentionPolicy.RUNTIME)
public @interface FavouriteAnnotation {
int num() default 0;
String name() default "X JAPAN";
}
/** 默認(rèn)第0位最喜愛的是X JAPAN*/
@FavouriteAnnotation()
public class KUN {}
/** 指定第1位最喜愛的是Queen*/
@FavouriteAnnotation(num=1, name="Queen")
public class KUN {}
注解支持的數(shù)據(jù)類型有:
所有基本類型(int,float,boolean,byte,double,char,long,short)
String
Class
enum
Annotation
上述類型的數(shù)組
注解與反射
注解通過反射獲取灿巧,使用Class對象的isAnnotationPresent()方法判斷是否應(yīng)用了某個注解,如果指定類型的注解存在于此元素上赶袄,則返回 true,否則返回 false抠藕。
public boolean isAnnotationPresent(Class<? extend Annotation> annotationClass)
使用getAnnotation()方法獲取Annotation對象饿肺。
public <A extebds Annotation> A getAnnotation(Class<A> annotationClass) {}
使用getAnnotations() 獲取此元素上存在的所有注解,包括從父類繼承的幢痘。
public Annotation[] getAnnotations() {}
使用getDeclaredAnnotations(),返回直接存在于此元素上的所有注解唬格,注意家破,不包括父類的注解颜说,調(diào)用者可以隨意修改返回的數(shù)組购岗;這不會對其他調(diào)用者返回的數(shù)組產(chǎn)生任何影響,沒有則返回長度為0的數(shù)組
public native Annotation[] getDeclaredAnnotations()
注解的應(yīng)用場景
注解是一系列元數(shù)據(jù)门粪,它提供數(shù)據(jù)用來解釋程序代碼喊积,但是注解并非是所解釋的代碼本身的一部分。注解對于代碼的運(yùn)行效果沒有直接影響玄妈。
注解有許多用處乾吻,主要如下:
- 提供信息給編譯器: 編譯器可以利用注解來探測錯誤和警告信息
- 編譯階段時的處理: 軟件工具可以用來利用注解信息來生成代碼、Html文檔
或者做其它相應(yīng)處理拟蜻。- 運(yùn)行時的處理: 某些注解可以在程序運(yùn)行的時候接受代碼的提取