關(guān)于注解
定義:注解(Annotation),也叫元數(shù)據(jù)。一種代碼級(jí)別的說明。它是JDK1.5及以后版本引入的一個(gè)特性,與類、接口、枚舉是在同一個(gè)層次。它可以聲明在包、類、字段、方法、局部變量、方法參數(shù)等的前面,用來對(duì)這些元素進(jìn)行說明,注釋。
作用分類:
①編寫文檔:通過代碼里標(biāo)識(shí)的元數(shù)據(jù)生成文檔【生成文檔doc文檔】
② 代碼分析:通過代碼里標(biāo)識(shí)的元數(shù)據(jù)對(duì)代碼進(jìn)行分析【使用反射】
③編譯檢查:通過代碼里標(biāo)識(shí)的元數(shù)據(jù)讓編譯器能夠?qū)崿F(xiàn)基本的編譯檢查【Override】
Annotation(注解)是JDK1.5及以后版本引入的。它可以用于創(chuàng)建文檔,跟蹤代碼中的依賴性,甚至執(zhí)行基本編譯時(shí)檢查。注解是以‘@注解名’在代碼中存在的,根據(jù)注解參數(shù)的個(gè)數(shù),我們可以將注解分為:標(biāo)記注解、單值注解、完整注解三類。它們都不會(huì)直接影響到程序的語義,只是作為注解(標(biāo)識(shí))存在,我們可以通過反射機(jī)制編程實(shí)現(xiàn)對(duì)這些元數(shù)據(jù)(用來描述數(shù)據(jù)的數(shù)據(jù))的訪問。另外,你可以在編譯時(shí)選擇代碼里的注解是否只存在于源代碼級(jí),或者它也能在class文件、或者運(yùn)行時(shí)中出現(xiàn)(SOURCE/CLASS/RUNTIME)。(來自百度)
元注解
基本內(nèi)置注解
@Override
它的作用是對(duì)覆蓋超類中方法的方法進(jìn)行標(biāo)記,如果被標(biāo)記的方法并沒有實(shí)際覆蓋超類中的方法,則編譯器會(huì)發(fā)出錯(cuò)誤警告。
public?class?OverrideDemoTest?{
????@Override
????public?String?tostring()?{
????????return?"測試注解";
????}
}
@Deprecated
它的作用是對(duì)不應(yīng)該再使用的方法添加注解,當(dāng)編程人員使用這些方法時(shí),將會(huì)在編譯時(shí)顯示提示信息,它與javadoc里的@deprecated標(biāo)記有相同的功能,準(zhǔn)確的說,它還不如javadoc @deprecated,因?yàn)樗恢С謪?shù),使用@Deprecated的示例代碼示例如下:
public?class?DeprecatedDemoTest?{
????public?static?void?main(String[]args)?{
????????//?使用DeprecatedClass里聲明被過時(shí)的方法
????????DeprecatedClass.DeprecatedMethod();
????}
}
class?DeprecatedClass?{
????@Deprecated
????public?static?void?DeprecatedMethod()?{
????}
}
@SuppressWarnings
其參數(shù)有:
deprecation,使用了過時(shí)的類或方法時(shí)的警告
unchecked,執(zhí)行了未檢查的轉(zhuǎn)換時(shí)的警告
fallthrough,當(dāng) switch 程序塊直接通往下一種情況而沒有 break 時(shí)的警告
path,在類路徑、源文件路徑等中有不存在的路徑時(shí)的警告
serial,當(dāng)在可序列化的類上缺少serialVersionUID 定義時(shí)的警告
finally ,任何 finally 子句不能正常完成時(shí)的警告
all,關(guān)于以上所有情況的警告
public?class?SuppressWarningsDemoTest?{
????@SuppressWarnings("unchecked")
????public?void?say(String?str)?{
????}
}
自定義注解
它類似于新創(chuàng)建一個(gè)接口文件,但為了區(qū)分,我們需要將它聲明為@interface
public?@interface?TestAnnotation?{
}
使用自定義的注解類型
public?class?AnnotationTest?{
????@TestAnnotation
????public?static?void?main(String[]args)?{
????}
}
為自定義注解添加變量? 變量說明:
第一,只能用public或默認(rèn)(default)這兩個(gè)訪問權(quán)修飾.例如,String value();這里把方法設(shè)為defaul默認(rèn)類型.
第二,參數(shù)成員只能用基本類型byte,short,char,int,long,float,double,boolean八種基本數(shù)據(jù)類型和String,Enum,Class,annotations等數(shù)據(jù)類型,以及這一些類型的數(shù)組.例如,String value();這里的參數(shù)成員就為String.
第三,如果只有一個(gè)參數(shù)成員,最好把參數(shù)名稱設(shè)為"value",后加小括號(hào).例:上面的例子就只有一個(gè)參數(shù)成員.
public?@interface?NewAnnotation?{
????String?value();
}
public?class?AnnotationTest?{
????@TestAnnotation("mainmethod")
????public?static?void?main(String[]args)?{
????????say();
????}
????@TestAnnotation(value="一個(gè)外地的招呼")
????public?static?void?say()?{
????}
}
定義一個(gè)枚舉類型,然后將參數(shù)設(shè)置為該枚舉類型,并賦予默認(rèn)值
public?@interface?ColorBox{
????public?enum?BOX {
????????BLUE,
????????RED,
????????GREEN
????};
????String?name();
????BOX box()?default?BOX.RED;
}
這里有兩種選擇,其實(shí)變數(shù)也就是在賦予默認(rèn)值的參數(shù)上,我們可以選擇使用該默認(rèn)值
也可以重新設(shè)置一個(gè)值來替換默認(rèn)值
public?class?AnnotationTest?{
????@TestAnnotation("mainmethod")
????public?static?void?main(String[]args)?{
????????say();
????????defaultColor();
????????dedColor();
????}
????@TestAnnotation("來自外地的招呼")
????public?static?void?say()?{
????}
????//?此時(shí)的fontColor為默認(rèn)的RED
????@ColorBox(name="defaultfontcolor")
????public?static?void?defaultColor()?{
????}
????//?將box改為BLUE
????@ColorBox(name="notdefault",?box=ColorBox.BOX.BLUE)
????public?static?void?redColor()?{
????}
}
----------------------------------------------
注解高級(jí)應(yīng)用
使用范圍
用@Target指定ElementType屬性
public?enum?ElementType?{
????//?用于類,接口,枚舉但不能是注解??TYPE,
????//?字段上,包括枚舉值???FIELD,
????//?方法,不包括構(gòu)造方法????METHOD,
????//?方法的參數(shù)????PARAMETER,
????//?構(gòu)造方法????CONSTRUCTOR,
????//?本地變量或catch語句????LOCAL_VARIABLE,
????//?注解類型(無數(shù)據(jù))????ANNOTATION_TYPE,
????//?Java包????PACKAGE
}
具體例子:
//?限制注解使用范圍
@Target({ElementType.METHOD,ElementType.CONSTRUCTOR})
public?@interface?ColorBox{
????//?使用枚舉類型
????public?enum?Box{
????????BLUE,RED,GREEN
????};
????String?name();
????Box box()?default?Box.RED;
}
注解保持性策略
在Java編譯器編譯時(shí),它會(huì)識(shí)別在源代碼里添加的注解是否還會(huì)保留,這就是RetentionPolicy。下面是Java定義的RetentionPolicy枚舉:
編譯器的處理有三種策略:
將注解保留在編譯后的類文件中,并在第一次加載類時(shí)讀取它;
將注解保留在編譯后的類文件中,但是在運(yùn)行時(shí)忽略它;
按照規(guī)定使用注解,但是并不將它保留到編譯后的類文件中。
public?enum?RetentionPolicy?{
????//?此類型會(huì)被編譯器丟棄????SOURCE,
????//?此類型注解會(huì)保留在class文件中,但JVM會(huì)忽略它????CLASS,
????//?此類型注解會(huì)保留在class文件中,JVM會(huì)讀取它???RUNTIME
}
//?讓保持性策略為運(yùn)行時(shí)態(tài),即將注解編碼到class文件中,讓虛擬機(jī)讀取
@Retention(RetentionPolicy.RUNTIME)
public?@interface?ColorBox{
????//?使用枚舉類型
????public?enum?Box{
????????BLUE,RED,GREEN
????};
????String?name();
????Box box()?default?Box.RED;
}
文檔化功能
Java提供的Documented元注解跟Javadoc的作用是差不多的,其實(shí)它存在的好處是開發(fā)人員可以定制Javadoc不支持的文檔屬性
并在開發(fā)中應(yīng)用。它的使用跟前兩個(gè)也是一樣的,簡單代碼示例如下:
//?讓它定制文檔化功能
//?使用此注解時(shí)必須設(shè)置RetentionPolicy為RUNTIME
@Documented
public?@interface?ColorBox{
????//?使用枚舉類型
????public?enum?Box{
????????BLUE,RED,GREEN
????};
????String?name();
????Box box()?default?Box.RED;
}
標(biāo)注繼承
//?讓它允許繼承,可作用到子類
@Inherited
public?@interface?ColorBox{
????//?使用枚舉類型
????public?enum?Box{
????????BLUE,RED,GREEN
????};
????String?name();
????Box box()?default?Box.RED;
}
------------------------------------------------------------------
讀取方法
屬于重點(diǎn),在系統(tǒng)中用到注解權(quán)限時(shí)非常有用,可以精確控制權(quán)限的粒度
注意:要想使用反射去讀取注解,必須將Retention的值選為Runtime
import?java.lang.annotation.Annotation;
import?java.lang.reflect.Method;
//讀取注解信息
public?class?ReadAnnotationInfoTest?{
????public?static?void?main(String[]?args)?throws?Exception?{
????????//?測試AnnotationTest類,得到此類的類對(duì)象
????????Class?c?=?Class.forName("com.iwtxokhtd.annotation.AnnotationTest");
????????//?獲取該類所有聲明的方法
????????Method[]?methods?=?c.getDeclaredMethods();
????????//?聲明注解集合
????????Annotation[]?annotations;
????????//?遍歷所有的方法得到各方法上面的注解信息
????????for?(Method?method?:?methods)?{
????????????//?獲取每個(gè)方法上面所聲明的所有注解信息
????????????annotations?=?method.getDeclaredAnnotations();
????????????//?再遍歷所有的注解,打印其基本信息
????????????System.out.println(method.getName());
????????????for?(Annotation?an?:?annotations)?{
????????????????System.out.println("方法名為:"?+?method.getName()?+?"其上面的注解為:"?+?an.annotationType().getSimpleName());
????????????????Method[]?meths?=?an.annotationType().getDeclaredMethods();
????????????????//?遍歷每個(gè)注解的所有變量
????????????????for?(Method?meth?:?meths)?{
????????????????????System.out.println("注解的變量名為:"?+?meth.getName());
????????????????}
????????????}
????????}
????}
}
Class?c?=?Class.forName("com.iwtxokhtd.annotation.AnnotationTest");
boolean flag = c.isAnnotationPresent(Description.class);
????????if (flag) {
????????????// 得到 Description? 的描述
????????????Description des = (Description) c.getAnnotation(Description.class);
????????????System.out.println("描述:" + des.value());
????????}
// 把某個(gè)引用指定注解的類有利用到@(具體注解的類名)的全部方法保存到Set中去???????
?Set set = new HashSet();
Method[] method = c.getMethods();
????????for (int i = 0; i < method.length; i++) {
????????????boolean otherFlag = method[i].isAnnotationPresent(注解類名.class);
????????????if (otherFlag)
????????????????set.add(method[i]);
????????}
然后就可以遍歷set獲取注解的設(shè)置的值了