Java註解 看這一篇就夠了
阿新 • • 發佈:2020-03-20
# 註解
## 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();