Java 反射机制深入研究
轉(zhuǎn)載自??Java 反射機(jī)制深入研究
Java反射機(jī)制深入研究
?
Java 反射是Java語言的一個(gè)很重要的特征,它使得Java具體了“動(dòng)態(tài)性”。
?
在Java運(yùn)行時(shí)環(huán)境中,對于任意一個(gè)類,能否知道這個(gè)類有哪些屬性和方法?對于任意一個(gè)對象,能否調(diào)用它的任意一個(gè)方法?答案是肯定的。這種動(dòng)態(tài)獲取類的信息以及動(dòng)態(tài)調(diào)用對象的方法的功能來自于Java 語言的反射(Reflection)機(jī)制。
?
Java 反射機(jī)制主要提供了以下功能:
在運(yùn)行時(shí)判斷任意一個(gè)對象所屬的類。
在運(yùn)行時(shí)構(gòu)造任意一個(gè)類的對象。
在運(yùn)行時(shí)判斷任意一個(gè)類所具有的成員變量和方法。
在運(yùn)行時(shí)調(diào)用任意一個(gè)對象的方法。
?
Reflection 是Java被視為動(dòng)態(tài)(或準(zhǔn)動(dòng)態(tài))語言的一個(gè)關(guān)鍵性質(zhì)。這個(gè)機(jī)制允許程序在運(yùn)行時(shí)透過Reflection APIs取得任何一個(gè)已知名稱的class的內(nèi)部信息,包括其modifiers(諸如public, static 等等)、superclass(例如Object)、實(shí)現(xiàn)之interfaces(例如Serializable),也包括fields和methods的所有信息,并可于運(yùn)行時(shí)改變fields內(nèi)容或調(diào)用methods。
?
一般而言,開發(fā)者社群說到動(dòng)態(tài)語言,大致認(rèn)同的一個(gè)定義是:“程序運(yùn)行時(shí),允許改變程序結(jié)構(gòu)或變量類型,這種語言稱為動(dòng)態(tài)語言”。從這個(gè)觀點(diǎn)看,Perl,Python,Ruby是動(dòng)態(tài)語言,C++,Java,C#不是動(dòng)態(tài)語言。
?
盡管在這樣的定義與分類下Java不是動(dòng)態(tài)語言,它卻有著一個(gè)非常突出的動(dòng)態(tài)相關(guān)機(jī)制:Reflection。這個(gè)字的意思是“反射、映象、倒影”,用在Java身上指的是我們可以于運(yùn)行時(shí)加載、探知、使用編譯期間完全未知的classes。換句話說,Java程序可以加載一個(gè)運(yùn)行時(shí)才得知名稱的class,獲悉其完整構(gòu)造(但不包括methods定義),并生成其對象實(shí)體、或?qū)ζ鋐ields設(shè)值、或喚起其methods。這種“看透class”的能力(the ability of the program to examine itself)被稱為introspection(內(nèi)省、內(nèi)觀、反省)。Reflection和introspection是常被并提的兩個(gè)術(shù)語。
?
在JDK中,主要由以下類來實(shí)現(xiàn)Java反射機(jī)制,這些類都位于java.lang.reflect包中:
Class類:代表一個(gè)類。
Field 類:代表類的成員變量(成員變量也稱為類的屬性)。
Method類:代表類的方法。
Constructor 類:代表類的構(gòu)造方法。
Array類:提供了動(dòng)態(tài)創(chuàng)建數(shù)組,以及訪問數(shù)組的元素的靜態(tài)方法。
?
下面給出幾個(gè)例子看看Reflection API的實(shí)際運(yùn)用:
?
一、通過Class類獲取成員變量、成員方法、接口、超類、構(gòu)造方法等
?
在java.lang.Object 類中定義了getClass()方法,因此對于任意一個(gè)Java對象,都可以通過此方法獲得對象的類型。Class類是Reflection API 中的核心類,它有以下方法
getName():獲得類的完整名字。
getFields():獲得類的public類型的屬性。
getDeclaredFields():獲得類的所有屬性。
getMethods():獲得類的public類型的方法。
getDeclaredMethods():獲得類的所有方法。
getMethod(String name, Class[] parameterTypes):獲得類的特定方法,name參數(shù)指定方法的名字,parameterTypes 參數(shù)指定方法的參數(shù)類型。
getConstructors():獲得類的public類型的構(gòu)造方法。
getConstructor(Class[] parameterTypes):獲得類的特定構(gòu)造方法,parameterTypes 參數(shù)指定構(gòu)造方法的參數(shù)類型。
newInstance():通過類的不帶參數(shù)的構(gòu)造方法創(chuàng)建這個(gè)類的一個(gè)對象。
?
下面給出一個(gè)綜合運(yùn)用的例子:
?
public class RefConstructor {public static void main(String args[]) throws Exception {RefConstructor ref = new RefConstructor();ref.getConstructor();}public void getConstructor() throws Exception {Class c = null;c = Class.forName("java.lang.Long");Class cs[] = {java.lang.String.class};System.out.println("\n-------------------------------\n");Constructor cst1 = c.getConstructor(cs);System.out.println("1、通過參數(shù)獲取指定Class對象的構(gòu)造方法:");System.out.println(cst1.toString());Constructor cst2 = c.getDeclaredConstructor(cs);System.out.println("2、通過參數(shù)獲取指定Class對象所表示的類或接口的構(gòu)造方法:");System.out.println(cst2.toString());Constructor cst3 = c.getEnclosingConstructor();System.out.println("3、獲取本地或匿名類Constructor 對象,它表示基礎(chǔ)類的立即封閉構(gòu)造方法。");if (cst3 != null) System.out.println(cst3.toString());else System.out.println("-- 沒有獲取到任何構(gòu)造方法!");Constructor[] csts = c.getConstructors();System.out.println("4、獲取指定Class對象的所有構(gòu)造方法:");for (int i = 0; i < csts.length; i++) {System.out.println(csts[i].toString());}System.out.println("\n-------------------------------\n");Type types1[] = c.getGenericInterfaces();System.out.println("1、返回直接實(shí)現(xiàn)的接口:");for (int i = 0; i < types1.length; i++) {System.out.println(types1[i].toString());}Type type1 = c.getGenericSuperclass();System.out.println("2、返回直接超類:");System.out.println(type1.toString());Class[] cis = c.getClasses();System.out.println("3、返回超類和所有實(shí)現(xiàn)的接口:");for (int i = 0; i < cis.length; i++) {System.out.println(cis[i].toString());}Class cs1[] = c.getInterfaces();System.out.println("4、實(shí)現(xiàn)的接口");for (int i = 0; i < cs1.length; i++) {System.out.println(cs1[i].toString());}System.out.println("\n-------------------------------\n");Field fs1[] = c.getFields();System.out.println("1、類或接口的所有可訪問公共字段:");for (int i = 0; i < fs1.length; i++) {System.out.println(fs1[i].toString());}Field f1 = c.getField("MIN_VALUE");System.out.println("2、類或接口的指定已聲明指定公共成員字段:");System.out.println(f1.toString());Field fs2[] = c.getDeclaredFields();System.out.println("3、類或接口所聲明的所有字段:");for (int i = 0; i < fs2.length; i++) {System.out.println(fs2[i].toString());}Field f2 = c.getDeclaredField("serialVersionUID");System.out.println("4、類或接口的指定已聲明指定字段:");System.out.println(f2.toString());System.out.println("\n-------------------------------\n");Method m1[] = c.getMethods();System.out.println("1、返回類所有的公共成員方法:");for (int i = 0; i < m1.length; i++) {System.out.println(m1[i].toString());}Method m2 = c.getMethod("longValue", new Class[]{});System.out.println("2、返回指定公共成員方法:");System.out.println(m2.toString());}}輸出結(jié)果:輸出結(jié)果很長,這里不再給出。
?
?
二、運(yùn)行時(shí)復(fù)制對象
?
例程ReflectTester 類進(jìn)一步演示了Reflection API的基本使用方法。ReflectTester類有一個(gè)copy(Object object)方法,這個(gè)方法能夠創(chuàng)建一個(gè)和參數(shù)object 同樣類型的對象,然后把object對象中的所有屬性拷貝到新建的對象中,并將它返回
這個(gè)例子只能復(fù)制簡單的JavaBean,假定JavaBean 的每個(gè)屬性都有public 類型的getXXX()和setXXX()方法。
?
public class ReflectTester {public Object copy(Object object) throws Exception {// 獲得對象的類型Class<?> classType = object.getClass();System.out.println("Class:" + classType.getName());// 通過默認(rèn)構(gòu)造方法創(chuàng)建一個(gè)新的對象Object objectCopy = classType.getConstructor(new Class[]{}).newInstance(new Object[]{});// 獲得對象的所有屬性Field fields[] = classType.getDeclaredFields();for (int i = 0; i < fields.length; i++) {Field field = fields[i];String fieldName = field.getName();String firstLetter = fieldName.substring(0, 1).toUpperCase();// 獲得和屬性對應(yīng)的getXXX()方法的名字String getMethodName = "get" + firstLetter + fieldName.substring(1);// 獲得和屬性對應(yīng)的setXXX()方法的名字String setMethodName = "set" + firstLetter + fieldName.substring(1);// 獲得和屬性對應(yīng)的getXXX()方法Method getMethod = classType.getMethod(getMethodName, new Class[]{});// 獲得和屬性對應(yīng)的setXXX()方法Method setMethod = classType.getMethod(setMethodName, new Class[]{field.getType()});// 調(diào)用原對象的getXXX()方法Object value = getMethod.invoke(object, new Object[]{});System.out.println(fieldName + ":" + value);// 調(diào)用拷貝對象的setXXX()方法setMethod.invoke(objectCopy, new Object[]{value});}return objectCopy;}public static void main(String[] args) throws Exception {Customer customer = new Customer("Tom", 21);customer.setId(new Long(1));Customer customerCopy = (Customer) new ReflectTester().copy(customer);System.out.println("Copy information:" + customerCopy.getId() + " " + customerCopy.getName() + " "+ customerCopy.getAge());} }class Customer {private Long id;private String name;private int age;public Customer() {}public Customer(String name, int age) {this.name = name;this.age = age;}public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;} }輸出結(jié)果:
Class:com.langsin.reflection.Customer
id:1
name:Tom
age:21
Copy information:1 Tom 21
?
Process finished with exit code 0
?
解說:
ReflectTester 類的copy(Object object)方法依次執(zhí)行以下步驟
(1)獲得對象的類型:
Class classType=object.getClass();
System.out.println("Class:"+classType.getName());
?
(2)通過默認(rèn)構(gòu)造方法創(chuàng)建一個(gè)新對象:
Object objectCopy=classType.getConstructor(new Class[]{}).newInstance(new Object[]{});
以上代碼先調(diào)用Class類的getConstructor()方法獲得一個(gè)Constructor 對象,它代表默認(rèn)的構(gòu)造方法,然后調(diào)用Constructor對象的newInstance()方法構(gòu)造一個(gè)實(shí)例。
?
3)獲得對象的所有屬性:
Field fields[]=classType.getDeclaredFields();
Class 類的getDeclaredFields()方法返回類的所有屬性,包括public、protected、默認(rèn)和private訪問級(jí)別的屬性
?
(4)獲得每個(gè)屬性相應(yīng)的getXXX()和setXXX()方法,然后執(zhí)行這些方法,把原來對象的屬性拷貝到新的對象中
?
三、用反射機(jī)制調(diào)用對象的方法
?
public class InvokeTester {public int add(int param1, int param2) {return param1 + param2;}public String echo(String msg) {return "echo: " + msg;}public static void main(String[] args) throws Exception {Class<?> classType = InvokeTester.class;Object invokeTester = classType.newInstance();// Object invokeTester = classType.getConstructor(new// Class[]{}).newInstance(new Object[]{});//獲取InvokeTester類的add()方法Method addMethod = classType.getMethod("add", new Class[]{int.class, int.class});//調(diào)用invokeTester對象上的add()方法Object result = addMethod.invoke(invokeTester, new Object[]{new Integer(100), new Integer(200)});System.out.println((Integer) result);//獲取InvokeTester類的echo()方法Method echoMethod = classType.getMethod("echo", new Class[]{String.class});//調(diào)用invokeTester對象的echo()方法result = echoMethod.invoke(invokeTester, new Object[]{"Hello"});System.out.println((String) result);} }在例程InvokeTester類的main()方法中,運(yùn)用反射機(jī)制調(diào)用一個(gè)InvokeTester對象的add()和echo()方法
?
add()方法的兩個(gè)參數(shù)為int 類型,獲得表示add()方法的Method對象的代碼如下:
Method addMethod=classType.getMethod("add",new Class[]{int.class,int.class});
Method類的invoke(Object obj,Object args[])方法接收的參數(shù)必須為對象,如果參數(shù)為基本類型數(shù)據(jù),必須轉(zhuǎn)換為相應(yīng)的包裝類型的對象。invoke()方法的返回值總是對象,如果實(shí)際被調(diào)用的方法的返回類型是基本類型數(shù)據(jù),那么invoke()方法會(huì)把它轉(zhuǎn)換為相應(yīng)的包裝類型的對象,再將其返回。
?
在本例中,盡管InvokeTester 類的add()方法的兩個(gè)參數(shù)以及返回值都是int類型,調(diào)用add Method 對象的invoke()方法時(shí),只能傳遞Integer 類型的參數(shù),并且invoke()方法的返回類型也是Integer 類型,Integer 類是int 基本類型的包裝類:
Object result=addMethod.invoke(invokeTester, new Object[]{new Integer(100),new Integer(200)}); System.out.println((Integer)result); //result 為Integer類型四、動(dòng)態(tài)創(chuàng)建和訪問數(shù)組
java.lang.Array 類提供了動(dòng)態(tài)創(chuàng)建和訪問數(shù)組元素的各種靜態(tài)方法。
例程ArrayTester1 類的main()方法創(chuàng)建了一個(gè)長度為10 的字符串?dāng)?shù)組,接著把索引位置為5 的元素設(shè)為“hello”,然后再讀取索引位置為5 的元素的值
public class ArrayTester1 {public static void main(String args[]) throws Exception {Class<?> classType = Class.forName("java.lang.String");// 創(chuàng)建一個(gè)長度為10的字符串?dāng)?shù)組Object array = Array.newInstance(classType, 10);// 把索引位置為5的元素設(shè)為"hello"Array.set(array, 5, "hello");// 獲得索引位置為5的元素的值String s = (String) Array.get(array, 5);System.out.println(s);} }例程ArrayTester2 類的main()方法創(chuàng)建了一個(gè) 5 x 10 x 15 的整型數(shù)組,并把索引位置為[3][5][10] 的元素的值為設(shè)37。
public class ArrayTester2 {public static void main(String args[]) {int[] dims = new int[]{5, 10, 15};//創(chuàng)建一個(gè)具有指定的組件類型和維度的新數(shù)組。Object array = Array.newInstance(Integer.TYPE, dims);Object arrayObj = Array.get(array, 3);Class<?> cls = arrayObj.getClass().getComponentType();System.out.println(cls);arrayObj = Array.get(arrayObj, 5);Array.setInt(arrayObj, 10, 37);int arrayCast[][][] = (int[][][]) array;System.out.println(arrayCast[3][5][10]);} }深入認(rèn)識(shí)Class類
?
眾所周知Java有個(gè)Object類,是所有Java類的繼承根源,其內(nèi)聲明了數(shù)個(gè)應(yīng)該在所有Java類中被改寫的方法:hashCode()、equals()、clone()、toString()、getClass()等。其中g(shù)etClass()返回一個(gè)Class類的對象。
?
Class類十分特殊。它和一般classes一樣繼承自O(shè)bject,其實(shí)體用以表達(dá)Java程序運(yùn)行時(shí)的classes和interfaces,也用來表達(dá)enum、array、primitive Java types
(boolean, byte, char, short, int, long, float, double)以及關(guān)鍵詞void。當(dāng)一個(gè)class被加載,或當(dāng)加載器(class loader)的defineClass()被JVM調(diào)用,JVM 便自動(dòng)產(chǎn)生一個(gè)Class object。如果您想借由“修改Java標(biāo)準(zhǔn)庫源碼”來觀察Class object的實(shí)際生成時(shí)機(jī)(例如在Class的constructor內(nèi)添加一個(gè)println()),不能夠!因?yàn)镃lass并沒有public constructor
?
Class是Reflection起源。針對任何您想探勘的class,唯有先為它產(chǎn)生一個(gè)Class object,接下來才能經(jīng)由后者喚起為數(shù)十多個(gè)的Reflection APIs
?
Java允許我們從多種途徑為一個(gè)class生成對應(yīng)的Class對象。參看本人的《?深入研究java.long.Class類?》一文。
?
欲生成對象實(shí)體,在Reflection 動(dòng)態(tài)機(jī)制中有兩種作法,一個(gè)針對“無自變量ctor”,一個(gè)針對“帶參數(shù)ctor”。如果欲調(diào)用的是“帶參數(shù)ctor“就比較麻煩些,不再調(diào)用Class的newInstance(),而是調(diào)用Constructor 的newInstance()。首先準(zhǔn)備一個(gè)Class[]做為ctor的參數(shù)類型(本例指定
為一個(gè)double和一個(gè)int),然后以此為自變量調(diào)用getConstructor(),獲得一個(gè)專屬ctor。接下來再準(zhǔn)備一個(gè)Object[] 做為ctor實(shí)參值(本例指定3.14159和125),調(diào)用上述專屬ctor的newInstance()。
?
動(dòng)態(tài)生成“Class object 所對應(yīng)之class”的對象實(shí)體;無自變量。
?
這個(gè)動(dòng)作和上述調(diào)用“帶參數(shù)之ctor”相當(dāng)類似。首先準(zhǔn)備一個(gè)Class[]做為參數(shù)類型(本例指定其中一個(gè)是String,另一個(gè)是Hashtable),然后以此為自變量調(diào)用getMethod(),獲得特定的Method object。接下來準(zhǔn)備一個(gè)Object[]放置自變量,然后調(diào)用上述所得之特定Method object的invoke()。
為什么獲得Method object時(shí)不需指定回返類型?
?
因?yàn)閙ethod overloading機(jī)制要求signature必須唯一,而回返類型并非signature的一個(gè)成份。換句話說,只要指定了method名稱和參數(shù)列,就一定指出了一個(gè)獨(dú)一無二的method。
?
?
四、運(yùn)行時(shí)變更field內(nèi)容
?
與先前兩個(gè)動(dòng)作相比,“變更field內(nèi)容”輕松多了,因?yàn)樗恍枰獏?shù)和自變量。首先調(diào)用Class的getField()并指定field名稱。獲得特定的Field object之后便可直接調(diào)用Field的get()和set()。
public class RefFiled {public double x;public Double y;public static void main(String args[]) throws NoSuchFieldException, IllegalAccessException {Class c = RefFiled.class;Field xf = c.getField("x");Field yf = c.getField("y");RefFiled obj = new RefFiled();System.out.println("變更前x=" + xf.get(obj));//變更成員x值xf.set(obj, 1.1);System.out.println("變更后x=" + xf.get(obj));System.out.println("變更前y=" + yf.get(obj));//變更成員y值yf.set(obj, 2.1);System.out.println("變更后y=" + yf.get(obj));} }運(yùn)行結(jié)果:
變更前x=0.0
變更后x=1.1
變更前y=null
變更后y=2.1
?
Process finished with exit code 0
?
?
參考資料:
此例的部分文字解說和源碼來自浪曦論壇 http://bbs.langsin.com。
侯捷的《候捷談Java反射機(jī)制》[url]http://www.j2medev.com/Article/Class3/Class7/200604/1995.html[/url]
think in java
Java核心技術(shù)
?
聲明:本文是在參考了大量資料基礎(chǔ)上,摸索運(yùn)用,總結(jié)的基礎(chǔ)上完成的。
由于每次書寫間隔非常長,參考的資料不能一一寫出來,如有侵權(quán),本人將在第一時(shí)間刪除侵權(quán)的內(nèi)容。
特別說明:
?
Java的反射其實(shí)內(nèi)容遠(yuǎn)遠(yuǎn)不至這些,這里看到僅僅是冰山一腳,如果你想要更加深入學(xué)習(xí)和研究Java的反射機(jī)制,你可以參考《Java Reflection in Action》一書,網(wǎng)上有免費(fèi)下載的。
總結(jié)
以上是生活随笔為你收集整理的Java 反射机制深入研究的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 小机箱如何装进大尺寸显卡小机箱能装多大显
- 下一篇: 苹果 CEO 库克到访成都,晒 iPho