1. 程式人生 > >黑馬程式設計師-------Java高階特性--------反射

黑馬程式設計師-------Java高階特性--------反射

黑馬程式設計師—–Java高階特性—–反射

一.概述

Java 反射是Java語言的一個很重要的特徵,它使得Java具體了“動態性”。
這個機制允許程式在執行時透過Reflection APIs取得任何一個已知名稱的class的內部資訊,包括其modifiers(諸如public, static 等等)、superclass(例如Object)、實現之interfaces(例如Serializable),也包括fields和methods的所有資訊,並可於執行時改變fields內容或呼叫methods。
Java程式可以載入一個執行時才得知名稱的class,獲悉其完整構造(但不包括methods定義),並生成其物件實體、或對其fields設值、或喚起其methods。這種“看透class”的能力(the ability of the program to examine itself)被稱為introspection(內省、內觀、反省)。Reflection(反射)

introspection(內省)是常被並提的兩個術語。

二.Java 反射的API

反射中常常用的幾個類如下所示:
java.lang 包中:
–Class 類:代表一個類
Java 中,無論生成某個類的多少個物件,這些物件都會對應同一個 Class 物件
Java.lang.reflect 包中:
–Field 類:代表類的成員變數(類的屬性)
–Method 類:代表類的方法,一個方法對應一個 Method 物件
–Constructor 類:代表類的構造方法
–Array 類:提供動態建立陣列,以及訪問陣列的元素的靜態方法

三.使用JAVA反射綜合示例

1. Class物件的獲取及例項化

//1.獲取該類的 Class 物件
//常用方法有三種:
//a)通過 Class 的 forName 的靜態方法獲得(引數:包名 + 類名):
    Class<?> classType = Class.forName("java.lang.String");
//b)通過類名 + 點 class 的方法獲得:
     Class<?> c = String.class;
//c)通過類的物件的 getClass 的方法獲得:
    String str = "test";
    Class<?> typeClass = str.getClass();


//2.例項化一個該類的物件
//兩種方法: //a)通過 Class 物件的 newInstance 的方法建立(該方法只對無參構造方法適用): Class<?> classType = Class.forName("java.lang.String"); Object obj = = classType.newInstance(); //b)通過獲取 Constructor 構造方法類的物件,然後通過該物件的 newInstance (Object ... initargs) 方法獲得: Class<?> classType = cus.getClass(); Constructor con= classType.getConstructor(new Class[]{String.class,int.class}); Object object= con.newInstance(new Object[]{cus.getName(),cus.getAge()}); 該方法中的若寫成兩個引數寫成 Class[]{} 和 new Object[ ]{} ,就等同於 a) 的方法

2.訪問被private 修飾的方法和屬性

public class Private  
{  
    private String name = "張三";  

    private String getName()  
    {  
        return name;  
    }  
}  

把該類中private修飾的 name 值改成“李四”,並且用反射機制呼叫 getName 方法,返回修改後的值。

package com.wangzhuo.reflect;  

import java.lang.reflect.Constructor;  
import java.lang.reflect.Field;  
import java.lang.reflect.Method;  

public class PrivateTest  
{  
    public static void main(String[] args)throws Exception  
    {  
        //獲取Private類的Class物件  
        Class<?> classType = Class.forName("com.wangzhuo.reflect.Private");  

         //獲取其構造方法對應的Constructor物件  
        Constructor con = classType.getDeclaredConstructor(new Class[]{});  

         //建立Private的物件  
        Object object =con.newInstance(new Object[]{});  

       //獲取Private類中name屬性對應的Field物件  
        Field field = classType.getDeclaredField("name");  

        //設定避開java訪問控制檢測  
        field.setAccessible(true);  

        //獲取修改前的值  
        Object str = field.get(object);  

        System.out.println("修改之前name的值:"+(String)str);  

        //給name屬性賦值  
        field.set(object, "李四");  

       //獲取getName方法對應的Method物件  
        Method getNameMethod = classType.getDeclaredMethod("getName", new Class[]{});  

        //設定避開java訪問控制檢測  
        getNameMethod.setAccessible(true);  

        //呼叫方法,返回值  
        Object  o = getNameMethod.invoke(object, new Object[]{});  
        System.out.println("修改之後name的值:"+(String)o);  
    }  
}  

3.執行時複製物件(重要)
ReflectTester 類進一步演示了Reflection API的基本使用方法。ReflectTester類有一個copy(Object object)方法,這個方法能夠建立一個和引數object 同樣型別的物件,然後把object物件中的所有屬性拷貝到新建的物件中,並將它返回。

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;
    }
}

輸出結果:

Class:com.langsin.reflection.Customer
id:1
name:Tom
age:21
Copy information:1 Tom 21

示例四.執行時變更field內容

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

示例五.用反射機制呼叫物件的方法

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型別

六.動態建立和訪問陣列
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]);
    }
}