反射以及動態(tài)代理

反射

??反射之中包含了一個'反'字,那就肯定有正,那我們就先從'正'開始
一般情況下,我們使用類的時候必定知道他是什么類,是用來做什么的,于是我們直接對這個類進行實例化,之后使用類的對象進行操作。
??反射則是一開始我們并知道我要初始化的這個類的對象是什么,自然也無法使用new關(guān)鍵字來創(chuàng)建對象了,這個時候,我們就要引入JDK 給我們提供的反射api 進行反射調(diào)用,反射就是在運行時才知道要操作的類是什么,并且可以再運行時獲取類的完整構(gòu)造,并調(diào)用對應(yīng)的方法。
Reflection(反射)是java被視為動態(tài)語言的關(guān)鍵,反射幾只允許程序在執(zhí)行期借助Reflection API 取得任何類的內(nèi)部信息,并直接操作任意對象的內(nèi)部屬性及方法。

Java反射機制主要提供了一下功能

  • 在運行時構(gòu)造任意一個類的對象
  • 在運行時獲取任意一個類所具有的成員變量和方法
  • 在運行時調(diào)用任意一個對象的方法(屬性)

??java是一門面向?qū)ο蟮恼Z言,在面向?qū)ο蟮氖澜缋?,萬事萬物皆對象,既然萬事萬物皆對象,那么我們的類是不是對象呢?我們寫的每一個類都可以看成一個對象,是java.lang.Class類的對象,每一個類對應(yīng)的Class放在哪里呢?當(dāng)我們寫完一個類的java文件,編譯成class文件的時候,編譯期都會將這個類的對應(yīng)的calss對象放在class的末尾,里面都保存了些什么?大家可以理解保存了類的元數(shù)據(jù)信息,一個類的元數(shù)據(jù)信息包括什么?有哪些屬性,方法,構(gòu)造器,實現(xiàn)了哪些接口等等,那么這些信息在java里都有對應(yīng)的類來表示。

Class

Class是一個類,封裝了當(dāng)前對象多對應(yīng)的類的信息

??一個類中有屬性,方法,構(gòu)造器等,比如說有一個person類,一個Order類,一個Book類,這些都是不同的類,現(xiàn)在需要一個稱呼用來描述類,這就是Class,他應(yīng)該有類名,屬性,方法,構(gòu)造器等,Class是用來描述類的類(這個有點拗口)
??Class類就像是對象本身自己照鏡子一樣,可以看到自己的全部,有哪些屬性,方法,構(gòu)造器,實現(xiàn)了哪些接口等
??對于每個類而且,JRE都為期保留一個不變的Class類型的對象,一個Class對象包含了特定某個類的有關(guān)信息。
??對象只能由系統(tǒng)建立對象,一個類(而不是一個對象)在JVM中只會有一個Class實例

獲取Class對象的三種方式
名稱 方式
1.通過類名獲取 類型.class
2.通過對象獲取 對象名.getClass()
3.通過全類名獲取 Class.forName(全類名)
        //實例化對象的標(biāo)準(zhǔn)用法,也就是所謂的正
        Servant servant = new Servant();
        //獲取class三種方法
        Class servantClass = Servant.class;
        Class servantClass1 = servant.getClass();
        Class servantClass3 = Class.forName("com.company.Reflect.Servant");
Class類的常用方法
方法

接下來我們就來使用反射來獲取屬性,方法,構(gòu)造器等。

ps:因為都是固定的調(diào)一些API 此處就直接上代碼了

public class Person {
    String name;
    private int age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
        System.out.println("this is setName()");
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
        System.out.println("this is setAge()");
    }
    //包含一個帶參的構(gòu)造器和一個不帶參的構(gòu)造器
    public Person(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }
    public Person() {
        super();
    }
    private void privateMethod(){
        System.out.println("this is private method");
    }
}
構(gòu)造器相關(guān)
內(nèi)容 方法
獲取全部constructor對象 getConstructors()
獲取某一個constructor對象 需要傳入?yún)?shù)列表 getConstructor(Object...)
調(diào)用構(gòu)造器的 newInstance() 方法創(chuàng)建對象 有參數(shù)需傳入 構(gòu)造器.newInstance(Object...)
public class TestConstructor {
    /*構(gòu)造器相關(guān)*/
    public void testConstructor() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        String className ="com.company.Reflect.Person";
        Class<Person> clazz = (Class<Person>) Class.forName(className);
        System.out.println("獲取全部constructor對象");
        Constructor<Person>[] constructors = (Constructor<Person>[]) clazz.getConstructors();
        for (Constructor<Person> constructor:constructors) {
            System.out.println(constructor);
        }

        System.out.println("獲取某一個constructor對象 需要參數(shù)列表-----");
        Constructor<Person> constructor = clazz.getConstructor(String.class, int.class);
        System.out.println(constructor);

        System.out.println("調(diào)用構(gòu)造器的 newInstance() 方法創(chuàng)建對象-----");
        Person hellow = constructor.newInstance("hellow", 18);
        System.out.println(hellow.getName());
    }
}

接下來我們看一下打印結(jié)果


打印結(jié)果
方法相關(guān)
內(nèi)容 方法
獲取對應(yīng)類中的所有方法,不能獲取private方法,且獲取從父類繼承來的所有方法 getMethods()
獲取所有方法,包括私有方法 所有聲明的方法,都可以獲取到,且只獲取當(dāng)前類的方法 getDeclaredMethods()
獲取指定的方法 需要參數(shù)名稱和參數(shù)列表,無參數(shù)則不需要寫 clazz.getDeclaredMethod(String name,Class<?>... paramterTypes)
執(zhí)行方法,第一個參數(shù)表示執(zhí)行那個對象的方法,剩下的參數(shù)時執(zhí)行方法時需要的參數(shù) invoke(Object obj,Obkect... obj)
執(zhí)私有方法的執(zhí)行,必須在調(diào)用invoke之前加上一句 method.setAccessible(ture)不然會報錯 setAccessible(ture)
public class TestMethod {
    public void testMethod() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
        String className ="com.company.Reflect.Person";
        Class clazz =Class.forName(className);
        System.out.println("獲取clazz中對應(yīng)類中的素有方法 不能獲取private方法,且獲取從父類繼承來的所有方法");
        Method[] methods = clazz.getMethods();
        for (Method method :methods) {
            System.out.println(" "+method.getName()+"()");
        }
        System.out.println("--------------------------");
        System.out.println("獲取所有方法,包括私有方法 所有聲明的方法,都可以獲取到,且只獲取當(dāng)前類的方法");
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method method :declaredMethods) {
            System.out.println(" "+method.getName()+"()");
        }
        System.out.println("--------------------------");
        System.out.println("獲取指定的方法 需要參數(shù)名稱和參數(shù)列表,無參數(shù)則不需要寫");
        Method method = clazz.getDeclaredMethod("setAge", int.class);
        System.out.println(method);
        System.out.println("--------------------------");

        System.out.println("執(zhí)行方法,第一個參數(shù)表示執(zhí)行那個對象的方法,剩下的參數(shù)時執(zhí)行方法時需要的參數(shù)");
        Object instance = clazz.newInstance();
        method.invoke(instance,18);

        System.out.println("執(zhí)私有方法的執(zhí)行,必須在調(diào)用invoke之前加上一句 method.setAccessible(ture)");
        Method privateMethod = clazz.getDeclaredMethod("privateMethod");
        System.out.println(privateMethod);
        System.out.println("--------------------------");
        System.out.println("執(zhí)行私有方法");
        privateMethod.setAccessible(true);
        privateMethod.invoke(instance);

    }
打印結(jié)果

當(dāng)不加setAccessible(true)時的結(jié)果


不加時報錯結(jié)果
屬性相關(guān)
內(nèi)容 方法
獲取公用和私有的所有字段 但是不能獲取父類的字段 getDeclaredFields()
獲取指定字段 getDeclaredField(String name)
獲取指定字段的值 字段.get(Object obj)
設(shè)置指定對象指定字段的值 字段.set(Object obj)
字段是私有的,不管是讀還是寫都必須先調(diào)用 setAccessible(ture) 方法 setAccessible(true)

動態(tài)模式

代理模式和靜態(tài)代理

??代理模式 給某一個對象提供一個代理對象,并有代理對象控制對原對象的引用,通俗的來講代理模式就是我們生活中常見的中介
??舉個例子:張三想買某種生活用品,雖然他可以自己去找,但是有點浪費時間和精力,或者自己不好意思去買,于是張三就通過中介Mark來買,Mark來幫張三買一個,張三只是負責(zé)選擇喜歡的尺寸,然后付錢給Mark就可以了

目的:
1.通過引入代理對象的方式來間接訪問目標(biāo)對象,防止直接訪問目標(biāo)對象給系統(tǒng)帶來的不必要復(fù)雜性;
2.通過代理對象對原有的業(yè)務(wù)增強;
代理模式一般會有三個角色:

  • 抽象角色:指代理角色和真實角色對外提供的公共方法,一般為一個接口
  • 真實角色:需要實現(xiàn)抽象角色接口,定義了真實角色所要實現(xiàn)的業(yè)務(wù)邏輯,以便供代理角色調(diào)用。也就是真正的業(yè)務(wù)邏輯在此。
  • 代理角色:需要實現(xiàn)抽象角色接口,是真實角色的代理,通過真實角色的業(yè)務(wù)邏輯方法來實現(xiàn)抽象方法,并可以附加自己的操作。將統(tǒng)一的流程控制都放到代理角色中處理!
    ??而訪問者不再訪問真實角色,而是去訪問代理角色。
    ??靜態(tài)代理在使用時,需要定義接口或者父類,被代理對象與代理對象一起實現(xiàn)相同的接口或者是繼承相同父類。一般來說,被代理對象和代理對象是一對一的關(guān)系,當(dāng)然一個代理對象對應(yīng)多個被代理對象也是可以的。
    ??靜態(tài)代理,一對一則會出現(xiàn)時靜態(tài)代理對象量多、代碼量大,從而導(dǎo)致代碼復(fù)雜,可維護性差的問題,一對多則代理對象會出現(xiàn)擴展能力差的問題。

接下來我們用代碼來簡單的表述一下靜態(tài)代理

//代表真實角色
public class AaFactory implements ManToolsFactory {
    @Override
    public void saleManTools(String size) {
        System.out.println("按需求定制了一個size為"+size+"的女model");
    }
}
//抽象角色
public interface ManToolsFactory {
    void saleManTools(String size);
}

/*代理角色*/
public class Mark1 implements ManToolsFactory {
    
    public ManToolsFactory factory;

    public Mark1(ManToolsFactory factory) {
        this.factory = factory;
    }
    
    /*后置處理器*/
    private void doSthAfter() {
        System.out.println("精美包裝,快遞一條龍服務(wù)");
    }

    /*前置處理器*/
    private void doSthBefore() {
        System.out.println("根據(jù)需求,進行市場調(diào)研和產(chǎn)品分析");
    }

    @Override
    public void saleManTools(String size) {
        doSthBefore();
        factory.saleManTools(size);
        doSthAfter();
    }
}

public static void main(String[] args) {
        ManToolsFactory factory = new AaFactory();
        Mark mark = new Mark(factory);
        mark.saleManTools("D");
    }

靜態(tài)代理運行結(jié)果

??通過運行我們發(fā)現(xiàn)確實完成了整個工作邏輯,但是有一個問題,如果這時張三的老婆來了,但是需求不同,需要另一種生活用品,這時怎么辦呢?智能是再去增加一個新的抽象接口,去做新的邏輯,這樣就造成了一個問題,需要不停去新增,不停的去修改,這就違反了面向?qū)ο蟮拈_閉原則。同時擴展能力差 可維護性差,所以在一般的正常使用中我們都是采用動態(tài)代理來實現(xiàn)。

動態(tài)代理

??是指在使用時再創(chuàng)建代理類和實例
??優(yōu)點
??只需要1個動態(tài)代理類就可以解決創(chuàng)建多個靜態(tài)代理的問題,避免重復(fù)、多余代碼,更強的靈活性
??缺點
??效率低,相比靜態(tài)代理中 直接調(diào)用目標(biāo)對象方法,動態(tài)代理則需要先通過Java反射機制 從而 間接調(diào)用目標(biāo)對象方法
??應(yīng)用場景局限,因為 Java 的單繼承特性(每個代理類都繼承了 Proxy 類),即只能針對接口 創(chuàng)建 代理類,不能針對類創(chuàng)建代理類。
??在java的動態(tài)代理機制中,有兩個重要的類或接口,一個是InvocationHandler接口、另一個則是 Proxy類,這個類和接口是實現(xiàn)我們動態(tài)代理所必須用到的。
??InvocationHandler接口是給動態(tài)代理類實現(xiàn)的,負責(zé)處理被代理對象的操作的,而Proxy是用來創(chuàng)建動態(tài)代理類實例對象的,因為只有得到了這個對象我們才能調(diào)用那些需要代理的方法。

下面簡單的通過代碼來實現(xiàn)一下

public class AaFactory implements ManToolsFactory {
    @Override
    public void saleManTools(String size) {
        System.out.println("按需求定制了一個size為"+size+"的女model");
    }
}

public class BbFactory implements WomanToolsFactory {
    @Override
    public void saleWomanTools(float length) {
        System.out.println("按需求定制了一個高度為"+length+"的男model");
    }
}

public class MarkCompany implements InvocationHandler {

    /*持有的真實對象*/
    private Object factory;

    public Object getFactory() {
        return factory;
    }

    public void setFactory(Object factory) {
        this.factory = factory;
    }

    /*通過proxy獲得動態(tài)代理對象*/
    public Object getProxyInstance(){
        return Proxy.newProxyInstance(factory.getClass().getClassLoader(),factory.getClass().getInterfaces(),this);
    }


    /*通過動態(tài)代理對象方法進行增強*/
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        doSthBefore();
        Object result = method.invoke(factory, args);
        doSthAfter();
        return result;
    }

    /*后置處理器*/
    private void doSthAfter() {
        System.out.println("精美包裝,快遞一條龍服務(wù)");
    }

    /*前置處理器*/
    private void doSthBefore() {
        System.out.println("根據(jù)需求,進行市場調(diào)研和產(chǎn)品分析");
    }
}

public interface ManToolsFactory {
    void saleManTools(String size);
}

public interface WomanToolsFactory {

    void saleWomanTools(float length);

}

public static void main(String[] args) {
        /*動態(tài)代理*/
        ManToolsFactory manToolsFactory = new AaFactory();
        MarkCompany company = new MarkCompany();
        company.setFactory(manToolsFactory);
        ManToolsFactory employee = (ManToolsFactory) company.getProxyInstance();
        employee.saleManTools("E");

        WomanToolsFactory womanToolsFactory = new BbFactory();
        company.setFactory(womanToolsFactory);
        WomanToolsFactory employee1 = (WomanToolsFactory) company.getProxyInstance();
        employee1.saleWomanTools(1.9f);
    }


動態(tài)代理運行結(jié)果

通過打印結(jié)果可以看到我們同樣的實現(xiàn)了需求,但是工作量要比靜態(tài)代理少,同時后期維護的話要更省事

??今天的介紹,對于龐大的java來說只是冰山一角,只是通過了一個小的demo來介紹一下反射和代理,所說的這些并不能面面俱到,只能對自己的學(xué)習(xí)所一個總結(jié),以便于以后的查閱。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容