Java注解原来如此通俗易懂
目錄:
1.注解概述
2.注解的作用分析
3.JDK中預定義的一些注解
4.自定義注解
5.元注解
6.解析注解
1.注解概述
Java注解是一系列元數據,它提供數據用來解釋程序代碼,它是JDK1.5之后版本引入的新的特性,與類,接口,枚舉是同一個層次,它可以聲明在包,類,字段,方法,局部變量,方法參數等的前面,用來說明程序的,注解并非是所解釋的代碼本身的一部分。注解對于代碼的運行效果沒有直接影響
2.注解的作用分析
簡單了解注解的作用:
注解作用:
1.編譯檢查:通過代碼里標識的注解據讓編譯器實現基本的檢查
2.編寫文檔:通過代碼里標識的注解生成文檔【doc文檔】
3.代碼分析:通過代碼標識的注解對代碼進行分析【使用反射】
1.編譯檢查
就比如我們接觸過的Override注解:
Override注解作用是檢查所注解方法是否重寫了父類的方法,如果不是注解的正確使用,編譯就不會通過,這就是所謂的注解檢查:
2.編寫文檔
就是我們在文檔注釋上加一點注解:
然后如果我們把上邊代碼編寫成文檔那么注解的內容將會被翻譯成文檔中相應的內容
ps:博主不知道為啥不能生成文檔所以不能演示生成文檔后,文檔里邊的內容,mmp這文檔肯定看不起我
當然其實只要了解到它會在生成文檔中會被翻譯成對應信息就行
3.代碼分析:通過代碼標識的注解對代碼進行分析【使用反射】,在下面詳細說明
3.JDK預定義的注解
1.@Override:檢查被注解的方法是否是繼承自父類的
2.@Deprecated:將該注解標注的內容已經過時
3.@Suppresswarnings:壓制警告
1.@Override:這個不多講,上邊的演示有
2.@Deprecated
例子:
我們可以看到當我們選擇方法時,show1上是有一個刪除線的,而show2是沒有的,這就是本注解的作用,雖然注解內容已經過時但是不影響我們對它的調用,java中標注這個注解說明該方法已經存在更好的方法進行替換
3.@Suppresswarnings
例子:
乍一看好像沒啥啊,但是可以把鼠標放在紅色嘆號下邊的黃色橫杠上,就會出現提示信息,上圖就有一個Method 'show2()' is never used這個警告信息,如果不想要這些警告,那么就可以在你不想要的警告的方法上添加此注解,就會屏蔽。這個注解一般加在類上邊的部分,這樣就能屏蔽全部的警告
注意使用此注解一般需要加一個參數all,正確格式為@Suppresswarnings("all")
4.自定義注解
1.我們看一個系統自定義注解Override的注解格式分析:
@Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override { }格式:
1.元注解:下邊會講
2.public @interface 注解名稱{}:一個類似于普通接口的接口,作用和使用規則和普通方法一樣
注解本質:
所有注解都會繼承得的公共接口,系統默認為你繼承java.lang.annotation.Annotation接口
注解屬性:
就是注解的接口里邊可以定義的成員方法
要求:
1.屬性的返回值類型:
(1)基本數據類型
(2)String
(3)注解
(4)以上類型的數組
2.定義屬性后:
(1)使用時需要給屬性賦值
(2)如果定義屬性時使用default關鍵字給屬性默認初始化值,則使用注解時,可以不進行屬性的賦值
(3)數組賦值的時候使用{}包裹,如果數組只有一個元素{}可以省略,如果只有一個屬性需要賦值,并且屬性的名稱是value那么賦值時value可以直接省略
為了方便理解舉個例子(對屬性返回值類型的要求):
很明顯void不合適(編譯不通過),注意我們把注解里邊的方法叫做屬性
接下來我們看看定義屬性后的對屬性進行賦值的案例:
package annotation; public @interface AnnoDemo1 {int age();String str(); }@AnnoDemo1(age=12,str="張三") class person{int a;void fly(){System.out.println("我要飛的更高,摔的更慘");}public static void main(String[] args) {new person().fly();} }很好奇是不是為啥方法會等于一個值,回答是java規定的,懂了嗎,你定義的方法必須要在使用的時候為方法賦值,否則編譯不通過當然不包括default
下面是對default和value這個特殊名稱的使用,str默認值是李四,不使用默認值的話就必須用注釋的部分進行賦值:
package annotation; public @interface AnnoDemo1 {int value();String str() default "李四"; } @AnnoDemo1(12) //@AnnoDemo1(value = 12,str="王五") class person{int a;void fly(){System.out.println("我要飛的更高,摔的更慘");}public static void main(String[] args) {new person().fly();} }當我們賦一些特殊的值:
package annotation; public @interface AnnoDemo1 {MyEnum p();String[] str() ;Override over(); } @AnnoDemo1(p=MyEnum.p1,str={"123","234"},over = @Override) class person{int a;void fly(){System.out.println("我要飛的更高,摔的更慘");}public static void main(String[] args) {new person().fly();} }附帶枚舉的代碼:
package annotation; public enum MyEnum {p1,p2; }5.元注解
我們再把Override的源碼拿來分析:
@Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override { }簡單的說元注解就是描述注解的注解,比如上邊的 @Target和@Retention
元注解是Java中定義好的注解,常用元注解:
| @Target | 描述注解能夠作用的位置 |
| @Retention | 描述注解被保留的階段 |
| @Documented | 描述注解能否被抽取到api文檔中 |
| @Inherited | 描述注解是否被子類繼承 |
1.@Target
源碼:
我們打開 ElementType源碼:
public enum ElementType {/** Class, interface (including annotation type), or enum declaration */TYPE,/** Field declaration (includes enum constants) */FIELD,/** Method declaration */METHOD,/** Formal parameter declaration */PARAMETER,/** Constructor declaration */CONSTRUCTOR,/** Local variable declaration */LOCAL_VARIABLE,/** Annotation type declaration */ANNOTATION_TYPE,/** Package declaration */PACKAGE,/*** Type parameter declaration** @since 1.8*/TYPE_PARAMETER,/*** Use of a type** @since 1.8*/TYPE_USE }發現是一個枚舉類型的數據,我們了解枚舉的三個取值:
(1)TYPE:可以作用于類上
(2)METHOD:可以作用于方法上
(3)FIELD:可以作用于成員變量上
ps:注意value可以省略不寫因為只有它一個需要賦值的
看到那個報錯的小紅燈泡沒,就是因為你用它修飾方法
2.@Retention
源碼:
打開RetentionPolicy 源碼:
public enum RetentionPolicy {/*** Annotations are to be discarded by the compiler.*/SOURCE,/*** Annotations are to be recorded in the class file by the compiler* but need not be retained by the VM at run time. This is the default* behavior.*/CLASS,/*** Annotations are to be recorded in the class file by the compiler and* retained by the VM at run time, so they may be read reflectively.** @see java.lang.reflect.AnnotatedElement*/RUNTIME }也是枚舉類型:
1.SOURCE:源碼階段
2. CLASS:類對象階段
3. RUNTIME:運行時階段
一般參數是RUNTIME,意思是當前被描述的注解會保留到class字節碼文件中,并被JVM讀取到
如果參數是CLASS意思是當前被描述的注解會保留到class字節碼文件中,但不會被JVM讀取到
如果參數是SOURCE意思是當前被描述的注解不會保留到class文件里
3.@Documented
就是描述此方法或其它的注解會被抽取到api文檔里邊:
畫紅線得到部分就是由于@Documented注解才使得劃線所代表注保留到了api文檔中
4.@Inherited
很好理解,如果父類的注解包含@Inherited那么子類即使不加注解也會默認含有該注解
6.解析注解
那么什么是解析注解:即獲取注解中定義的屬性值
package untl2; import java.lang.reflect.Method; @pro(MethodName ="fly") public class MyReflect {public int a=12;public void fly(){System.out.println("我要飛的更高");}public static void main(String[] args) throws Exception {Class cls=MyReflect.class;//其實就是在內存中生成了一個注解接口的子類實現類對象pro an=(pro) cls.getAnnotation(pro.class);String method=an.MethodName();Object obj=cls.newInstance();Method method1=cls.getMethod(method);method1.invoke(obj);} }運行結果: 我要飛的更高其實以上代碼等同:
package untl2; import java.lang.reflect.Method; @pro(MethodName ="fly") public class MyReflect {public int a=12;public void fly(){System.out.println("我要飛的更高");}public static void main(String[] args) throws Exception {Class cls=MyReflect.class;//其實就是在內存中生成了一個注解接口的子類實現類對象Object obj=cls.newInstance();Method method1=cls.getMethod("fly");method1.invoke(obj);} } 運行結果: 我要飛的更高pro注解的定義代碼:
package untl2; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface pro {String MethodName(); }getAnnotation是獲得注解的Class對象方法,然后就可以通過.方法名拿到我們需要的注解里邊的屬性值。
一般來說我們主要替換配置文件的時候用的注解比較多
總結
以上是生活随笔為你收集整理的Java注解原来如此通俗易懂的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java资深反射玩家
- 下一篇: 从0到掌握Java泛型有这一篇博客就够了