Java注解(Annotation)的学习
文章目錄
- 注解定義
- 作用分類
- API 文檔注解
- JDK 預定義的注解
- 自定義注解
- 注解的格式
- 注解的本質
- 注解的屬性
- 屬性的返回值類型
- 屬性的特點
- 屬性的賦值
- 元注解
- @Target
- @Retention
- @Documented
- @Inherited
- 解析注解
- 總結
注解定義
注解( Annotation),也叫元數據。一種代碼級別的說明。它是 JDK1.5 及以后版本引入的一個特性,與類、接口、枚舉是在同一個層次。它可以聲明在包、類、字段、方法、局部變量、方法參數等的前面,用來對這些元素進行說明,注釋。
注解的叫法很多:元數據、標簽、標記…
使用注解的叫法很多:使用xxx注解標注、使用xxx注解標記、使用xxx注解描述…
作用分類
①編寫文檔:通過代碼里標識的元數據生成文檔
編寫程序時以一套特定的標簽(即注解)作注釋,在程序編寫完成后,通過 Javadoc 就可以同時形成程序的開發文檔了。
②代碼分析:通過代碼里標識的元數據對代碼進行分析
使用反射技術,獲取注解屬性的值,然后處理有關的業務邏輯
③編譯檢査:通過代碼里標識的元數據讓編譯器能夠實現基本的編譯檢查
例如:@Override 注解,就可以檢測被注解的方法是否正確覆蓋重寫父類的方法。
API 文檔注解
演示代碼:
package priv.lwx.javaprac.annotation;/*** 生成文檔(javadoc)的注解演示代碼** @author liaowenxiong* @date 2021/9/16 下午3:58* @since JDK 1.5*/ public class Demo01Annotation {public static void main(String[] args) {}/*** 計算兩個整數的和* @param a 整數* @param b 整數* @return 兩個整數的和*/public int add(int a, int b) {return a + b;} }如上的演示代碼,在注釋中有很多的 @xxx,這些就是文檔注解,可以將這些注解的內容提取成為 API 文檔。
關于如何編寫文檔注解,以及如何生成 javadoc 請參見《JDK 命令之 javadoc – 生成API文檔》。
JDK 預定義的注解
@Override
用來檢查被該注解標注的方法是不是有效的方法重寫。在方法簽名相同的情況下覆蓋重寫父類的方法,在其它地方如果有問題,會直接報編譯錯誤,無需該注解來檢測。只有方法簽名不同的情況下,而又希望覆蓋重寫父類的方法,使用該注解檢測才有意義。那么什么情況下會出現這個問題,那么就是父類方法的參數很多,確實容易寫錯,而你的本意又確實是重寫父類的方法,那么此時使用此注解就可以幫到你了。
@Deprecated
用來表明被該注解標注的類成員已經過時,如果標注的是方法則會在方法名上顯示一條“刪除線”
@SuppressWarnings
抑制警告,禁止警告
注:一般傳遞參數“all”
自定義注解
可以通過反編譯來查看注解實際的代碼。
例如,查看注解 @Deprecated 的實際代碼,你需要先編譯它的源代碼,再反編譯字節碼文件才能看到。
@Deprecated 注解的源代碼如下:
package java.lang;import java.lang.annotation.*; import static java.lang.annotation.ElementType.*;/*** A program element annotated {@code @Deprecated} is one that programmers* are discouraged from using. An element may be deprecated for any of several* reasons, for example, its usage is likely to lead to errors; it may* be changed incompatibly or removed in a future version; it has been* superseded by a newer, usually preferable alternative; or it is obsolete.** <p>Compilers issue warnings when a deprecated program element is used or* overridden in non-deprecated code. Use of the {@code @Deprecated}* annotation on a local variable declaration or on a parameter declaration* or a package declaration has no effect on the warnings issued by a compiler.** <p>When a module is deprecated, the use of that module in {@code* requires}, but not in {@code exports} or {@code opens} clauses causes* a warning to be issued. A module being deprecated does <em>not</em> cause* warnings to be issued for uses of types within the module.** <p>This annotation type has a string-valued element {@code since}. The value* of this element indicates the version in which the annotated program element* was first deprecated.** <p>This annotation type has a boolean-valued element {@code forRemoval}.* A value of {@code true} indicates intent to remove the annotated program* element in a future version. A value of {@code false} indicates that use of* the annotated program element is discouraged, but at the time the program* element was annotated, there was no specific intent to remove it.** @apiNote* It is strongly recommended that the reason for deprecating a program element* be explained in the documentation, using the {@code @deprecated}* javadoc tag. The documentation should also suggest and link to a* recommended replacement API, if applicable. A replacement API often* has subtly different semantics, so such issues should be discussed as* well.** <p>It is recommended that a {@code since} value be provided with all newly* annotated program elements. Note that {@code since} cannot be mandatory,* as there are many existing annotations that lack this element value.** <p>There is no defined order among annotation elements. As a matter of* style, the {@code since} element should be placed first.** <p>The {@code @Deprecated} annotation should always be present if* the {@code @deprecated} javadoc tag is present, and vice-versa.** @author Neal Gafter* @since 1.5* @jls 9.6.4.6 @Deprecated*/ @Documented @Retention(RetentionPolicy.RUNTIME) @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, MODULE, PARAMETER, TYPE}) public @interface Deprecated {/*** Returns the version in which the annotated element became deprecated.* The version string is in the same format and namespace as the value of* the {@code @since} javadoc tag. The default value is the empty* string.** @return the version string* @since 9*/String since() default "";/*** Indicates whether the annotated element is subject to removal in a* future version. The default value is {@code false}.** @return whether the element is subject to removal* @since 9*/boolean forRemoval() default false; }把這份源代碼文件復制到其它地方,使用命令編譯和反編譯,命令如下:
liaowenongdeair:test liaowenxiong$ javac Deprecated.java # 先編譯源代碼 liaowenongdeair:test liaowenxiong$ javap Deprecated.class # 反編譯字節碼文件 Compiled from "Deprecated.java" public interface Deprecated extends java.lang.annotation.Annotation {public abstract java.lang.String since();public abstract boolean forRemoval(); } liaowenongdeair:test liaowenxiong$注解的格式
元注解 public @interface 注解名稱 {屬性列表(本質就是抽象方法) }注解的本質
注解本質就是接口,繼承自父接口 Annotation。
注解的屬性
所謂“屬性”就是注解接口體(即大括號 {})中聲明的常量和方法。所以“屬性”的本質就是抽象方法。聲明了屬性則使用注解時必須給屬性賦值。
為什么將方法稱之為屬性,看下面的示例代碼:
// 使用自定義的注解 @MyAnno1(name = "李瓶兒") // 其中name是注解聲明的抽象方法名,使用注解時需要賦值,賦值語法格式類似屬性賦值的格式,所以將注解中聲明的抽象方法稱為"屬性" public void test() {show(); }屬性的返回值類型
返回值類型:
1.基本數據類型
2.String
3.枚舉
4.注解
5.以上類型的數組
除了以上五種,其它類型不能作為注解接口中聲明的抽象方法的返回值類型。
屬性的特點
1.屬性的默認值:在聲明注解的屬性時,如果使用關鍵字 default 給屬性默認值,則使用注解時可以不進行屬性的賦值,會取默認值。
2.在使用注解時,如果只有一個屬性,且屬性名稱為 value,那么在給該屬性賦值時,可以省略屬性名稱,即本來要 這么寫 @MyAnno1(value = "李瓶兒"),可以省略成 @MyAnno1("李瓶兒")。
3.注解類中聲明的抽象方法名,返回值是字符串數組,那么給屬性賦值時,如果多個值使用大括號包裹,如果只有一個值,則可以省略大括號。
聲明定義注解及屬性示例代碼:
package priv.lwx.javaprac.annotation;/*** 自定義注解** @author liaowenxiong* @date 2021/9/20 下午5:18*/ public @interface MyAnno1 {String name(); // 這是抽象方法,在注解中可以稱為屬性,使用此注解時,需要賦值,賦值格式:name = 一個字符串int age() default 12; // 默認值12,在使用注解時,沒有指定該屬性,那么該屬性的默認值就是12 }屬性的賦值
各種返回值類型的屬性如何賦值,請看下面的示例代碼:
@MyAnno1(name = "高圓圓", setColor = Color.C1, test = @MyAnno2, names = {"雙兒", "小栗子"}) /* name方法的返回值是字符串,所以賦值字符串;setColor方法的返回值是枚舉類,所以賦值時取枚舉值,類似類的靜態常量test方法的返回值是注解類,所以賦值時格式為 @注解類名稱names是注解類中聲明的抽象方法名,返回值是字符串數組,那么給屬性names賦值時, 如果多個值使用大括號包裹,如果只有一個值,則可以省略大括號。 */ public void test() {show(); }元注解
用于描述注解的注解。
@Target
描述注解可以作用的位置
@Target(value={ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.LOCAL_VARIABLE, ElementType.METHOD, ElementType.PACKAGE, ElementType.MODULE, ElementType.PARAMETER, ElementType.TYPE}) public @interface MyAnno3 { }如上示例,表示被描述的注解(MyAnno3)可以作用于構造器、字段、局部變量、方法、包、模塊、參數、類上。{} 內的都是枚舉類 ElementType 的枚舉值。
@Retention
描述注解被保留的階段(源碼階段、編譯階段、運行時階段)
有三個值:
SOURCE:被描述的注解僅在源碼階段保留,編譯時就被舍棄了
CLASS:被描述的注解會保留到字節碼文件中,類加載進內存時被舍棄了
RUNTIME:被描述的注解會保留到運行時階段,即類加載進入內存時,注解也會被加載進內存,可以通過反射獲取相關信息
@Documented
描述注解是否可以被 javadoc 抽取到文檔中,即被描述的注解會原樣出現在API文檔中
@Inherited
描述注解是否被子類繼承
解析注解
獲取注解屬性中定義的值。
本質:就是獲取注解類的實例對象,然后調用注解屬性對應的成員方法,獲得對應的返回值
步驟:
1.獲得被注解的類/方法/字段對應的反射對象,即類就是 Class 對象,方法就是 Method 對象,變量就是 Field 對象
2.通過反射對象獲得注解的實例對象,即調用反射對象的 getAnnotation/getAnnotations 等方法獲取注解對象
3.調用注解對象的方法,獲得返回值,該返回值就是對應注解屬性中定義的值
演示代碼:
package priv.lwx.javaprac.annotation;import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method;/*** 該類可以創建任意類型的對象,執行其中的任意方法.** 使用注解的方式取代屬性文件的方式來獲取類名和方法名.* @author liaowenxiong* @date 2021/9/21 上午7:49*/ @Pro(className = "priv.lwx.javaprac.annotation.Person", methodName = "eat") public class Demo03Annotation { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {// 1.獲取當前類的Class對象Class<Demo03Annotation> c = Demo03Annotation.class;// 2.獲取當前類的注解類的實例對象// 其實就是在內存中生成了一個注解接口的實現類對象/*其實編譯器會自動生成如下的代碼:public class ProImpl implements Pro {public String className() {return "priv/lwx/javaprac/annotation/Demo03Annotation";}public String methodName() {return "eat";}}*/Pro pro = c.getAnnotation(Pro.class);// 3.調用注解對象上的抽象方法,獲取返回值String className = pro.className(); // 返回值就是使用Pro注解時所定義的className屬性值String methodName = pro.methodName(); // 返回值就是使用Pro注解時所定義的methodName屬性值System.out.println(className);System.out.println(methodName);// 使用Class的靜態方法forName將類加載進內存中Class c2 = Class.forName(className);// 獲取無參構造器Constructor constructor = c2.getConstructor();// 通過無參構造器創建對象Object obj = constructor.newInstance();// 獲取方法對象Method method = c2.getMethod(methodName);// 執行方法Object result = method.invoke(obj);System.out.println(result);}}總結
在實際開發過程中,多數情況我們不會自定義注解,而是使用注解。
注解給誰用?
給解析程序用,編譯器也屬于解析程序,解析程序識別注解,然后實現有關的業務邏輯。
注解可以理解為程序中一種標簽、標記
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的Java注解(Annotation)的学习的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 网卡驱动 linux(网卡linux驱动
- 下一篇: IntelliJ IDEA for Ma