【168期】面试官——框架中处处可见反射的运用,你对它了解多少?

本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

原文链接:blog.ouyangsihai.cn >> 【168期】面试官——框架中处处可见反射的运用,你对它了解多少?

点击上方“Java面试题精选”,关注公众号

面试刷图,查缺补漏

号外:往期面试题,10篇为一个单位归置到本公众号菜单栏-面试题,有需要的欢迎翻阅

阶段汇总集合:

什么是反射?

反射是一种能够在程序运行时动态访问、修改某个类中任意属性(状态)和方法(行为)的机制(包括private实例和方法),java反射机制提供了以下几个功能:

  • 在运行时判断任意一个对象所属的类;
  • 在运行时构造任意一个类的对象;
  • 在运行时判断任意一个类所具有的成员变量和方法;
  • 在运行时调用任意一个对象的方法。
  • 反射涉及到四个核心类:

  • java.lang.**Class.java**:类对象;
  • java.lang.reflect.**Constructor.java**:类的构造器对象;
  • java.lang.reflect.**Method.java**:类的方法对象;
  • java.lang.reflect.**Field.java**:类的属性对象;
  • 反射有什么用?

  • 操作因访问权限限制的属性和方法;
  • 实现自定义注解;
  • 动态加载第三方jar包,解决android开发中方法数不能超过65536个的问题;
  • 按需加载类,节省编译和初始化APK的时间;
  • 反射工作原理

    当我们编写完一个Java项目之后,每个java文件都会被编译成一个.class文件,这些Class对象承载了这个类的所有信息,包括父类、接口、构造函数、方法、属性等,这些class文件在程序运行时会被ClassLoader加载到虚拟机中。

    当一个类被加载以后,Java虚拟机就会在内存中自动产生一个Class对象。我们通过new的形式创建对象实际上就是通过这些Class来创建,只是这个过程对于我们是不透明的而已。

    反射的工作原理就是借助Class.java、Constructor.java、Method.java、Field.java这四个类在程序运行时动态访问和修改任何类的行为和状态。

    反射实例

    分别演示三种获取类信息的方式、获取当前类的所有方法和获取当前类及其父类的所有方法、获取当前类的所有实例和获取当前类及其父类的所有实例、获取父类信息、获取接口信息、比较反射方法和实例的性能差异等几个方面:

  • **示例类:**
  • 父类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;
        }
    
        /**
         * 获取当前类中的所有方法
         *
         * */
        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());
    
                // 获取方法的参数类型列表
                Class?[] paramClasses = getSalaryPerMonthMethod.getParameterTypes() ;
                for (Class? class1 : paramClasses) {
                    LogE("getSalaryPerMonth 方法的参数类型 : " + class1.getName());
                }
    
                // 是否是 private 函数,属性是否是 private 也可以使用这种方式判断
                LogE(getSalaryPerMonthMethod.getName() + " is private " + Modifier.isPrivate(getSalaryPerMonthMethod.getModifiers()));
    
                // 执行方法
                Object result = getSalaryPerMonthMethod.invoke(programMonkey);
                LogE("getSalaryPerMonth 方法的返回结果: " + result);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 获取当前类和父类的所有公有方法
         *
         * */
        private void getAllMethods() {
            ProgramMonkey programMonkey = new ProgramMonkey("小明", "男", 12);
            // 获取当前类和父类的所有公有方法
            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());
    
                // 获取方法的参数类型列表
                Class?[] paramClasses = setmLanguageMethod.getParameterTypes() ;
                for (Class? class1 : paramClasses) {
                    LogE("setmLanguage 方法的参数类型 : " + class1.getName());
                }
    
                // 是否是 private 函数,属性是否是 private 也可以使用这种方式判断
                LogE(setmLanguageMethod.getName() + " is private " + Modifier.isPrivate(setmLanguageMethod.getModifiers()));
    
                // 执行方法
                Object result = setmLanguageMethod.invoke(programMonkey, "Java");
                LogE("setmLanguage 方法的返回结果: " + 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;
        }
    
        /**
         * 得到当前类的所有实例
         *
         * */
        private void getCurrentClassFields() {
            ProgramMonkey programMonkey = new ProgramMonkey("小明", "男", 12);
            // 获取当前类的所有属性
            Field[] publicFields = programMonkey.getClass().getDeclaredFields();
            for (Field field : publicFields) {
                LogE("declared field name : " + field.getName());
            }
    
            try {
                // 获取当前类的某个属性
                Field ageField = programMonkey.getClass().getDeclaredField("mAge");
                // 获取属性值
                LogE(" my age is : " + ageField.getInt(programMonkey));
                // 设置属性值
                ageField.set(programMonkey, 10);
                LogE(" my age is : " + ageField.getInt(programMonkey));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 得到当前类和父类的所有公有属性
         *
         * */
        private void getAllFields() {
            ProgramMonkey programMonkey = new ProgramMonkey("小明", "男", 12);
            // 得到当前类和父类的所有公有属性
            Field[] publicFields = programMonkey.getClass().getFields();
            for (Field field : publicFields) {
                LogE("field name : " + field.getName());
            }
    
            try {
                // 获取当前类和父类的某个公有属性
                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);
        }
    }
    
  • **演示结果:**
  • 三种获取类信息的方式:

    获取当前类的方法、获取当前类和父类的所有公有方法:

    获取当前类的所有实例、获取当前类和父类的所有公有实例:

    获取父类信息:

    获取接口信息:

    比较反射方法和实例的性能差异:

    通过上面的示例可以发现,通过反射能够完成之前所描述的事情,并且反射方法比反射实例要慢很多。

    反射的特点

    优点

  • **灵活、自由度高:** 不受类的访问权限限制,想对类做啥就做啥;
  • 缺点

  • **性能问题:** 通过反射访问、修改类的属性和方法时会远慢于直接操作,但性能问题的严重程度取决于在程序中是如何使用反射的。如果使用得很少,不是很频繁,性能将不会是什么问题;
  • **安全性问题:** 反射可以随意访问和修改类的所有状态和行为,破坏了类的封装性,如果不熟悉被反射类的实现原理,随意修改可能导致潜在的逻辑问题;
  • **兼容性问题:** 因为反射会涉及到直接访问类的方法名和实例名,不同版本的API如果有变动,反射时找不到对应的属性和方法时会报异常;
  • 说明

  • 通过反射访问方法比实例慢很多;
  • 有用到反射的类不能被混淆;
  • 反射存在性能问题,但使用不频繁、按需使用时,对程序性能影响并不大;
  • 反射存在安全性问题,因为可以随意修改类的所有状态和行为(包括private方法和实例);
  • 使用反射访问Android的API时需要注意因为不同API版本导致的兼容性问题;
  • 来源:https://zhuanlan.zhihu.com/p/21423208

    与其在网上拼命找题?** 不如马上关注我们~**

    【168期】面试官:框架中处处可见反射的运用,你对它了解多少?

    原文始发于微信公众号(Java面试题精选):

    本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

    本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

    原文链接:blog.ouyangsihai.cn >> 【168期】面试官——框架中处处可见反射的运用,你对它了解多少?


     上一篇
    【167期】面试官——反射是如何影响性能的,它到底慢在哪里? 【167期】面试官——反射是如何影响性能的,它到底慢在哪里?
    点击上方“Java面试题精选”,关注公众号 面试刷图,查缺补漏 号外:往期面试题,10篇为一个单位归置到本公众号菜单栏-面试题,有需要的欢迎翻阅 阶段汇总集合: 介绍完反射的实现后有朋友问反射具体是怎么影响性能的?这引起了我的反思。是啊,在
    2021-04-05
    下一篇 
    【169期】面试官——同学,分析一下MySQL,InnoDB的加锁过程吧 【169期】面试官——同学,分析一下MySQL,InnoDB的加锁过程吧
    点击上方“Java面试题精选”,关注公众号 面试刷图,查缺补漏 号外:往期面试题,10篇为一个单位归置到本公众号菜单栏-面试题,有需要的欢迎翻阅 阶段汇总集合: 1、背景MySQL/InnoDB的加锁分析,一直是一个比较困难的话题
    2021-04-05