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); } } } }
結果:
-------初始化------- 怎麼說呢? 刪除操作 -------銷燬--------- -------初始化------- 怎麼說呢? 儲存操作 -------銷燬---------