Java語言的反射機制java
在Java運行時環境中,對於任意一個類,可否知道這個類有哪些屬性和方法?對於任意一個對象,可否調用它的任意一個方法?答案是確定的。這種動態獲取類的信息以及動態調用對象的方法的功能來自於Java語言的反射(Reflection)機制。數組
Java反射機制主要提供瞭如下功能this
Reflection是Java被視爲動態(或準動態)語言的一個關鍵性質。這個機制容許程序在運行時透過Reflection APIs取得任何一個已知名稱的class的內部信息。包括其modifiers(諸如public、static等)、 superclass(例如Object)、實現了的 interfaces (例如Serializable)、也包括其fields和methods的全部信息,並可於運行時改變fields內容或調用methods。spa
通常而言,開發者社羣說到動態語言,大體認同的一個定義是"程序運行時,容許改變程序結構或者變量類型,這種語言稱爲動態語言"。從這個觀點看,Perl,Python,Ruby是動態語言,C++,Java,C#不是動態語言。儘管在這樣的定義與分類下Java不是動態語言,它卻有着一個很是突出的動態相關機制:Reflection。這個字的意思是:反射、映像、倒影,用在Java身上指的是咱們能夠於運行時加載、探知、使用編譯期間徹底未知的classes。換句話說,Java程序能夠加載一個運行時才得知名稱的class,獲悉其完整構造(但不包括methods定義),並生成其對象實體、或對其fields設值、或喚起其methods。這種「看透」class的能力(the ability of the program to examine itself)被稱爲introspection(內省、內觀、檢討)。Reflection和introspection是常被並提的兩個術語。命令行
Java Reflection API簡介orm
在JDK中,主要由如下類來實現Java反射機制,這些類(除了Class類)都位於java.lang.reflect包中對象
Class類:表明一個類,位於java.lang包下。blog
Field類:表明類的成員變量(成員變量也稱爲類的屬性)。繼承
Method類:表明類的方法。開發
Constructor類:表明類的構造方法。
Array類:提供了動態建立數組,以及訪問數組的元素的靜態方法。
Class對象
要想使用反射,首先須要得到待操做的類所對應的Class對象。
Java中,不管生成某個類的多少個對象,這些對象都會對應於同一個Class對象。
這個Class對象是由JVM生成的,經過它可以獲悉整個類的結構。
經常使用的獲取Class對象的3種方式:
1.使用Class類的靜態方法。例如:
Class.forName("java.lang.String");
2.使用類的.class語法。如:
String.class;
3.使用對象的getClass()方法。如:
String str = "hello world";
Class<?> classType = str.getClass();
(getClass()方法定義在Object類中,不是靜態方法,須要經過對象來調用,而且它聲明爲final,代表不能被子類所覆寫。)
實例:
例程DumpMethods類演示了Reflection API的基本做用,它讀取命令行參數指定的類名,而後打印這個類所具備的方法信息。
package com.test.reflection; import java.lang.reflect.Method; public class DumpMethods { public static void main(String[] args) throws Exception { // 加載並初始化命令行參數指定的類(運行期的行爲) Class<?> classType = Class.forName(args[0]); // 得到類的全部方法 Method[] methods = classType.getMethods(); for(int i = 0; i < methods.length; i++) { System.out.println(methods[i]); } } }
運行時參數設爲java.lang.Object輸出以下:
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException public final void java.lang.Object.wait() throws java.lang.InterruptedException public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException public boolean java.lang.Object.equals(java.lang.Object) public java.lang.String java.lang.Object.toString() public native int java.lang.Object.hashCode() public final native java.lang.Class java.lang.Object.getClass() public final native void java.lang.Object.notify() public final native void java.lang.Object.notifyAll()
例程ReflectTester類進一步演示了Reflection API的基本使用方法。ReflectTester類有一個copy(Object object)方法,這個方法可以建立一個和參數object一樣類型的對象,而後把object對象中的全部屬性拷貝到新建的對象中,並將它返回。
package com.test.reflection; import java.lang.reflect.Field; import java.lang.reflect.Method; public class ReflectTester { public Object copy(Object object) throws Exception { // 得到對象的類型 Class classType = object.getClass(); System.out.println("Class:" + classType.getName()); // 經過默認構造方法建立一個新的對象 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; } } /* output: Class:com.test.reflection.Customer id:1 name:Tom age:21 Copy information:1 Tom 21 */
這個例子只能複製簡單的JavaBean,假定JavaBean的每一個屬性都有public類型的getXXX()和setXXX()方法。
ReflectTester類的copy(Object object)方法依次執行如下步驟
(1)得到對象的類型:
–Class classType=object.getClass();
–System.out.println("Class:"+classType.getName());
在java.lang.Object類中定義了getClass()方法,所以對於任意一個Java對象,均可以經過此方法得到對象的類型。Class類是Reflection API 中的核心類,它有如下方法
–getName():得到類的完整名字。
–getFields():得到類的public類型的屬性。
–getDeclaredFields():得到類的全部屬性。
–getMethods():得到類的public類型的方法。
–getDeclaredMethods():得到類的全部方法。
-getMethod(String name, Class[] parameterTypes):得到類的特定方法,name參數指定方法的名字,parameterTypes參數指定方法的參數類型。
-getConstructors():得到類的public類型的構造方法。
-getConstructor(Class[] parameterTypes):得到類的特定構造方法,parameterTypes參數指定構造方法的參數類型。
-newInstance():經過類的不帶參數的構造方法建立這個類的一個對象。
(2)經過默認構造方法建立一個新對象:
Object objectCopy=classType.getConstructor(new Class[]{}).newInstance(new Object[]{});
以上代碼先調用Class類的getConstructor()方法得到一個Constructor 對象,它表明默認的構造方法,而後調用Constructor對象的newInstance()方法構造一個實例。
(3)得到對象的全部屬性:
Field fields[]=classType.getDeclaredFields();
Class 類的getDeclaredFields()方法返回類的全部屬性,包括public、protected、默認和private訪問級別的屬性
(4)得到每一個屬性相應的getXXX()和setXXX()方法,而後執行這些方法,把原來對象的屬性拷貝到新的對象中
關於Class類:
衆所周知Java有個Object class,是全部Java classes的繼承根源,其內聲明瞭數個應該在全部Java class中被改寫的methods:hashCode()、equals()、clone()、toString()、getClass()等。其中getClass()返回一個Class object。Class class十分特殊。它和通常classes同樣繼承自Object,其實體用以表達Java程序運行時的classes和interfaces,也用來表達enum、array、primitive Java types(boolean, byte, char, short, int, long, float, double)以及關鍵詞void。當一個class被加載,或當加載器(class loader)的defineClass()被JVM調用,JVM 便自動產生一個Class object。若是您想借由「修改Java標準庫源碼」來觀察Class object的實際生成時機(例如在Class的constructor內添加一個println()),不可以!由於Class並無public constructor。Class是Reflection起源。針對任何您想探勘的class,惟有先爲它產生一個Class object,接下來才能經由後者喚起爲數十多個的Reflection APIs。
"Class" object 的獲取途徑
1.運用getClass()方法,返回Class對象。
運用Class.getSuperclass()能夠獲得父類的Class對象,若是是Object類則返回null。
2.運用靜態方法Class.forName()
3.運用.class語法。類名.class。
其中,還能夠經過int[].class的形式得到整形數組的Class對象。
包裝類的.TYPE語法實際返回的是所對應的原生數據類型的Class對象。
利用反射調用私有方法、訪問私有屬性
利用反射,首先是Class對象的獲取,以後是Method和Field對象的獲取。
以Method爲例,從文檔中能夠看到:getMethod()方法返回的是public的Method對象,而getDeclaredMethod()返回的Method對象能夠是非public的。
Field的方法同理。
訪問私有屬性和方法,在使用前要經過AccessibleObject類(Constructor、 Field和Method類的基類)中的setAccessible()方法來抑制Java訪問權限的檢查。
實例1,調用私有方法:假設有這樣一個類,其中包含私有方法。
public class PrivateClass { private String sayHello(String name) { return "Hello: " + name; } }
利用反射機制在外部訪問該方法:
import java.lang.reflect.Method; public class TestPrivate { public static void main(String[] args) throws Exception { PrivateClass p = new PrivateClass(); Class<?> classType = p.getClass(); // 獲取Method對象 Method method = classType.getDeclaredMethod("sayHello", new Class[] { String.class }); method.setAccessible(true); // 抑制Java的訪問控制檢查 // 若是不加上上面這句,將會Error: TestPrivate can not access a member of class PrivateClass with modifiers "private" String str = (String) method.invoke(p, new Object[] { "zhangsan" }); System.out.println(str); } }
實例2,訪問私有屬性,直接訪問私有屬性,將例子中的私有屬性改值。
一個包含私有屬性的類:
public class PrivateClass2 { private String name = "zhangsan"; public String getName() { return name; } }
利用反射修改其私有屬性的值:
import java.lang.reflect.Field; public class TestPrivate2 { public static void main(String[] args) throws Exception { PrivateClass2 p = new PrivateClass2(); Class<?> classType = p.getClass(); Field field = classType.getDeclaredField("name"); field.setAccessible(true); // 抑制Java對修飾符的檢查 field.set(p, "lisi"); System.out.println(p.getName()); } }