深入淺出反射

什么是反射?

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

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

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

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

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

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

  • java.lang.Class.java:類對象;

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

  • java.lang.reflect.Method.java:類的方法對象;

  • java.lang.reflect.Field.java:類的屬性對象;

反射有什么用?

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

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

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

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

反射工作原理

當(dāng)我們編寫完一個(gè)Java項(xiàng)目之后,每個(gè)java文件都會(huì)被編譯成一個(gè).class文件,這些Class對象承載了這個(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對象。我們通過new的形式創(chuàng)建對象實(shí)際上就是通過這些Class來創(chuàng)建,只是這個(gè)過程對于我們是不透明的而已。

反射的工作原理就是借助Class.java、Constructor.java、Method.java、Field.java這四個(gè)類在程序運(yùn)行時(shí)動(dòng)態(tài)訪問和修改任何類的行為和狀態(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í)例的性能差異

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

反射的特點(diǎn)

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

  • 靈活、自由度高:不受類的訪問權(quán)限限制,想對類做啥就做啥;

缺點(diǎn)

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

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

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

說明

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

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

  • 反射存在性能問題,但使用不頻繁、按需使用時(shí),對程序性能影響并不大;

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

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

參考資料

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

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

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

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