注解
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窗口反編譯看看:
格式:
public @interface 注解名稱{
/* 屬性列表;
* 注解中的屬性 主要定義抽象方法 不定義常量
* 抽象方法的返回值類型有要求():
* 1.基本數據類型
* 2.String
* 3.枚舉類型
* 4.注解
* 5.數組(以上幾種類型的數組)
*/
}
//屬性列表中抽象方法的舉例:
public @interface MyAnno {
int getName();
String getStr();
Season getSeason(); //新建Enum枚舉類Season
MyAnno2 getMyAnno2(); //新建Annotation注解類MyAnno2
String[] value();
//int getName() default 0; //如果不想為注解中的某個屬性賦值,可以為其定義默認值
}
//枚舉類
public enum Season {
SPRING,SUMMER,AUTUMN,WINtER
}
注解類定義了屬性,那么在使用時必須給屬性賦值。
/*
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() {}
元注解:用於描述注解的注解(注解前面的注解)
@Target:描述注解能夠作用的位置
ElementType取值:
TYPE:可以作用於類上
FIELD:可以作用於成員變量上
METHOD:可以作用於方法上
@Retention:描述注解被保留的階段
SOURCE: 源代碼階段, 被編譯器忽略
CLASS: 注解將會被保留在Class文件中,但在運行時並不會被VM保留。這是默認行為,所有沒 有用Retention注解的注解,都會采用這種策略。
RUNTIME:保留至運行時。所以我們可以通過反射去獲取注解信息。
@Retention(RetentionPolicy.RUNTIME):當前被描述的注解,會保留到class字節碼文件中,並被JVM讀取到
//下面兩個了解
@Documented:描述注解是否被抽取到api文檔中
@Inherited:描述注解是否被子類繼承
對Java的三大注解之一的SuppressWarnings注解進行分析:
@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.注解的使用
注解的作用:將我們為注解中的屬性(抽象方法)賦的值提取出來,在類中使用
-
獲取注解定義的位置的對象 (Class,Field,Method)
-
獲取指定的注解:
getAnnotation(Class annotationClass):方法返回該元素的指定類型的注釋,如果是這樣的注釋,否則返回null
參數:annotationClass -- 對應於注釋類型的Class對象。
6.案例
1.定義該類使用注解加反射,實現不修改任何代碼(只需要修改注解中的值),就可以實現調用任意類中的任意方法
//自定義注解類
@Retention(RUNTIME)
@Target(TYPE)
public @interface Pro {
String className(); //通過該屬性獲取到類名
String methodName(); //通過該屬性獲取到方法名
}
@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注解的方法),判斷方法內是否有異常
-
如果沒有就算了,如果有異常,會自動記錄到特定的文件中,文件中記錄哪些方法出異常了,異常的名稱是什么異常的原因是什么
-
得出總結:本次共測試了多少方法,出現了多少次異常
//Check注解:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Check {
}
//自定義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...");
}
}
//測試類
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();
}
}