Java反射机制深入研究
生活随笔
收集整理的這篇文章主要介紹了
Java反射机制深入研究
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
ava 反射是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
轉(zhuǎn)載于:https://www.cnblogs.com/hanfeihanfei/p/5392317.html
總結(jié)
以上是生活随笔為你收集整理的Java反射机制深入研究的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 女人梦到鞋子坏了是什么意思
- 下一篇: No1_6.字符串的基本操作2_Java