Java高級特性-反射

 

Roadmap

1. 基本概念

1.1 什麼是反射

反射機制是在運行狀態中,對於任意一個類,都可以知道這個類的全部屬性和方法;對於任意一個對象,都可以調用它的任意一個方法和屬性。java

 

1.2 反射的做用

在運行時判斷任意一個對象所屬的類
在運行時構造任意一個類的對象
在運行時判斷任意一個類所具備的成員變量和方法
在運行時調用任意一個對象的方法
生成動態代理
反向工程 .class -> .java數組

前提是在運行時,不是編譯時,也就是在運行前並不知道調用哪個類,經過反射就能夠作到這些 安全

1.3 反射中的類

java.lang.Class
java.lang.reflect.Constructor
java.lang.reflect.Field
java.lang.reflect.Method
java.lang.reflect.Modifier框架

2 反射的使用

2.1 反射機制獲取類

public class MyReflection {

   // 屬性方法省略

    public static void main(String[] arg) throws ClassNotFoundException {
        //第一種方式:  
        Class c1 = Class.forName("refelection.MyReflection");
        //第二種方式:每一個類都有 .class  
        Class c2 = MyReflection.class;
        //第三種方式:
        Class c3 = new MyReflection().getClass();
   }
}

2.2 獲取全部屬性

public static void main(String[] arg) throws ClassNotFoundException {
       
       
        Class c1 = Class.forName("refelection.MyReflection");
        Class c2 = MyReflection.class;
        Class c3 = new MyReflection().getClass();


        Field[] fs1 = c1.getDeclaredFields();
        Field[] fs2 = c2.getFields();


        Method[] method1 = c1.getDeclaredMethods();
        Method[] method2 = c2.getMethods();

        Constructor[] cts1 = c1.getDeclaredConstructors();
        Constructor[] cts12 =c2.getDeclaredConstructors();

        for(Field field:fs1){
            System.out.println("** get declaredFields **");
            System.out.println(Modifier.toString(field.getModifiers()));
            System.out.println( field.getType().getSimpleName());
            System.out.println( field.getName());
        }

        for(Field field:fs2){
            System.out.println("** get getFields **");
            System.out.println(Modifier.toString(field.getModifiers()));
            System.out.println( field.getType().getSimpleName());
            System.out.println( field.getName());
        }

        for(Method method:method1){
            System.out.println("** get getDeclaredMethods **");
            System.out.println(Modifier.toString(method.getModifiers()));
            System.out.println( method.getName());
        }

        for(Method method:method2){
            System.out.println("** get getMethods **");
            System.out.println(Modifier.toString(method.getModifiers()));
            System.out.println( method.getName());
        }

        for(Constructor constructor:cts1){
            System.out.println("** get getDeclaredConstructors **");
            System.out.println(Modifier.toString(constructor.getModifiers()));
            System.out.println( constructor.getName());
        }

        for(Constructor constructor:cts12){
            System.out.println("** get getConstructors **");
            System.out.println(Modifier.toString(constructor.getModifiers()));
            System.out.println( constructor.getName());
        }

    }

* getFields()與getDeclaredFields()區別:getFields()只能訪問類中聲明爲公有的字段,私有的字段它沒法訪問,能訪問從其它類繼承來的公有方法.getDeclaredFields()能訪問類中全部的字段,與public,private,protect無關,不能訪問從其它類繼承來的方法  函數

* getMethods()與getDeclaredMethods()區別:getMethods()只能訪問類中聲明爲公有的方法,私有的方法它沒法訪問,能訪問從其它類繼承來的公有方法.getDeclaredFields()能訪問類中全部的字段,與public,private,protect無關,不能訪問從其它類繼承來的方法  this

* getConstructors()與getDeclaredConstructors()區別:getConstructors()只能訪問類中聲明爲public的構造函數.getDeclaredConstructors()能訪問類中全部的構造函數,與public,private,protect無關  spa

 

2.3 獲取指定屬性

獲取指定屬性與獲取所有屬性的方法相似 而且一一對應,方法名沒有S之外,返回的對象是具體對象而不是數組了。代理

//獲取類  
    Class c = Class.forName("User");  
    //獲取id屬性  
    Field idF = c.getDeclaredField("id");

 

2.4 打破封裝

 類方法有一些是私有的,反射機制提供了setAccessiable(true)方法,打破封裝性,這樣能夠經過反射解析一些私有方法,如讀取,賦值等。 顯然這是一個不安全的操做。code

//獲取類  
    Class c = Class.forName("User");  
    //獲取id屬性  
    Field idF = c.getDeclaredField("id");  
    //實例化這個類賦給o  
    Object o = c.newInstance();  
    //打破封裝  
    idF.setAccessible(true); //使用反射機制能夠打破封裝性,致使了java對象的屬性不安全。  
    idF.set(o, "110"); //set

    另外,在框架滿天飛的時代下面,有的時候在運行程序的時候 會遇到 java.lang.IllegalAccessException:   對象

       多數狀況下是在用反射機制下,訪問了私有屬性的緣故

3 動態代理

3.1 InvocationHandler

        每個動態代理類都必需要實現InvocationHandler這個接口,而且每一個代理類的實例都關聯到了一個handler,當咱們經過代理對象調用一個方法的時候,這個方法的調用就會被轉發爲由InvocationHandler這個接口的 invoke 方法來進行調用。咱們來看看InvocationHandler這個接口的惟一一個方法 invoke 方法:

Object invoke(Object proxy, Method method, Object[] args) throws Throwable

 

proxy:  指代咱們所代理的那個真實對象

method: 指代的是咱們所要調用真實對象的某個方法的Method對象

args:  指代的是調用真實對象某個方法時接受的參數

3.2 Proxy    

    Proxy這個類的做用就是用來動態建立一個代理對象的類,它提供了許多的方法,可是咱們用的最多的就是 newProxyInstance 這個方法:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,  InvocationHandler h)  throws IllegalArgumentException

 

這個方法的做用就是獲得一個動態的代理對象,其接收三個參數,

loader:一個ClassLoader對象,定義了由哪一個ClassLoader對象來對生成的代理對象進行加載

interfaces:一個Interface對象的數組,表示的是我將要給我須要代理的對象提供一組什麼接口,若是我提供了一組接口給它,那麼這個代理對象就宣稱實現了該接口(多態),這樣我就能調用這組接口中的方法了

h:一個InvocationHandler對象,表示的是當我這個動態代理對象在調用方法的時候,會關聯到哪個InvocationHandler對象上

3.3 開始動態代理

 

3.3.1 構建真實對象

public interface Subject {

    void call(String content);
}


public class RealSubject implements Subject{

    public void call(String content){
        System.out.println(content);
    }
}

3.3.2 構建動態代理類

public class DynamicProxy implements InvocationHandler {


    private Object subject;

    //    構造方法,給咱們要代理的真實對象賦初值
    public DynamicProxy(Object subject)
    {
        this.subject = subject;
    }


    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //  在代理真實對象前咱們能夠添加一些本身的操做
        System.out.println("before call");

        System.out.println("Method:" + method);

        //    當代理對象調用真實對象的方法時,其會自動的跳轉到代理對象關聯的handler對象的invoke方法來進行調用
        method.invoke(subject, args);

        //  在代理真實對象後咱們也能夠添加一些本身的操做
        System.out.println("after call");

        return null;
    }
}

3.3.3 客戶端調用

public static void main(String[] arg){

        //    咱們要代理的真實對象
        Subject realSubject = new RealSubject();

        //    咱們要代理哪一個真實對象,就將該對象傳進去,最後是經過該真實對象來調用其方法的
        InvocationHandler handler = new DynamicProxy(realSubject);

        /*
         * 經過Proxy的newProxyInstance方法來建立咱們的代理對象,咱們來看看其三個參數
         * 第一個參數 handler.getClass().getClassLoader() ,咱們這裏使用handler這個類的ClassLoader對象來加載咱們的代理對象
         * 第二個參數realSubject.getClass().getInterfaces(),咱們這裏爲代理對象提供的接口是真實對象所實行的接口,表示我要代理的是該真實對象,這樣我就能調用這組接口中的方法了
         * 第三個參數handler, 咱們這裏將這個代理對象關聯到了上方的 InvocationHandler 這個對象上
         */
        Subject subject = (Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject
                .getClass().getInterfaces(), handler);

        System.out.println(subject.getClass().getName());
        subject.call("test");
    }

輸出:

com.sun.proxy.$Proxy0 before call Method:public abstract void refelection.proxy.Subject.call(java.lang.String) test after call

相關文章
相關標籤/搜索