Java中的反射機制(一)

基本概念

  在Java運行時環境中,對於任意一個類,可否知道這個類有哪些屬性和方法?對於任意一個對象,可否調用它的任意一個方法?html

  答案是確定的。java

  這種動態獲取類的信息以及動態調用對象的方法的功能來自於Java語言的反射(Reflection)機制api

  

  Java反射機制主要提供瞭如下功能:數組

  1.在運行時判斷任意一個對象所屬的類。oracle

  2.在運行時構造任意一個類的對象。spa

  3.在運行時判斷任意一個類所具備的成員變量和方法。命令行

  4.在運行時調用任意一個對象的方法。code

 

  Reflection是Java被視爲動態(或準動態)語言的一個關鍵性質。視頻

  這個機制容許程序在運行時透過Reflection APIs取得任何一個已知名稱的class的內部信息。htm

  包括其modifiers(諸如public、static等)、 superclass(例如Object)、實現了的 interfaces (例如Serializable)、也包括其fields和methods的全部信息,並可於運行時改變fields內容或調用methods。

 

動態語言

  動態語言的定義「程序運行時,容許改變程序結構或者變量類型,這種語言稱爲動態語言」。

  從這個觀點看,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(內省、內觀、檢討)。Reflectionintrospection是常被並提的兩個術語。

 

Java Reflection API簡介

  在JDK中,主要由如下類來實現Java反射機制,這些類(除了第一個)都位於java.lang.reflect包中

  Class類:表明一個類,位於java.lang包下。

  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 = "aa";
Class<?> classType = str.getClass();

 

  getClass()方法定義在Object類中,不是靜態方法,須要經過對象來調用,而且它聲明爲final,代表不能被子類所覆寫。直接print所得到的Class對象的classType會輸出:class 完整類名

  若是調用classType.getName()方法會輸出:完整類名    不加class。

 

例程1:獲取方法

  例程DumpMethods類演示了Reflection API的基本做用,它讀取命令行參數指定的類名,而後打印這個類所具備的方法信息。 

public Method[] getDeclaredMethods()返回Method對象的一個數組,這些對象反映此Class對象表示的類或接口聲明的全部方法,包括公共、保護、默認(包)訪問和私有方法,但不包括繼承的方法。

public Method[] getMethods()返回類的全部public方法,包括其繼承類的公有方法,固然也包括它所實現接口的方法。

import java.lang.reflect.Method;

public class DumpMethods
{
    public static void main(String[] args) throws Exception //在方法後加上這句,異常就消失了
    {
        //得到字符串所標識的類的class對象
        Class<?> classType = Class.forName("java.lang.String");//在此處傳入字符串指定類名,因此參數獲取能夠是一個運行期的行爲,能夠用args[0]
        
        //返回class對象所對應的類或接口中,所聲明的全部方法的數組(包括私有方法)
        Method[] methods = classType.getDeclaredMethods();
        
        //遍歷輸出全部方法聲明
        for(Method method : methods)
        {
            System.out.println(method);
        }
    }

}

 

例程2:經過反射調用方法

  經過反射調用方法。詳情見代碼及註釋:

 

import java.lang.reflect.Method;

public class InvokeTester
{
    public int add(int param1, int param2)
    {
        return param1 + param2;

    }

    public String echo(String message)
    {
        return "Hello: " + message;
    }

    public static void main(String[] args) throws Exception
    {

        // 之前的常規執行手段
        InvokeTester tester = new InvokeTester();
        System.out.println(tester.add(1, 2));
        System.out.println(tester.echo("Tom"));
        System.out.println("---------------------------");

        // 經過反射的方式

        // 第一步,獲取Class對象
        // 前面用的方法是:Class.forName()方法獲取
        // 這裏用第二種方法,類名.class
        Class<?> classType = InvokeTester.class;

        // 生成新的對象:用newInstance()方法
        Object invokeTester = classType.newInstance();
        System.out.println(invokeTester instanceof InvokeTester); // 輸出true

        // 經過反射調用方法
        // 首先須要得到與該方法對應的Method對象
        Method addMethod = classType.getMethod("add", new Class[] { int.class,
                int.class });
        // 第一個參數是方法名,第二個參數是這個方法所須要的參數的Class對象的數組

        // 調用目標方法
        Object result = addMethod.invoke(invokeTester, new Object[] { 1, 2 });
        System.out.println(result); // 此時result是Integer類型
        
        //調用第二個方法
        Method echoMethod = classType.getDeclaredMethod("echo", new Class[]{String.class});
        Object result2 = echoMethod.invoke(invokeTester, new Object[]{"Tom"});
        System.out.println(result2);

    }
}

 

生成對象

 

  若想經過類的不帶參數的構造方法來生成對象,咱們有兩種方式:

  1.先得到Class對象,而後經過該Class對象的newInstance()方法直接生成便可:

     Class<?> classType = String.class;

     Object obj = classType.newInstance();

 

  2.先得到Class對象,而後經過該對象得到對應的Constructor對象,再經過該Constructor對象的newInstance()方法生成

  (其中Customer是一個自定義的類,有一個無參數的構造方法,也有帶參數的構造方法):

    Class<?> classType = Customer.class;

    // 得到Constructor對象,此處獲取第一個無參數的構造方法的
    Constructor cons = classType.getConstructor(new Class[] {});

    // 經過構造方法來生成一個對象
    Object obj = cons.newInstance(new Object[] {});

 

  若想經過類的帶參數的構造方法生成對象,只能使用下面這一種方式:

  (Customer爲一個自定義的類,有無參數的構造方法,也有一個帶參數的構造方法,傳入字符串和整型)

    Class<?> classType = Customer.class;

    Constructor cons2 = classType.getConstructor(new Class[] {String.class, int.class});

    Object obj2 = cons2.newInstance(new Object[] {"ZhangSan",20});

  能夠看出調用構造方法生成對象的方法和調用通常方法的相似,不一樣的是從Class對象獲取Constructor對象時不須要指定名字,而獲取Method對象時須要指定名字。

 

參考資料

  張龍老師Java SE教學視頻。

  文檔連接:

  Class:

  http://docs.oracle.com/javase/7/docs/api/java/lang/Class.html

  Field:

  http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/Field.html

  Method:

  http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/Method.html

  Constructor:

  http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/Constructor.html

  Array:

  http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/Array.html

原文地址:http://www.cnblogs.com/mengdd/archive/2013/01/26/2877972.html

相關文章
相關標籤/搜索