Java基礎(chǔ)知識(shí)之注解、元注解
注解
Java注解也稱Java標(biāo)注,是jdk1.5(5.0)后的新特征。Java語(yǔ)言中的類、方法、變量、參數(shù)和包等都可以被標(biāo)注。和Javadoc不同,Java注解可以通過(guò)反射獲取標(biāo)注內(nèi)容,在編譯器生成類文件時(shí),標(biāo)注可以被嵌入到字節(jié)碼中,Java虛擬機(jī)可以保留標(biāo)注內(nèi)容,在運(yùn)行時(shí)可以獲取到標(biāo)注內(nèi)容,當(dāng)然它也支持自定義Java標(biāo)注
功能:用于說(shuō)明程序
用途:一般用在框架中使用
格式:@AnnotationName
文檔注釋:
? @param @return @Exception從根本上是一個(gè)注釋,不存在代碼編譯,不會(huì)生成對(duì)應(yīng)的文檔
注解:
? @Override并不是沒(méi)有編譯就有效果,是因?yàn)椴还苁荅clipse還是IDEA都可以預(yù)編譯Java代碼生成對(duì)應(yīng)的.class文件的
注解作用
生成文檔
代碼中生成對(duì)應(yīng)的JavaDoc API文檔
@param @return
【IDEA JavaDoc工具使用參數(shù)】
? Other Command Line Arguments:-encoding utf-8 -charset utf-8
? 解決中文亂碼,因?yàn)镮DEA默認(rèn)編碼集為UTF-8 windows默認(rèn)編碼集為 GBK
代碼檢查
繼承重寫(xiě),或者說(shuō)接口遵從之后的實(shí)現(xiàn)中,存在@Override
代碼數(shù)據(jù)獲取:【小型框架】
通過(guò)反射獲取指定注解中的一些內(nèi)容,例如:配置,數(shù)據(jù),操作,驗(yàn)證等
Java預(yù)定義的注解
@Override
? 重寫(xiě)/實(shí)現(xiàn)方法的情況下,檢查方法聲明是否和父類或者接口中的方法聲明一致,強(qiáng)制格式檢查
@Deprecated
標(biāo)記當(dāng)前方法已過(guò)時(shí)
@SuppressWarnings(“all”)
壓制警告,可以用于一些代碼中存在明確無(wú)異常的情況下,壓制一些警告
Annotation注解屬性【難點(diǎn)】
屬性
? 開(kāi)發(fā)實(shí)際使用注解的方式中,數(shù)據(jù)使用方式更加偏向于屬性概念
? 使用
- 書(shū)寫(xiě)代碼中使用
@MyAnnotation(id=1, name=“ocean”, age=16) - 使用反射時(shí),會(huì)涉及到getXXX方法
通過(guò)屬性名獲取對(duì)應(yīng)值的概念來(lái)完成的
實(shí)際上是利用abstract方法來(lái)完成屬性概念的
屬性使用的格式【實(shí)際按照方法格式操作】
- 屬性的值數(shù)據(jù)類型和對(duì)應(yīng)的具體數(shù)據(jù) => 返回值類型和返回的數(shù)據(jù)屬性類型支持:
- 基本數(shù)據(jù)類型
- String類型
- 其他數(shù)據(jù)類型
- enmu枚舉類型,一個(gè)帶有名字的常量,為了更好的閱讀性和操作
- 以上類型對(duì)應(yīng)的數(shù)組
- 屬性值要求
- 定義屬性時(shí)可以使用default關(guān)鍵字,加上默認(rèn)值,該屬性在使用的過(guò)程中是沒(méi)有強(qiáng)制要求屬性值,如果沒(méi)有賦予屬性值,采用對(duì)應(yīng)的默認(rèn)值操作,如果賦值,使用對(duì)應(yīng)值
- 如果注解中有且只有一個(gè)value屬性,或者說(shuō)注解中除value屬性之外,都有默認(rèn)值,不管是類,方法,成員變量,包使用當(dāng)前注解是可以直接在括號(hào)內(nèi)加入
對(duì)應(yīng)數(shù)據(jù)類型數(shù)值- 如果屬性是數(shù)組類型, {}大括號(hào)保存,并且不同的內(nèi)容,使用,隔開(kāi)屬性的鍵名字 ==> 方法的名字
自定義注解
格式: public @interface AnnotationName { 屬性列表; } Annotation注解是可以編譯得到對(duì)應(yīng)的.class字節(jié)碼文件,驗(yàn)證了注解是可以參與編譯過(guò)程的 通過(guò)反編譯工具可以得到一下內(nèi)容 【Annotation本質(zhì)】 public interface MyAnnotation1 extends java.lang.annotation.Annotation { } MyAnnotation1 本質(zhì)是一個(gè)interface,同時(shí)java.lang.annotation.Annotation 子接口
package cn.ocean888.a_annotation.MyAnnotation; /** * 自定義注解?。?! * public interface MyAnnotation1 extends java.lang.annotation.Annotation { * } * * @author Anonymous * @date 2020/3/10 11:01 */ public @interface MyAnnotation1 { // 屬性 ==> 方法形式 }
元注解
基于注解的解釋,用來(lái)約束注解的的一些操作問(wèn)題
@Retention
表示這個(gè)注解的保存方式,是只在代碼中,還是編入class文件中,或者是運(yùn)行時(shí)可以通過(guò)反射訪問(wèn)
RetentionPolicy.RUNTIME:當(dāng)前注解會(huì)編譯生成對(duì)應(yīng)的.class字節(jié)碼文件,并且可以加載到JVM中,參與代碼執(zhí)行
RetentionPolicy.CLASS
RetentionPolicy.SOURCE:注解將被編譯器丟棄(該類型的注解信息只會(huì)保留在源碼里,源碼經(jīng)過(guò)編譯后,注解信息會(huì)被丟棄,不會(huì)保留在編譯好的class文件里)
@Document
標(biāo)記這些注解是否包含在用戶文檔中
是否可以通過(guò)JavaDoc工具,生成對(duì)應(yīng)的API文檔
@Target
標(biāo)記這個(gè)注解應(yīng)該是那種Java成員
屬性:ElementType
? TYPE:當(dāng)前注解可以用于類聲明
? METHOD:當(dāng)前注解可以用于方法聲明位置
? FIELD:當(dāng)前注解可以用于成員變量聲明位置
@Inherited
標(biāo)記這個(gè)注解是繼承于那個(gè)注解類(默認(rèn) 注解不繼承于任何子類)
獲取類上的注解
Java獲取類上的注解有下面3個(gè)方法:
- Class.getAnnotations() 獲取所有的注解,包括自己聲明的以及繼承的
- Class.getAnnotation(Class< A > annotationClass) 獲取指定的注解,該注解可以是自己聲明的,也可以是繼承的
- Class.getDeclaredAnnotations() 獲取自己聲明的注解、
java.lang.Class類的isAnnotation()方法用于檢查此Class是否為Annotation類型
java.lang.Class類的isAnnotationPresent()方法用于檢查此類中是否存在指定注釋類型的注釋
實(shí)例:
實(shí)例1:兩種屬性文件的加載的方式
1.properties
className = cn.ocean888.a_annotation.Person id = 1 name = "ocean"
Person類
package cn.ocean888.a_annotation; public class Person { Integer id; String name; public Person() { } public Person(Integer id, String name) { this.id = id; this.name = name; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Person{" + "id=" + id + ", name='" + name + '\'' + '}'; } }
第一種使用反射來(lái)完成
package cn.ocean888.a_annotation; import java.io.FileInputStream; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.util.Properties; public class Demo1 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException, NoSuchFieldException { Properties properties = new Properties(); properties.load(new FileInputStream("./src/1.properties")); String className = properties.getProperty("className"); String id = properties.getProperty("id"); String name = properties.getProperty("name"); System.out.println(className); /* 使用反射的方法 */ Class<?> aClass = Class.forName(className); Person person = (Person) aClass.getConstructor().newInstance(); System.out.println(person); Field declaredField = aClass.getDeclaredField("id"); declaredField.setAccessible(true); declaredField.set(person, Integer.parseInt(id)); Field declaredField2 = aClass.getDeclaredField("name"); declaredField2.setAccessible(true); declaredField2.set(person, name); System.out.println(person); } }
第二種使用反射加注解的方式來(lái)完成
自定義注解
package cn.ocean888.a_annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; // 聲明當(dāng)前注解有且只能用于類名之上 @Target(ElementType.TYPE) // 當(dāng)前注解參與代碼運(yùn)行 @Retention(RetentionPolicy.RUNTIME) @interface MyAnnotaion1 { // 屬性 String className(); int id(); String name(); }
ReflectAnnotation.java
package cn.ocean888.a_annotation; import java.lang.annotation.Annotation; @MyAnnotaion1(className = "cn.ocean888.a_annotation.Person", id = 2, name = "ocean") public class ReflectAnnotation { public static void main(String[] args) { // 加載ReflectAnnotation Class<ReflectAnnotation> cls = ReflectAnnotation.class; // 因?yàn)樽⒔庠兕惷?通過(guò)Class獲取對(duì)應(yīng)的Annotation MyAnnotaion1 annotation = cls.getAnnotation(MyAnnotaion1.class); String s = annotation.className(); int id = annotation.id(); String name = annotation.name(); System.out.println(s); System.out.println(id); System.out.println(name); } }
實(shí)例2使用注解測(cè)試代碼運(yùn)行
對(duì)Tools方法中帶有@Check注解標(biāo)記的方法進(jìn)行檢查
Tools.java
package cn.ocean888.a_annotation_checkMethod; import java.util.ArrayList; /** * 需要測(cè)試的方法 */ public class Tools { @Check public void test1() { String str = null; System.out.println(str.toString()); } @Check public void test2() { int[] arr = null; System.out.println(arr[5]); } @Check public void test3() { int[] arr = {1,2,3,4,5}; System.out.println(arr[3]); } @Check public void test4() { ArrayList<Integer> integers = new ArrayList<>(); System.out.println(integers.get(20).toString()); } @Check public void test5() { throw new NullPointerException("NullPointException"); } }
Utils.java
package cn.ocean888.a_annotation_checkMethod; public class Utils { @Check public void test() { throw new IndexOutOfBoundsException("下標(biāo)越界"); } }
Check.java
package cn.ocean888.a_annotation_checkMethod; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 該注解沒(méi)有任何屬性,只是作為是否需要測(cè)試的標(biāo)記 */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Check { }
ClassAnnotation.java
package cn.ocean888.a_annotation_checkMethod; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 自定義注解 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface ClassAnnotation { String className(); }
TestProject.java
package cn.ocean888.a_annotation_checkMethod; import java.io.BufferedWriter; import java.io.FileWriter; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * 測(cè)試Tools類內(nèi)的方法,如果方法帶有Check注解,執(zhí)行測(cè)試并記錄異常 */ @ClassAnnotation(className = "cn.ocean888.a_annotation_checkMethod.Tools") public class TestProject { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException { // 從注解中獲取對(duì)應(yīng)的屬性 Class<TestProject> testProjectClass = TestProject.class; // 獲取所有指定的注解 ClassAnnotation annotation = testProjectClass.getAnnotation(ClassAnnotation.class); String s = annotation.className(); // s = cn.ocean888.a_annotation_checkMethod.Utils Class<?> aClass = Class.forName(s); Object tools = aClass.getConstructor().newInstance(); // 獲取所有Tools類內(nèi)的方法,不包括父類方法 Method[] declaredMethods = aClass.getDeclaredMethods(); // 記錄錯(cuò)誤出現(xiàn)次數(shù) int count = 0; long l = System.currentTimeMillis(); BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("./src/log.txt")); // 遍歷方法數(shù)組 for (Method declaredMethod : declaredMethods) { declaredMethod.setAccessible(true); // 判斷當(dāng)前方法是否帶有注解@Check標(biāo)記 if (declaredMethod.isAnnotationPresent(Check.class)) { try { declaredMethod.invoke(tools); } catch (Exception e) { count += 1; // 1.哪一個(gè)方法出現(xiàn)異常 bufferedWriter.write("方法:" + declaredMethod.getName()); bufferedWriter.newLine(); // 2.發(fā)生異常原因,獲取對(duì)應(yīng)的類型 bufferedWriter.write("異常類型:" + e.getCause().getClass().getSimpleName()); bufferedWriter.newLine(); // 3.異常信息 bufferedWriter.write("異常信息:" + e.getCause().getMessage()); bufferedWriter.newLine(); } } } long l1 = System.currentTimeMillis(); bufferedWriter.write("出現(xiàn)錯(cuò)誤的次數(shù)" + count); bufferedWriter.newLine(); bufferedWriter.write("總耗時(shí)" + (l1 - l)); bufferedWriter.close(); } }
代碼的靈活性在于可以對(duì)className直接進(jìn)行替換
注解使用總結(jié)
- 注解在大多數(shù)情況下,都是使用過(guò)程,而不是自定義,會(huì)使用到框架中預(yù)處理好的注解
- 注解使用對(duì)象
- 編譯器
- 解析代碼
- JVM運(yùn)行代碼使用
- 注解是一個(gè)標(biāo)簽,有時(shí)候是做標(biāo)記,有時(shí)候標(biāo)記有屬性
注解和python裝飾器的區(qū)別
先說(shuō)java的注解(Annotation),實(shí)際上是給語(yǔ)法元素打一個(gè)標(biāo)記。比如你可以給一個(gè)函數(shù)打一個(gè)標(biāo)記,給一個(gè)類打一個(gè)標(biāo)記等等。Java只保證記錄這個(gè)標(biāo)記,但是不會(huì)主動(dòng)根據(jù)這給標(biāo)記做任何事。
比如,你在Spring里,給一個(gè)私有成員打 @Autowired 這個(gè)標(biāo)記。
public class XXXService { @Autowired private XXXXRepository xxxxRepository; // ... }
如果你不用Spring框架的話,不會(huì)有任何事情發(fā)生,直接訪問(wèn)這個(gè)字段就是空。當(dāng)如果你配置了合適的處理流程,而這個(gè)流程就會(huì)根據(jù)有沒(méi)有這個(gè)標(biāo)記干活。比如你要求Spring “Auto Scan” 并且注入依賴,這個(gè)處理過(guò)程會(huì)用反射去讀哪些元素被做了某個(gè)特定標(biāo)記。沒(méi)有標(biāo)記就不理,有標(biāo)記就注入。
python里的decorator是一個(gè)語(yǔ)法糖,是希望把“decorator”這個(gè)形式寫(xiě)得更漂亮。比如,你想記錄一個(gè)函數(shù)開(kāi)始執(zhí)行之前和之后的log:
def foo(): print("Hello") def logit(fn): def inner(): print("before execute") fn() printf("after execute") return inner
這時(shí),你可以魔改以下foo的實(shí)現(xiàn),用logit這個(gè)“裝飾器”來(lái)部分修改foo的行為,然后執(zhí)行:
foo = logit(foo) foo()
但python里的語(yǔ)法可以讓這個(gè)東西寫(xiě)成:
@logit def foo(): print("Hello") foo()
也就是說(shuō),python這里的裝飾器是一個(gè)有邏輯的,可以執(zhí)行的函數(shù),只不過(guò)其寫(xiě)法有些特殊要求;而Java里面的Annotation只是個(gè)標(biāo)記,需要其他代碼來(lái)“根據(jù)標(biāo)記執(zhí)行“。
當(dāng)然,裝飾器模式是個(gè)很通用的東西,無(wú)論是python,java還是其他語(yǔ)言都可以寫(xiě)。只是python提供了特殊的語(yǔ)法糖而已。但java世界里做類似decorator的事情,希望動(dòng)態(tài)魔改一個(gè)函數(shù)的行為,可以用動(dòng)態(tài)代理或者AOP。
Java的Annotation因?yàn)橄喈?dāng)于多加了一層(標(biāo)記 + 處理邏輯),是一把雙刃劍。好處是,在不動(dòng)代碼的情況下你可以通過(guò)外部配置來(lái)修改程序的行為。比如給一個(gè)函數(shù)打上@Test標(biāo)。如果通過(guò)UT框架運(yùn)行,這些打標(biāo)的函數(shù)會(huì)被當(dāng)作是測(cè)試用例;但如果外部直接用普通的main啟動(dòng),這些@Test就會(huì)沒(méi)有一樣,不會(huì)影響代碼本身的邏輯。但反過(guò)來(lái),也容易引來(lái)一些問(wèn)題。比如有的時(shí)候,你很難知道那個(gè)根據(jù)標(biāo)記執(zhí)行的邏輯是不是真的跑了。也許你哪里配置拼錯(cuò)一個(gè)字,或者classpath少依賴一個(gè)包,就造成那個(gè)邏輯并沒(méi)有真的執(zhí)行。這時(shí)從表面上也許很難看出來(lái)出錯(cuò)了。
總結(jié)
到此這篇關(guān)于Java基礎(chǔ)知識(shí)之注解、元注解的文章就介紹到這了,更多相關(guān)Java注解、元注解內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring Boot中Bean定義方調(diào)用方式解析
這篇文章主要介紹了Spring Boot中Bean定義方調(diào)用方式解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07使用json字符串插入節(jié)點(diǎn)或者覆蓋節(jié)點(diǎn)
這篇文章主要介紹了使用json字符串插入節(jié)點(diǎn)或者覆蓋節(jié)點(diǎn)的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08SpringBoot?整合MyBatis+MyBatis-Plus+MyBatisX插件使用
本文主要介紹了SpringBoot?整合MyBatis+MyBatis-Plus+MyBatisX插件使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-04-04Spring Cloud Stream分區(qū)分組原理圖解
這篇文章主要介紹了Spring Cloud Stream的分區(qū)和分組,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03java注解實(shí)現(xiàn)websocket服務(wù)的兩種方式
Java WebSocket是一種基于TCP協(xié)議的雙向全雙工消息傳輸技術(shù),它允許服務(wù)器和客戶端之間實(shí)時(shí)通信,具有低延遲和高效率的特點(diǎn),下面這篇文章主要給大家介紹了關(guān)于java注解實(shí)現(xiàn)websocket服務(wù)的兩種方式,需要的朋友可以參考下2024-08-08Action訪問(wèn)Servlet的API的簡(jiǎn)單實(shí)例
下面小編就為大家?guī)?lái)一篇Action訪問(wèn)Servlet的API的簡(jiǎn)單實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-06-06java中使用雙向鏈表實(shí)現(xiàn)貪吃蛇程序源碼分享
這篇文章主要介紹了java中使用雙向鏈表實(shí)現(xiàn)貪吃蛇程序源碼分享,本文直接給出了實(shí)現(xiàn)代碼,需要的朋友可以參考下2015-03-03SpringMVC Json自定義序列化和反序列化的操作方法
這篇文章主要介紹了SpringMVC Json自定義序列化和反序列化的操作方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-01-01