Java進(jìn)階基礎(chǔ)--注解與反射
一岗喉、注解
Java注解(Annotation)又稱Java標(biāo)注,是JDK5.0引入的一種注釋機(jī)制炸庞。注解是元數(shù)據(jù)的一種形式钱床,提供有關(guān)于程序但不屬于程序本身的數(shù)據(jù)。注解對(duì)它們注解的代碼的操作沒(méi)有直接影響埠居。
1查牌、注解聲明
Java中所有的注解,默認(rèn)實(shí)現(xiàn)Annotation接口:
package java.lang.annotation;
public interface Annotation {
????boolean equals(Object obj);
????int hashCode();
????String toString();
????Class<? extends Annotation> annotationType();
}
于聲明一個(gè)“Class”不同的是滥壕,注解的聲明使用@interface關(guān)鍵字纸颜。
public @interface Lance{ }
1.1、元注解
在定義注解時(shí)捏浊,注解類也能夠使用其他的注解聲明懂衩。對(duì)注解類型進(jìn)行注解的注解類,我們稱之為meta-annotation(元注解)金踪。一般的浊洞,我們?cè)诙x自定義注解時(shí),需要指定的元注解有兩個(gè):
@Target
注解標(biāo)記另一個(gè)注解胡岔,以限制可以應(yīng)用注解的Java元素類型法希。目標(biāo)注解指定以下元素類型之一作為其值:
①ElementType.ANNOTATION_TYPE? 可以應(yīng)用于注解類型。
②ElementType.CONSTRUCTOR 可以應(yīng)用于構(gòu)造函數(shù)靶瘸。
③ElementType.FIELD 可以應(yīng)用于字段或?qū)傩浴?/p>
④ElementType.LOCAL_VARIABLE 可以應(yīng)用于局部變量苫亦。
⑤ElementType.METHPD 可以應(yīng)用于方法級(jí)注解。
⑥ElementType.PACKAGE 可以應(yīng)用于包聲明怨咪。
⑦ElementType.PARAMETER 可以應(yīng)用于方法的參數(shù)屋剑。
⑧ElementType.TYPE 可以應(yīng)用于類的任何元素。
@Retention
注解指定標(biāo)記注解的存儲(chǔ)方式:
①RetentionPolicy.SOURCE 標(biāo)記的注解僅保留在源碼級(jí)別中诗眨,并被編譯器忽略唉匾。
②RetentionPolicy.CLASS 標(biāo)記的注解在編譯時(shí)由編譯器保留,但Java虛擬機(jī)(JVM)會(huì)忽略匠楚。
③RetentionPolicy.RUNTIME 標(biāo)記的注解由JVM保留巍膘,因此運(yùn)行時(shí)環(huán)境可以使用它。
另外還有@Documented 與 @Inherited 元注解芋簿,前者用于被javadoc工具提取成文檔峡懈,后者表示允許子類繼承父類中定義的注解。
@Retention 三個(gè)值中 SOURCE < CLASS < RUNTIME与斤,即CLASS包含了SOURCE肪康,RUNTIME包含SOURCE荚恶、CLASS。
例子:
@Target({ElementType.TYPE,ElementType.FIELD}) // 允許在類與類屬性上標(biāo)記該注解
@Retention(RetentionPolicy.SOURCE) //注解保留在源碼中
public @interface Lance {}
1.2梅鹦、注解類型元素
在聲明注解中裆甩,允許在使用注解時(shí)傳遞參數(shù)冗锁。
@Target({ElementType.TYPE,ElementType.FIELD})
@Retention(RetentionPolicy.SOURCE)
public @interface Lance {
????String value(); //無(wú)默認(rèn)值
????int age() default 1; //有默認(rèn)值
}
注意:在使用注解時(shí)齐唆,如果定義的注解中的類型元素?zé)o默認(rèn)值,則必須進(jìn)行傳值冻河。
1.3箍邮、注解應(yīng)用場(chǎng)景
按照@Retention元注解定義的注解存儲(chǔ)方式,注解可以被在三種場(chǎng)景中使用:
1.4叨叙、Android注解語(yǔ)法檢查
在Android中我們需要設(shè)計(jì)接口以供使用者調(diào)用時(shí)锭弊,如出現(xiàn)需要對(duì)入?yún)⑦M(jìn)行類型限定,如限定為資源ID擂错,布局ID等類型參數(shù)味滞,將參數(shù)類型直接給定int即可。然而钮呀,我們可以利用Android為我們提供的語(yǔ)法檢查注解剑鞍,來(lái)輔助進(jìn)行更為直接的參數(shù)類型檢查與提示。
參數(shù)限制為:圖片資源ID
public Drawable getDrawable(@Drawable int id) throw是 NotFoundException
同時(shí)爽醋,我們也可以使用@intDef來(lái)定義自己的入?yún)㈩愋蜋z查蚁署。
二、反射
一般情況下蚂四,我們使用某個(gè)類時(shí)必定知道它是什么類光戈,是用來(lái)做什么的,并且能都獲得此類的引用遂赠。于是我們直接對(duì)這個(gè)類進(jìn)行實(shí)例化久妆,之后使用這個(gè)類進(jìn)行操作。
反射則是一開(kāi)始并不知道我們初始化的類對(duì)象是什么跷睦,自然也無(wú)法使用new關(guān)鍵字來(lái)創(chuàng)建對(duì)象筷弦。這時(shí)候,我們使用JDK提供的反射API進(jìn)行反射調(diào)用送讲。
反射就是在運(yùn)行狀態(tài)中奸笤,對(duì)于任意一個(gè)類,都能夠知道這個(gè)類的所有屬性和方法哼鬓;對(duì)于任意一個(gè)對(duì)象监右,都能夠調(diào)用它的任意方法和屬性;并且能改變它的屬性异希。是Java被視為動(dòng)態(tài)語(yǔ)言的關(guān)鍵健盒。
Java反射機(jī)制主要提供了以下功能:
①在運(yùn)行時(shí)構(gòu)造任意一個(gè)類的對(duì)象
②在運(yùn)行時(shí)獲取或者修改任意一個(gè)類所具有的成員變量和方法
③在運(yùn)行時(shí)調(diào)用任意一個(gè)對(duì)象的方法和屬性
1、Class
反射始于Class,Class是一個(gè)類扣癣,封裝了當(dāng)前對(duì)象所對(duì)應(yīng)的類的信息惰帽。
1.1、獲得Class對(duì)象
①通過(guò)類名獲取? ?類名.class
②通過(guò)對(duì)象獲取? ? 對(duì)象名.getClass()
③通過(guò)全類名獲取? ? ?Class.forName(全類名)? ? classLoader.loadClass(全類名)
2父虑、判斷是否為某個(gè)類的實(shí)例
一般地该酗,我們用 instanceof 關(guān)鍵字來(lái)判斷是否為某個(gè)類的實(shí)例。同時(shí)我們也可以借助反射中 Class 對(duì)象的isInstance() 方法來(lái)判斷是否為某個(gè)類的實(shí)例士嚎,它是一個(gè) native 方法: public native boolean isInstance(Object obj);?
判斷是否為某個(gè)類的類型:??public boolean isAssignableFrom(Class<?> cls)
3呜魄、創(chuàng)建實(shí)例
①使用Class對(duì)象的newInstance()方法來(lái)創(chuàng)建Class對(duì)象對(duì)應(yīng)類的實(shí)例
Class<?> c = String.class;? ? ? ? ?Object str = c.newInstance();
②先通過(guò)Class對(duì)象獲取指定的Constructor對(duì)象,再調(diào)用Constructor對(duì)象的newInstance()方法來(lái)創(chuàng)建實(shí)例莱衩。
//獲取String所對(duì)應(yīng)的Class對(duì)象? ? ? ? ? ? ?Class<?> c = String.class;
//獲取String類帶一個(gè)String參數(shù)的構(gòu)造器? ? ? ? Constructor constructor = c.getConstructor(String.class);
//根據(jù)構(gòu)造器創(chuàng)建實(shí)例? ? ? ? ? Object obj = constructor.newInstance("23333");
4爵嗅、獲取構(gòu)造器信息
獲取構(gòu)造器的方法:
①Constructor getConstructor(Class[] params)? 獲得使用特殊的參數(shù)類型的public構(gòu)造函數(shù)(包括父類)
②Constructor[] getConstructors()? ?獲得類的所有公共構(gòu)造函數(shù)
③Constructor getDeclaredConstructor(Class[] params)? ?獲得使用特定參數(shù)類型的構(gòu)造函數(shù)(包括私有)
④Constructor[] getDeclaredConstructors()? ?獲得類的所有構(gòu)造函數(shù)(與接入級(jí)別無(wú)關(guān))
5、獲取類的成員變量(字段)信息
獲取字段信息的方法:
①Field? getField(String name)? ? 獲得命名的公共字段(public修飾)
②Field[]? getFields()? ?獲得類的所有公共字段(public修飾)
③Field? getDeclaredField(String name)? ?獲得類聲明的命名的字段(包括private修飾)
④Field[]? getDeclaredFields? ?獲得類聲明的所有字段(包括private修飾)
當(dāng)我們從類中獲取了一個(gè)方法后笨蚁,就可以用 invoke() 方法來(lái)調(diào)用這個(gè)方法睹晒。
public Object invoke(Object obj, Object... args);
6、利用反射創(chuàng)建數(shù)組
數(shù)組在Java里是比較特殊的一種類型括细,它可以賦值一個(gè)Object Reference其中的Array類為java.lang.reflect.Array類伪很。我們通過(guò)Array.newInstance()創(chuàng)建數(shù)組對(duì)象??
public static Object newInstance(Class<?> componentType, int length);
7、反射獲取泛型真實(shí)類型
當(dāng)我們對(duì)一個(gè)泛型類進(jìn)行反射時(shí)勒极,需要得到泛型中的真實(shí)數(shù)據(jù)類型是掰,來(lái)完成如JSON反序列化的操作。此時(shí)需要通過(guò)Type體系來(lái)完成辱匿。Type接口包含了一個(gè)實(shí)現(xiàn)類(Class)和四個(gè)實(shí)現(xiàn)接口:
①TypeVariable? ? ? ? ? ? ? ? ?泛型類型變量键痛。可以獲得泛型上下限等信息
②ParameterizedType? ? ? ?具體的泛型類型匾七⌒醵蹋可以獲得元數(shù)據(jù)中泛型簽名類型(泛型真實(shí)類型)
③GenericArrayType? ? ? ? ?當(dāng)需要描述的類型是泛型類的數(shù)組時(shí),比如List[],Map[]昨忆,此接口會(huì)作為Type的實(shí)現(xiàn)
④WildcardType? ? ? ? ? ? ? ? 通配符泛型丁频。可以獲得上下限信息