官术网_书友最值得收藏!

第3章 反射

本章介紹Java中最強(qiáng)大的技術(shù):反射。

Java原生的反射語法艱澀難懂,于是我們一般將這些反射語法封裝成Utils類,包括反射類、反射方法(構(gòu)造函數(shù))、反射字段。這其中,做得最好的莫過于jOOR這個開源反射封裝庫。但是它有個缺點(diǎn),就是不適用于Android中定義為final的字段,就連作者也承認(rèn),jOOR只為了Java而設(shè)計(jì),而沒有考慮Android。

所有反射語法中最難的莫過于反射一個泛型類,而這又是我們在做插件化編程中不可避免的。

3.1 基本反射技術(shù)

反射包括以下技術(shù):

□ 根據(jù)一個字符串得到一個類的對象。

□ 獲取一個類的所有公用或私有、靜態(tài)或?qū)嵗淖侄巍⒎椒ā傩浴?/p>

□ 對泛型類的反射。

相比于其他語言,Java反射的語法是非常艱澀難懂的,我們按照上面的三點(diǎn)依次介紹。

提示

本節(jié)的示例代碼參見https://github.com/BaoBaoJianqiang/TestReflection

3.1.1 根據(jù)一個字符串得到一個類

1.getClass

通過一個對象,獲取它的類型。類型用Class表示:

    String str = “abc”;
    Class c1 = str.getClass();

2.Class.forName

這個方法用得最多。

通過一個字符串獲取一個類型。這個字符串由類的命名空間和類的名稱組成。而通過getSuperclass方法,獲取對象的父類類型:

    try {
        Class c2 = Class.forName("java.lang.String");
        Class c3 = Class.forName("android.widget.Button");

        //通過getSuperClass,每個Class都有這個函數(shù)
        Class c5 = c3.getSuperclass();  //得到TextView
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }

3.class屬性

每個類都有class屬性,可以得到這個類的類型:

    Class c6 = String.class;
    Class c7 = java.lang.String.class;
    Class c8 = MainActivity.InnerClass.class;
    Class c9 = int.class;
    Class c10 = int[].class;

4.TYPE屬性

基本類型,如BOOLEAN,都有TYPE屬性,可以得到這個基本類型的類型:

    Class c11 = Boolean.TYPE;
    Class c12 = Byte.TYPE;
    Class c13 = Character.TYPE;
    Class c14 = Short.TYPE;
    Class c15 = Integer.TYPE;
    Class c16 = Long.TYPE;
    Class c17 = Float.TYPE;
    Class c18 = Double.TYPE;
    Class c19 = Void.TYPE;

看到這里,讀者也許會問,為什么不厭其煩地想要得到類或?qū)ο蟮念愋汀T诤竺娴恼鹿?jié),我們在使用Proxy.newProxyInstance()的時候,會大量用到這些類型值作為參數(shù)。

3.1.2 獲取類的成員

1.獲取類的構(gòu)造函數(shù)

獲取類的構(gòu)造函數(shù),包括private和public兩種,也支持無參數(shù)和有參數(shù)這兩種類型的構(gòu)造函數(shù)。

比如TestClassCtor這個類,就有很多構(gòu)造函數(shù):

    public TestClassCtor() {
        name = "baobao";
    }

    public TestClassCtor(int a) {

    }

    public TestClassCtor(int a, String b) {
        name = b;
    }

    private TestClassCtor(int a, double c) {

    }

1)獲取類的所有構(gòu)造函數(shù)。

通過Class的getDeclaredConstructors方法,獲取類的所有構(gòu)造函數(shù),包括public和private的構(gòu)造函數(shù),然后就可以通過for循環(huán)遍歷每一個構(gòu)造函數(shù)了:

    TestClass r = new TestClass();
        Class temp = r.getClass();
        String className = temp.getName();        // 獲取指定類的類名

        Log.v("baobao", "獲取類的所有ctor,不分public還是private---------------------");
        //獲取類的所有ctor,不分public還是private
        try {
            Constructor[] theConstructors = temp.getDedaredConstructors();
                                              // 獲取指定類的公有構(gòu)造方法

            for (int i = 0; i < theConstructors.length; i++) {
                int mod = theConstructors[i].getModifiers();    // 輸出修飾域和方法名稱
                Log.v("baobao", Modifier.toString(mod) + " " + className + "(");

                Class[] parameterTypes = theConstructors[i].getParameterTypes();
                                              //獲取指定構(gòu)造方法參數(shù)的集合
                for (int j = 0; j < parameterTypes.length; j++) {
                                              // 輸出打印參數(shù)列表
                    Log.v("baobao", parameterTypes[j].getName());
                    if (parameterTypes.length > j + 1) {
                        Log.v("baobao", ", ");
                    }
                }
                Log.v("baobao", ")");
            }
        } catch (Exception e) {
            e.printStackTrace();
            }

如果只想獲取類的所有public構(gòu)造函數(shù),就不能再使用Class的getConstructors方法了,而要使用getDeclaredConstructors方法。

2)獲取類的某個構(gòu)造函數(shù)。

獲取無參數(shù)的構(gòu)造函數(shù):

    Constructor c1 = temp.getDeclaredConstructor();

獲取有一個參數(shù)的構(gòu)造函數(shù),參數(shù)類型int:

    Class[] p2 = {int.class};
    Constructor c2 = temp.getDeclaredConstructor(p2);

獲取有兩個參數(shù)的構(gòu)造函數(shù),參數(shù)類型依次是int和String:

    Class[] p3 = {int.class, String.class};
    Constructor c3 = temp.getDeclaredConstructor(p3);

反射到類的構(gòu)造函數(shù)很重要,這是下述流程中至關(guān)重要的一步:通過字符串反射出一個類,然后通過反射獲取到類的構(gòu)造函數(shù),執(zhí)行構(gòu)造函數(shù)就得到了類的實(shí)例。有了實(shí)例,就可以通過反射進(jìn)一步得到實(shí)例的所有字段和方法。

3)調(diào)用構(gòu)造函數(shù)。

接下來通過反射調(diào)用構(gòu)造函數(shù),得到類的實(shí)例,這要借助于Constructor的newInstance方法:

    Class r = Class.forName("jianqiang.com.testreflection.TestClassCtor");

    //含參
    Class[] p3 = {int.class, String.class};
    Constructor ctor = r.getDeclaredConstructor(p3);
    Object obj = ctor.newInstance(1, "bjq");

    //無參
    Constructor ctor2 = r.getDeclaredConstructor();
    Object obj2 = ctor2.newInstance();

如果構(gòu)造函數(shù)是無參數(shù)的,那么可以直接使用Class的newInstance方法:

    Class r = Class.forName("jianqiang.com.testreflection.TestClassCtor");
    Object obj4 = r.newInstance();

2.獲取類的私有實(shí)例方法并調(diào)用它

在TestClassCtor中,有一個私有方法doSOmething:

    private String doSOmething(String d) {
        Log.v("baobao", "TestClassCtor, doSOmething");

        return "abcd";
    }

想獲取這個私有方法并執(zhí)行它,要寫如下代碼:

    Class r = Class.forName("jianqiang.com.testreflection.TestClassCtor");
    Class[] p3 = {int.class, String.class};
    Constructor ctor = r.getDeclaredConstructor(p3);
    Object obj = ctor.newInstance(1, "bjq");

    //以下4句話,調(diào)用一個private方法
    Class[] p4 = {String.class};
    Method method = r.getDeclaredMethod("doSOmething", p4); //在指定類中獲取指定的方法
    method.setAccessible(true);

    Object argList[] = {"jianqiang"};   //這里寫死,下面有個通用的函數(shù)getMethodParamObject
    Object result = method.invoke(obj, argList);

3.獲取類的靜態(tài)的私有方法并調(diào)用它

在TestClassCtor中,有一個靜態(tài)的私有方法work:

    private static void work() {
        Log.v("baobao", "TestClassCtor, work");
    }

想獲取這個靜態(tài)的私有方法并執(zhí)行它,要寫如下代碼:

    Class r = Class.forName("jianqiang.com.testreflection.TestClassCtor");
    //以下3句話,調(diào)用一個private靜態(tài)方法
    Method method = r.getDeclaredMethod("work"); //在指定類中獲取指定的方法
    method.setAccessible(true);
    method.invoke(null);

4.獲取類的私有實(shí)例字段并修改它

在TestClassCtor中,有一個私有的實(shí)例字段name:

    public class TestClassCtor {
        private String name;

        public String getName() {
            return name;
        }
    }

想獲取這個私有實(shí)例字段并修改它的值,要寫如下代碼:

    //以下4句話,創(chuàng)建一個對象
    Class r = Class.forName("jianqiang.com.testreflection.TestClassCtor");
    Class[] p3 = {int.class, String.class};
    Constructor ctor = r.getDeclaredConstructor(p3);
    Object obj = ctor.newInstance(1, "bjq");

    //獲取name字段,private
    Field field = r.getDeclaredField("name");
    field.setAccessible(true);
    Object fieldObject = field.get(obj);

    //只對obj有效
    field.set(obj, "jianqiang1982");

值得注意的是,這次修改僅對當(dāng)前這個對象有效,如果接下來我們再次創(chuàng)建一個TestClassCtor對象,它的name字段的值為空而不是jianqiang1982:

    TestClassCtor testClassCtor = new TestClassCtor(100);
    testClassCtor.getName(); //仍然返回null,并沒有修改

5.獲取類的私有靜態(tài)字段并修改它

在TestClassCtor中,有一個靜態(tài)的私有字段address,想獲取這個私有的靜態(tài)字段并修改它的值,要寫如下代碼:

    //以下4句話,創(chuàng)建一個對象
    Class r = Class.forName("jianqiang.com.testreflection.TestClassCtor");

    //獲取address靜態(tài)字段,private
    Field field = r.getDeclaredField("address");
    field.setAccessible(true);

    Object fieldObject = field.get(null);

    field.set(fieldObject, "ABCD");

    //靜態(tài)變量,一次修改,終生受用
    TestClassCtor.printAddress();

與前面介紹的實(shí)例字段不同,靜態(tài)字段的值被修改了,下次再使用,這個字段的值是修改后的值。所謂“一次修改,終生受用”。

3.1.3 對泛型類的反射

Android系統(tǒng)源碼中存在大量泛型,所以插件化技術(shù)離不開對泛型進(jìn)行反射,比如單例模式(Singleton),下述代碼是從Android源碼中找出來的:

    public abstract class Singleton<T> {
        private T mInstance;

        protected abstract T create();

        public final T get() {
            synchronized (this) {
                if (mInstance == null) {
                    mInstance = create();
                }
                return mInstance;
            }
        }
    }

Singleton是一個泛型類,我們可以通過以下三行代碼,取出Singleton中的mInstance字段:

    Class<? > singleton = Class.forName("jianqiang.com.testreflection.Singleton");
    Field mInstanceField = singleton.getDeclaredField("mInstance");
    mInstanceField.setAccessible(true);

同時,Singleton也是一個抽象類,在實(shí)例化Singleton的時候,一定要實(shí)現(xiàn)create這個抽象方法。

接下來我們看ActivityManagerNative(AMN)這個類,其中和Singleton有關(guān)的是下面幾行代碼:

    public class AMN {
        private  static  final  Singleton<ClassB2Interface>  gDefault  =  new  Singleton
            <ClassB2Interface>() {
            protected ClassB2Interface create() {
                ClassB2 b2 = new ClassB2();
                b2.id = 2;
                return b2;
            }
        };

        static public ClassB2Interface getDefault() {
            return gDefault.get();
        }
    }

上面的代碼中g(shù)Default是AMN的靜態(tài)私有變量,它是Singleton類型的,所以要實(shí)現(xiàn)create方法,返回一個ClassB2類型的對象。

在Android的源碼中,可通過AMN.getDefault()來獲取create方法創(chuàng)建的ClassB2對象。

我們可以通過以下幾行代碼來獲取AMN的gDefault靜態(tài)私有字段,進(jìn)一步得到生成的ClassB2類型對象rawB2Object:

    Class<? > activityManagerNativeClass = Class.forName("jianqiang.com.testreflection.
        AMN");
    Field gDefaultField = activityManagerNativeClass.getDeclaredField("gDefault");
    gDefaultField.setAccessible(true);
    Object gDefault = gDefaultField.get(null);
    // AMNgDefault對象里面原始的 B2對象
    Object rawB2Object = mInstanceField.get(gDefault);

后來我們可能發(fā)現(xiàn),rawB2Object不是我們所需要的,我們希望把它換成ClassB2Mock類型的對象proxy。

ClassB2Mock是對rawB2Object的動態(tài)代理,這里使用了Proxy.newProxyInstance方法,額外打印了一行日志:

    // 創(chuàng)建一個這個對象的代理對象ClassB2Mock, 然后替換這個字段, 讓我們的代理對象幫忙干活
    Class<? > classB2Interface = Class.forName("jianqiang.com.testreflection.ClassB2Interface");
    Object proxy = Proxy.newProxyInstance(
        Thread.currentThread().getContextClassLoader(),
        new Class<? >[] { classB2Interface },
        new ClassB2Mock(rawB2Object));
    mInstanceField.set(gDefault, proxy);

最后一行代碼就是把AMN中的gDefault字段的mInstance字段,設(shè)置為代理對象proxy。

經(jīng)過Hook, AMN.getDefault().doSomething()將執(zhí)行ClassB2Mock里面的邏輯。

Android源碼中AMN的思路和我的這些代碼在思路上是一致的,只是這里用ClassB2和ClassB2Mock來模擬,比較簡單。

3.2 jOOR

上面例子的語法都是基于原始的Java語法,有沒有覺得這些語法很艱澀?我們希望用一種自然的、簡單的、面向?qū)ο蟮恼Z法,來取代上面這些艱澀的語言,于是便有了jOOR這個開源庫。開源地址:https://github.com/jOOQ/jOOR

jOOR庫就兩個類,Reflect.java和ReflectException.java,所以我一般不依賴于gradle,而是直接把這兩個類拖到項(xiàng)目中來。

其中,Reflect.java最為重要,包括6個核心方法:

□ on:包裹一個類或者對象,表示在這個類或?qū)ο笊线M(jìn)行反射,類的值可以是Class,也可以是完整的類名(包含包名信息)。

□ create:用來調(diào)用之前的類的構(gòu)造方法,有兩種重載,一種有參數(shù),一種無參數(shù)。

□ call:方法調(diào)用,傳入方法名和參數(shù),如有返回值還需要調(diào)用get。

□ get:獲取(field和method返回)值相關(guān),會進(jìn)行類型轉(zhuǎn)換,常與call組合使用。

□ set:設(shè)置屬性值。

我們使用jOOR把上一節(jié)的代碼重構(gòu)一下。

提示

本節(jié)的示例代碼參見https://github.com/BaoBaoJianqiang/TestReflection2

3.2.1 根據(jù)一個字符串得到一個類

1.getClass

通過一個字符串,獲取它的類型。類型用Class表示:

    String str = "abc";
    Class c1 = str.getClass();

這些代碼還是傳統(tǒng)的語法,并沒有改變。

2.根據(jù)字符串獲取一個類

對于jOOR,我們一般import它的Reflect.on方法,這樣我們就可以直接在代碼中使用on了,讓編碼更加簡單。代碼如下:

    import static jianqiang.com.testreflection.joor.Reflect.on;

        //以下3個語法等效
        Reflect r1 = on(Object.class);
        Reflect r2 = on("java.lang.Object");
        Reflect r3 = on("java.lang.Object", ClassLoader.getSystemClassLoader());

        //以下2個語法等效,實(shí)例化一個Object變量,得到Object.class
        Object o1 = on(Object.class).<Object>get();
        Object o2 = on("java.lang.Object").get();

        String j2 = on((Object)"abc").get();
        int j3 = on(1).get();

        //等價(jià)于Class.forName()
        try {
            Class j4 = on("android.widget.Button").type();
        }
        catch (ReflectException e) {
            e.printStackTrace();
        }

3.2.2 獲取類的成員

1.調(diào)用類的構(gòu)造函數(shù)

調(diào)用類的構(gòu)造函數(shù),包括private和public兩種,也支持無參數(shù)和有參數(shù)這兩種類型的構(gòu)造函數(shù)。

jOOR認(rèn)為構(gòu)造函數(shù)就是用來調(diào)用的,所以沒有給出獲取構(gòu)造函數(shù)的方法,而是直接給出了調(diào)用構(gòu)造函數(shù)的方法create:

    TestClassCtor r = new TestClassCtor();
    Class temp = r.getClass();
    String className = temp.getName();        // 獲取指定類的類名

    //public構(gòu)造函數(shù)
    Object obj = on(temp).create().get();    //無參
    Object obj2 = on(temp).create(1, "abc").get();  //有參

    //private構(gòu)造函數(shù)
    TestClassCtor obj3 = on(TestClassCtor.class).create(1, 1.1).get();
    String a = obj3.getName();

2.獲取類的私有實(shí)例方法

獲取類的私有實(shí)例方法并調(diào)用它:

    //以下4句話,創(chuàng)建一個對象
        TestClassCtor r = new TestClassCtor();
        Class temp = r.getClass();
        Reflect reflect = on(temp).create();

        //調(diào)用一個實(shí)例方法
        String a1 = reflect.call("doSOmething", "param1").get();

3.獲取類的私有靜態(tài)方法

獲取類的私有靜態(tài)方法并調(diào)用它:

    //以下4句話,創(chuàng)建一個對象
    TestClassCtor r = new TestClassCtor();
    Class temp = r.getClass();
    Reflect reflect = on(temp).create();

    //調(diào)用一個靜態(tài)方法
    on(TestClassCtor.class).call("work").get();

4.獲取類的私有實(shí)例字段

獲取類的私有實(shí)例字段并修改它:

    Reflect obj = on("jianqiang.com.testreflection.TestClassCtor").create(1, 1.1);
    obj.set("name", "jianqiang");
    Object obj1 = obj.get("name");

5.獲取類的私有靜態(tài)字段

獲取類的私有靜態(tài)字段并修改它:

    on("jianqiang.com.testreflection.TestClassCtor").set("address", "avcccc");
    Object obj2 = on("jianqiang.com.testreflection.TestClassCtor").get("address");

3.2.3 對泛型類的反射

這個例子用jOOR寫起來會更簡單:

    //獲取AMNgDefault單例gDefault, gDefault是靜態(tài)的
    Object gDefault = on("jianqiang.com.testreflection.AMN").get("gDefault");

    // gDefault是一個 android.util.Singleton對象我們?nèi)〕鲞@個單例里面的mInstance字段
    // mInstance就是原始的ClassB2Interface對象
    Object mInstance = on(gDefault).get("mInstance");

    // 創(chuàng)建一個這個對象的代理對象ClassB2Mock, 然后替換這個字段, 讓我們的代理對象幫忙干活
    Class<? > classB2Interface = on("jianqiang.com.testreflection.ClassB2Interface").type();
    Object proxy = Proxy.newProxyInstance(
        Thread.currentThread().getContextClassLoader(),
        new Class<? >[] { classB2Interface },
        new ClassB2Mock(mInstance));

    on(gDefault).set("mInstance", proxy);

外界對jOOR的評價(jià)很高,我看了相關(guān)的文章,大多是一些浮于表面的介紹,然后就被媒體無限放大,殊不知jOOR在Android領(lǐng)域有個很大的缺陷,那就是不支持反射final類型的字段。

看一個例子,User類有兩個final字段,其中userId是靜態(tài)字段,name是實(shí)例字段:

    public class User {
        private final static int userId = 3;
        private final String name = "baobao";
    }

在使用jOOR反射時的語法如下:

    //實(shí)例字段
    Reflect obj = on("jianqiang.com.testreflection.User").create();
    obj.set("name", "jianqiang");
    Object newObj = obj.get("name");

    //靜態(tài)字段
    Reflect obj2 = on("jianqiang.com.testreflection.User");
    obj2.set("userId", "123");
    Object newObj2 = obj2.get("userId");

上面這段代碼在執(zhí)行set語法時必然報(bào)錯,拋出一個NoSuchFieldException異常。究其原因,是jOOR的Reflect的set方法會在遇到final時,嘗試反射出Field類的modifiers字段,在Java環(huán)境是有這個字段的,但是Android版本的Field類并沒有這個字段,于是就報(bào)錯了。

3.3 對基本反射語法的封裝

考慮到j(luò)OOR的局限性,我們不得不另辟蹊徑。對基本的Java反射語法進(jìn)行封裝,以得到簡單的語法。

考察前面介紹的種種語法,無論是反射出一個類,還是反射出一個構(gòu)造函數(shù)并調(diào)用它,都是為了進(jìn)一步讀寫類的方法和字段,所以我們只要封裝以下幾個方法即可:

□ 反射出一個構(gòu)造函數(shù)并調(diào)用它。

□ 調(diào)用靜態(tài)方法。

□ 調(diào)用實(shí)例方法。

□ 獲取和設(shè)置一個字段的值。

□ 對泛型的處理。

提示

本節(jié)的示例代碼參見https://github.com/BaoBaoJianqiang/TestReflection3

3.3.1 反射出一個構(gòu)造函數(shù)

在RefInvoke類定義方法如下:

    public static Object createObject(String className, Class[] pareTyples, Object[]
        pareVaules) {
        try {
            Class r = Class.forName(className);
            Constructor ctor = r.getDeclaredConstructor(pareTyples);
            ctor.setAccessible(true);
            return ctor.newInstance(pareVaules);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

以下是對這個封裝函數(shù)的調(diào)用:

    Class r = Class.forName(className);

    //含參
    Class[] p3 = {int.class, String.class};
    Object[] v3 = {1, "bjq"};
    Object obj = RefInvoke.createObject(className, p3, v3);

    //無參
    Object obj2 = RefInvoke.createObject(className, null, null);

3.3.2 調(diào)用實(shí)例方法

在RefInvoke類定義方法如下:

    public static Object invokeInstanceMethod(Object obj, String methodName, Class[]
        pareTyples, Object[] pareVaules) {
        if(obj == null)
            return null;

        try {
            //調(diào)用一個private方法
            Method method = obj.getClass().getDeclaredMethod(methodName, pareTyples);
                //在指定類中獲取指定的方法
            method.setAccessible(true);
            return method.invoke(obj, pareVaules);

        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

以下是調(diào)用這個封裝函數(shù):

    Class[] p3 = {};
    Object[] v3 = {};
    RefInvoke.invokeStaticMethod(className, "work", p3, v3);

3.3.3 調(diào)用靜態(tài)方法

在RefInvoke類定義方法如下:

    public  static  Object  invokeStaticMethod(String  className,  String  method_name,
        Class[] pareTyples, Object[] pareVaules) {
        try {
                Class obj_class = Class.forName(className);
            Method method = obj_class.getDeclaredMethod(method_name, pareTyples);
            method.setAccessible(true);
            return method.invoke(null, pareVaules);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

以下是調(diào)用這個封裝函數(shù):

    Class[] p4 = {String.class};
    Object[] v4 = {"jianqiang"};
    Object result = RefInvoke.invokeInstanceMethod(obj, "doSOmething", p4, v4);

3.3.4 獲取并設(shè)置一個字段的值

在RefInvoke類定義方法如下:

    public static Object getFieldObject(String className, Object obj, String filedName) {
        try {
                Class obj_class = Class.forName(className);
            Field field = obj_class.getDeclaredField(filedName);
            field.setAccessible(true);
            return field.get(obj);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    public static void setFieldObject(String classname, Object obj, String filedName,
        Object filedVaule) {
        try {
            Class obj_class = Class.forName(classname);
            Field field = obj_class.getDeclaredField(filedName);
            field.setAccessible(true);
            field.set(obj, filedVaule);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

以下是調(diào)用這兩個封裝函數(shù):

    //獲取實(shí)例字段
    Object fieldObject = RefInvoke.getFieldObject(className, obj, "name");
    RefInvoke.setFieldObject(className, obj, "name", "jianqiang1982");

    //獲取靜態(tài)字段
    Object fieldObject = RefInvoke.getFieldObject(className, null, "address");
    RefInvoke.setFieldObject(className, null, "address", "ABCD");

3.3.5 對泛型類的處理

借助于前面封裝的5個方法,我們重寫對泛型的反射調(diào)用:

    //獲取AMNgDefault單例gDefault, gDefault是靜態(tài)的
    Object  gDefault  =  RefInvoke.getFieldObject("jianqiang.com.testreflection.AMN",
        null, "gDefault");

    // gDefault是一個 android.util.Singleton對象我們?nèi)〕鲞@個單例里面的mInstance字段
    Object rawB2Object = RefInvoke.getFieldObject(
        "jianqiang.com.testreflection.Singleton",
        gDefault, "mInstance");

    // 創(chuàng)建一個這個對象的代理對象ClassB2Mock, 然后替換這個字段, 讓我們的代理對象幫忙干活
    Class<? > classB2Interface = Class.forName("jianqiang.com.testreflection.
        ClassB2Interface");
    Object proxy = Proxy.newProxyInstance(
        Thread.currentThread().getContextClassLoader(),
        new Class<? >[] { classB2Interface },
        new ClassB2Mock(rawB2Object));

    //SingletonmInstance替換為proxy
    RefInvoke.setFieldObject("jianqiang.com.testreflection.Singleton",  gDefault,
        "mInstance", proxy);

雖然不是面向?qū)ο蟮恼Z法,但總要簡單得多。

3.4 對反射的進(jìn)一步封裝

我們在3.3節(jié)對反射語法進(jìn)行了簡單的封裝,但在實(shí)際的使用中,我們發(fā)現(xiàn),有的時候,有很多不方便的地方。在本節(jié)中會對此進(jìn)行優(yōu)化。

提示

本節(jié)示例代碼參考https://github.com/BaoBaoJianqiang/TestReflection4

1.對于無參數(shù)和只有一個參數(shù)的處理

通過反射調(diào)用方法或者構(gòu)造函數(shù)的時候,它們有時只需要一個參數(shù),有時根本不需要參數(shù),但我們每次都要按照多個參數(shù)的方式來編寫代碼,如下所示:

    Class r = Class.forName(className);

    //含參
    Class[] p3 = {int.class, String.class};
    Object[] v3 = {1, "bjq"};
    Object obj = RefInvoke.createObject(className, p3, v3);

    //無參
    Object obj2 = RefInvoke.createObject(className, null, null);

我們希望把代碼寫的更簡單一些,比如說這樣:

    //無參
    public static Object createObject(String className) {
        Class[] pareTyples = new Class[]{};
        Object[] pareVaules = new Object[]{};

        try {
            Class r = Class.forName(className);
            return createObject(r, pareTyples, pareVaules);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        return null;
    }

    //一個參數(shù)
    public static Object createObject(String className, Class pareTyple, Object pareVaule) {
        Class[] pareTyples = new Class[]{ pareTyple };
        Object[] pareVaules = new Object[]{ pareVaule };

        try {
            Class r = Class.forName(className);
            return createObject(r, pareTyples, pareVaules);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        return null;
    }

    //多個參數(shù)
    public static Object createObject(String className, Class[] pareTyples, Object[]
        pareVaules) {
        try {
            Class r = Class.forName(className);
            return createObject(r, pareTyples, pareVaules);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        return null;
    }

    //多個參數(shù)
    public  static  Object  createObject(Class  clazz,  Class[]  pareTyples,  Object[]
        pareVaules) {
        try {
            Constructor ctor = clazz.getDeclaredConstructor(pareTyples);
            ctor.setAccessible(true);
            return ctor.newInstance(pareVaules);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

以此類推,構(gòu)造函數(shù)就是方法,我們封裝的invokeStaticMethod和invokeInstanceMethod方法也可以有這許多種重載方式。

2.字符串可以替換為Class

截止到現(xiàn)在,我們加載類的方式,都是通過字符串的方式,比如createObject的實(shí)現(xiàn):

    public static Object createObject(String className, Class[] pareTyples, Object[]
        pareVaules) {
        try {
            Class r = Class.forName(className);
            Constructor ctor = r.getConstructor(pareTyples);
            return ctor.newInstance(pareVaules);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

但有時候,我們直接就擁有這個類的Class類型,而不再用Class.forName(className)的方式再去生成,于是就可以有字符串和Class這兩種形式的方法重載,如下所示:

    //多個參數(shù)
    public static Object createObject(String className, Class[] pareTyples, Object[]
        pareVaules) {
        try {
            Class r = Class.forName(className);
            return createObject(r, pareTyples, pareVaules);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        return null;
    }

    //多個參數(shù)
    public  static  Object  createObject(Class  clazz,  Class[]  pareTyples,  Object[]
        pareVaules) {
        try {
            Constructor ctor = clazz.getConstructor(pareTyples);
            return ctor.newInstance(pareVaules);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

RefInvoke中的所有方法,大都擁有String和Class這兩種方法的重載,限于篇幅,這里就不一一介紹了,具體情況可以參考源碼例子。

3.區(qū)分靜態(tài)字段和實(shí)例字段

在反射字段的時候,我們發(fā)現(xiàn),對靜態(tài)字段和實(shí)例字段的處理,區(qū)別就在一個地方,由于靜態(tài)字段的反射不需要obj參數(shù),所以設(shè)置為null,如下所示:

    //獲取實(shí)例字段
    Object fieldObject = RefInvoke.getFieldObject(className, obj, "name");
    RefInvoke.setFieldObject(className, obj, "name", "jianqiang1982");

    //獲取靜態(tài)字段
    Object fieldObject = RefInvoke.getFieldObject(className, null, "address");
    RefInvoke.setFieldObject(className, null, "address", "ABCD");

為了區(qū)分靜態(tài)字段和實(shí)例字段這兩種場景,我們把靜態(tài)字段的讀寫方法改名為getStaticFieldObject和setStaticFieldObject,它們間接的調(diào)用了實(shí)例字段的getFieldObject和setFieldObject方法,但是省略了obj參數(shù),如下所示,

    public static Object getStaticFieldObject(String className, String filedName) {
        return getFieldObject(className, null, filedName);
    }

    public static void setStaticFieldObject(String classname, String filedName, Object
        filedVaule) {
        setFieldObject(classname, null, filedName, filedVaule);
    }

那么在反射靜態(tài)字段的時候,就可以優(yōu)雅的寫出下列代碼了:

    Object fieldObject = RefInvoke.getFieldObject(className, null, "address");
    RefInvoke.setStaticFieldObject(className, "address", "ABCD");

4.對反射讀寫字段的優(yōu)化

繼續(xù)觀察對實(shí)例字段的封裝方法,以getFieldObject為例:

    public static Object getFieldObject(Class clazz, Object obj, String filedName) {
        try {
            Field field = clazz.getDeclaredField(filedName);
            field.setAccessible(true);
            return field.get(obj);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

我們發(fā)現(xiàn),大多數(shù)情況下,obj的類型就是clazz,所以可以省略clazz參數(shù),于是編寫兩個簡易版的重載方法,如下所示:

    public static Object getFieldObject(Object obj, String filedName) {
        return getFieldObject(obj.getClass(), obj, filedName);
    }

    public static void setFieldObject(Object obj, String filedName, Object filedVaule) {
        setFieldObject(obj.getClass(), obj, filedName, filedVaule);
    }

然后就可以優(yōu)雅的編寫代碼了:

    Object fieldObject = RefInvoke.getFieldObject(className, "address");
    RefInvoke.setStaticFieldObject(className, "address", "ABCD");

但是也有例外,如果obj的類型不是clazz(obj有可能是clazz的孫子,甚至輩分更低),那么就只能老老實(shí)實(shí)的使用原先封裝的getFieldObject和setFieldObject方法。

3.5 本章小結(jié)

本章給出了反射語法的三種編寫方式:

1)基本反射語法。

2)jOOR語法。

3)對基本反射語法的封裝。

由于jOOR對Android的支持不是很好,所以業(yè)內(nèi)開源框架一般采用的是方式1和3。

在本書后面的章節(jié)中,使用方式3來編寫插件化的反射代碼。

主站蜘蛛池模板: 万盛区| 洞口县| 巴林左旗| 长汀县| 镇宁| 朝阳市| 咸丰县| 五指山市| 镇巴县| 昌都县| 股票| 南开区| 柳河县| 大石桥市| 临漳县| 新竹县| 高清| 布尔津县| 蛟河市| 微博| 宜良县| 景德镇市| 永顺县| 韶山市| 如东县| 达拉特旗| 攀枝花市| 广丰县| 阿拉善左旗| 锡林郭勒盟| 洪泽县| 冀州市| 平武县| 达拉特旗| 万载县| 剑川县| 龙南县| 宝兴县| 桦南县| 烟台市| 义马市|