日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > java >内容正文

java

java 反射api_Java学习笔记--反射API

發(fā)布時(shí)間:2023/12/3 java 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java 反射api_Java学习笔记--反射API 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

反射API

1.反射API的介紹

通過(guò)反射API可以獲取Java程序在運(yùn)行時(shí)刻的內(nèi)部結(jié)構(gòu)。比如Java類中包含的構(gòu)造方法、域和方法等元素,并可以與這些元素進(jìn)行交換。

按照 一般地面向?qū)ο蟮脑O(shè)計(jì)思路,一個(gè)對(duì)象的內(nèi)部狀態(tài)都應(yīng)該通過(guò)相應(yīng)的方法來(lái)改變,而不是直接去修改屬性的值。一般Java類中的屬性設(shè)置獲取方法的命名都遵循JavaBeans規(guī)范中的要求,即利用setXxx和getXxx這樣的的方法聲明,因此可以實(shí)現(xiàn)一個(gè)使用工具類來(lái)完成對(duì)任意對(duì)象的屬性設(shè)置和獲取的操作,只要設(shè)置和獲取屬性的方法滿足JavaBeans規(guī)范。

使用反射API設(shè)置對(duì)象的屬性值的示例

public class ReflectSetter{

public static void invokeSetter(Object obj,String field,Object value) throws NoSuchMethodException,InvocationTargetException,IllegalAccessException{

String methodName = "set" + field.substring(0,1).toUpperCase() + field.substring();

Class> clazz = obj.getClass();

Method method = clazz.getMethod(methodName,value.getClass());

method.invole(obj,value);

}

}

從根本上來(lái)說(shuō),反射API實(shí)際上定義了一種功能提供者和使用之間的松散契約。以方法調(diào)用為例,按照J(rèn)ava語(yǔ)言的一般用法,在調(diào)用方法的時(shí)候,在代碼中首先需要一個(gè)對(duì)象的變量作為調(diào)用的接受者,再把方法的名稱直接寫在代碼中。方法的名稱不可能是變量。編譯器會(huì)檢查這個(gè)對(duì)象中是否確實(shí)有待調(diào)用的方法,如果沒有就會(huì)出現(xiàn)編譯錯(cuò)誤。這種一般地做法,是提供者和使用者之間的一種緊密的契約,由編譯器來(lái)保證其合法性。而是用反射API,兩者的契約只需要建立在名稱和參數(shù)類型這個(gè)層次上就足夠了。方法名稱可以是變量,參數(shù)值也可以動(dòng)態(tài)生成。調(diào)用的合法性由開發(fā)人員自己保證。如果方法調(diào)用不是合法的,相關(guān)的異常會(huì)在運(yùn)行時(shí)拋出。

反射API的一個(gè)重要的使用場(chǎng)合是要調(diào)用的方法或者要操作的域的名稱按照某種規(guī)律變化的時(shí)候。一個(gè)典型的場(chǎng)景就是在Servlet中用HTTP請(qǐng)求值來(lái)填充領(lǐng)域?qū)ο?。比如在用戶注?cè)的時(shí)候,包含在HTTP請(qǐng)求中的用戶所填寫的相關(guān)信息,需要被填充到程序中定義好的領(lǐng)域?qū)ο笾?。只需要利用Servlet提供的API遍歷請(qǐng)求中的所有參數(shù),然后用ReflectSetter類中invokeSetter方法設(shè)置領(lǐng)域?qū)ο笾信c參數(shù)名稱相對(duì)應(yīng)的屬性的值即可。另外一個(gè)場(chǎng)景是在數(shù)據(jù)庫(kù)操作中,從SQL查詢結(jié)果集中創(chuàng)建并填充領(lǐng)域?qū)ο蟆?shù)據(jù)庫(kù)的別名和領(lǐng)域?qū)ο髮傩悦Q也存在著類似的對(duì)應(yīng)關(guān)系。

反射API在為Java程序帶來(lái)靈活性的同時(shí),也產(chǎn)生了額外的性能代價(jià)。由于反射API的實(shí)現(xiàn)機(jī)制,對(duì)于相同的操作,比如調(diào)用同一個(gè)方法,用反射API來(lái)動(dòng)態(tài)實(shí)現(xiàn)比直接在源代碼中編寫的方式大概慢到一到兩個(gè)數(shù)量級(jí)。隨之Java虛擬機(jī)實(shí)現(xiàn)的改進(jìn),反射API的性能已經(jīng)有了非常大的提升。但是這種性能的差距是客觀存在。因此,在某些對(duì)性能要求比較高的應(yīng)用,要慎用反射API。

2.使用反射API獲取構(gòu)造方法

通過(guò)反射API可以獲取到Java類中的構(gòu)造方法。通過(guò)構(gòu)造方法可以在運(yùn)行動(dòng)態(tài)地創(chuàng)建Java對(duì)象,而不只是通過(guò)new操作符來(lái)進(jìn)行創(chuàng)建。在得到Class類的對(duì)象之后,可以通過(guò)其中的方法來(lái)獲取構(gòu)造方法。相關(guān)的方法有4個(gè),其中g(shù)etConstructors用來(lái)獲取所有的public構(gòu)造方法的列表,getConstructor則根據(jù)參數(shù)類型來(lái)獲取public的構(gòu)造方法。另外兩個(gè)對(duì)應(yīng)方法getDeclaredConstructors和getDeclaredConstructor的作用類似。只不過(guò)它們會(huì)獲取類中真正聲明的構(gòu)造方法,而忽略從父類中繼承下來(lái)的構(gòu)造方法。得到了表示構(gòu)造函數(shù)的java.lang.reflect.Constructor對(duì)象之后,就可以獲取關(guān)于構(gòu)造方法的更多信息,以及通過(guò)newInstance方法創(chuàng)建出的新的對(duì)象。

一般地構(gòu)造方法的獲取和使用沒有什么特殊之處,需要特別說(shuō)明的是對(duì)參數(shù)長(zhǎng)度可變的構(gòu)造方法和嵌套類(nested class)的構(gòu)造方法的使用。

如果構(gòu)造方法聲明了長(zhǎng)度可變的參數(shù),在獲取構(gòu)造方法的時(shí)候,要使用對(duì)應(yīng)的數(shù)組類型的Class對(duì)象。這是因?yàn)殚L(zhǎng)度可變的參數(shù)實(shí)際上是通過(guò)數(shù)組來(lái)實(shí)現(xiàn)的。

使用反射API獲取參數(shù)可變的構(gòu)造方法

public class VarargsConstructor{

public VarargsConstructor(String... names) {}

}

public void useVarargsConstructor() throws Exception{

Constructor constructor = VarargsConstructo.class.getDeclaredConstructor(String[].class);

//為了避免方法調(diào)用時(shí)的歧義,這樣編譯器就知道把這個(gè)字符串?dāng)?shù)組作為一個(gè)可變長(zhǎng)度的參數(shù)來(lái)傳遞

//在調(diào)用newInstance的時(shí)候,要把作為實(shí)際參數(shù)的字符串?dāng)?shù)組先轉(zhuǎn)換成Object類型,

constructor.newInstance((Object) new String[] {"A","B","C"});

}

對(duì)嵌套類的構(gòu)造方法的獲取,需要區(qū)分靜態(tài)和非靜態(tài)兩種情況,即是否在聲明嵌套類的時(shí)候使用static關(guān)鍵詞。靜態(tài)的嵌套類并沒有特別之處,按照一般的方式來(lái)使用即可。而對(duì)非靜態(tài)類嵌套類來(lái)說(shuō),其特殊之處在于它的對(duì)象實(shí)例中都有一個(gè)隱含的對(duì)象引用,指向包含它的外部類對(duì)象,也正是這個(gè)隱含的對(duì)象引用的存在,使得非靜態(tài)類嵌套類中的代碼可以直接引用外部類中的包含的私有域和方法。因此,在獲取非靜態(tài)類的構(gòu)造方式時(shí)候,類型參數(shù)列表的第一個(gè)值必須是外部類的Class對(duì)象。

使用反射API獲取嵌套類的構(gòu)造方法

public class ConstNestedClass{

static class StaticNestedClass{

public StaticNestedClass(String name){}

}

class NestedClass{

public NestedClass(int count){}

}

public void useNestedClassConstrcutor() throws Exception{

Constructor sncc = StaticNestedClass.class.getDeclaredConstructor(String.class);

sncc.newInstance("Alex");

Constructor ncc = NestedClass.class.getDeclaredConstructor(ConstructorUsage.class,int.class);

NestedClass nc = ncc.newInstance(this,3);

}

}

3.使用反射API獲取域

通過(guò)反射API可以獲取到類中公開的靜態(tài)域和對(duì)象中的實(shí)例域。得到表示域的java.lang.reflect.Field類的對(duì)象之后,就可以獲取和設(shè)置域的值。Class類中有4個(gè)方法用來(lái)獲取域,分別是getFields、getField、getDeclaredFields和getDeclaredField,其含義與獲取構(gòu)造方法的4個(gè)方法類似。

使用反射API獲取和使用靜態(tài)域和實(shí)例域

public void useField() throws Exception{

Field fieldCount = FieldContainer.class.getDeclaredField("count");

fieldCount.set(null,3);

Field fieldName = FieldContainer.class.getDeclaredField("name");

fieldContainer fieldContainer = new FieldContainer();

fieldName.set(fieldContainer,"Arthur");

}

總的來(lái)說(shuō),對(duì)域的獲取和設(shè)置都比較簡(jiǎn)單,但是只能對(duì)類中的公開域進(jìn)行操作。私有域可以通過(guò)反射API的進(jìn)行獲取、操作(必須知曉字段名,且必須事先取消Java語(yǔ)言訪問(wèn)檢查)。

4.使用反射API獲取方法

最后一個(gè)可以通過(guò)反射API獲取的元素是方法,這也是最常使用反射API的場(chǎng)景,即獲取一個(gè)對(duì)象中的方法,并在運(yùn)行時(shí)調(diào)用該方法。與之前提到的構(gòu)造方法和域類似,Class類也有4個(gè)方法用來(lái)獲取方法,分別是getMethods、getMethod、getDeclaredMethods和getDeclaredMethod。這4個(gè)方法的含義類似于獲取構(gòu)造方法和域的對(duì)應(yīng)方法。在得到了表示方法的java.lang.reflect.Method類的對(duì)象之后,就可以查詢?cè)摲椒ǖ脑敿?xì)信息,比如方法的參數(shù)和返回值的類型等。最重要的是可以通過(guò)invoke方法來(lái)傳入實(shí)際參數(shù)并調(diào)用該方法。

使用反射API獲取和使用公開和私有方法

public void useMethod() throws Exception{

MethodContainer mc = new MethodContainer();

Method publicMethod = MethodContainer.class.getDeclaredMethod("publicMethod");

publicMethod.invoke(mc);

Method privateMethod = MethodComtainer.class.getDeclareMethod("privateMethod");

privateMethod.setAccessible(true);

privateMethod.invoke(mc);

}

5.使用反射API操作數(shù)組

使用反射API對(duì)數(shù)組進(jìn)行操作的方式不同于一般的Java對(duì)象,是通過(guò)專門的java.lang.reflect.Array這個(gè)實(shí)用工具類來(lái)實(shí)現(xiàn)的。Array類提供的方法包括創(chuàng)建數(shù)組和操作數(shù)組中的元素。

使用反射API操作數(shù)組

public void useArray(){

String name = (String[])Array.mewInstance(String.class,10);

names[0] = "Hello";

Array.set(names,1,"World");

String str = (String)Array.get(names,0);

int[][][] matrix1 = (int[][][])Array.newInstance(int.class,3,3,3);

martix1[0][0][0] = 1;

int[][][] martix2 = (int[][][])Array.newInstance(int[].class,3,4);

martix2[0][0] = new int[10];

martix2[0][1] = new int[3];

martix2[0][0][1] = 1;

}

6.訪問(wèn)權(quán)限與異常處理

使用反射API的一個(gè)重要好處是可以繞過(guò)Java語(yǔ)言中默認(rèn)的訪問(wèn)控制權(quán)限。比如正正常的代碼中,一個(gè)類的對(duì)象是不能訪問(wèn)在另外一個(gè)類中聲明的私有方法的,但是通過(guò)反射API是可以做到這一點(diǎn)。Constructor、Field和Method都繼承自java.lang.reflect.AccessibleObject,其中的方法setAccessible可以用來(lái)設(shè)置是否繞開默認(rèn)的權(quán)限。

在利用invoke方法來(lái)調(diào)用方法時(shí),如果方法本身拋了異常,invoke方法會(huì)拋出InvocationTargetException異常來(lái)表示這種情況。在捕獲到InvocationTargetException異常的時(shí)候,通過(guò)InvocationTargetExeption異常的getCause方法可以獲取到真正的異常信息,幫助進(jìn)行調(diào)試。

在處理與反射相關(guān)的異常的時(shí)候,可以直接捕獲java.lang.ReflectiveOperationException,這是所有與反射操作相關(guān)的異常類的父類。

總結(jié)

以上是生活随笔為你收集整理的java 反射api_Java学习笔记--反射API的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。