什麼是註解
網上java註解描述:Java 註解(Annotation)又稱 Java 標註,是 JDK5.0 引入的一種註釋機制。
Java 語言中的類、方法、變量、參數和包等都可以被標註。和 Javadoc 不同,Java 標註可以通過反射獲取標註內容。
在編譯器生成類文件時,標註可以被嵌入到字節碼中。Java 虛擬機可以保留標註內容,在運行時可以獲取到標註內容 。
當然它也支持自定義 Java 標註。
提取重點一下
1、JDK1.5 之後的新特性
2、用來說明程序的、
3、是元數據
4、使用註解:@註解名稱(參數)
作用
有三種:
1、編譯檢查:通過代碼裏標識的註解讓編譯器能夠實現基本的編譯檢查【Override】
2、編寫文檔:通過代碼裏標識的註解生成文檔【生成文檔doc文檔】,API文檔是通過抽取代碼中的文檔註釋生成的。
3、代碼分析:通過代碼裏標識的註解對代碼進行分析【使用方式】(重點說明)
JDK中預定義的一些註解
@Override : 檢測被該註解標註的方法是否蒐集繼承自父類(接口)的
@Deprecated :將該註解標註的內容,表示已過時
@SuppressWarnings :壓制警告,一般傳遞參數all @SuppressWarnings(“all”)
在編碼中最常見的就是 @Override、 @Deprecated
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
使用
使用的話我相信大家不陌生吧,開發中大大小小的框架都有使用註解的模式,註解在java開發中是日常可見。他們是如何實現相關功能的呢,我們從一個自定義註解說起。
自定義註解
我們先了解註解的張什麼樣子,已@Override爲剖析點
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
分成兩部分,先說接口部分
元註解
public @interface Override {
屬性
}
其實使用了@interface,我們依葫蘆畫瓢
public @interface MyAnno {
}
這個是什麼呢,根據@interface,推測註解是特殊接口
我們可以先對文件javac編譯後再javap反編譯看看其結構如何
public interface MyAnno extends java.lang.annotation.Annotation{
}
確定其本質是接口
屬性
接口的話就有抽象方法,我們把其中的抽象方法稱爲屬性
1.屬性的返回值類型只能爲以下幾種:
基本數據類型
String
枚舉
註解
以上類型的數組
- 定義了的屬性(本質上是抽象方法),在使用時需要進行賦值
如果定義屬性時,使用default關鍵字給屬性默認初始化值,則使用註解時,可以不進行屬性的賦值。
如果只有一個屬性需要賦值,並且這個屬性的名稱是value,那麼value可以省略,直接賦值即可。
public @interface MyAnno {
int value;
}
@MyAnno(1)
public class Test(){
}
數組賦值時,值使用大括號包裹。如果數組中只有一個值,那麼{}可以省略。
public @interface MyAnno {
int age();
String name() default "小可愛";
MyAnno2 anno2();
String[] str();
}
@MyAnno(age = 1,name="小夥子",anno2 = @MyAnno,str = "1324")
第二部分:
元註解
概念:用於描述註解的註解。
@Target
:描述能夠作用的位置
@Target(value = {ElementType.TYPE}) //表示該MyAnno註解只能作用於類上
public @interface MyAnno {
}
其中value中ElementType取值可以有以下幾種情況:
TYPE:可以作用在類上
METHOD:可以作用在方法上
FIELD:可以作用於成員變量上
@Target(value = {ElementType.TYPE,ElementType.METHOD,ElementType.FIELD})
public @interface MyAnno {
}
表示該MyAnno註解可以同時作用於類上,方法上和成員變量上
@Retention
:描述註解被保留的階段
java三個運行階段,SOURCE、CLASS、RUNTIME
@Retention(RetentionPolicy.RUNTIME)
:當前被描述的註解,會保留到字節碼文件中,並被JVM讀取到,一般自己定義的註解都加RUNTIME
@Documented
:描述該註解是否會被抽取到api文檔中
@Inherited
:描述註解是否被子類繼承
註解一般是用來處理配置
可以在註解屬性中加入配置信息,如
SpringBoot中
也可以在類中獲取註解設置的屬性
@Target(ElementType.TYPE) //可以被作用在類上
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnoReflect {
String className();
String methodName();
@AnnoReflect(className = "cn.other.annotation.AnnoTest1",methodName = "play")
public class ReflectAnnotationTest {
public static void main(String[] args) throws Exception {
//1. 解析註解
//1.1 獲取該類的字節碼文件對象
Class<ReflectAnnotationTest> rac = ReflectAnnotationTest.class;
//1.2 獲取上面的註解對象,其實就是在內存中生成了一個該註解接口的子類實現對象
AnnoReflect an = rac.getAnnotation(AnnoReflect.class);
/*
相當於
public class AnnotationReflect implements AnnoReflect{
public String className(){
return "cn.other.annotation.AnnoTest1";
}
public String methodName(){
return "play";
}
}
*/
//2. 調用註解對象中定義的抽象方法,獲取返回值
String className = an.className();
String methodName = an.methodName();
}
在程序中使用註解:獲取註解中定義的屬性值
獲取註解定義的位置的對象 (Class, Method, Field)
獲取指定的註解:getAnnotation(Class)
調用註解中的抽象方法獲取配置的屬性值
網上經典教學案例
註解加反射監控檢測一個類中的方法使用有異常,並進行統計
待測試的類
public class calculator {
public void add(){
System.out.println("1+0="+(1+0));
}
public void sub(){
System.out.println("1-0="+(1-0));
}
public void mul(){
System.out.println("1*0="+(1*0));
}
public void div(){
System.out.println("1/0="+(1/0));
}
public void show(){
System.out.println("今天天氣真不錯!");
}
}
實現步驟
自定義一個註解
@Retention(RetentionPolicy.RUNTIME) //運行時
@Target(ElementType.METHOD) //加在方法前面
public @interface Check {
}
然後編寫一個類專門用於檢查(注意註釋):
/** 簡單的測試框架
* 當主方法執行後,會自動自行檢測所有方法(加了check註解的方法),判斷方法是否有異常並記錄
*/
public class TestCheck {
public static void main(String[] args) throws IOException {
//1. 創建計算機對象
calculator c = new calculator();
//2. 獲取字節碼文件對象
Class cls = c.getClass();
//3. 獲取所有方法
Method[] methods = cls.getMethods();
int num = 0; //記錄出現異常的次數
BufferedWriter bw = new BufferedWriter(new FileWriter("bug.txt"));
for(Method method:methods){
//4. 判斷方法上是否有Check註解
if(method.isAnnotationPresent(Check.class)){
//5. 有註解就執行,捕獲異常
try {
method.invoke(c);
} catch (Exception e) {
e.printStackTrace();
//6.將異常記錄在文件中
num++;
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("本次測試一共出現"+num+"次異常");
bw.flush();
bw.close();
}
}
在待測試的類中每個需要測試的方法前面都加上@Check
public class calculator {
@Check
public void add(){
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");
}
}
行TestCheck類中的主方法,就會自動檢查所有註解@Check的方法是否異常
開發總結
大多數時候,我們會使用註解而不是自定義註解
註解給編譯器和解析程序用(如TestCheck)
註解不是程序的一部分,可以理解爲標籤