Java註解
簡介
由於無論在Java後臺或者Android開發中我們經常遇到註解這個功能機制,例如常用的框架Java後臺開發中,Spring、MyBatis等,Android的Dagger2,butterknife等,都是註解框架。今天我們就瞭解java是如何進行設定註解的?我們可以可以定義一個註解,方便我們使用等等。
註解元
在進行了解註解時我們先來了解一下,一般註解主要包含以下幾個重要的註解元,java註解的機制離不開這幾個註解元。
註解元 | 介紹<功能> |
---|---|
1.@Target | 註解用於什麼地方,下面會介紹 |
2.@Retention | 什麼時候使用該註解 |
3.@Documented | 註解是否將包含在JavaDoc中 |
4.@Inherited | 是否允許子類繼承該註解,表示父類如果新增此註解,子類也可以使用 |
5.@Repeatable | java8新增的,可重複的,表該註解可以多次使用 |
註解元解釋
@Target
通過 @Target
進行新增到註解中,說明了Annotation所修飾的物件範圍:Annotation可被用於 packages、types(類、介面、列舉、Annotation型別)、型別成員(方法、構造方法、成員變數、列舉值)、方法引數和本地變數(如迴圈變數、catch引數)。在Annotation型別的宣告中使用了 @target
可更加明晰其修飾的目標,用於什麼地方,修飾什麼東西,如下常用的幾種方式。
- CONSTRUCTOR: 用於描述構造器
- FIELD:用於描述域
- LOCAL_VARIABLE:用於描述區域性變數
- METHOD:用於描述方法
- PACKAGE:用於描述包
- PARAMETER:用於描述引數
- TYPE:用於描述類、介面(包括註解型別) 或enum宣告
如下程式碼
@Target({ElementType.FIELD,ElementType.METHOD}) //多個的時候使用{}括起來,然後用逗號分隔開 public @interface TargetTest { }
@Retention
該註解是表示我們在執行或者編譯時的一個保留狀態,指示要保留帶註釋型別的註釋的時間長度。
- SOURCE:在原始檔中有效(即原始檔保留),在編譯時將其拋棄掉。
- CLASS:在class檔案中有效(即class保留),不會新增載到JVM中
- RUNTIME:在執行時有效(即執行時保留),這個和我們常用的載入是一致的,執行時會載入到JVM中,一般通過反射可獲取到,一般這個是我們常用的。
@Retention(RetentionPolicy.RUNTIME)//在使用該註解其,只能選擇其中一種屬性,不能定義多個 public @interface RetentionTest { }
@Documented
@Documented
用於描述其它型別的annotation應該被作為被標註的程式成員的公共API。
如下程式碼
@Documented //新增文件註解 @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface AnnotationTest { }
@Inherited:
@Inherited
元註解是一個標記註解, @Inherited
闡述了某個被標註的型別是被繼承的。如果一個使用了 @Inherited
修飾的annotation型別被用於一個class,則這個annotation將被用於該class的子類。
@Documented @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Inherited//新增繼承註解 public @interface AnnotationTest { }
@Repeatable
這個是在java8新增的註解特性, @Repeatable
的值表示可重複註釋型別的包含註釋型別。
@Target({ElementType.TYPE,ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface Repeatables { RepeatablesTest[] value(); } @Repeatable(Repeatables.class) public @interface RepeatablesTest { String value() default "哈哈哈"; }
例子
我們簡單的介紹了註解的常用的5個註解元,解下來我們通過例子來實現註解。
程式碼如下,根據上面的提示我們進行測試。
在看例子之前我們先來了解一下常用的幾個方法。
- Class.getAnnotations() 獲取所有的註解,包括自己宣告的以及繼承的
- Class.getAnnotation(Class< A > annotationClass) 獲取指定的註解,該註解可以是自己宣告的,也可以是繼承的
- Class.getDeclaredAnnotations() 獲取自己宣告的註解
- Class.getDeclaredField(String name); //獲取該類的宣告欄位
- Class.getDeclaredMethods();//返回的是一個Method[]陣列
@Documented @Target({ElementType.TYPE,ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface AnnotationTest { String name() default "小明"; //表示預設為小明。 }
@AnnotationTest() //在該類添加註解 public class TestAnnotationMain { public static void main(String[] args) { boolean hasAnnotation = TestAnnotationMain.class.isAnnotationPresent(AnnotationTest.class); if (hasAnnotation) { AnnotationTest annotation = TestAnnotationMain.class.getAnnotation(AnnotationTest.class); System.out.println(annotation.name()); } } }
列印輸出結果:

輸出1.png
如果我們想更改name的值可以這麼弄
@AnnotationTest(name = "小剛") public class TestAnnotationMain { public static void main(String[] args) { boolean hasAnnotation = TestAnnotationMain.class.isAnnotationPresent(AnnotationTest.class); if (hasAnnotation) { AnnotationTest annotation = TestAnnotationMain.class.getAnnotation(AnnotationTest.class); System.out.println(annotation.name()); } } }

輸出2.png
如果我們想給一個類的屬性進行賦值可以這麼做
1.建立一個註解如下
@Documented @Retention(RetentionPolicy.RUNTIME) public @interface AnnotationTest1 { String value(); //value來定義 }
2.引用該主解
public class Test { @AnnotationTest1(value = "小云") //引用註解進行賦值 public String name; }
3.測試
public class TestAnnotation1Main { public static void main(String[] args) { try { Field name = Test.class.getDeclaredField("name");//該該類的欄位 name.setAccessible(true); AnnotationTest1 annotationTest1 = name.getAnnotation(AnnotationTest1.class);//獲取該欄位的註解 if (annotationTest1 != null) { System.out.println(annotationTest1.value()); //輸出值 } } catch (NoSuchFieldException e) { e.printStackTrace(); } } }
獲取方法上的註解類 如AnnotationTest2
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface AnnotationTest2 { //todo無任何方法或者屬性 }
public class Test { @AnnotationTest1(value = "小云") public String name; @AnnotationTest2 //目的獲取該AnnotationTest2 public void fun() { System.out.println("方法執行"); } }
獲取
public class TestAnnotation1Main { public static void main(String[] args) { try { Field name = Test.class.getDeclaredField("name"); //獲取該類的宣告欄位 name.setAccessible(true); AnnotationTest1 annotationTest1 = name.getAnnotation(AnnotationTest1.class);//獲取該欄位的註解 if (annotationTest1 != null) { System.out.println(annotationTest1.value()); //輸出值 } Method fun = Test.class.getDeclaredMethod("fun"); if (fun != null) { Annotation[] annotations = fun.getAnnotations(); for (int i = 0; i < annotations.length; i++) { System.out.println(annotations[i].annotationType().getSimpleName()); } } } catch (NoSuchFieldException | NoSuchMethodException e) { e.printStackTrace(); } } }
舉例
@Documented @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { } //////////////////////////////////////////////////////////////////////////// public class MyTest { //進行獲取註解方法的全部資料 @MyAnnotation public void mytestLoad() { System.out.println("測試載入"); } @MyAnnotation public void mytestRequest() { System.out.println("測試請求"); } @MyAnnotation public void mytestProgress() { System.out.println("測試進度"); } @MyAnnotation public void mytestError() { System.out.println(1 ); } ///////該方法不執行 public void mytestNoAnno(){ System.out.println("沒有註解的方法"); } } //////////////////////////////////////////////////////////////////////////// public class TestMain { public static void main(String[] args) { MyTest myTest = new MyTest(); Method[] methods = myTest.getClass().getDeclaredMethods(); for (int i = 0; i < methods.length; i++) { Method method = methods[i]; if (method.isAnnotationPresent(MyAnnotation.class)) { try { method.setAccessible(true); method.invoke(myTest, null);//呼叫該類的註解方法 } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } } System.out.println("==================輸出完成!===================="); } }

輸出3.png
下載
ofollow,noindex"> Annotation案例程式碼下載
總結
我們在開發中長見的註解如下:
常用註解 | 解釋 |
---|---|
@Override | 方法重寫 |
@SuppressWarnings | 提示警告 |
@SafeVarargs | 引數安全 |
@Deprecated | 作用是對不應該再使用的方法添加註解 |
@FunctionalInterface | 函數語言程式設計,用於Lambda 表示式 |
註解給我們帶來了許多方便,但是我們也得知道其優缺點。
-
優點
註解方便我們進行單元測試,有利於進行開發。
程式碼整潔,通過註解的方式變可知修飾的變數,或者方法。
節省配置,減少配置檔案大小。
-
缺點
通過反射進行設定,可能會產生效能上的問題。
若要對配置進行修改需要重新編譯,擴充套件性差。
參考
https://blog.csdn.net/briblue/article/details/73824058
https://blog.csdn.net/ryo1060732496/article/details/80891093