1. 程式人生 > >Java註解 看這一篇就夠了

Java註解 看這一篇就夠了

# 註解 ## 1.概念 註解:說明程式的。給計算機看的 註釋:用文字描述程式的。給程式設計師看的 註解的定義:註解(Annotation),也叫元資料。一種程式碼級別的說明。它是JDK1.5及以後版本引入的一個特性,與類、介面、列舉是在同一個層次。它可以宣告在包、類、欄位、方法、區域性變數、方法引數等的前面,用來對這些元素進行說明,註釋。 ## 2.作用 ①編寫文件:通過程式碼裡標識的註解生成文件【生成文件doc文件】: ​ shift+右鍵 -> 在此處開啟Powershell視窗 -> 輸入:javadoc .\類名.java ②程式碼分析:通過程式碼裡標識的註解對程式碼進行分析【使用反射】 ③編譯檢查:通過程式碼裡標識的註解讓編譯器能夠實現基本的編譯檢查【Override】 ## 3.Java 的三大註解 1.@Override:表明子類中覆蓋了超類中的某個方法,如果寫錯了覆蓋形式,編譯器會報錯 2.@deprecated:廢棄的(過時的)表明不希望別人在以後使用這個類,方法,變數等. 3.@suppresswarnings:抑制警告 ​ 達到抑制編譯器產生警告的目的,但是不很不建議使用,因為後期編碼人員看不懂編譯器 提示的警告,不能更好的選擇更好的類去完成任務。 ​ 一般傳遞引數:all @SuppressWarnings("all") 可以加在類的上面一行,這樣程式碼就 沒有警告了,顯得比較乾淨。 ## 4.自定義註解: 本質:註解本質上就是一個介面,該介面預設繼承Annotation介面 ​ public interface MyAnno extends java.lang.annotation.Annotation {} ​ 可以在Powershell視窗反編譯看看: ![](https://img2020.cnblogs.com/blog/1971784/202003/1971784-20200320101724210-2029190552.jpg) 格式: ```java public @interface 註解名稱{ /* 屬性列表; * 註解中的屬性 主要定義抽象方法 不定義常量 * 抽象方法的返回值型別有要求(): * 1.基本資料型別 * 2.String * 3.列舉型別 * 4.註解 * 5.陣列(以上幾種型別的陣列) */ } ``` ```java //屬性列表中抽象方法的舉例: public @interface MyAnno { int getName(); String getStr(); Season getSeason(); //新建Enum列舉類Season MyAnno2 getMyAnno2(); //新建Annotation註解類MyAnno2 String[] value(); //int getName() default 0; //如果不想為註解中的某個屬性賦值,可以為其定義預設值 } ``` ```java //列舉類 public enum Season { SPRING,SUMMER,AUTUMN,WINtER } ``` 註解類定義了屬性,那麼在使用時必須給屬性賦值。 ```java /* 1. 如果定義屬性時,使用default關鍵字給屬性預設初始化值,則使用註解時,可以不進行屬性的賦值。int getName() default 0; @MyAnno() public static void test02() {} 2. 如果只有一個屬性需要賦值,並且屬性的名稱是value,則value可以省略,直接定義值即可。 3. 陣列賦值時,值使用{}包裹。如果陣列中只有一個值,則{}可以省略。 */ //對我們自定義的MyAnno註解類的使用: @MyAnno(getName = 1, getStr = "100", getSeason = Season.SPRING, getMyAnno2= @MyAnno2,value = {"1","2"}) public static void test01() {} ``` 元註解:用於描述註解的註解(註解前面的註解) ```java @Target:描述註解能夠作用的位置 ElementType取值: TYPE:可以作用於類上 FIELD:可以作用於成員變數上 METHOD:可以作用於方法上 @Retention:描述註解被保留的階段 SOURCE: 原始碼階段, 被編譯器忽略 CLASS: 註解將會被保留在Class檔案中,但在執行時並不會被VM保留。這是預設行為,所有沒 有用Retention註解的註解,都會採用這種策略。 RUNTIME:保留至執行時。所以我們可以通過反射去獲取註解資訊。 @Retention(RetentionPolicy.RUNTIME):當前被描述的註解,會保留到class位元組碼檔案中,並被JVM讀取到 //下面兩個瞭解 @Documented:描述註解是否被抽取到api文件中 @Inherited:描述註解是否被子類繼承 ``` 對Java的三大註解之一的SuppressWarnings註解進行分析: ```java @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @Retention(RetentionPolicy.SOURCE) public @interface SuppressWarnings { String[] value(); //只有一個屬性,且名稱為value,且為陣列賦值 } @SuppressWarnings(value={"all"}) public class AnnoTest {} //同樣可以這麼使用 @SuppressWarnings({"all"}) //使用時可以省略value @SuppressWarnings("all") //使用時可以省略{} ``` ## 5.註解的使用 註解的作用:將我們為註解中的屬性(抽象方法)賦的值提取出來,在類中使用 1. 獲取註解定義的位置的物件 (Class,Field,Method) 2. 獲取指定的註解: getAnnotation(Class annotationClass):方法返回該元素的指定型別的註釋,如果是這樣的註釋,否則返回null ​ 引數:annotationClass -- 對應於註釋型別的Class物件。 ## 6.案例 1.定義該類使用註解加反射,實現不修改任何程式碼(只需要修改註解中的值),就可以實現呼叫任意類中的任意方法 ```java //自定義註解類 @Retention(RUNTIME) @Target(TYPE) public @interface Pro { String className(); //通過該屬性獲取到類名 String methodName(); //通過該屬性獲取到方法名 } ``` ```java @SuppressWarnings("all") @Pro(className = "com.huike.b.useanno.Demo2", methodName = "show") public class AnnoTest { public static void main(String[] args) throws Exception { //1.解析註解 //1.1:獲取加註解的類的位元組碼檔案物件 //1.2:獲取註解物件 Class cls = AnnoTest.class; //通過當前類的Class物件獲取到類上的註解物件 Pro pro = (Pro) cls.getAnnotation(Pro.class); //2.呼叫註解中的抽象方法 獲取到返回值 String className = pro.className(); String methodName = pro.methodName(); //3.獲取到返回值所對應的類的Class物件 Class cls1 = Class.forName(className); //4.建立該類的物件 Object object = cls1.newInstance(); //5.獲取到該類的特定方法物件 Method method = cls1.getMethod(methodName); //6.執行方法 method.invoke(object); } } ``` 2.測試框架: * 當main方法執行後,會自動執行被檢測的所有方法(被加了Check註解的方法),判斷方法內是否有異常 * 如果沒有就算了,如果有異常,會自動記錄到特定的檔案中,檔案中記錄哪些方法出異常了,異常的名稱是什麼異常的原因是什麼 * 得出總結:本次共測試了多少方法,出現了多少次異常 ```java //Check註解: @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Check { } ``` ```java //自定義Calculator類,被用於測試 public class Calculator { //加法 @Check public void add() { String str = null; str.toString(); System.out.println("1 + 0="+(1 + 0)); } //減法 @Check public void sub() { System.out.println("1 - 0="+(1 - 0)); } //乘法 @Check public void mul() { System.out.println("1 * 0="+(1 * 0)); } //除法 @Check public void div() { System.out.println("1 / 0="+(1 / 0)); } public void show() { System.out.println("永無bug..."); } } ``` ```java //測試類 public class CheckDemo { public static void main(String[] args) throws Exception{ //1.建立計算器物件 得到對應的Class物件 Calculator c = new Calculator(); Class cls = c.getClass(); //2.獲取到該物件中的所有的方法 Method[] methods = cls.getDeclaredMethods(); int num01 = 0; //定義一個int型別的值用於記錄出現的異常次數 int num02 = 0; //定義一個int型別的值用於記錄帶有Check註解的方法數 BufferedWriter bw = new BufferedWriter(new FileWriter("bug.txt")); //3.判斷哪些方法上有Check註解 for (Method method : methods) { //該方法用於判斷 方法上是否有特定的註解 //4.如果有Check註解,執行該方法 如果該方法無任何異常,就算了 if (method.isAnnotationPresent(Check.class)) { num02++; try { method.invoke(c); //如果有註解,則執行該方法 } catch (Exception e) { num01++; //5.如果有異常,記錄異常資訊,並通過IO流列印到檔案中 //如果方法存在異常 需要在此通過IO流捕獲 bw.write(method.getName()+" 方法出異常了..."); bw.newLine(); //獲取到異常的簡短名稱 bw.write("異常的名稱為:"+ e.getCause().getClass().getSimpleName()); bw.newLine(); bw.write("異常的原因是:"+ e.getCause().getMessage()); bw.newLine(); bw.write("------------------------------------------------"); bw.newLine(); } } } bw.write("本次測試結束了,一共測試了"+num02+"個方法,共出現了"+num01+"次異常!"); bw.flush(); bw.close();