Java基礎---反射(Reflection)

一:定義

能夠分析類能力的程序稱爲反射。

二:分析:

Java 反射是Java語言的一個很重要的特徵,它使得Java具體了“動態性”。 在Java運行時環境中,對於任意一個類,能否知道這個類有哪些屬性和方法?對於任意一個對象,能否調用它的任意一個方法?答案是肯定的。這種動態獲取類的信息以及動態調用對象的方法的功能來自於Java 語言的反射(Reflection)機制

三:反射機制的功能:

1.在運行中分析類的能力;

2.在運行中查看對象;

3.實現通用的數組操作代碼;

4.利用Method對象,這個對象很像C++中的函數指針。----From CoreJava

或者:

1.在運行時判斷任意一個對象所屬的類。
2.在運行時構造任意一個類的對象。
3.在運行時判斷任意一個類所具有的成員變量和方法。
4.在運行時調用任意一個對象的方法。---From Web

四:Class類

在JDK中,主要由以下類來實現Java反射機制,這些類都位於java.lang.reflect包中:

Class類:代表一個類。
Field 類:代表類的成員變量(成員變量也稱爲類的屬性)。
Method類:代表類的方法。
Constructor 類:代表類的構造方法。
Array類:提供了動態創建數組,以及訪問數組的元素的靜態方法。

五:實例

一、通過Class類獲取成員變量、成員方法、接口、超類、構造方法等
在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():通過類的不帶參數的構造方法創建這個類的一個對象。
 
下面給出一個綜合運用的例子:
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、通過參數獲取指定Class對象的構造方法:");
        System.out.println(cst1.toString());
        Constructor cst2 = c.getDeclaredConstructor(cs);
        System.out.println("2、通過參數獲取指定Class對象所表示的類或接口的構造方法:");
        System.out.println(cst2.toString());
        Constructor cst3 = c.getEnclosingConstructor();
        System.out.println("3、獲取本地或匿名類Constructor 對象,它表示基礎類的立即封閉構造方法。");
        if (cst3 != null) System.out.println(cst3.toString());
        else System.out.println("-- 沒有獲取到任何構造方法!");
        Constructor[] csts = c.getConstructors();
        System.out.println("4、獲取指定Class對象的所有構造方法:");
        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、返回直接實現的接口:");
        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、返回超類和所有實現的接口:");
        for (int i = 0; i < cis.length; i++) {
            System.out.println(cis[i].toString());
        }
        Class cs1[] = c.getInterfaces();
        System.out.println("4、實現的接口");
        for (int i = 0; i < cs1.length; i++) {
            System.out.println(cs1[i].toString());
        }
        System.out.println("\n-------------------------------\n");
        java.lang.reflect.Field[] fs1 = c.getFields();
        System.out.println("1、類或接口的所有可訪問公共字段:");
        for (int i = 0; i < fs1.length; i++) {
            System.out.println(fs1[i].toString());
        }
        java.lang.reflect.Field f1 = c.getField("MIN_VALUE");
        System.out.println("2、類或接口的指定已聲明指定公共成員字段:");
        System.out.println(f1.toString());
        java.lang.reflect.Field[] fs2 = c.getDeclaredFields();
        System.out.println("3、類或接口所聲明的所有字段:");
        for (int i = 0; i < fs2.length; i++) {
            System.out.println(fs2[i].toString());
        }
        java.lang.reflect.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());
    }
}

輸出結果:
-------------------------------

1、通過參數獲取指定Class對象的構造方法:
public java.lang.Long(java.lang.String) throws java.lang.NumberFormatException
2、通過參數獲取指定Class對象所表示的類或接口的構造方法:
public java.lang.Long(java.lang.String) throws java.lang.NumberFormatException
3、獲取本地或匿名類Constructor 對象,它表示基礎類的立即封閉構造方法。
-- 沒有獲取到任何構造方法!
4、獲取指定Class對象的所有構造方法:
public java.lang.Long(long)
public java.lang.Long(java.lang.String) throws java.lang.NumberFormatException
-------------------------------
1、返回直接實現的接口:
java.lang.Comparable<java.lang.Long>
2、返回直接超類:
class java.lang.Number
3、返回超類和所有實現的接口:
4、實現的接口
interface java.lang.Comparable
-------------------------------
1、類或接口的所有可訪問公共字段:
public static final long java.lang.Long.MIN_VALUE
public static final long java.lang.Long.MAX_VALUE
public static final java.lang.Class java.lang.Long.TYPE
public static final int java.lang.Long.SIZE
2、類或接口的指定已聲明指定公共成員字段:
public static final long java.lang.Long.MIN_VALUE
3、類或接口所聲明的所有字段:
public static final long java.lang.Long.MIN_VALUE
public static final long java.lang.Long.MAX_VALUE
public static final java.lang.Class java.lang.Long.TYPE
private final long java.lang.Long.value
public static final int java.lang.Long.SIZE
private static final long java.lang.Long.serialVersionUID
4、類或接口的指定已聲明指定字段:
private static final long java.lang.Long.serialVersionUID
-------------------------------
1、返回類所有的公共成員方法:
public static int java.lang.Long.numberOfLeadingZeros(long)
public static int java.lang.Long.numberOfTrailingZeros(long)
public static int java.lang.Long.bitCount(long)
public boolean java.lang.Long.equals(java.lang.Object)
public java.lang.String java.lang.Long.toString()
public static java.lang.String java.lang.Long.toString(long,int)
public static java.lang.String java.lang.Long.toString(long)
public int java.lang.Long.hashCode()
public static long java.lang.Long.reverseBytes(long)
public int java.lang.Long.compareTo(java.lang.Long)
public int java.lang.Long.compareTo(java.lang.Object)
public static java.lang.Long java.lang.Long.getLong(java.lang.String)
public static java.lang.Long java.lang.Long.getLong(java.lang.String,java.lang.Long)
public static java.lang.Long java.lang.Long.getLong(java.lang.String,long)
public byte java.lang.Long.byteValue()
public short java.lang.Long.shortValue()
public int java.lang.Long.intValue()
public long java.lang.Long.longValue()
public float java.lang.Long.floatValue()
public double java.lang.Long.doubleValue()
public static java.lang.Long java.lang.Long.valueOf(long)
public static java.lang.Long java.lang.Long.valueOf(java.lang.String,int) throws java.lang.NumberFormatException
public static java.lang.Long java.lang.Long.valueOf(java.lang.String) throws java.lang.NumberFormatException
public static java.lang.String java.lang.Long.toHexString(long)
public static int java.lang.Long.compare(long,long)
public static java.lang.Long java.lang.Long.decode(java.lang.String) throws java.lang.NumberFormatException
public static long java.lang.Long.reverse(long)
public static long java.lang.Long.highestOneBit(long)
public static long java.lang.Long.lowestOneBit(long)
public static long java.lang.Long.rotateLeft(long,int)
public static long java.lang.Long.rotateRight(long,int)
public static int java.lang.Long.signum(long)
public static java.lang.String java.lang.Long.toBinaryString(long)
public static java.lang.String java.lang.Long.toOctalString(long)
public static long java.lang.Long.parseLong(java.lang.String) throws java.lang.NumberFormatException
public static long java.lang.Long.parseLong(java.lang.String,int) throws java.lang.NumberFormatException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
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 native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
2、返回指定公共成員方法:
public long java.lang.Long.longValue()
 
二、運行時複製對象
 
例子:ReflectTester 類進一步演示了Reflection API的基本使用方法。ReflectTester類有一個copy(Object object)方法,這個方法能夠創建一個和參數object 同樣類型的對象,然後把object對象中的所有屬性拷貝到新建的對象中,並將它返回
這個例子只能複製簡單的JavaBean,假定JavaBean 的每個屬性都有public 類型的getXXX()和setXXX()方法。
 
<span style="color:#555555;">public class ReflectTester {
    public Object copy(Object object) throws Exception {
        // 獲得對象的類型
        Class<?> classType = object.getClass();
        System.out.println("Class:" + classType.getName());
        // 通過默認構造方法創建一個新的對象
        </span><span style="color:#ff0000;">Object objectCopy = classType.getConstructor(new Class[]{}).newInstance(new Object[]{});</span><span style="color:#555555;">
        // 獲得對象的所有屬性
        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;
    }
}</span>
輸出結果:
 
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)方法依次執行以下步驟
(1)獲得對象的類型:
Class classType=object.getClass();
System.out.println("Class:"+classType.getName());
(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()方法,然後執行這些方法,把原來對象的屬性拷貝到新的對象中
三、用反射機制調用對象的方法
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});
        //調用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});
        //調用invokeTester對象的echo()方法
        result = echoMethod.invoke(invokeTester, new Object[]{"Hello"});
        System.out.println((String) result);
    }
}

在例程InvokeTester類的main()方法中,運用反射機制調用一個InvokeTester對象的add()和echo()方法
add()方法的兩個參數爲int 類型,獲得表示add()方法的Method對象的代碼如下:
Method addMethod=classType.getMethod("add",new Class[]{int.class,int.class});
Method類的invoke(Object obj,Object args[])方法接收的參數必須爲對象,如果參數爲基本類型數據,必須轉換爲相應的包裝類型的對象。invoke()方法的返回值總是對象,如果實際被調用的方法的返回類型是基本類型數據,那麼invoke()方法會把它轉換爲相應的包裝類型的對象,再將其返回。
 
在本例中,儘管InvokeTester 類的add()方法的兩個參數以及返回值都是int類型,調用add Method 對象的invoke()方法時,只能傳遞Integer 類型的參數,並且invoke()方法的返回類型也是Integer 類型,Integer 類是int 基本類型的包裝類:
Object result=addMethod.invoke(invokeTester,
new Object[]{new Integer(100),new Integer(200)});
System.out.println((Integer)result); //result 爲Integer類型
四、動態創建和訪問數組
java.lang.Array 類提供了動態創建和訪問數組元素的各種靜態方法。
例程ArrayTester1 類的main()方法創建了一個長度爲10 的字符串數組,接着把索引位置爲5 的元素設爲“hello”,然後再讀取索引位置爲5 的元素的值
public class ArrayTester1 {
    public static void main(String args[]) throws Exception {
        Class<?> classType = Class.forName("java.lang.String");
        // 創建一個長度爲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);
    }
}
例程 ArrayTester2 類的main()方法創建了一個 5 x 10 x 15 的整型數組,並把索引位置爲[3][5][10] 的元素的值爲設37。
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類
衆所周知Java有個Object類,是所有Java類的繼承根源,其內聲明瞭數個應該在所有Java類中被改寫的方法:hashCode()、equals()、clone()、toString()、getClass()等。其中getClass()返回一個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
Java允許我們從多種途徑爲一個class生成對應的Class對象。參看的《 深入研究java.long.Class類 》一文。
欲生成對象實體,在Reflection 動態機制中有兩種作法,一個針對“無自變量ctor”,一個針對“帶參數ctor”。如果欲調用的是“帶參數ctor“就比較麻煩些,不再調用Class的newInstance(),而是調用Constructor 的newInstance()。首先準備一個Class[]做爲ctor的參數類型(本例指定
爲一個double和一個int),然後以此爲自變量調用getConstructor(),獲得一個專屬ctor。接下來再準備一個Object[] 做爲ctor實參值(本例指定3.14159和125),調用上述專屬ctor的newInstance()。
動態生成“Class object 所對應之class”的對象實體;無自變量。
這個動作和上述調用“帶參數之ctor”相當類似。首先準備一個Class[]做爲參數類型(本例指定其中一個是String,另一個是Hashtable),然後以此爲自變量調用getMethod(),獲得特定的Method object。接下來準備一個Object[]放置自變量,然後調用上述所得之特定Method object的invoke()。
爲什麼獲得Method object時不需指定回返類型?
因爲method overloading機制要求signature必須唯一,而回返類型並非signature的一個成份。換句話說,只要指定了method名稱和參數列,就一定指出了一個獨一無二的method。
四、運行時變更field內容
與先前兩個動作相比,“變更field內容”輕鬆多了,因爲它不需要參數和自變量。首先調用Class的getField()並指定field名稱。獲得特定的Field object之後便可直接調用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));
    }
}


運行結果:
變更前x=0.0
變更後x=1.1
變更前y=null
變更後y=2.1
參考資料:
此例的部分文字解說和源碼來自浪曦論壇 http://bbs.langsin.com。
Think in Java
Core Java

http://lavasoft.blog.51cto.com/62575/43218/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章