什么是注解
先舉個(gè)日常生活中的例子:在超市中有很多商品,并不是每個(gè)商品都有商標(biāo)/條形碼的,但是超市為了方便管理,給每個(gè)商品都貼了條碼,并在系統(tǒng)中存儲(chǔ)了信息,結(jié)賬時(shí)直接掃碼就行,這樣,條碼即不會(huì)對(duì)商品有影響,超市還提高了效率。
如果你明白了上面的例子,那么注解就很好理解了。你寫的代碼其實(shí)就是超市中的商品;寫的代碼是為了運(yùn)行實(shí)現(xiàn)功能,對(duì)應(yīng)的商品的意義也是實(shí)現(xiàn)價(jià)值,被購(gòu)買;為了方便商品的操作,我們對(duì)商品貼了條碼,對(duì)商品來(lái)說(shuō)沒什么影響,只是對(duì)我們、對(duì)收銀員有一個(gè)標(biāo)識(shí)的作用,對(duì)應(yīng)的就是注解。
下面引入注解的正經(jīng)說(shuō)明:
注解是插入你代碼中的一種注釋或者說(shuō)是一種元數(shù)據(jù)(meta data)。這些注解信息可以在編譯期使用預(yù)編譯工具進(jìn)行處理(pre-compiler tools)
用我的的話就是:
注解就是給你的代碼加一些解釋性的標(biāo)注,代碼該怎么跑還是怎么跑。注解的意義就是有一些工具對(duì)你的代碼進(jìn)行處理時(shí)給那些工具指明個(gè)目標(biāo)。
注解的定義
注解的定義方法
注解的定義很簡(jiǎn)單,使用 @interface關(guān)鍵字進(jìn)行定義。
注解里的屬性定義格式是 “屬性類型 屬性名+括號(hào);”,如果像加個(gè)默認(rèn)值的話就“屬性類型 屬性名+括號(hào) default 默認(rèn)值;”。
示例如下:
public @interface StudentCard{
int id();
String name() default "未命名";
int level default 1;
}
使用此注解的操作如下:
//張三,學(xué)號(hào)001,卡級(jí)別10
@StudentCard(id=001,name="張三",level=10)
//名稱為“未命名”,學(xué)號(hào)002,卡級(jí)別為默認(rèn)的1
@StudentCard(id=002)
元注解
元注解就是可以注釋到注解上邊的注解,也稱為基本注解。
元注解就是專門為注解定義而使用的,下面對(duì)元注解進(jìn)行羅列
@Retention
Retention英文意思是保留的意思,就是指你的注解(也就是你代碼的標(biāo)簽)保留到什么時(shí)候。是編譯時(shí)后摘了還是保存到運(yùn)行時(shí)。
取值如下:
-
RetentionPolicy.SOURCE:只在源碼階段保留,編譯之前就扔了【它的聲明周期就能接觸到編譯器了吧,一般是寫給編譯器看的】 -
RetentionPolicy.CLASS:保留到編譯階段,也就是說(shuō)編譯生成的.class文件中還有,運(yùn)行時(shí)就沒了【沒有特備聲明的話默認(rèn)就是這個(gè)選項(xiàng)】 -
RetentionPolicy.RUNTIME:在運(yùn)行階段也保留,從頭到尾都保留【可以用來(lái)反射編程】
標(biāo)記的格式如下:
@Retention(RetentionPolicy.SOURCE)
@Documented
這個(gè)標(biāo)注的意思是把這個(gè)加入javaDoc,不怎么用,至少我是沒怎么用過(guò)。
@Target
這個(gè)標(biāo)注是用來(lái)限制你定義的注解適用的對(duì)象的。
可取值如下:
-
ElementType.TYPE:表明此定義的注解可適用于類、接口(包括注解)、枚舉 -
ElementType.FIELD:表明此定義的注解可適用于類內(nèi)的變量(枚舉常量也行) -
ElementType.METHOD:表明此定義的注解可適用于方法 -
ElementType.PARAMETER:表明此定義的注解可適用于方法傳入的參數(shù)【舉個(gè)栗子@PathVariable】 -
ElementType.CONSTRUCTOR:表明此定義的注解可適用于構(gòu)造函數(shù) -
ElementType.LOCAL_VARIABLE:表明此定義的注解可適用于局部變量 -
ElementType.ANNOTATION_TYPE:表明此定義的注解可適用于聲明的變量【什么鬼???】 -
ElementType.PACKAGE:表明此定義的注解可適用于包 -
ElementType.TYPE_PARAMETER:表明此定義的注解可適用于傳入的類型參數(shù) -
ElementType.TYPE_USE:不清楚了,源碼的注解是 use of a type ,后面接觸到再完善吧
因?yàn)橐粋€(gè)注解可以用用在很多地方(看你想怎么用嘍),所以@Target的使用方法如下:
@Target({ElementType.PARAMETER,ElementType.LOCAL_VARIABLE,ElementType.FIELD})
@Inherited
這個(gè)注解的意思是可繼承。你寫了一個(gè)注解X,然后用X標(biāo)注了一個(gè)類A,B是A的子類,如果你定義注解時(shí)用了@Inherited標(biāo)注,那么B就等于也被注解X標(biāo)注了。
@Repeatable
這個(gè)注解的意思是是否可重復(fù)的意思,這個(gè)的使用方法有點(diǎn)復(fù)雜了,因?yàn)榭芍貜?fù)意味著你可以對(duì)一個(gè)地方打多個(gè)注解,注解的存儲(chǔ)就得用數(shù)組了,所以你得在你的注解的基礎(chǔ)上再定義一個(gè)以此注解數(shù)組為值的注解,說(shuō)的有點(diǎn)拗口,我們直接看示例代碼:
//先聲明一個(gè)注解,你要用的注解
//這里加了IdCards.class 在處理IdCard.class 注解時(shí)就放在IdCards.class的值中
@Repeatable(IdCards.class)
public @interface IdCard{
int id() default 1;
String name() default "未命名";
String department() default "商城研發(fā)部";
}
//下面是IdCards.class的定義
public @interface IdCards {
IdCard[] value();
}
IdCards.class的定義有幾個(gè)注意點(diǎn):
- 屬性名一定要用value
- 盡可能做到見名知意,知道是為誰(shuí)服務(wù)的
IdCards的適用對(duì)象范圍、聲明周期范圍應(yīng)不小于IdCard的范圍
使用的時(shí)候IdCards不用使用,直接用IdCard瘋狂的重復(fù)標(biāo)注就行了
注解的使用
注解很好用,打標(biāo)簽嘛,貼上去就行了,這里主要將如何把標(biāo)簽讀出來(lái),也就是超市的掃碼過(guò)程。
注解的提取
注解通過(guò)反射提取,可以使用Class對(duì)象的方法進(jìn)行判斷是否使用了哪個(gè)注解:
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass);
可以通過(guò)下面的方法來(lái)獲取特定的注解的對(duì)象:
public <A extends Annotation> A getAnnotation(Class<A> annotationClass);
可以通過(guò)下面的方法獲得所有的注解對(duì)象:
public Annotation[] getAnnotations();
注解對(duì)象內(nèi)往往是私有屬性,不可達(dá),還要用一個(gè)方法來(lái)設(shè)置可達(dá)性:
Field.setAccessable(true);
附一個(gè)完整的使用的例子:
注解定義如下:
@Target({ElementType.PARAMETER,ElementType.LOCAL_VARIABLE,ElementType.FIELD,ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Hehe {
int intValue() default -1;
String stringValue() default "undefinedStringValue";
double doubleValue() default -1d;
}
注解的使用如下:
@Hehe(intValue = 200)
public class testAnnotation {
@Hehe(stringValue = "hehe")
private String lpc;
public static void main(String args[]){
testAnnotation testAnnotation = new testAnnotation();
try{
Field lpc = testAnnotation.getClass().getDeclaredField("lpc");
lpc.setAccessible(true);
Hehe hehe = lpc.getAnnotation(Hehe.class);
if (hehe != null){
System.out.println("變量注解中的值為: int="+ hehe.intValue());
System.out.println("變量注解中的值為: string="+ hehe.stringValue());
System.out.println("變量注解中的值為: double="+ hehe.doubleValue());
}
if ( testAnnotation.class.isAnnotationPresent(Hehe.class) ) {
Hehe hehe1 = testAnnotation.class.getAnnotation(Hehe.class);
System.out.println("類的注解中的值為 int="+hehe1.intValue());
}
try {
Method testMethod = testAnnotation.class.getDeclaredMethod("wlgq",String.class);
if ( testMethod != null ) {
// 獲取方法中的注解
Annotation[] ans = testMethod.getAnnotations();
for( int i = 0;i < ans.length;i++) {
System.out.println("method testMethod annotation:"+ans[i].annotationType().getSimpleName());
}
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
System.out.println(testAnnotation.getLpc());
}
@Hehe(intValue=100)
public static void wlgq(String temp){
System.out.println(temp);
}
public String getLpc() {
return lpc;
}
public void setLpc(String lpc) {
this.lpc = lpc;
}
}
獲得注解的值后就該怎么操作怎么操作唄。
注解的用法
注解是一系列元數(shù)據(jù),它提供數(shù)據(jù)用來(lái)解釋程序代碼,但是注解并非是所解釋的代碼本身的一部分。注解對(duì)于代碼的運(yùn)行效果沒有直接影響。
注解有許多用處,主要如下:
- 提供信息給編譯器: 編譯器可以利用注解來(lái)探測(cè)錯(cuò)誤和警告信息
- 編譯階段時(shí)的處理: 軟件工具可以用來(lái)利用注解信息來(lái)生成代碼、Html文檔或者做其它相應(yīng)處理。
- 運(yùn)行時(shí)的處理: 某些注解可以在程序運(yùn)行的時(shí)候接受代碼的提取
還是回到官方文檔的解釋上,注解主要針對(duì)的是編譯器和其它工具軟件(SoftWare tool)。
當(dāng)開發(fā)者使用了Annotation 修飾了類、方法、Field 等成員之后,這些 Annotation 不會(huì)自己生效,必須由開發(fā)者提供相應(yīng)的代碼來(lái)提取并處理 Annotation 信息。這些處理提取和處理 Annotation 的代碼統(tǒng)稱為 APT(Annotation Processing Tool)。
現(xiàn)在,我們可以給自己答案了,注解有什么用?給誰(shuí)用?給 編譯器或者 APT 用的。
參考文獻(xiàn)
https://blog.csdn.net/briblue/article/details/73824058 感謝大佬的作品