前言,在Java運行時(shí)刻,能否知道一個(gè)類(lèi)的屬性方法并調用改動(dòng)之?對于任意一個(gè)對象,能否知道他的所屬類(lèi),并調用他的方法?答案是肯定的。這種動(dòng)態(tài)的獲取信息及動(dòng)態(tài)調用方法的機制在Java中稱(chēng)為“反射”(reflection)。
Java反射機制主要提供以下功能:
在運行時(shí)判斷任意一個(gè)對象所屬的類(lèi);
在運行時(shí)構造任意一個(gè)類(lèi)的對象;
在運行時(shí)判斷任意一個(gè)類(lèi)所具有的成員變量和方法;
在運行時(shí)調用任意一個(gè)對象的方法。
Reflection 是Java被視為動(dòng)態(tài)(或準動(dòng)態(tài))語(yǔ)言的一個(gè)關(guān)鍵性質(zhì)。這個(gè)機制允許程序在運行時(shí)透過(guò)Reflection APIs取得任何一個(gè)已知名稱(chēng)的class的內部信息,包括其modifiers(諸如public, static 等等)、superclass(例如Object)、實(shí)現之interfaces(例如Serializable),也包括fields和methods的所有信息,并可于運行時(shí)改變fields內容或調用methods。
一般而言,開(kāi)發(fā)者社群說(shuō)到動(dòng)態(tài)語(yǔ)言,大致認同的一個(gè)定義是:“程序運行時(shí),允許改變程序結構或變量類(lèi)型,這種語(yǔ)言稱(chēng)為動(dòng)態(tài)語(yǔ)言”。
在JDK中,主要由以下類(lèi)來(lái)實(shí)現Java反射機制,這些類(lèi)都位于java.lang.reflect包中:
Class類(lèi):代表一個(gè)類(lèi);
Field 類(lèi):代表類(lèi)的成員變量(成員變量也稱(chēng)為類(lèi)的屬性);
Method類(lèi):代表類(lèi)的方法;
Constructor 類(lèi):代表類(lèi)的構造方法;
Array類(lèi):提供了動(dòng)態(tài)創(chuàng )建數組,以及訪(fǎng)問(wèn)數組的元素的靜態(tài)方法;
例程DateMethodsTest類(lèi)演示了Reflection API的基本作用,它讀取命令行參數指定的類(lèi)名,然后打印這個(gè)類(lèi)所具有的方法信息,代碼如下:
public class DateMethodsTest{public static void main(String args[]) throws Exception{// 加載并初始化命令行參數指定的類(lèi)Class<?> classType = Class.forName("java.util.Date");// 獲得類(lèi)的所有方法Method methods[] = classType.getDeclaredMethods();for (int i = 0; i < methods.length; i++){System.out.println(methods[i].toString());}}}
例程ReflectTester類(lèi)進(jìn)一步演示了Reflection API的基本使用方法。ReflectTester類(lèi)有一個(gè)copy(Object object)方法,這個(gè)方法能夠創(chuàng )建一個(gè)和參數object同樣類(lèi)型的對象,然后把object對象中的所有屬性拷貝到新建的對象中,并將它返回這個(gè)例子只能復制簡(jiǎn)單的JavaBean,假定JavaBean 的每個(gè)屬性都有public 類(lèi)型的getXXX()和setXXX()方法,代碼如下:
public class ReflectTester {public Object copy(Object object) throws Exception {// 獲得對象的類(lèi)型Class<?> classType = object.getClass();System.out.println("Class:" + classType.getName());// 通過(guò)默認構造方法創(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();// 獲得和屬性對應的getXXX()方法的名字String getMethodName = "get" + firstLetter + fieldName.substring(1);// 獲得和屬性對應的setXXX()方法的名字String setMethodName = "set" + firstLetter + fieldName.substring(1);// 獲得和屬性對應的getXXX()方法Method getMethod = classType.getMethod(getMethodName, new Class[] {});// 獲得和屬性對應的setXXX()方法Method setMethod = classType.getMethod(setMethodName, new Class[] { field.getType() });// 調用原對象的getXXX()方法Object value = getMethod.invoke(object, new Object[] {});System.out.println(fieldName + ":" + value);// 調用拷貝對象的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;}}
ReflectTester 類(lèi)的copy(Object object)方法依次執行以下步驟:
(1)獲得對象的類(lèi)型:
Class classType=object.getClass();
System.out.println("Class:"+classType.getName());
在java.lang.Object 類(lèi)中定義了getClass()方法,因此對于任意一個(gè)Java對象,都可以通過(guò)此方法獲得對象的類(lèi)型。Class類(lèi)是Reflection API 中的核心類(lèi),它有以下方法:
getName():獲得類(lèi)的完整名字;
getFields():獲得類(lèi)的public類(lèi)型的屬性;
getDeclaredFields():獲得類(lèi)的所有屬性;
getMethods():獲得類(lèi)的public類(lèi)型的方法;
getDeclaredMethods():獲得類(lèi)的所有方法;
getMethod(String name, Class[] parameterTypes):獲得類(lèi)的特定方法,name參數指定方法的名字,parameterTypes 參數指定方法的參數類(lèi)型;
getConstructors():獲得類(lèi)的public類(lèi)型的構造方法;
getConstructor(Class[] parameterTypes):獲得類(lèi)的特定構造方法,parameterTypes 參數指定構造方法的參數類(lèi)型;
newInstance():通過(guò)類(lèi)的不帶參數的構造方法創(chuàng )建這個(gè)類(lèi)的一個(gè)對象;
(2)通過(guò)默認構造方法創(chuàng )建一個(gè)新對象:
Object objectCopy=classType.getConstructor(new Class[]{}).newInstance(new Object[]{});
以上代碼先調用Class類(lèi)的getConstructor()方法獲得一個(gè)Constructor 對象,它代表默認的構造方法,然后調用Constructor對象的newInstance()方法構造一個(gè)實(shí)例。
(3)獲得對象的所有屬性:
Field fields[]=classType.getDeclaredFields();
Class 類(lèi)的getDeclaredFields()方法返回類(lèi)的所有屬性,包括public、protected、默認和private訪(fǎng)問(wèn)級別的屬性。
(4)獲得每個(gè)屬性相應的getXXX()和setXXX()方法,然后執行這些方法,把原來(lái)對象的屬性拷貝到新的對象中。
以上是一個(gè)反射(reflection)的比較詳細的解說(shuō),當然真正工程上是不會(huì )這樣麻煩的,這里是底層的講解。
以下這個(gè)類(lèi)運用反射機制調用其add()和echo()方法,代碼如下:
import java.lang.reflect.Method;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();// 調用InvokeTester對象的add()方法Method addMethod = classType.getMethod("add", new Class[] { int.class,int.class });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 });result = echoMethod.invoke(invokeTester, new Object[] { "Hello" });System.out.println((String) result);}}
add()方法的兩個(gè)參數為int 類(lèi)型,獲得表示add()方法的Method對象的代碼如下:
Method addMethod=classType.getMethod("add",new Class[]{int.class,int.class});
Method類(lèi)的invoke(Object obj,Object args[])方法接收的參數必須為對象,如果參數為基本類(lèi)型數據,必須轉換為相應的包裝類(lèi)型的對象。invoke()方法的返回值總是對象,如果實(shí)際被調用的方法的返回類(lèi)型是基本類(lèi)型數據,那么invoke()方法會(huì )把它轉換為相應的包裝類(lèi)型的對象,再將其返回。
在本例中,盡管InvokeTester 類(lèi)的add()方法的兩個(gè)參數以及返回值都是int類(lèi)型,調用add Method 對象的invoke()方法時(shí),只能傳遞Integer 類(lèi)型的參數,并且invoke()方法的返回類(lèi)型也是Integer 類(lèi)型,Integer 類(lèi)是int 基本類(lèi)型的包裝類(lèi):
Object result=addMethod.invoke(invokeTester,
new Object[]{new Integer(100),new Integer(200)});
System.out.println((Integer)result); //result 為Integer類(lèi)型。
java.lang.Array 類(lèi)提供了動(dòng)態(tài)創(chuàng )建和訪(fǎng)問(wèn)數組元素的各種靜態(tài)方法。下面的例子ArrayTester1.java的main()方法創(chuàng )建了一個(gè)長(cháng)度為10 的字符串數組,接著(zhù)把索引位置為5 的元素設為“hello”,然后再讀取索引位置為5 的元素的值,代碼如下:
import java.lang.reflect.Array;public class ArrayTester1{public static void main(String args[]) throws Exception{Class<?> classType = Class.forName("java.lang.String");// 創(chuàng )建一個(gè)長(cháng)度為10的字符串數組Object array = Array.newInstance(classType, 10);// 把索引位置為5的元素設為"hello"Array.set(array, 5, "hello");// 獲得索引位置為5的元素的值String s = (String) Array.get(array, 5);System.out.println(s);}}
Java API是個(gè)很好的幫助資料,大家可以查閱觀(guān)看。比如說(shuō)以上的例子:classType就是String類(lèi)型的一個(gè)對象。通過(guò)Array類(lèi)的newInstance(類(lèi)型, 長(cháng)度)創(chuàng )建了一個(gè)長(cháng)度為10的,類(lèi)型為String的Array數組。通過(guò)Array的get和set方法可以將字符串“hello”插進(jìn)下標為5(實(shí)際上是Array中第六個(gè)元素)的“數組方塊”中。
下面例子是一個(gè)復雜的多維數組例子,不過(guò)只要了解了一維數組,應該不難了解多維數組的。所謂多維數組只是下級數組作為一個(gè)“元素”存在于上級數組中。
例程ArrayTester2 類(lèi)的main()方法創(chuàng )建了一個(gè) 5 x 10 x 15 的整型數組,并把索引位置為[3][5][10] 的元素的值為設37,代碼如下:
import java.lang.reflect.Array;public class ArrayTester2{public static void main(String args[]){int[] dims = new int[] { 5, 10, 15 };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]);}} Class是Reflection起源。針對任何您想探勘的class,唯有先為它產(chǎn)生一個(gè)Class object,接下來(lái)才能經(jīng)由后者喚起為數十多個(gè)的Reflection APIs。
Java允許我們從多種途徑為一個(gè)class生成對應的Class object。
(一)運用getClass()方法:
String str = "abc";
Class class = str.getClass();
(二)運用Class.getSuperclass()方法:
Button b = new Button();
Class c1 = b.getSuperclass();
Class c2 = c1.getSuperclass();
(三)運用靜態(tài)方法Class.forName(),這個(gè)最常用:
Class c1 = Class.forName("java.lang.String");
Class c2 = Class.forName("java.util.Date");
(四)運用.class語(yǔ)法:
Class c1 = String.class;
Class c2 = java.awt.Button.class;
Class c3 = int.class;
Class C4 = int[].class;
(五)運用原始包裝類(lèi)的TYPE方法:
Class c1 = Integer.TYPE;
Class c2 = Character.TYPE;
Class c3 = Boolean.TYPE;
Class c4 = Void.TYPE;
通過(guò)這五種方法,可以生成我們想要的類(lèi)對象。
聯(lián)系客服