Java注解
Annotation键闺,注解,可理解為對代碼貼上的標(biāo)簽虽抄。
注解的定義: 跟接口類似走搁,只是多了一個@符號
public @interface TestAnnotation {}
注解的使用:
@TestAnnotation
public class Test {}
元注解,或者元標(biāo)簽有 @Retention迈窟、@Documented私植、@Target、@Inherited车酣、@Repeatable 5 種曲稼,是用來給普通的注解進行解釋說明,可以理解為標(biāo)簽的標(biāo)簽湖员。
@Retention:用來表明注解的有效時間贫悄,也就是生存時間,分為:源碼時保留破衔、編譯時保留清女、運行時保留。
它的取值如下:
-
生存周期 RUNTIME > CLASS > SOURCE.- RetentionPolicy.SOURCE 注解只在源碼階段保留晰筛,在編譯器進行編譯時它將被丟棄忽視嫡丙。 - RetentionPolicy.CLASS 注解只被保留到編譯進行的時候,它并不會被加載到 JVM 中读第。 - RetentionPolicy.RUNTIME 注解可以保留到程序運行的時候曙博,它會被加載進入到 JVM 中,所以在程序運行時可以獲取到它們怜瞒。
@Documented:表明可以將注解的元素包含到Document中父泳。
@Target:目標(biāo)般哼,指定了注解應(yīng)該運用的地方,如果沒有此項惠窄,可以認(rèn)為標(biāo)簽?zāi)軌螂S便貼蒸眠,如果使用Target了,則標(biāo)簽只能貼到指定的地方了杆融。
取值如下:
ElementType.TYPE:可以給一個類型進行注解楞卡,包括類、接口脾歇、枚舉蒋腮。
ElementType.Method:可以給一個方法進行注解
ElementType.Filed:給成員變量注解
ElementType.CONSTRUCTOR:給構(gòu)造方法注解
以及其他。
@Inherited 繼承藕各,當(dāng)一個超類被Inherited注解過的A注解的話池摧,那么如果這個超類的子類沒有應(yīng)用其他注解的話,它會繼承超類的注解A激况。
@Repeatable:重復(fù)作彤,Java8 的新特性。指注解的值可以同時取多個乌逐。例如一個人的身份可以是多重的宦棺,
@Person(role="artist")
@Person(role="coder")
@Person(role="PM")
public class SuperMan{
}
注解的屬性
注解只有成員變量,沒有成員方法黔帕,并且成員變量的定義形式需要使用無形參的方法來聲明代咸,包括 返回值+變量名+();返回值表示其類型成黄。
如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
int id();
String msg();
}
可以設(shè)置默認(rèn)值:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
int id() default -1;
String msg() default "Hi";
}
注解的成員屬性值的設(shè)置如下:
@TestAnnotation(id=5, msg="Hello")
public class Test {}
如果只有一個成員呐芥,則可以直接將值寫在括號中。
@TestAnn("one")
如果注解沒有屬性則連括號都可以省略奋岁。
@TestNoFiled
public class Test {}
Java預(yù)置的注解:@Override思瘟、@Deprecated、@SuppressWarnings闻伶、
@FunctionalInterface: java8新增的及汉,表示這個接口是一個函數(shù)接口抹凳,即只有一個抽象方法。
前面把注解定義好,寫好凌蔬,然后像標(biāo)簽一樣貼到代碼相應(yīng)的位置上崭歧,后續(xù)就是對標(biāo)簽的檢閱顿仇,對注解的提取角雷。
注解的提取方式是通過反射機制實現(xiàn)。
- 通過Class對象的方法: isAnnotationPresent()來判斷該Class對象是否使用了某個注解爆雹。
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) - 通過getAnnotation()來獲取對應(yīng)的注解對象停蕉。反射中的Class/Field/Method等都可以調(diào)用該方法愕鼓。
public <A extends Annotation> A getAnnotation(Class<A> annotationClass) - 或者是通過 getAnnotations() 方法獲取該類的所有注解對象,前面一個方法只是獲取指定的注解對象慧起。
public Annotation[] getAnnotations()
如果Annotation對象不為null,就可以讀取它的屬性了菇晃,如下:
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
int id() default -1;
String msg() default "Hi";
}
@TestAnnotation(id=3, msg="good")
public class Test {
public static void main(String[] args) {
boolean hasAnnotation = Test.class.isAnnotationPresent(TestAnnotation.class);
if (hasAnnotation) {
TestAnnotation annotation = Test.class.getAnnotation(TestAnnotation.class);
print(annotation.id());
print(annotation.msg());
}
}
}
需要注意的是,如果一個注解想要在運行時被獲取蚓挤,那么它應(yīng)該有元注解:@Retention(RetentionPolicy.RUNTIME)
如果是對類的成員變量的注解進行提取解析谋旦,則需要借助反射
public @interface Check {
int value() default 0;
}
class Test {
```
@Check(5)
int age;
@Check(value = 9)
void setAge(int age) {
}
public static void main() throws Exception{
Field ageFiled = Test.class.getField("age");
ageFiled.setAccessible(true);
Check checkAnn = ageFiled.getAnnotation(Check.class);
print(checkAnn.value());
Method setAgeMethod = Test.class.getMethod("setAge", Integer.TYPE);
setAgeMethod.setAccessible(true);
if (setAgeMethod != null) {
checkAnn = setAgeMethod.getAnnotation(Check.class);
print(checkAnn.value());
}
}
```
}
注解的使用場景?
注解是一系列元數(shù)據(jù)屈尼,可以提供一些數(shù)據(jù)來解釋程序代碼,并非代碼的一部分拴孤,也不會對代碼的運行有影響的脾歧。主要作用有3個:
- 提供信息給編譯器,編譯器可以利用注解來探測錯誤或告警信息演熟。
- 編譯階段的處理:某些軟件工具可以根據(jù)注解信息生成代碼鞭执、html文檔或者進行其他處理。
- 運行階段的處理:某些注解可以在代碼運行時候芒粹,被代碼提取出來使用兄纺。
即注解本身是不會生效或者執(zhí)行什么動作,它是被別人拿來用的化漆。被編譯器估脆、軟件工具、代碼使用座云,提供輔助信息疙赠,可以看成一種特殊的注釋信息。
例子朦拖,一個測試框架圃阳,測試代碼是否正常:
- 定義注解
@Retention(RetentionPolicy.RUNTIME)
public @interface Jiecha {}
- 對被測試的代碼添加注解
public class NoBug {
@Jiecha
public void jiafa(){
System.out.println("1+1="+1+1);
}
@Jiecha
public void chufa(){
System.out.println("6 / 0="+ 6 / 0);
}
public void jianfa(){
System.out.println("1-1="+(1-1));
}
}
-
抽取注解,進行測試
public class TestTool { public static void main(String[] args) { NoBug nobug = new NoBug(); ``` Class clz = nobug.getClass(); Method[] methods = cls.getDeclaredMethods(); for (Method m : methods) { if (m.isAnnotationPresent(Jiecha.class) { try { m.setAccessible(true); m.invoke(nobug, null); } catch (Exception e) { print("錯誤報告"); } } } ``` } }
使用注解時璧帝,需要借助反射捍岳,但反射比較慢,所以需要謹(jǐn)慎計算時間成本睬隶。