前言:
Java反射機制可以讓我們在編譯期(Compile Time)之外的運行期(Runtime)檢查類,接口,變量以及方法的信息。反射還可以讓我們在運行期實例化對象,調用方法,通過調用get/set方法獲取變量的值。new 創建的對象是靜態加載類,在編譯時刻就要加載所有可能要用到的類,而反射創建的對象是是是動態加載類,在運行時刻加載需要用的類。
class對象
在程序運行之期間,JAVA運行時系統始終爲所有的對象維護一個被稱爲運行時的類型標識。這個信息跟蹤着每個對象所屬的類。虛擬機利用運行時類型信息選擇相應的方法執行,保存這些信息的類被稱爲Class.簡單的說就是,所有類其實都是java.lang.Class的實例,這個對象被稱爲類的類類型。
在你想檢查一個類的信息之前,你首先需要獲取類的Class對象。
獲取類的Class的對象有三種方式:
第一種表達方式:
Class c1=類名.class;//這也可以說明,任何一個類都有一個隱含的靜態成員變量class
第二種表達方式:
已經知道該類的對象通過getClass方法
Person person=new Person();
Class c2=person.getClass();
第三種表達方式:
Class c3=null;
c3=Class.forName("類的全路徑名");
具體代碼如下:
package reflection;
class Demo1{
//。。。。。
}
class Demo2{
public static void main(String[] args) {
Class<?> demo1=null;
Class<?> demo2=null;
Class<?> demo3=null;
try{
//一般儘量採用這種形式
demo1=Class.forName("reflection.Demo1");
}catch(Exception e){
e.printStackTrace();
}
demo2=new Demo1().getClass();
demo3=Demo1.class;
System.out.println(demo1);
System.out.println(demo2);
System.out.println(demo3);
運行結果:
class reflection.Demo1
class reflection.Demo1
class reflection.Demo1
class reflection.Demo1
class reflection.Demo1
利用反射分析類的能力
在java.lang.reflect包中有三個類Field,Methof和Constructor分別用於描述類的域、方法和構造器。這三個類都有一個叫做getName的對象,用來返回項目的名稱。Field類有一個getType方法,用來返回描述所屬類型的Class。Method和Constructor類有能夠報告參數類型的方法,Method類還有一個可以報告返回類型的方法。
Class類中的getFields,getMethods和getConstructors方法將分別返回類提供的public域、方法和構造器數組,其中包括超類的共有成員。Class類的getDeclareFields、getDeclareMethods和getDeclareConstructors方法將分別返回類中聲明的全部域、方法和構造器,其中包括私有和保護成員,但不包括超類中的成員。
獲取構造函數:
利用Java的反射機制你可以檢查一個類的構造方法,並且可以在運行期創建一個對象。這些功能都是通過java.lang.reflect.Constructor這個類實現的。若想通過類的的構造方法來生成對象,有兩種方式:
1.想獲得Class 對象,然後通過該Class對象的newInstance()方法直接生成即可:Class<?> classType=String.class;Object obj=classType.newInstance();
2.先獲得Class對象,然後通過該對象獲得對應的Constructor對象,在通過該Constructor對象的newInstance()方法生成:
-
.
Constructor getConstructor(Class[] params)
公共構造函數 -
. Constructor[] getConstructors()
-- 獲得類的所有公共構造函數 -
. Constructor getDeclaredConstructor(Class[] params)
-- 本類聲明的構造函數 -
. Constructor[] getDeclaredConstructors()
-- 獲得類的所有構造函數 - 其中區別在於第一種不能解決構造方法傳遞參數
示例代碼:
Class c=obj.getClass();
//獲取所有的public的構造函數
Constructor[] cs=c.getConstructors();
//得到自己生命的構造函數
Constructor[] cs1=c.getDeclaredConstructors();
for(Constructor constructor:cs)
{
System.out.print(constructor.getName()+"(");
//獲取構造函數的參數列表--得到 是參數列表的類類型
Class [] paramTypes=constructor.getParameterTypes();
for(Class class1:paramTypes)
{
System.out.print(class1.getName()+",");
}
System.out.println(")");
}
獲取字段的信息:
-
Field getField(String name)
-- 獲得命名的公共字段 -
Field[] getFields()
-- 獲得類的所有公共字段 -
Field getDeclaredField(String name)
-- 獲得類聲明的命名的字段 -
Field[] getDeclaredFields()
-- 獲得類聲明的所有字段 - 示例代碼:
Field[] fs=c.getFields();
Field[] fs2=c.getDeclaredFields();
for(Field field:fs)
{
//得到成員變量的類型的類類型
Class fieldType=field.getType();
String typeName=fieldType.getName();
//得到成員變量的名稱
String fieldName=field.getName();
System.out.println(typeName+" "+fieldName);
獲取方法信息
獲取方法信息的class反射調用和用於構造函數和字段的調用非常類似
-
Method getMethod(String name, Class<?>...parameterTypes)
-- 獲得命名的公共方法 -
Method[] getMethods()
-- 獲得類的所有公共方法 -
Method getDeclaredMethod(String name, Class<?>... parameterTypes)
-- 獲得類聲明的命名的方法 -
Method[] getDeclaredMethods()
-- 獲得類聲明的所有方法 - 示例代碼:
-
public static void print(Object obj) { Class c=obj.getClass(); System.out.println("類名是"+c.getName()); Method[] ms =c.getMethods();//獲取的是所有的public函數,包括父類繼承而來的 //Method[] ms1=c.getDeclaredMethods();//獲取的是所有該類自己聲明的方法 for(int i=0;i<ms.length;i++) { //得到方法的返回值類型的類類型 Class returnType= ms[i].getReturnType(); System.out.print(returnType.getName()+" "); //得到方法的名稱 System.out.print(ms[i].getName()+"("); //獲取參數類型--得到是參數列表的類型的類類型 Class[] paramTypes=ms[i].getParameterTypes(); for(Class class1:paramTypes) { System.out.print(class1.getName()+","); } System.out.println(")"); } }
總結:
反射是一種強大的工具。它使您能夠創建靈活的代碼,這些代碼可以在運行時裝配,無需在組件之間進行源代表鏈接。反射有兩個缺點。第一個是性能問題。當用於字段和方法接入時反射要遠慢於直接代碼。性能問題的程度取決於程序中是如何使用反射的。如果它作爲程序運行中相對很少涉及的部分,緩慢的性能將不會是一個問題。僅反射在性能關鍵的應用的核心邏輯中使用時性能問題才變得至關重要。
許多應用更嚴重的一個缺點是使用反射會模糊程序內部實際要發生的事情。程序人員希望在源代碼中看到程序的邏輯,反射等繞過了源代碼的技術會帶來維護問題。反射代碼比相應的直接代碼更復雜,正如性能比較的代碼實例中看到的一樣。解決這些問題的最
佳方案是保守地使用反射-- 僅在它可以真正增加靈活性的地方 。
參考文獻:1,《java編程思想》
2,http://www.ibm.com/developerworks/cn/java/j-dyn0603/