java之美妙的註解
註解
一、認識註解
先看百度百科對java註解的解釋:
定義:註解(Annotation),也叫元資料。一種程式碼級別的說明。它是JDK1.5及以後版本引入的一個特性,與類、介面、列舉是在同一個層次。它可以宣告在包、類、欄位、方法、區域性變數、方法引數等的前面,用來對這些元素進行說明,註釋。
作用分類:
①編寫文件:通過程式碼裡標識的元資料生成文件【生成文件doc文件】
② 程式碼分析:通過程式碼裡標識的元資料對程式碼進行分析【使用反射】
③編譯檢查:通過程式碼裡標識的元資料讓編譯器能夠實現基本的編譯檢查【Override】
提到的元資料,可以理解為描述資料的資料,看起來很繞,元註解也是描述註解的註解,後面說。
Annotation都是java.lang.annotation.Annotation介面的子介面,註解是一種特殊的介面,從程式碼格式上來看確實是,是在interface前加了一個@
符號,格式為@interface xxx{}
二、JDK內建註解
jdk就存在註解,比如我們用eclipse子類覆蓋父類方法時候用快捷間alt+/會自帶在方法名稱上面貼上一個@Override的標籤。
@Override
這個就是在子類覆蓋父類的方法時候,經常遇到
@Deprecated
這個元素是用來標記過時的元素,想必大家在日常開發中經常碰到。編譯器在編譯階段遇到這個註解時會發出提醒警告,告訴開發者正在呼叫一個過時的元素比如過時的方法、過時的類、過時的成員變數。 如java.util.Data類中有很多過時的方法
@SupressWarings
抑制警告,有很多人有程式碼潔癖,看到黃色的警告不爽,用它就可以抑制住。
@SafeVarargs
java7出現的,抑制堆汙染警告,有點自欺欺人的感覺。
三、元註解
元註解就是註解的註解,來看一下有哪些元註解
@Retention
Retention英文是保留的意思,在這裡可以約束註解的存活週期,程式碼執行有三個週期,分別為Source(原始碼)、Class(位元組碼)、Runtime(執行時期) 三個時期的值保留在RetentionPolicy這個列舉類中。所以我們可以這樣來玩,
@Retention(RetetionPolicy.常量值)//RESOURCE,CLASS,RUNTIME
@Target
Target是目標的意思,這裡約束這個註解在那可以貼(類,方法,構造器,引數等),位置的常量在ElementType這個列舉類中
@Target({ElementsType.常亮值})//TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, TYPE表示類,介面,列舉
@Documented
Document文件的意思,也就是使用這個標籤到時候會帶到API文件中去
@Inherited
Inherited是遺傳的意思,顧名思義,這個註解會遺傳到子類去
@Repeatable 可重複的,Java8的一個新特性
四、自定義註解
1.註解的屬性
在註解中定義屬性,必須是8 種基本資料型別外加 類、介面、註解及它們的陣列。不能是Integer..註解中屬性可以有預設值,預設值需要用 default 關鍵值指定。
如:
@interface TestAnnotation{
int id() default 0;
String msg() default "hello";
}
@TestAnnotation(id = 3,msg = "aa")
class Test1{
}
//當註解中只有一個value屬性時候,貼標籤的時候可以省略value。
//當註解中沒有屬性時,括號都可以省略(override註解)
@interface Test1Annotation{
int[] value();
}
@Test1Annotation({1,2})
class Test2{
}
2.用反射操作註解
註解可以在類Class,方法Method,欄位Field,構造器Constructor上等,所以在各自的類中都存在獲取註解的API
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
如果指定型別的註釋存在於此元素上,則返回 true,否則返回 false。
Annotation[] getAnnotations() 返回此元素上存在的所有註釋。
<Annotation> getAnnotation(Class annotationClass)如果存在該元素的指定型別的註釋,則返回這些註釋物件,否則返回 null。 獲取註解物件
自定義一個註解:
@Target({TYPE, FIELD, METHOD})//註解可以放的目標
@Retention(RetentionPolicy.RUNTIME)//註解保留週期
public @interface AnnotationTest {
int id();
String name();
}
測試一下這個自定義的註解:
@AnnotationTest(id = 1,name = "na")
class Test{
public static void main(String[] args) throws Exception {
//判斷Test(類元素)是否應用了這個註解
boolean hasAnnotation = Test.class.isAnnotationPresent(AnnotationTest.class);
if(hasAnnotation){
//獲取註解物件
AnnotationTest t = Test.class.getAnnotation(AnnotationTest.class);
System.out.println(t.name());
System.out.println(t.id());
//獲取所有的註解s
Annotation[] ans = Test.class.getAnnotations();
for (Annotation an : ans) {
System.out.println(an);
}
}
System.out.println("============");
//獲取方法中上的註解
boolean has = Test.class.getMethod("work").isAnnotationPresent(AnnotationTest.class);
if(has){
AnnotationTest an = Test.class.getMethod("work").getAnnotation(AnnotationTest.class);
System.out.println(an.name());
}
}
@AnnotationTest(id = 0,name="hello")
public void work(){
}
}
結果為:
na
1
@annotation.AnnotationTest(id=1, name=na)
============
hello
五、註解的用途
實踐:模擬Junit4.x
Junit是做單元測試的,junit4用了註解,用法是在一個公共無參的方法貼上一個@Test標籤表示要測試這個方法,但是在測試之前有可能會出現一些初始化操作,在結束後可能需要釋放一些資源,需要初始化的方法上面貼上一個@Before,Test之後需要執行的需要貼上@After。
現在我們來自定義註解來模擬一下這些操作,需要在這裡提的是,其實註解本身沒什麼意義,需要人為的設定它完成一些功能。
我們先定義三個註解:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAfter {
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyBefore {
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest {
}
來一個測試這些註解功能的類
public class Employee {
@MyBefore
public void init(){
System.out.println("-------初始化-------");
}
@MyAfter
public void end(){
System.out.println("-------銷燬---------");
}
@MyTest
public void save() {
System.out.println("儲存操作");
}
@MyTest
public void delete() {
System.out.println("刪除操作");
}
@MyBefore
public void ddd(){
System.out.println("怎麼說呢?");
}
}
好了,接下來我們就得具體去設定這個註解的意義
思路和步驟:
獲取Employee類的位元組碼物件,得到所有的方法
- 對所有方法進行迭代,將三類註解分別儲存,把有@MyBefore註解的存在beforeList,帶有@MyAfter存在afterList,帶有@MyTest存在testList 對testList集合迭代,在迭代 過程中先執行beforeList中的方法,在執行afterList中的方法
public class JunitTest {
public static void main(String[] args) throws Exception {
//得到Employee中所有的方法
Method[] mds = Employee.class.getDeclaredMethods();
//建立Employee的物件,用來執行方法
Employee e = Employee.class.newInstance();
//用集合儲存三類註解標註的方法
List<Method> beforeList = new ArrayList<>();
List<Method> afterList = new ArrayList<>();
List<Method> testList = new ArrayList<>();
//對所有的方法進行迭代,分類
for (Method md : mds) {
if(md.isAnnotationPresent(MyBefore.class)){
beforeList.add(md);
}else if(md.isAnnotationPresent(MyAfter.class)){
afterList.add(md);
}else if(md.isAnnotationPresent(MyTest.class)){
testList.add(md);
}
}
//在對test迭代的過程中,先執行所有被before註解了的方法,然後執行test註解的方法,最後再執行after註解了的方法
for (Method method : testList) {
for (Method before : beforeList) {
before.invoke(e); //方法都沒有引數
}
method.invoke(e);
for (Method after : afterList) {
after.invoke(e);
}
}
}
}
結果:
-------初始化-------
怎麼說呢?
刪除操作
-------銷燬---------
-------初始化-------
怎麼說呢?
儲存操作
-------銷燬---------