一.什么是注解
官方解釋:
Java 注解用于為Java程序提供元數(shù)據(jù)篡石。作為元數(shù)據(jù)芥喇,注解不直接影響代碼的執(zhí)行,但也有一些注解實(shí)際上可以用于這一目的夏志。
什么是元數(shù)據(jù)乃坤,即一種描述數(shù)據(jù)的數(shù)據(jù)。所以可以說注解是描述源代碼的數(shù)據(jù)沟蔑。
簡(jiǎn)單理解注解可以看出一個(gè)個(gè)標(biāo)簽湿诊,用來標(biāo)記你的代碼,是一種應(yīng)用于類瘦材,方法厅须,參數(shù),變量食棕,構(gòu)造器及包的一種特殊修飾符朗和。
二.注解的定義
注解和class,interface一樣也是一種類型簿晓,通過@interface定義
如下:
public @interface TestAnnotation {
}
三.元注解
注解的應(yīng)用應(yīng)用很簡(jiǎn)單眶拉,我們用使用TestAnnotation注解,在你想注解的類中@TestAnnotation就可以了
如下(注解類):
@TestAnnotation
public class Test {
}
但是要想注解想要正常工作還需要元注解的幫助
1.什么是元注解
元注解就是注解到注解上的注解憔儿,或者說元注解是一種基本注解忆植,它能用來注解其他注解。
我們可以將元注解看成一種特殊的修飾符谒臼,用來解釋說明注解朝刊,它是注解的元數(shù)據(jù)。
2.元注解的種類
元注解一共用5種:
-
@Retention
Retention意為保留期蜈缤,@Retention用來解釋說明一個(gè)注解的存活周期
@Retention取值:- RetentionPolicy.SOURCE 注解只在源碼階段保留拾氓,在編譯器進(jìn)行編譯時(shí)它將被丟棄忽視。
- RetentionPolicy.CLASS 注解只被保留到編譯進(jìn)行的時(shí)候底哥,它并不會(huì)被加載到 JVM 中咙鞍。
- RetentionPolicy.RUNTIME 注解可以保留到程序運(yùn)行的時(shí)候,它會(huì)被加載進(jìn)入到 JVM 中叠艳,所以在程序運(yùn)行時(shí)可以獲取到它們奶陈。
示例:
@Retention(RetentionPolicy.CLASS)
public @interface TestAnnotation {
}
@Documented
用于描述其它類型的annotation應(yīng)該被作為被標(biāo)注的程序成員的公共API,因此可以被例如javadoc此類的工具文檔化附较。Documented是一個(gè)標(biāo)記注解,沒有成員潦俺。-
@Target
指定注解應(yīng)用的地方拒课,用來限定注解的應(yīng)用場(chǎng)景(類徐勃,方法,參數(shù)等等)
(不使用@Target注解則默認(rèn)不限制)
取值如下:- ElementType.ANNOTATION_TYPE 可以給一個(gè)注解進(jìn)行注解
- ElementType.CONSTRUCTOR 可以給構(gòu)造方法進(jìn)行注解
- ElementType.FIELD 可以給屬性進(jìn)行注解
- ElementType.LOCAL_VARIABLE 可以給局部變量進(jìn)行注解
- ElementType.METHOD 可以給方法進(jìn)行注解
- ElementType.PACKAGE 可以給一個(gè)包進(jìn)行注解
- ElementType.PARAMETER 可以給一個(gè)方法內(nèi)的參數(shù)進(jìn)行注解
- ElementType.TYPE 可以給一個(gè)類型進(jìn)行注解早像,比如類僻肖、接口、枚舉
-
@Inherited
繼承的意思卢鹦,當(dāng)一個(gè)超類被@Inherited(@Inherited注解)注解的注解(A注解)進(jìn)行過注解的話臀脏,如果它的子類沒有被如何其他注解進(jìn)行注解,那么這個(gè)子類就繼承了超類的注解(A注解)注意:@Inherited annotation類型是被標(biāo)注過的class的子類所繼承冀自。類并不從它所實(shí)現(xiàn)的接口繼承annotation揉稚,方法并不從它所重載的方法繼承annotation
@Retention(RetentionPolicy.CLASS)
@Inherited
public @interface TestAnnotation {
}
@TestAnnotation
public class TestA {
}
public class TestB extends TestA{
}
TestAnnotation 被@Retention注解,類TestA被@TestAnnotation注解熬粗,類TestB繼承類TestA搀玖,類TestB也擁有TestAnnotation注解
-
@Repeatable
@Repeatable是自然可重復(fù)的意思。這是Java 1.8加進(jìn)來的新特性
在需要對(duì)同一種注解多次使用時(shí)驻呐,往往需要借助@Repeatable
舉個(gè)列子
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Roles {
Role[] value();
}
@Repeatable(Roles.class)
public @interface Role {
String role() default "";
}
@Role(role="husband")
@Role(role="father")
@Role(role="son")
public class Person {
}
上面的代碼@Repeatable注解了Role 灌诅,@Repeatable 后面括號(hào)中的類相當(dāng)于一個(gè)容器注解
什么是容器注解:
本身也是注解,用來存放其他注解
按照規(guī)定含末,它里面必須要有一個(gè) value 的屬性猜拾,屬性類型是一個(gè)被 @Repeatable 注解過的注解數(shù)組
@Role(role="husband"),role="husband"表示給Role這個(gè)注解的role屬性賦值佣盒,關(guān)于注解的屬性下面會(huì)說明挎袜。
Person 類需要多次使用@Role注解,所以這里使用@Repeatable注解@Role
測(cè)試一下注解效果:
Annotation[] annotations = Person.class.getAnnotations();
System.out.println(annotations.length);
Roles p1=(Roles) annotations[0];
for(Role t:p1.value()){
System.out.println(t.role());
}
打印如下:
1
husband
father
son
四.注解的屬性
注解的屬性也叫成員變量沼撕。注解只有成員變量宋雏,沒有方法。注解的成員變量在注解的定義中以“無參的方法”的形式來聲明务豺,其方法名定義了該成員變量的名字磨总,其返回值定義了該成員變量的類型。
- 注解的屬性類型必須以下幾種:
8中基本類型(byte笼沥,boolean蚪燕,char,short奔浅,int馆纳,long,float汹桦,double,)和
String鲁驶,Enum,Class舞骆,annotation類型钥弯,以及這些類型的數(shù)組
public @interface TestAnnotation {
int id();
String msg();
}
上面代碼定義TestAnnotation 這個(gè)注解有id和msg兩個(gè)屬性径荔。在使用的時(shí)候我們需要給它們賦值
- 賦值方式:括號(hào)內(nèi)以value=“”的形式賦值,多個(gè)屬性以'',''隔開
@TestAnnotation(id = 1, msg = "注解測(cè)試")
public class Test {
}
- 注解中可以設(shè)置默認(rèn)值脆霎,默認(rèn)值用default關(guān)鍵字指定
使用注解時(shí)對(duì)于指定了默認(rèn)值的屬性总处,如果不需要修改,可以不賦值
public @interface TestAnnotation {
int id() default 0;
String msg() default "msg";
}
@TestAnnotation()
public class Test {
}
- 當(dāng)一個(gè)注解只有一個(gè)屬性且屬性名為value時(shí)睛蛛,使用此注解可以省略括號(hào)內(nèi)的屬性名直接賦值
public @interface TestAnnotation {
String value();
}
@TestAnnotation("1")
public class Test {
}
- 如果注解沒有屬性鹦马,括號(hào)也可以省略
public @interface TestAnnotation {
}
@TestAnnotation
public class Test {
}
五.JDK預(yù)制注解
-
@Deprecated
標(biāo)記過時(shí)元素的注解,用來標(biāo)識(shí)類忆肾,方法或者變量已過時(shí)荸频,不建議使用。調(diào)用過時(shí)方法時(shí)編譯器會(huì)提醒难菌。 -
@Override
重寫注解试溯,用來表示子類的方法是覆蓋父類的方法。 -
@SuppressWarnings
抑制警告注解郊酒,用于抑制編譯器產(chǎn)生警告信息. -
@SafeVarargs
參數(shù)安全類型注解遇绞。它的目的是提醒開發(fā)者不要用參數(shù)做一些不安全的操作,它的存在會(huì)阻止編譯器產(chǎn)生 unchecked 這樣的警告。它是在 Java 1.7 的版本中加入的 -
@FunctionalInterface
函數(shù)式接口(函數(shù)式接口可以很容易轉(zhuǎn)換為 Lambda 表達(dá)式)注解燎窘,這個(gè)是 Java 1.8 版本引入的新特性摹闽。函數(shù)式編程很火,所以 Java 8 也及時(shí)添加了這個(gè)特性褐健。
六.注解的提取與應(yīng)用
注解通過反射獲取付鹿,通過Class對(duì)象的方法獲取注解
常用的三個(gè)方法:
- isAnnotationPresent()方法判斷是否應(yīng)用了某個(gè)注解
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {...}
- getAnnotation() 方法獲取指定類型的注解
public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {...}
- getAnnotations方法獲取注解到當(dāng)前元素上的所有注解
public Annotation[] getAnnotations() {
示例(以運(yùn)行時(shí)注解為例):獲取下面TestAnnotation 注解的msg屬性內(nèi)容,可以如下獲取
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.FIELD})
public @interface TestAnnotation {
String msg();
}
@TestAnnotation(msg="注解內(nèi)容")
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);
System.out.println(annotation.msg());
}
}
打印結(jié)果:
注解內(nèi)容
方法和屬性也可以借助返回來獲取注解
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
String msg();
}
@TestAnnotation(msg="注解類")
public class Test {
@TestAnnotation(msg="注解成員變量")
private String msg;
@TestAnnotation(msg="注解方法")
private void setMsg(){
}
}
public static void main(String[] args) {
boolean hasAnnotation = Test.class.isAnnotationPresent(TestAnnotation.class);
if (hasAnnotation) {
TestAnnotation annotation = Test.class.getAnnotation(TestAnnotation.class);
if (annotation != null) {
System.out.println(annotation.msg());
}
}
try {
//屬性獲取注解
Field msg = Test.class.getDeclaredField("msg");
if (msg != null) {
msg.setAccessible(true);
TestAnnotation annotation = msg.getAnnotation(TestAnnotation.class);
if (annotation != null) {
System.out.println(annotation.msg());
}
}
//方法獲取注解
Method setMsg = Test.class.getDeclaredMethod("setMsg");
if (setMsg != null){
TestAnnotation annotation = setMsg.getAnnotation(TestAnnotation.class);
if (annotation != null) {
System.out.println(annotation.msg());
}
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
打印結(jié)果:
注解類
注解成員變量
注解方法
這里要注意是蚜迅,如果一個(gè)注解要想在運(yùn)行時(shí)被提取舵匾,那么@Retention(RetentionPolicy.RUNTIME)是必須的
注解的應(yīng)用
- 提供信息給編譯器:編譯器可以通過注解來探測(cè)錯(cuò)誤和警告信息
- 編譯階段處理:軟件工具可以利用注解信息來自動(dòng)生成代碼,HTML文檔或者做其他相應(yīng)處理
- 運(yùn)行時(shí)的處理: 某些注解可以在程序運(yùn)行時(shí)接收代碼的提取
當(dāng)開發(fā)者使用注解修飾了類谁不,方法坐梯,變量等成員后,注解不會(huì)自己生效刹帕,必須由開發(fā)者提供對(duì)應(yīng)的代碼來提取處理注解信息吵血。
這些用來提取和處理注解信息的代碼統(tǒng)稱為APT(Annotation Processing Tool),注解處理器偷溺,它用來在編譯時(shí)掃描和處理注解蹋辅。具體可參考APT 基于Android工程
總結(jié)來說,注解(Annotation)相對(duì)于一種標(biāo)記挫掏,注解的應(yīng)用就是編譯器侦另,開發(fā)工具或者程序通過反射來提取你的類和各種元素有無這種標(biāo)記,有某種標(biāo)記就去做相應(yīng)的處理。