反射機(jī)制

前言

HI亏镰,歡迎來到《每周一博》。今天是十二月第二周拯爽,我給大家介紹一下反射的知識(shí)索抓,為什么要介紹這些呢,因?yàn)椴寮夹g(shù)需要它作為基礎(chǔ)某抓。

一. 反射機(jī)制

JAVA反射機(jī)制是在運(yùn)行狀態(tài)中纸兔,對(duì)于任意一個(gè)類,都能夠知道這個(gè)類的所有屬性和方法否副,對(duì)于任意一個(gè)對(duì)象汉矿,都能夠調(diào)用它的任意一個(gè)方法和屬性,這種在運(yùn)行時(shí)動(dòng)態(tài)的獲取信息以及動(dòng)態(tài)調(diào)用對(duì)象的方法的功能稱為Java的反射機(jī)制备禀。

加載一個(gè)類的時(shí)候洲拇,jvm會(huì)去尋找Class文件并載入到內(nèi)存中,在運(yùn)行期間一個(gè)Class對(duì)象在內(nèi)存里只有一個(gè)曲尸,反射就是在運(yùn)行時(shí)從內(nèi)存中解析Class對(duì)象赋续,把屬性和方法映射成一個(gè)個(gè)的Java對(duì)象,原理如圖另患。

反射就好比察看一個(gè)人的內(nèi)臟器官纽乱,然后判斷出這個(gè)人的健康狀況,反射是Java作為一種動(dòng)態(tài)語言的關(guān)鍵性質(zhì)昆箕,利用它可以實(shí)現(xiàn)動(dòng)態(tài)編譯鸦列。

二. 獲取Class對(duì)象

類運(yùn)行時(shí)的類型信息就是用Class對(duì)象表示的,它包含了與類有關(guān)的信息鹏倘。每一個(gè)類都有一個(gè)Class對(duì)象薯嗤,每當(dāng)編譯一個(gè)新類就產(chǎn)生一個(gè)Class對(duì)象,基本類型 (boolean, byte, char, short, int, long, float, and double)有Class對(duì)象纤泵,數(shù)組有Class對(duì)象骆姐,就連關(guān)鍵字void也有Class對(duì)象(void.class)。

Class對(duì)象對(duì)應(yīng)著java.lang.Class類,它沒有公共的構(gòu)造方法玻褪,Class對(duì)象是在類加載的時(shí)候由Java虛擬機(jī)以及通過調(diào)用類加載器中的 defineClass 方法自動(dòng)構(gòu)造的肉渴,因此不能顯式地聲明一個(gè)Class對(duì)象。一個(gè)類被加載到內(nèi)存并供我們使用需要經(jīng)歷如下三個(gè)階段:

  • 加載:這是由類加載器(ClassLoader)執(zhí)行的归园。通過一個(gè)類的全限定名來獲取其定義的二進(jìn)制字節(jié)流(Class字節(jié)碼)黄虱,將這個(gè)字節(jié)流所代表的靜態(tài)存儲(chǔ)結(jié)構(gòu)轉(zhuǎn)化為方法去的運(yùn)行時(shí)數(shù)據(jù)接口,根據(jù)字節(jié)碼在java堆中生成一個(gè)代表這個(gè)類的java.lang.Class對(duì)象庸诱。

  • 鏈接:在鏈接階段將驗(yàn)證Class文件中的字節(jié)流包含的信息是否符合當(dāng)前虛擬機(jī)的要求,為靜態(tài)域分配存儲(chǔ)空間并設(shè)置類變量的初始值晤揣,并且如果必需的話桥爽,將常量池中的符號(hào)引用轉(zhuǎn)化為直接引用。

  • 初始化:到了此階段昧识,才真正開始執(zhí)行類中定義的java程序代碼钠四。用于執(zhí)行該類的靜態(tài)初始器和靜態(tài)初始?jí)K,如果該類有父類的話跪楞,則優(yōu)先對(duì)其父類進(jìn)行初始化缀去。

所有的類都是在對(duì)其第一次使用時(shí),動(dòng)態(tài)加載到JVM中的甸祭。當(dāng)程序創(chuàng)建第一個(gè)對(duì)類的靜態(tài)成員的引用時(shí)缕碎,就會(huì)加載這個(gè)類,使用new創(chuàng)建類對(duì)象的時(shí)候也會(huì)被當(dāng)作對(duì)類的靜態(tài)成員的引用池户。因此Java程序程序在它開始運(yùn)行之前并非被完全加載咏雌,其各個(gè)類都是在必需時(shí)才加載的。

在類加載階段校焦,類加載器首先檢查這個(gè)類的Class對(duì)象是否已經(jīng)被加載赊抖,如果尚未加載,默認(rèn)的類加載器就會(huì)根據(jù)類的全限定名查找.class文件寨典。在這個(gè)類的字節(jié)碼被加載時(shí)氛雪,它們會(huì)接受驗(yàn)證,以確保其沒有被破壞耸成,并且不包含不良Java代碼报亩。一旦某個(gè)類的Class對(duì)象被載入內(nèi)存,我們就可以它來創(chuàng)建這個(gè)類的所有對(duì)象墓猎。

要想反射一個(gè)類捆昏,必須先要獲取到該類的Class對(duì)象,可以通過三種方法獲取Class對(duì)象毙沾。

  1. 通過Object類中的 getClass() 方法骗卜,這是有了對(duì)象時(shí)候調(diào)用的,但是有了對(duì)象其實(shí)沒必要反射了,除非調(diào)用私有屬性寇仓;
Car car = new Car()举户;
Class<?> s = car.getClass();
  1. 每個(gè)類都有一個(gè)靜態(tài)的屬性class遍烦,可以直接通過該屬性獲得Class對(duì)象俭嘁,這種情況是需要導(dǎo)入包的,依賴性太強(qiáng)服猪;
Class<?> s = Car.class;
  1. 通過Class.forName()方法完成供填,必須要指定類的全名含包名,適用于不知道類的情況罢猪,當(dāng)然它會(huì)拋ClassNotFoundException異常近她,一般會(huì)把字符串寫入配置文件中來實(shí)現(xiàn)模塊解耦。
Class<?> c = Class.forName("com.refelct.Car");

還有個(gè)好處是通過Class.forName()會(huì)初始化靜態(tài)塊膳帕,而前兩者不會(huì)粘捎。我們知道當(dāng)一個(gè)類的靜態(tài)塊被調(diào)用的時(shí)候會(huì)進(jìn)行首次加載,但如果一個(gè)字段被static final修飾危彩,那么在調(diào)用這個(gè)字段的時(shí)候是不會(huì)對(duì)類進(jìn)行初始化的攒磨。因?yàn)楸籹tatic和final修飾的字段,在編譯期就把結(jié)果放入了常量池中了汤徽,但是如果只是將一個(gè)域設(shè)置為static 或final的娩缰,還是會(huì)對(duì)類進(jìn)行初始化的。

Class的方法有很多泻骤,和反射的方法很類似漆羔,這里列舉一些常用的。

  • forName:產(chǎn)生Class引用狱掂,forName立即就進(jìn)行了初始化演痒;
  • Object-getClass:獲取Class對(duì)象的一個(gè)引用,返回表示該對(duì)象的實(shí)際類型的Class引用趋惨;
  • getName:取全限定的類名(包括包名)鸟顺;
  • getSimpleName:獲取類名(不包括包名);
  • isInterface:判斷Class對(duì)象是否是表示一個(gè)接口器虾;
  • getInterfaces:返回Class對(duì)象數(shù)組讯嫂,表示Class對(duì)象所引用的類所實(shí)現(xiàn)的所有接口;
  • getSupercalss:返回Class對(duì)象兆沙,表示Class對(duì)象所引用的類所繼承的直接基類欧芽,應(yīng)用該方法可在運(yùn)行時(shí)發(fā)現(xiàn)一個(gè)對(duì)象完整的繼承結(jié)構(gòu);
  • newInstance:返回一個(gè)Oject對(duì)象葛圃,是實(shí)現(xiàn)“虛擬構(gòu)造器”的一種途徑千扔,使用該方法創(chuàng)建的類憎妙,必須帶有無參的構(gòu)造器;
  • getFields:獲得某個(gè)類的所有的public字段曲楚,包括繼承自父類的所有公共字段厘唾。 類似的還有g(shù)etMethods和getConstructors;
  • getDeclaredFields:獲得某個(gè)類的自己聲明的字段龙誊,即包括public抚垃、private和proteced,默認(rèn)不包括父類聲明的任何字段趟大。類似的還有g(shù)etDeclaredMethods和getDeclaredConstructors鹤树;

三. 反射的使用

  1. 反射的API主要涉及這么幾個(gè)類:Constructor描述構(gòu)造函數(shù),F(xiàn)ield描述變量逊朽,Method描述方法魂迄。我們一般用getDeclaredXXX來獲取對(duì)應(yīng)的類型,getDeclaredXXX和getXXX的區(qū)別是前者可以獲取到私有類型惋耙,下面一個(gè)例子打印出了String類的信息。
package com.reflect;
 
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
 
public class Demo {
 
    public static void main(String[] args) throws ClassNotFoundException {
    
        System.out.println("printFieldInfo start===========");
        printFieldInfo("hello");
        System.out.println("printFieldInfo end=============");
        
        System.out.println("printMethodInfo start===========");
        printMethodInfo("hello");
        System.out.println("printMethodInfo end===========");
            
        System.out.println("printConstructorInfo start===========");
        printConstructorInfo("hello");
        System.out.println("printConstructorInfo end===========");
    }
    
    public static void printFieldInfo(Object o){
        Class<?> clazz = o.getClass();
        Field[] declaredFields = clazz.getDeclaredFields();
        for (int i = 0; i < declaredFields.length; i++) {
            Field field = declaredFields[i];
            System.out.print((i+1)+" ");
            System.out.print(Modifier.toString(field.getModifiers())+" ");
            System.out.print(field.getType().getSimpleName()+" ");
            System.out.print(field.getName());
            System.out.println(";");
        }
    }
    
    public static void printMethodInfo(Object o){
        Class<?> clazz = o.getClass();
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (int i = 0; i < declaredMethods.length; i++) {
            Method method = declaredMethods[i];
            System.out.print((i+1)+" ");
            System.out.print(Modifier.toString(method.getModifiers())+" ");
            System.out.print(method.getReturnType().getSimpleName());
            System.out.print("(");
            Class<?>[] parameterTypes = method.getParameterTypes();
            for (int j = 0; j < parameterTypes.length; j++) {
                Class<?> parameterType =parameterTypes[j];
                if(j==parameterTypes.length-1){
                    System.out.print(parameterType.getSimpleName()+" arg"+j);
                }else{
                    System.out.print(parameterType.getSimpleName()+" arg"+j+",");
                }
            }
            System.out.println(");");
        }
    }
    
    
    public static void printConstructorInfo(Object o){
        Class<?> clazz = o.getClass();
        Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
        for (int i = 0; i < declaredConstructors.length; i++) {
            Constructor<?> constructor = declaredConstructors[i];
            System.out.print((i+1)+" ");
            System.out.print(Modifier.toString(constructor.getModifiers())+" ");
            System.out.print(clazz.getSimpleName());
            System.out.print("(");
            Class<?>[] parameterTypes = constructor.getParameterTypes();
            for (int j = 0; j < parameterTypes.length; j++) {
                Class<?> parameterType =parameterTypes[j];
                if(j==parameterTypes.length-1){
                    System.out.print(parameterType.getSimpleName()+" arg"+j);
                }else{
                    System.out.print(parameterType.getSimpleName()+" arg"+j+",");
                }
            }
            System.out.println(");");
        }
    }
 
}

打印結(jié)果如下熊昌;

printFieldInfo start===========
1 private final char[] value;
2 private int hash;
3 private static final long serialVersionUID;
4 private static final ObjectStreamField[] serialPersistentFields;
5 public static final Comparator CASE_INSENSITIVE_ORDER;
printFieldInfo end=============
printMethodInfo start===========
1 public boolean(Object arg0);
2 public String();
……
76 public String(Locale arg0);
77 public String();
printMethodInfo end===========
printConstructorInfo start===========
1 public String(byte[] arg0,int arg1,int arg2);
2 public String(byte[] arg0,Charset arg1);
……
15 public String(byte[] arg0,int arg1);
16 public String(byte[] arg0,int arg1,int arg2,int arg3);
printConstructorInfo end===========
  1. 實(shí)際操練绽榛,編寫測(cè)試類,定義了有參和無參的構(gòu)造函數(shù)婿屹,普通變量灭美,靜態(tài)變量,普通方法昂利,靜態(tài)方法届腐。
public class BeReflected {
    // 私有普通變量
    private String field1 = "I am field1";
    // 私有靜態(tài)變量
    private static String staticField = "I am staticField";
    // 無參構(gòu)造方法
    public BeReflected() {
    }
    // 有參構(gòu)造方法
    public BeReflected(String s) {
        field1 = s;
    }
    // 普通無參方法
    private void method1() {
        System.out.println("I am method1");
    }
    // 普通帶1個(gè)參數(shù)方法
    private void method2(String param) {
        System.out.println("I am method1--param = " + param);
    }
    // 普通帶多個(gè)參數(shù)方法
    private void method3(String param, String param2, int param3) {
        System.out.println("param = " + param + " param2 = " + param2 + " param3 = " + param3);
    }
    // 靜態(tài)無參方法
    public static void staticMethod() {
        System.out.println("I am staticMethod");
    }
    // 靜態(tài)帶參數(shù)方法
    public static void staticMethod(String s) {
        System.out.println("I am staticMethod:s:" + s);
    }
}
  1. 反射創(chuàng)建對(duì)象:通過Class的newInstance對(duì)象就可以創(chuàng)建一個(gè)對(duì)象,所以new只是創(chuàng)建對(duì)象的一種方式蜂奸。當(dāng)然通過newInstance要求改類需要有一個(gè)空的無參構(gòu)造方法犁苏。
Class<?> c = Class.forName("com.refelct.BeReflected");
Object obj = c.newInstance();
  1. 反射調(diào)用構(gòu)造函數(shù):如果想調(diào)用有參數(shù)的構(gòu)造函數(shù),就要用到Constructor這個(gè)類了扩所,通過Class的getDeclaredConstructor方法可以獲得Constructor對(duì)象围详,傳入的參數(shù)是方法參數(shù)類型,比如int要傳入int.class祖屏,字符串傳String.class助赞,字符串?dāng)?shù)組傳String[].class;
Class<?> c = Class.forName("com.refelct.BeReflected");
Constructor ss = c.getDeclaredConstructor(String.class);
Object tt = ss.newInstance("測(cè)試構(gòu)造函數(shù)");
  1. 反射獲取靜態(tài)變量:調(diào)用屬性就要用到Field這個(gè)類了袁勺,通過getDeclaredField方法獲取到屬性后需要設(shè)置setAccessible(true)雹食。由于靜態(tài)變量是屬于類的,所以不需要類的實(shí)例期丰,直接調(diào)用Field類的get(null)即可獲得群叶;
Class<?> c = Class.forName("com.refelct.BeReflected");
Field field = c.getDeclaredField("staticField");
      if (field != null) {
            field.setAccessible(true);
            Object o = field.get(null);
            System.out.println("o:" + o);
      } 
  1. 反射獲取私有普通變量:由于普通變量是屬于對(duì)象的吃挑,所以需要先獲得類的實(shí)例,然后再調(diào)用Field類的get(obj)盖呼;
Class<?> c = Class.forName("com.refelct.BeReflected");
Object obj = c.newInstance();
Field field = c.getDeclaredField("field1");
      if (field != null) {
            field.setAccessible(true);
            Object o = field.get(obj);
            System.out.println("o:" + o);
      } 

所以靜態(tài)和非靜態(tài)的區(qū)別在于是否需要傳入對(duì)象儒鹿,當(dāng)然我試了靜態(tài)方法和屬性傳入obj也可以獲取到。

  1. 反射設(shè)置私有變量:通過Field類的set(obj几晤,value)可以修改私有變量约炎。
Class<?> c = Class.forName("com.refelct.BeReflected");
Object obj = c.newInstance();
 Field field = c.getDeclaredField("field1");
            if (field != null) {
                field.setAccessible(true);
                field.set(obj, "測(cè)試變量");
                System.out.println("o:" + field.get(obj));
            } 
  1. 反射調(diào)用無參方法:方法主要是用到Method類,先通過getDeclaredMethod()獲取方法蟹瘾,然后setAccessible(true)圾浅,接著執(zhí)行invoke函數(shù),同理靜態(tài)方法不需要對(duì)象憾朴。
Class<?> c = Class.forName("com.refelct.BeReflected");
// 調(diào)用靜態(tài)無參方法
Method method = c.getDeclaredMethod("method1");
            if (method != null) {
                method.setAccessible(true);
                method.invoke(null);
            }

// 調(diào)用普通無參方法
Object obj = c.newInstance();
Method method = c.getDeclaredMethod("method1");
            if (method != null) {
                method.setAccessible(true);
                method.invoke(obj);
            }
  1. 反射調(diào)用有參方法:有參是需要傳入?yún)?shù)類型Class<?>... parameterTypes和參數(shù)值Object... args的狸捕。
Class<?> c = Class.forName("com.refelct.BeReflected");
// 調(diào)用普通有1個(gè)參數(shù)的方法
Object obj = c.newInstance();
Method method = c.getDeclaredMethod("method2",String.class);
            if (method != null) {
                method.setAccessible(true);
                method.invoke(obj,"我是測(cè)試值");
            }

// 調(diào)用普通有多個(gè)參數(shù)的方法
Object obj = c.newInstance();
Method method = c.getDeclaredMethod("method3",
      new Class<?>[]{String.class, String.class, int.class});
            if (method != null) {
                method.setAccessible(true);
                method.invoke(obj, new Object[]{"1", "2", 3});
            }

四. 反射的其他知識(shí)

  1. final變量可以反射嗎?
    如果是直接聲明的众雷,是無法反射的灸拍,因?yàn)榫幾g期間final類型的數(shù)據(jù)自動(dòng)被優(yōu)化了,即所有用到該變量的地方都被替換成了常量砾省。所以 get方法在編譯后自動(dòng)優(yōu)化成了return "gps"鸡岗,而不是 return GPS_PROVIDER。
private static final String GPS_PROVIDER = "gps";

但如果不是直接定義的就可以反射

private static final String GPS_PROVIDER ;
public LocationManager(){
           GPS_PROVIDER = "gps";
}
  1. 只能反射自己jvm所包含的class编兄,不能反射別的進(jìn)程里的類轩性,比如想反射微信里的某個(gè)字段,那是不可能的狠鸳。

五. 反射的問題

  1. 反射的效率問題:反射比直接調(diào)用實(shí)例要慢揣苏,getMethod和getDeclaredField方法會(huì)比invoke和set方法耗時(shí),詳細(xì)介紹可以參考這篇文章件舵。這里我們做個(gè)測(cè)試卸察,反射調(diào)用靜態(tài)方法正常調(diào)用靜態(tài)方法各100000遍。
    private static void testTime() throws Exception {
        {
            long t1 = System.currentTimeMillis();
            Class<?> c = Class.forName("com.refelct.BeReflected");
            Object obj = c.newInstance();
            Method m = c.getDeclaredMethod("method1");
            m.setAccessible(true);
            for (int i = 0; i < 100000; i++) {
                m.invoke(obj);
            }
            long t2 = System.currentTimeMillis() - t1;
            System.out.println("反射消耗:" + t2);
        }
        {
            long t1 = System.currentTimeMillis();
            BeReflected beReflected = new BeReflected();
            for (int i = 0; i < 100000; i++) {
                beReflected.method1();
            }
            long t2 = System.currentTimeMillis() - t1;
            System.out.println("正常消耗:" + t2);
        }
    }


結(jié)果打勇:
正常消耗:8ms
反射消耗:65ms

可以看到但是調(diào)用invoke就很耗時(shí)了蛾派,還沒有把getMethod和Class.forName加入循環(huán),那么有什么可以提高反射效率的方法嗎个少?這里我想到這么幾點(diǎn)洪乍;

  • 使用接口代替Object:在用反射創(chuàng)建對(duì)象時(shí)轉(zhuǎn)成類的實(shí)例或者接口,然后執(zhí)行方法調(diào)用夜焦,避免調(diào)用invoke壳澳;
            IRe obj = (IRe) c.newInstance();
            // BeReflected obj = (BeReflected) c.newInstance();
            for (int i = 0; i < 100000; i++) {
                obj.method1();
            }
結(jié)果打印:
正常消耗:7ms
反射消耗:5ms
  • 使用緩存:對(duì)于中間產(chǎn)物使用緩存存儲(chǔ)下來茫经,比如class對(duì)象巷波,方法名萎津,變量,可以用一個(gè)map來做緩存抹镊;

  • 使用getDeclaredMethod(param)是要由于先調(diào)用getMethod然后去遍歷方法名的锉屈,getDeclaredField也是;

  1. 反射的安全問題:因?yàn)榭梢噪S意修改類的所有狀態(tài)和行為垮耳,包括private方法和實(shí)例颈渊,所以如果不熟悉被反射類的實(shí)現(xiàn)原理,隨意修改可能導(dǎo)致潛在的邏輯問題终佛;

  2. 兼容性問題:反射會(huì)涉及到直接訪問類的方法名和實(shí)例名俊嗽,不同版本的API如果有變動(dòng),反射時(shí)找不到對(duì)應(yīng)的屬性和方法時(shí)會(huì)報(bào)異常铃彰,最常見的就是針對(duì)安卓版本的適配绍豁;

  3. 其他問題:有用到反射的類不能被混淆,靜態(tài)編譯時(shí)沒有用到的類和資源不能被刪除牙捉,否則反射找不到竹揍;

  4. 反射的特點(diǎn):
    反射自由度高,不受類的訪問權(quán)限限制邪铲;
    反射存在性能問題鬼佣,但使用不頻繁時(shí)對(duì)程序性能影響并不大;
    反射是改不了方法霜浴,攔截方法需要采用動(dòng)態(tài)代理;

六. 反射的用途

為什么需要反射呢蓝纲,這用途說來就很多了阴孟。

  1. 構(gòu)建框架:一般構(gòu)建框架的時(shí)候會(huì)用到反射,比如創(chuàng)建Activity的時(shí)候就用到了newInstance方法税迷,Java當(dāng)中的很多框架都采用反射永丝。
  2. 構(gòu)建設(shè)計(jì)模式:比如一個(gè)采用反射來創(chuàng)建對(duì)象的工廠模式。
public class Factory {
    public static <T extends Product> T getProduct(String className){
        Class<?> cls = Class.forName(className);
        Product product = (Product) cls.newInstance();
        return (T) product;
    }
}
  1. 按需加載類箭养,節(jié)省編譯和初始化APK的時(shí)間慕嚷。動(dòng)態(tài)加載第三方j(luò)ar包,解決安卓開發(fā)中方法數(shù)不能超過65536個(gè)的問題毕泌;
  2. 通過反射運(yùn)行配置文件喝检,實(shí)現(xiàn)解耦,其實(shí)也是設(shè)計(jì)框架的思想撼泛;
  3. 跳過泛型檢查:如果我們往List里添加元素挠说,必須符合類型檢查,如果不符合就編譯不過愿题,泛型是在編譯期檢查的损俭,通過反射就可以在運(yùn)行時(shí)跳過這個(gè)限制蛙奖。
ArrayList<String> strList = new ArrayList<>();
strList.add("aaa");
strList.add("bbb");
// 正常情況下添加一個(gè)100是無法通過編譯的
// strList.add(100);

//獲取ArrayList的Class對(duì)象,反向的調(diào)用add()方法杆兵,添加數(shù)據(jù)
Class listClass = strList.getClass(); 
Method m = listClass.getMethod("add", Object.class);
m.invoke(strList, 100);

// 輸出的時(shí)候注意這里用了Object雁仲,而不是String
for(Object obj : strList){
    System.out.println(obj);
}
  1. 反射執(zhí)行main方法
    Class clazz = Class.forName("fanshe.main.Student");
    Method methodMain = clazz.getMethod("main", String[].class);
    // 方式一
    methodMain.invoke(null, (Object)new String[]{"a","b","c"});
    // 方式二
    // methodMain.invoke(null, new Object[]{new String[]{"a","b","c"}});
  1. 寫一個(gè)簡(jiǎn)單的反射工具類ReflectUtils,但實(shí)際做框架時(shí)還是要轉(zhuǎn)成接口的琐脏,還要考慮泛型攒砖,不能一味的使用Object。
public class ReflectUtils {

    // 反射一個(gè)對(duì)象的無參方法骆膝,靜態(tài)方法obj可以傳null
    // ReflectUtils.invoke(c, "staticMethod", obj)
    public static Object invoke(Class<?> c, String methodName, Object obj) {
        try {
            Method method = c.getDeclaredMethod(methodName);
            if (method != null) {
                method.setAccessible(true);
                return method.invoke(obj);
            } else {
                p("該方法不存在");
            }

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

    // 反射一個(gè)對(duì)象的有一個(gè)參數(shù)方法祭衩,靜態(tài)方法obj可以傳null
    // ReflectUtils.invoke(c, "staticMethod", obj, String.class, "test");
    public static void invoke(Class<?> c, String methodName, Object obj, Class<?> target, Object params) {
        try {
            Method method = c.getDeclaredMethod(methodName, target);
            if (method != null) {
                method.setAccessible(true);
                method.invoke(obj, params);
            } else {
                p("該方法不存在");
            }

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

    // 反射一個(gè)對(duì)象的有多個(gè)參數(shù)方法,靜態(tài)方法obj可以傳null
    // ReflectUtils.invoke(c, "method4", obj, new Class<?>[]{String.class, String.class},
    // new String[]{"1", "2"});
    // ReflectUtils.invoke(c, "method3", obj, new Class<?>[]{String.class, String.class, int.class},
    // new Object[]{"1", "2", 3});
    public static void invoke(Class<?> c, String methodName, Object obj, Class<?>[] target, Object[] params) {
        try {
            Method method = c.getDeclaredMethod(methodName, target);
            if (method != null) {
                method.setAccessible(true);
                method.invoke(obj, params);
            } else {
                p("該方法不存在");
            }

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

    // 獲取一個(gè)對(duì)象的值阅签,靜態(tài)變量obj可以傳null
    // ReflectUtils.getValue(c, "pubfield1", obj);
    public static Object getValue(Class<?> c, String fieldName, Object obj) {
        try {
            Field field = c.getDeclaredField(fieldName);
            if (field != null) {
                field.setAccessible(true);
                return field.get(obj);
            } else {
                p("該變量不存在");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    // 修改一個(gè)對(duì)象的值掐暮,靜態(tài)變量obj可以傳null
    // ReflectUtils.setValue(c, "staticField", obj, "測(cè)試變量")
    public static Object setValue(Class<?> c, String fieldName, Object obj, Object value) {
        try {
            Field field = c.getDeclaredField(fieldName);
            if (field != null) {
                field.setAccessible(true);
                field.set(obj, value);
                return field.get(obj);
            } else {
                p("該變量不存在");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    // 傳入被代理對(duì)象的classloader,實(shí)現(xiàn)的接口,還有DynamicProxyHandler的對(duì)象即可
    public static Object newProxyInstance(Object object, InvocationHandler invocationHandler) {
        return Proxy.newProxyInstance(object.getClass().getClassLoader(),
                object.getClass().getInterfaces(), invocationHandler);
    }


    public static void p(String s) {
        System.out.println("" + s);
        // Log.e("gzq",""+s);
    }
}

七. 總結(jié)

本文介紹了反射的一些基本知識(shí),也是為下篇?jiǎng)討B(tài)代理做一個(gè)鋪墊政钟,感謝大家的閱讀路克,我們下周再見。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末养交,一起剝皮案震驚了整個(gè)濱河市精算,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌碎连,老刑警劉巖灰羽,帶你破解...
    沈念sama閱讀 206,723評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異鱼辙,居然都是意外死亡廉嚼,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門倒戏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來怠噪,“玉大人,你說我怎么就攤上這事杜跷“睿” “怎么了?”我有些...
    開封第一講書人閱讀 152,998評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵葛闷,是天一觀的道長(zhǎng)憋槐。 經(jīng)常有香客問我,道長(zhǎng)淑趾,這世上最難降的妖魔是什么秦陋? 我笑而不...
    開封第一講書人閱讀 55,323評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮治笨,結(jié)果婚禮上驳概,老公的妹妹穿的比我還像新娘赤嚼。我一直安慰自己,他們只是感情好顺又,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評(píng)論 5 374
  • 文/花漫 我一把揭開白布更卒。 她就那樣靜靜地躺著,像睡著了一般稚照。 火紅的嫁衣襯著肌膚如雪蹂空。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,079評(píng)論 1 285
  • 那天果录,我揣著相機(jī)與錄音上枕,去河邊找鬼。 笑死弱恒,一個(gè)胖子當(dāng)著我的面吹牛辨萍,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播返弹,決...
    沈念sama閱讀 38,389評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼锈玉,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了义起?” 一聲冷哼從身側(cè)響起拉背,我...
    開封第一講書人閱讀 37,019評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎默终,沒想到半個(gè)月后椅棺,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,519評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡齐蔽,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評(píng)論 2 325
  • 正文 我和宋清朗相戀三年土陪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片肴熏。...
    茶點(diǎn)故事閱讀 38,100評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖顷窒,靈堂內(nèi)的尸體忽然破棺而出蛙吏,到底是詐尸還是另有隱情,我是刑警寧澤鞋吉,帶...
    沈念sama閱讀 33,738評(píng)論 4 324
  • 正文 年R本政府宣布鸦做,位于F島的核電站,受9級(jí)特大地震影響谓着,放射性物質(zhì)發(fā)生泄漏泼诱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評(píng)論 3 307
  • 文/蒙蒙 一赊锚、第九天 我趴在偏房一處隱蔽的房頂上張望然评。 院中可真熱鬧缠劝,春花似錦独悴、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽堤框。三九已至域滥,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蜈抓,已是汗流浹背启绰。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留沟使,地道東北人委可。 一個(gè)月前我還...
    沈念sama閱讀 45,547評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像格带,于是被迫代替她去往敵國(guó)和親撤缴。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容