詳解Java編程中Annotation注解對(duì)象的使用方法
注解(也被稱為元數(shù)據(jù))為我們?cè)诖a中添加信息提供了一種形式化的方法,使我們可以在稍后某個(gè)時(shí)刻非常方便地使用這些數(shù)據(jù)。
1.基本語法
Java SE5內(nèi)置三種標(biāo)準(zhǔn)注解
@Override:表示當(dāng)前的方法定義將覆蓋超類中的方法.如果你不小心拼寫錯(cuò)誤,或者方法簽名對(duì)不上被覆
蓋的方法,編譯器就會(huì)發(fā)出錯(cuò)誤提示
@Deprecated:如果程序員使用了注解為它的元素,那么編譯器就會(huì)發(fā)出警告信息
@SupperessWarnings:關(guān)閉不當(dāng)?shù)木幾g器警告信息.
Java SE5內(nèi)置四種元注解
@Target:表示該注解可以用于什么地方.可能的ElementType參數(shù)包括:
1)CONSTRUCTOR:構(gòu)造器的聲明
2)FIELD:域聲明(包括enum實(shí)例)
3)LOCAL_VARIABLE:局部變量聲明
4)METHOD:方法聲明
5)PACKAGE:包聲明
6)PARAMETER:參數(shù)聲明
7)TYPE:類、接口(包括注解類型)或enum聲明
@Retention:表示需要在什么級(jí)別保存該注解信息.可選的RetentionPolicy參數(shù)包括:
1)SOURCE:注解將被編譯器丟棄
2)CLASS:注解在class文件中可用,但會(huì)被VM丟棄
3)RUNTIME:VM將在運(yùn)行期也保留注解,因此可以通過反射機(jī)制讀取注解的信息
@Documented:將此注解包含在Javadoc中
@Inherited:允許子類繼承父類中的注解
大多數(shù)時(shí)候,程序員主要是定義自己的注解,并編寫自己的處理器來處理它們.
UseCase.java
package com; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD)//用來定義你的注解將應(yīng)用在什么地方,本處應(yīng)用為方法 //用來定義該注解在哪一個(gè)級(jí)別可用,在源代碼中(source)類文件中(class)或者運(yùn)行時(shí)(runtime) @Retention(RetentionPolicy.RUNTIME) public @interface UseCase { public int id(); public String description()default "no description"; } PasswordUtils .java package com; public class PasswordUtils { @UseCase(id=47,description="Passwords must contain at least one numeric") public boolean validatePassword(){ return true; } @UseCase(id=48) public String encryptPassword(String password){ return password; } @UseCase(id=49,description="Jong_Cai") public void showName(){ System.out.println("Jong_Cai"); } }
2.編寫注解處理器
如果沒有用來讀取注解的工具,那注解也不會(huì)比注釋更有用.使用注解的過程中.很重要的一個(gè)部
分就是創(chuàng)建與使用注解處理器.Java SE5擴(kuò)展了反射機(jī)制的API,以幫助程序員構(gòu)造這類工具.同時(shí),它還提供了一個(gè)外部工具apt幫助程序員解析帶有注解的Java源代碼.下面是一個(gè)非常簡(jiǎn)單的注解處理器,我們將用它來讀取PasswordUtils類,并使用反射機(jī)制查找@UseCase標(biāo)記.我們?yōu)槠涮峁┝艘唤Mid值得,然后它會(huì)列出在PasswordUtils中找到的用例,以及缺失的用例.
UseCaseTracker.java package com; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class UseCaseTracker { public static void trackUseCases(List<Integer> list, Class<?> cl) { for (Method m : cl.getDeclaredMethods()) { UseCase us = m.getAnnotation(UseCase.class); if (us != null) { System.out.println("Found Use Case:" + us.id() + " " + us.description()); list.remove(new Integer(us.id())); } } for (int i : list) { System.out.println("Warning:Missing use case-" + i); } } public static void main(String[] args) { List<Integer> list = new ArrayList<Integer>(); Collections.addAll(list, 47,48,49,50,51); trackUseCases(list, PasswordUtils.class); } }
這個(gè)程序用到了兩個(gè)反射的方法:getDeclaredMethods()和getAnnotation(),它們都屬于AnnotatedElement接口(class,Method與Field等類都實(shí)現(xiàn)了該接口).getAnnotation()方法返回指定類型的注解對(duì)象,在這里就是UseCase,如果被注解的方法上沒有該類型的注解,則返回null值.然后我們通過調(diào)用id()和description()方法從返回的UseCase對(duì)象中提取元素的值.其中encryptPassword()方法在注解的時(shí)候沒有指定description的值,因此處理器在處理它對(duì)應(yīng)的注解時(shí),通過description()方法取得的是默認(rèn)值no description.
Annotation在java的世界正鋪天蓋地展開,有空寫這一篇簡(jiǎn)單的annotations的文章,算是關(guān)于Annotation入門的文章吧,希望能各位們能拋磚,共同學(xué)習(xí)......
不講廢話了,實(shí)踐才是硬道理.
3.實(shí)例
下面講一下annotation的概念先,再來講一下怎樣設(shè)計(jì)自己的annotation.
首先在jdk自帶的java.lang.annotation包里,打開如下幾個(gè)源文件:
源文件Target.java
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Target { ElementType[] value(); @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Target { ElementType[] value(); }
其中的@interface是一個(gè)關(guān)鍵字,在設(shè)計(jì)annotations的時(shí)候必須把一個(gè)類型定義為@interface,而不能用class或interface關(guān)鍵字(會(huì)不會(huì)覺得sun有點(diǎn)吝嗇,偏偏搞得與interface這么像).
源文件Retention.java
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Retention { RetentionPolicy value(); } @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Retention { RetentionPolicy value(); }
看到這里,大家可能都模糊了,都不知道在說什么,別急,往下看一下. 在上面的文件都用到了RetentionPolicy,ElementType這兩個(gè)字段,你可能就會(huì)猜到這是兩個(gè)java文件.的確,這兩個(gè)文件的源代碼如下:
源文件RetentionPolicy.java
public enum RetentionPolicy { SOURCE, CLASS, RUNTIME } public enum RetentionPolicy { SOURCE, CLASS, RUNTIME }
這是一個(gè)enum類型,共有三個(gè)值,分別是SOURCE,CLASS 和 RUNTIME.
SOURCE代表的是這個(gè)Annotation類型的信息只會(huì)保留在程序源碼里,源碼如果經(jīng)過了編譯之后,Annotation的數(shù)據(jù)就會(huì)消失,并不會(huì)保留在編譯好的.class文件里面。
ClASS的意思是這個(gè)Annotation類型的信息保留在程序源碼里,同時(shí)也會(huì)保留在編譯好的.class文件里面,在執(zhí)行的時(shí)候,并不會(huì)把這一些信息加載到虛擬機(jī)(JVM)中去.注意一下,當(dāng)你沒有設(shè)定一個(gè)Annotation類型的Retention值時(shí),系統(tǒng)默認(rèn)值是CLASS.
第三個(gè),是RUNTIME,表示在源碼、編譯好的.class文件中保留信息,在執(zhí)行的時(shí)候會(huì)把這一些信息加載到JVM中去的.
舉一個(gè)例子,如@Override里面的Retention設(shè)為SOURCE,編譯成功了就不要這一些檢查的信息;相反,@Deprecated里面的Retention設(shè)為RUNTIME,表示除了在編譯時(shí)會(huì)警告我們使用了哪個(gè)被Deprecated的方法,在執(zhí)行的時(shí)候也可以查出該方法是否被Deprecated.
源文件ElementType.java
public enum ElementType { TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE,PACKAGE } public enum ElementType { TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE,PACKAGE }
@Target里面的ElementType是用來指定Annotation類型可以用在哪一些元素上的.說明一下:TYPE(類型), FIELD(屬性), METHOD(方法), PARAMETER(參數(shù)), CONSTRUCTOR(構(gòu)造函數(shù)),LOCAL_VARIABLE(局部變量), ANNOTATION_TYPE,PACKAGE(包),其中的TYPE(類型)是指可以用在Class,Interface,Enum和Annotation類型上.
另外,從1的源代碼可以看出,@Target自己也用了自己來聲明自己,只能用在ANNOTATION_TYPE之上.
如果一個(gè)Annotation類型沒有指明@Target使用在哪些元素上,那么它可以使用在任何元素之上,這里的元素指的是上面的八種類型.
舉幾個(gè)正確的例子:
@Target(ElementType.METHOD)
@Target(value=ElementType.METHOD)
@Target(ElementType.METHOD,ElementType.CONSTRUCTOR)
具體參考一下javadoc文檔
源文件它們都使用了@Documented,@Documented的目的就是讓這一個(gè)Annotation類型的信息能夠顯示在javaAPI說明文檔上;沒有添加的話,使用javadoc生成API文檔的時(shí)候就會(huì)找不到這一個(gè)類型生成的信息.
另外一點(diǎn),如果需要把Annotation的數(shù)據(jù)繼承給子類,那么就會(huì)用到@Inherited這一個(gè)Annotation類型.
下面講的設(shè)計(jì)一個(gè)最簡(jiǎn)單的Annotation例子,這一例子共用四個(gè)文件;
Description.java
package lighter.javaeye.com; import java.lang.annotation.Documented; 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) @Documented public @interface Description { String value(); } package lighter.javaeye.com; import java.lang.annotation.Documented; 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) @Documented public @interface Description { String value(); }
說明:所有的Annotation會(huì)自動(dòng)繼承java.lang.annotation這一個(gè)接口,所以不能再去繼承別的類或是接口.
最重要的一點(diǎn),Annotation類型里面的參數(shù)該怎么設(shè)定:
第一,只能用public或默認(rèn)(default)這兩個(gè)訪問權(quán)修飾.例如,String value();這里把方法設(shè)為defaul默認(rèn)類型.
第二,參數(shù)成員只能用基本類型byte,short,char,int,long,float,double,boolean八種基本數(shù)據(jù)類型和String,Enum,Class,annotations等數(shù)據(jù)類型,以及這一些類型的數(shù)組.例如,String value();這里的參數(shù)成員就為String.
第三,如果只有一個(gè)參數(shù)成員,最好把參數(shù)名稱設(shè)為"value",后加小括號(hào).例:上面的例子就只有一個(gè)參數(shù)成員.
Name.java
package lighter.javaeye.com; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; //注意這里的@Target與@Description里的不同,參數(shù)成員也不同 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Name { String originate(); String community(); } package lighter.javaeye.com; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; //注意這里的@Target與@Description里的不同,參數(shù)成員也不同 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Name { String originate(); String community(); }
JavaEyer.java
package lighter.javaeye.com; @Description("javaeye,做最棒的軟件開發(fā)交流社區(qū)") public class JavaEyer { @Name(originate="創(chuàng)始人:robbin",community="javaEye") public String getName() { return null; } @Name(originate="創(chuàng)始人:江南白衣",community="springside") public String getName2() { return "借用兩位的id一用,寫這一個(gè)例子,請(qǐng)見諒!"; } } package lighter.javaeye.com; @Description("javaeye,做最棒的軟件開發(fā)交流社區(qū)") public class JavaEyer { @Name(originate="創(chuàng)始人:robbin",community="javaEye") public String getName() { return null; } @Name(originate="創(chuàng)始人:江南白衣",community="springside") public String getName2() { return "借用兩位的id一用,寫這一個(gè)例子,請(qǐng)見諒!"; } }
寫一個(gè)可以運(yùn)行提取JavaEyer信息的類TestAnnotation
package lighter.javaeye.com; import java.lang.reflect.Method; import java.util.HashSet; import java.util.Set; public class TestAnnotation { /** * author lighter * 說明:具體關(guān)天Annotation的API的用法請(qǐng)參見javaDoc文檔 */ public static void main(String[] args) throws Exception { String CLASS_NAME = "lighter.javaeye.com.JavaEyer"; Class test = Class.forName(CLASS_NAME); Method[] method = test.getMethods(); boolean flag = test.isAnnotationPresent(Description.class); if(flag) { Description des = (Description)test.getAnnotation(Description.class); System.out.println("描述:"+des.value()); System.out.println("-----------------"); } //把JavaEyer這一類有利用到@Name的全部方法保存到Set中去 Set<Method> set = new HashSet<Method>(); for(int i=0;i<method.length;i++) { boolean otherFlag = method[i].isAnnotationPresent(Name.class); if(otherFlag) set.add(method[i]); } for(Method m: set) { Name name = m.getAnnotation(Name.class); System.out.println(name.originate()); System.out.println("創(chuàng)建的社區(qū):"+name.community()); } } } package lighter.javaeye.com; import java.lang.reflect.Method; import java.util.HashSet; import java.util.Set; public class TestAnnotation { /** * author lighter * 說明:具體關(guān)天Annotation的API的用法請(qǐng)參見javaDoc文檔 */ public static void main(String[] args) throws Exception { String CLASS_NAME = "lighter.javaeye.com.JavaEyer"; Class test = Class.forName(CLASS_NAME); Method[] method = test.getMethods(); boolean flag = test.isAnnotationPresent(Description.class); if(flag) { Description des = (Description)test.getAnnotation(Description.class); System.out.println("描述:"+des.value()); System.out.println("-----------------"); } //把JavaEyer這一類有利用到@Name的全部方法保存到Set中去 Set<Method> set = new HashSet<Method>(); for(int i=0;i<method.length;i++) { boolean otherFlag = method[i].isAnnotationPresent(Name.class); if(otherFlag) set.add(method[i]); } for(Method m: set) { Name name = m.getAnnotation(Name.class); System.out.println(name.originate()); System.out.println("創(chuàng)建的社區(qū):"+name.community()); } } }
運(yùn)行結(jié)果:
描述:javaeye,做最棒的軟件開發(fā)交流社區(qū) 創(chuàng)始人:robbin 創(chuàng)建的社區(qū):javaEye 創(chuàng)始人:江南白衣 創(chuàng)建的社區(qū):springside
相關(guān)文章
Java線性結(jié)構(gòu)中棧、隊(duì)列和串的基本概念和特點(diǎn)詳解
前幾天小編給大家介紹了Java線性結(jié)構(gòu)中的鏈表,除了鏈表這種結(jié)構(gòu)之外,實(shí)際上還有棧、隊(duì)列、串等結(jié)構(gòu),那么這些結(jié)構(gòu)又有哪些特點(diǎn)呢,本文就給大家詳細(xì)的介紹一下,感興趣的小伙伴跟著小編一起來看看吧2023-07-07Java實(shí)現(xiàn)級(jí)聯(lián)下拉結(jié)構(gòu)的示例代碼
在開發(fā)過程中,會(huì)遇到很多的實(shí)體需要將查出的數(shù)據(jù)處理為下拉或者級(jí)聯(lián)下拉的結(jié)構(gòu),提供給前端進(jìn)行展示。本文為大家介紹了java封裝下拉和級(jí)聯(lián)下拉的通用工具類,需要的可以參考一下2022-06-06JAVA實(shí)現(xiàn)FTP斷點(diǎn)上傳的方法
這篇文章主要介紹了JAVA實(shí)現(xiàn)FTP斷點(diǎn)上傳的方法,涉及java使用FTP實(shí)現(xiàn)文件傳輸?shù)南嚓P(guān)技巧,需要的朋友可以參考下2015-06-06springboot與vue詳解實(shí)現(xiàn)短信發(fā)送流程
隨著人工智能的不斷發(fā)展,機(jī)器學(xué)習(xí)這門技術(shù)也越來越重要,很多人都開啟了學(xué)習(xí)機(jī)器學(xué)習(xí),本文就介紹了機(jī)器學(xué)習(xí)的基礎(chǔ)內(nèi)容2022-06-06Redisson分布式閉鎖RCountDownLatch的使用詳細(xì)講解
分布式鎖和我們java基礎(chǔ)中學(xué)習(xí)到的synchronized略有不同,synchronized中我們的鎖是個(gè)對(duì)象,當(dāng)前系統(tǒng)部署在不同的服務(wù)實(shí)例上,單純使用synchronized或者lock已經(jīng)無法滿足對(duì)庫(kù)存一致性的判斷。本次主要講解基于rediss實(shí)現(xiàn)的分布式鎖2023-02-02詳談Spring是否支持對(duì)靜態(tài)方法進(jìn)行Aop增強(qiáng)
這篇文章主要介紹了Spring是否支持對(duì)靜態(tài)方法進(jìn)行Aop增強(qiáng),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12java實(shí)現(xiàn)文件打包壓縮輸出到瀏覽器下載
這篇文章主要介紹了java實(shí)現(xiàn)文件打包壓縮輸出到瀏覽器下載,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11