深入淺出反射

什么是反射涵紊?

反射是一種能夠在程序運(yùn)行時(shí)動(dòng)態(tài)訪問(wèn)掌逛、修改某個(gè)類中任意屬性(狀態(tài))和方法(行為)的機(jī)制(包括private實(shí)例和方法),java反射機(jī)制提供了以下幾個(gè)功能:

  • 在運(yùn)行時(shí)判斷任意一個(gè)對(duì)象所屬的類禽篱;

  • 在運(yùn)行時(shí)構(gòu)造任意一個(gè)類的對(duì)象八千;

  • 在運(yùn)行時(shí)判斷任意一個(gè)類所具有的成員變量和方法;

  • 在運(yùn)行時(shí)調(diào)用任意一個(gè)對(duì)象的方法袖裕。

反射涉及到四個(gè)核心類:

  • java.lang.Class.java:類對(duì)象曹抬;

  • java.lang.reflect.Constructor.java:類的構(gòu)造器對(duì)象;

  • java.lang.reflect.Method.java:類的方法對(duì)象急鳄;

  • java.lang.reflect.Field.java:類的屬性對(duì)象谤民;

反射有什么用?

  • 操作因訪問(wèn)權(quán)限限制的屬性和方法攒岛;

  • 實(shí)現(xiàn)自定義注解赖临;

  • 動(dòng)態(tài)加載第三方j(luò)ar包,解決android開(kāi)發(fā)中方法數(shù)不能超過(guò)65536個(gè)的問(wèn)題灾锯;

  • 按需加載類兢榨,節(jié)省編譯和初始化APK的時(shí)間;

反射工作原理

當(dāng)我們編寫完一個(gè)Java項(xiàng)目之后顺饮,每個(gè)java文件都會(huì)被編譯成一個(gè).class文件吵聪,這些Class對(duì)象承載了這個(gè)類的所有信息,包括父類兼雄、接口吟逝、構(gòu)造函數(shù)、方法赦肋、屬性等块攒,這些class文件在程序運(yùn)行時(shí)會(huì)被ClassLoader加載到虛擬機(jī)中励稳。當(dāng)一個(gè)類被加載以后,Java虛擬機(jī)就會(huì)在內(nèi)存中自動(dòng)產(chǎn)生一個(gè)Class對(duì)象囱井。我們通過(guò)new的形式創(chuàng)建對(duì)象實(shí)際上就是通過(guò)這些Class來(lái)創(chuàng)建驹尼,只是這個(gè)過(guò)程對(duì)于我們是不透明的而已。

反射的工作原理就是借助Class.java庞呕、Constructor.java新翎、Method.java、Field.java這四個(gè)類在程序運(yùn)行時(shí)動(dòng)態(tài)訪問(wèn)和修改任何類的行為和狀態(tài)住练。

反射實(shí)例

分別演示三種獲取類信息的方式地啰、獲取當(dāng)前類的所有方法和獲取當(dāng)前類及其父類的所有方法、獲取當(dāng)前類的所有實(shí)例和獲取當(dāng)前類及其父類的所有實(shí)例讲逛、獲取父類信息亏吝、獲取接口信息、比較反射方法和實(shí)例的性能差異等幾個(gè)方面:

  • 示例類:

父類Personon.java:

package com.eebbk.reflectdemo;

public class Person{
    String mName;
    String mSex;
    public int mAge;

    public Person(String aName, String aSex, int aAge) {
        mName = aName;
        mSex = aSex;
        mAge = aAge;
    }

    public int getmAge(){
        return mAge;
    }

    public void setmAge(int mAge){
        this.mAge = mAge;
    }

    public String getmName(){
        return mName;
    }

    public void setmName(String mName){
        this.mName = mName;
    }

    public String getmSex(){
        return mSex;
    }

    public void setmSex(String mSex){
        this.mSex = mSex;
    }

    private String getDescription(){
        return "黃種人";
    }
}

接口ICompany.java:

package com.eebbk.reflectdemo;

public interface ICompany{
    String getCompany();
}

子類ProgramMonkey.java:

package com.eebbk.reflectdemo;

public class ProgramMonkey extends Person implements ICompany{
    String mLanguage = "C#";
    String mCompany = "BBK";

    public ProgramMonkey(String aName, String aSex, int aAge){
        super(aName, aSex, aAge);
    }

    public ProgramMonkey(String language, String company, String aName, String aSex, int aAge){
        super(aName, aSex, aAge);
        mLanguage = language;
        mCompany = company;
    }

    public String getmLanguage(){
        return mLanguage;
    }

    public void setmLanguage(String mLanguage){
        this.mLanguage = mLanguage;
    }

    private int getSalaryPerMonth(){
        return 12306;
    }

    @Override
    public String getCompany(){
        return mCompany;
    }
}

示例類ReflectActivity.java:

public class ReflectActivity extends Activity{
    @Override
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_reflect_layout);
    }

    public void onClick(View v){
        switch(v.getId()){
            case R.id.getClassObjectBtnId:{
                getClassObject();
            }
            break;
            case R.id.getMethodInfoBtnId:{
                getMethodInfo();
            }
            break;
            case R.id.getFieldInfoBtnId:{
                getFieldInfo();
            }
            break;
            case R.id.getSuperClassInfoBtnId:{
                getSuperClass();
            }
            break;
            case R.id.getInterfaceInfoBtnId:{
                getInterfaces();
            }
            break;
            case R.id.compareMethodAndFieldBtnId:{
                compareCallMethodAndField();
            }
            break;
            default:{

            }
            break;
        }
    }

    private void getClassObject(){
        Class<?> classObject = null;

        classObject = getClassObject_1();
        LogE("classObject_1 name : " + classObject.getName());
        classObject = getClassObject_2();
        LogE("classObject_2 name : " + classObject.getName());
        classObject = getClassObject_3();
        LogE("classObject_3 name : " + classObject.getName());
    }

    private void getMethodInfo(){
        getAllMethods();
        getCurrentClassMethods();
    }

    private void getFieldInfo(){
        getAllFields();
        getCurrentClassFields();
    }

    private void getSuperClass(){
        ProgramMonkey programMonkey = new ProgramMonkey("小明", "男", 12);
        Class<?> superClass = programMonkey.getClass().getSuperclass();
        while (superClass != null) {
            LogE("programMonkey's super class is : " + superClass.getName());
            // 再獲取父類的上一層父類妆绞,直到最后的 Object 類顺呕,Object 的父類為 null
            superClass = superClass.getSuperclass();
        }
    }

    private void getInterfaces() {
        ProgramMonkey programMonkey = new ProgramMonkey("小明", "男", 12);
        Class<?>[] interfaceses = programMonkey.getClass().getInterfaces();
        for (Class<?> class1 : interfaceses) {
            LogE("programMonkey's interface is : " + class1.getName());
        }
    }

    private void compareCallMethodAndField(){
        long callMethodCostTime = getCallMethodCostTime(10000);
        LogE("callMethodCostTime == " + callMethodCostTime);
        long callFieldCostTime = getCallFieldCostTime(10000);
        LogE("callFieldCostTime == " + callFieldCostTime);
    }

    private long getCallMethodCostTime(int count){
        long startTime = System.currentTimeMillis();
        for(int index = 0 ; index < count; index++){
            ProgramMonkey programMonkey = new ProgramMonkey("小明", "男", 12);
            try{
                Method setmLanguageMethod = programMonkey.getClass().getMethod("setmLanguage", String.class);
                setmLanguageMethod.setAccessible(true);
                setmLanguageMethod.invoke(programMonkey, "Java");
            }catch(IllegalAccessException e){
                e.printStackTrace();
            }catch(InvocationTargetException e){
                e.printStackTrace();
            }catch(NoSuchMethodException e){
                e.printStackTrace();
            }
        }

        return System.currentTimeMillis()-startTime;
    }

    private long getCallFieldCostTime(int count){
        long startTime = System.currentTimeMillis();
        for(int index = 0 ; index < count; index++){
            ProgramMonkey programMonkey = new ProgramMonkey("小明", "男", 12);
            try{
                Field ageField = programMonkey.getClass().getDeclaredField("mLanguage");
                ageField.set(programMonkey, "Java");
            }catch(NoSuchFieldException e){
                e.printStackTrace();
            }catch(IllegalAccessException e){
                e.printStackTrace();
            }
        }

        return System.currentTimeMillis()-startTime;
    }

    /**
     * 獲取當(dāng)前類中的方法
     *
     * */
    private void getCurrentClassMethods() {
        ProgramMonkey programMonkey = new ProgramMonkey("小明", "男", 12);
        Method[] methods = programMonkey.getClass().getDeclaredMethods();
        for (Method method : methods) {
            LogE("declared method name : " + method.getName());
        }

        try {
            Method getSalaryPerMonthMethod = programMonkey.getClass().getDeclaredMethod("getSalaryPerMonth");
            getSalaryPerMonthMethod.setAccessible(true);
            // 獲取返回類型
            Class<?> returnType = getSalaryPerMonthMethod.getReturnType();
            LogE("getSalaryPerMonth 方法的返回類型 : " + returnType.getName());

            // 獲取方法的參數(shù)類型列表
            Class<?>[] paramClasses = getSalaryPerMonthMethod.getParameterTypes() ;
            for (Class<?> class1 : paramClasses) {
                LogE("getSalaryPerMonth 方法的參數(shù)類型 : " + class1.getName());
            }

            // 是否是 private 函數(shù),屬性是否是 private 也可以使用這種方式判斷
            LogE(getSalaryPerMonthMethod.getName() + " is private " + Modifier.isPrivate(getSalaryPerMonthMethod.getModifiers()));

            // 執(zhí)行方法
            Object result = getSalaryPerMonthMethod.invoke(programMonkey);
            LogE("getSalaryPerMonth 方法的返回結(jié)果: " + result);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 獲取當(dāng)前類和父類的所有方法
     *
     * */
    private void getAllMethods() {
        ProgramMonkey programMonkey = new ProgramMonkey("小明", "男", 12);
        // 獲取當(dāng)前類和父類的所有方法
        Method[] methods = programMonkey.getClass().getMethods();
        for (Method method : methods) {
            LogE("method name : " + method.getName());
        }

        try {
            Method setmLanguageMethod = programMonkey.getClass().getMethod("setmLanguage", String.class);
            setmLanguageMethod.setAccessible(true);

            // 獲取返回類型
            Class<?> returnType = setmLanguageMethod.getReturnType();
            LogE("setmLanguage 方法的返回類型 : " + returnType.getName());

            // 獲取方法的參數(shù)類型列表
            Class<?>[] paramClasses = setmLanguageMethod.getParameterTypes() ;
            for (Class<?> class1 : paramClasses) {
                LogE("setmLanguage 方法的參數(shù)類型 : " + class1.getName());
            }

            // 是否是 private 函數(shù)括饶,屬性是否是 private 也可以使用這種方式判斷
            LogE(setmLanguageMethod.getName() + " is private " + Modifier.isPrivate(setmLanguageMethod.getModifiers()));

            // 執(zhí)行方法
            Object result = setmLanguageMethod.invoke(programMonkey, "Java");
            LogE("setmLanguage 方法的返回結(jié)果: " + result);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private Class<?> getClassObject_1(){
        return ProgramMonkey.class;
    }

    private Class<?> getClassObject_2(){
        ProgramMonkey programMonkey = new ProgramMonkey("小明", "男", 12);
        return programMonkey.getClass();
    }

    private Class<?> getClassObject_3(){
        try{
            return Class.forName("com.eebbk.reflectdemo.ProgramMonkey");
        }catch(ClassNotFoundException e){
            e.printStackTrace();
        }

        return null;
    }

    /**
     * 得到當(dāng)前類的所有實(shí)例
     *
     * */
    private void getCurrentClassFields() {
        ProgramMonkey programMonkey = new ProgramMonkey("小明", "男", 12);
        // 獲取當(dāng)前類的所有屬性
        Field[] publicFields = programMonkey.getClass().getDeclaredFields();
        for (Field field : publicFields) {
            LogE("declared field name : " + field.getName());
        }

        try {
            // 獲取當(dāng)前類的某個(gè)屬性
            Field ageField = programMonkey.getClass().getDeclaredField("mAge");
            // 獲取屬性值
            LogE(" my age is : " + ageField.getInt(programMonkey));
            // 設(shè)置屬性值
            ageField.set(programMonkey, 10);
            LogE(" my age is : " + ageField.getInt(programMonkey));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 得到當(dāng)前類及其父類的所有實(shí)例
     *
     * */
    private void getAllFields() {
        ProgramMonkey programMonkey = new ProgramMonkey("小明", "男", 12);
        // 獲取當(dāng)前類和父類的所有屬性
        Field[] publicFields = programMonkey.getClass().getFields();
        for (Field field : publicFields) {
            LogE("field name : " + field.getName());
        }

        try {
            // 獲取當(dāng)前類和父類的某個(gè)屬性
            Field ageField = programMonkey.getClass().getField("mAge");
            LogE(" age is : " + ageField.getInt(programMonkey));
            ageField.set(programMonkey, 8);
            LogE(" my age is : " + ageField.getInt(programMonkey));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void LogE(String msg){
        Log.e("Reflection", "============== " + msg);
    }
}
  • 演示結(jié)果:

三種獲取類信息的方式:

三種獲取類信息的方式

獲取當(dāng)前類的方法株茶、獲取當(dāng)前類和父類的所有方法:

獲取當(dāng)前類的方法、獲取當(dāng)前類和父類的所有方法

獲取當(dāng)前類的所有實(shí)例图焰、獲取當(dāng)前類和父類的所有實(shí)例:

獲取當(dāng)前類的所有實(shí)例启盛、獲取當(dāng)前類和父類的所有實(shí)例

獲取父類信息:

獲取父類信息

獲取接口信息:

獲取接口信息

比較反射方法和實(shí)例的性能差異:

比較反射方法和實(shí)例的性能差異

通過(guò)上面的示例可以發(fā)現(xiàn),通過(guò)反射能夠完成之前所描述的事情技羔,并且反射方法比反射實(shí)例要慢很多僵闯。

反射的特點(diǎn)

優(yōu)點(diǎn)

  • 靈活、自由度高:不受類的訪問(wèn)權(quán)限限制藤滥,想對(duì)類做啥就做啥鳖粟;

缺點(diǎn)

  • 性能問(wèn)題:通過(guò)反射訪問(wèn)、修改類的屬性和方法時(shí)會(huì)遠(yuǎn)慢于直接操作拙绊,但性能問(wèn)題的嚴(yán)重程度取決于在程序中是如何使用反射的向图。如果使用得很少,不是很頻繁标沪,性能將不會(huì)是什么問(wèn)題榄攀;

  • 安全性問(wèn)題:反射可以隨意訪問(wèn)和修改類的所有狀態(tài)和行為,破壞了類的封裝性金句,如果不熟悉被反射類的實(shí)現(xiàn)原理檩赢,隨意修改可能導(dǎo)致潛在的邏輯問(wèn)題;

  • 兼容性問(wèn)題:因?yàn)榉瓷鋾?huì)涉及到直接訪問(wèn)類的方法名和實(shí)例名违寞,不同版本的API如果有變動(dòng)贞瞒,反射時(shí)找不到對(duì)應(yīng)的屬性和方法時(shí)會(huì)報(bào)異常偶房;

說(shuō)明

  • 通過(guò)反射訪問(wèn)方法比實(shí)例慢很多;

  • 有用到反射的類不能被混淆憔狞;

  • 反射存在性能問(wèn)題蝴悉,但使用不頻繁、按需使用時(shí)瘾敢,對(duì)程序性能影響并不大;

  • 反射存在安全性問(wèn)題尿这,因?yàn)榭梢噪S意修改類的所有狀態(tài)和行為(包括private方法和實(shí)例)簇抵;

  • 使用反射訪問(wèn)Android的API時(shí)需要注意因?yàn)椴煌珹PI版本導(dǎo)致的兼容性問(wèn)題;

參考資料

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末射众,一起剝皮案震驚了整個(gè)濱河市碟摆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌叨橱,老刑警劉巖典蜕,帶你破解...
    沈念sama閱讀 216,591評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異罗洗,居然都是意外死亡愉舔,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門伙菜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)轩缤,“玉大人,你說(shuō)我怎么就攤上這事贩绕』鸬模” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 162,823評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵淑倾,是天一觀的道長(zhǎng)馏鹤。 經(jīng)常有香客問(wèn)我,道長(zhǎng)娇哆,這世上最難降的妖魔是什么湃累? 我笑而不...
    開(kāi)封第一講書人閱讀 58,204評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮迂尝,結(jié)果婚禮上脱茉,老公的妹妹穿的比我還像新娘。我一直安慰自己垄开,他們只是感情好琴许,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著溉躲,像睡著了一般榜田。 火紅的嫁衣襯著肌膚如雪益兄。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 51,190評(píng)論 1 299
  • 那天箭券,我揣著相機(jī)與錄音净捅,去河邊找鬼。 笑死辩块,一個(gè)胖子當(dāng)著我的面吹牛蛔六,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播废亭,決...
    沈念sama閱讀 40,078評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼国章,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了豆村?” 一聲冷哼從身側(cè)響起液兽,我...
    開(kāi)封第一講書人閱讀 38,923評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎掌动,沒(méi)想到半個(gè)月后四啰,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,334評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡粗恢,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評(píng)論 2 333
  • 正文 我和宋清朗相戀三年柑晒,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片适滓。...
    茶點(diǎn)故事閱讀 39,727評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡敦迄,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出凭迹,到底是詐尸還是另有隱情罚屋,我是刑警寧澤,帶...
    沈念sama閱讀 35,428評(píng)論 5 343
  • 正文 年R本政府宣布嗅绸,位于F島的核電站脾猛,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏鱼鸠。R本人自食惡果不足惜猛拴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蚀狰。 院中可真熱鬧愉昆,春花似錦、人聲如沸麻蹋。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,672評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至芳室,卻和暖如春专肪,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背堪侯。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,826評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工嚎尤, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人伍宦。 一個(gè)月前我還...
    沈念sama閱讀 47,734評(píng)論 2 368
  • 正文 我出身青樓芽死,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親雹拄。 傳聞我的和親對(duì)象是個(gè)殘疾皇子收奔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評(píng)論 2 354

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

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)滓玖,斷路器,智...
    卡卡羅2017閱讀 134,652評(píng)論 18 139
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法质蕉,類相關(guān)的語(yǔ)法势篡,內(nèi)部類的語(yǔ)法,繼承相關(guān)的語(yǔ)法模暗,異常的語(yǔ)法禁悠,線程的語(yǔ)...
    子非魚_t_閱讀 31,623評(píng)論 18 399
  • 今天電腦系統(tǒng)更新完以后突然很卡,不知道原因兑宇,eclipse 服務(wù)也啟動(dòng)不起來(lái)了碍侦,就這樣別人都在干活,我一個(gè)人在這兒...
    魂歸瀟湘閱讀 140評(píng)論 0 0
  • 沒(méi)傘的挨著有傘的人走隶糕,靠得再近也躲不過(guò)雨瓷产;而無(wú)傘也有雨過(guò)天晴的時(shí)候,也能擁有屬于自己的陽(yáng)光天地枚驻。
    呂明超閱讀 117評(píng)論 0 0