Android反射类加载与动态代理面试题汇总(含详细解析 四)

77 阅读7分钟

Android并发编程高级面试题汇总最全最细面试题讲解持续更新中👊👊 👀你想要的面试题这里都有👀 👇👇👇

反射为什么慢?(滴滴)

这道题想考察什么?

是否了解反射的代价

考察的知识点

反射

考生如何回答

以反射方法为例,在使用反射时,首先需要获得对应的 Class 对象,然后获取需要反射执行的 Method 对象,调用 invoke 方法。

Java 反射效率低主要原因是:

  1. 在反射执行方法时,参数需要拆装箱3

    invoke 方法的参数是 Object[] 类型,当方法参数是基本数据类型时候,需要在此转化成 对应的包装类型,如 int需要转换为Integer ,也就会额外生成Integer 对象。接着还需要将这些参数封装为Object数组。

  2. 需要检查方法可见性

    反射时每次调用都必须检查方法的可见性,比如反射private方法时必须设置setAccessible(true),在执行时,invoke方法需要根据作用域与flag进行检查

  3. 需要校验参数

    反射时也必须检查每个实际参数与形式参数的类型匹配性

  4. 反射方法难以内联

    正常调用的方法能够进行内联优化,而反射将本来允许内联的方法变得复杂,无法内联

  5. JIT 无法优化

    反射涉及到动态加载 ,无法进行优化

动态代理是什么?如何实现?

这道题想考察什么?

面试者对设计模式中的代理模式掌握情况,是否了解并能合理运用静态代理与动态代理,知道两者的区别;动态代理原理与其所涉及到的知识点

考察的知识点

代理模式,反射

考生如何回答

代理模式,属于结构型模式。使用一个类代表另一个类的功能,在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。 比如生活中常见的中介与租房,租房者不需要直接与房东交互,房东把房屋租赁交给中介代理。代理模式的目的有:

(1)通过引入代理对象的方式来间接访问目标对象,防止直接访问目标对象给系统带来的不必要复杂性;

(2)通过代理对象对访问进行控制;

代理模式一般会有三个角色:

  • 抽象角色:

    • 指代理角色和真实角色对外提供的公共方法,一般为一个接口
  • 真实角色:

    • 需要实现抽象角色接口,定义了真实角色所要实现的业务逻辑,以便供代理角色调用。也就是真正的业

务逻辑在此。

  • 代理角色:

    • 需要实现抽象角色接口,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附

加自己的操作。将统一的流程控制都放到代理角色中处理!

静态代理

静态代理在进行一对一代理时,会出现时静态代理对象量多,可维护性差,而在一对多时,也会出现扩展能力差的问题。比如在进行开发时,同学们如果遇到NDK的问题,可以找我们享学课堂提问。因此我们创建对应的接口:

/**
 * 抽象角色: 定义了服务的接口
 */
public interface NDK {
    void ndk();
}

Lance老师擅长NDK问题:

/**
 * 真实角色: 需要实现抽象角色接口
 */
public class Lance implements NDK {
​
    @Override
    public void ndk() {
        System.out.println("回答问题");
    }
}

同学们一旦有NDK问题,就可以找到我们的助教老师:

/**
 * 代理角色: 需要实现抽象角色接口,代理真实角色
 */
public class Kiso implements NDK {
​
    private final NDK ndk;
​
    public VV(NDK ndk) {
        this.ndk = ndk;
    }
​
    //....前置处理
    public void before() {
        System.out.println("前置处理:了解同学问题");
    }
​
    //....后置处理
    public void after() {
        System.out.println("后置处理:回访答疑体验");
    }
​
    @Override
    public void ndk() {
        before();
        ndk.ndk();
        after();
    }
}
​
​

遇到了NDK问题直接向代理对象VV询问。

//创建真实对象
NDK lance = new Lance();
//Kiso代理真实对象
Kiso kiso = new Kiso(lance);
//向Kiso询问NDK问题
kiso.ndk();

在上面这个案例中,代理(Kiso)使客户端(学生)不需要知道实现类是什么(擅长NDK的是哪个老师),而客户端只需知道代理即可(解耦合)。

动态代理

作为一个传授系统性知识体系的线上教育机构,享学课堂不仅仅只能解决同学们的NDK问题,如果同学们遇到了其他的问题比如UI,享学课堂也要能够为同学们提供答疑服务。而此时静态代理的局限性就体现出来了: 当我们的系统更新或者增加新的业务需求,可能需要新增很多目标接口和代理类。

而动态代理就能够很好的解决这个问题:

而动态代理就能够很好的解决这个问题:

//真实对象
NDK lance = new Lance();
//JAVA动态代理
NDK ndk = (NDK) Proxy.newProxyInstance(lance.getClass().getClassLoader(),
                lance.getClass().getInterfaces(), new ProxyInvokeHandler(lance));       
ndk.ndk();
​
​
UI alvin = new Alvin();
UI ui = (UI) Proxy.newProxyInstance(alvin.getClass().getClassLoader(),
                alvin.getClass().getInterfaces(), new ProxyInvokeHandler(alvin));
ui.ui();
​
//代理多个接口
Object proxy = Proxy.newProxyInstance(alvin.getClass().getClassLoader(),
               new Class[]{NDK.class,UI.class}, new ProxyInvokeHandler(lance,alvin));
((UI)proxy).ui();
((NDK)proxy).ndk();
// 在Proxy.newProxyInstance创建的对象上调用任何方法都会回调此处的invoke方法
public class ProxyInvokeHandler implements InvocationHandler {
    //真实对象
    private Object realObject;
​
    public ProxyInvokeHandler(Object realObject) {
        this.realObject = realObject;
​
    }
​
    /**
     * 
     * @param o       代理对象
     * @param method  调用的方法
     * @param objects 方法的参数
     * @return
     * @throws Throwable
     */  
    @Override
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
        //反射执行真实对象方法
        return method.invoke(realObject, objects);
    }
}
Java动态代理原理

我们的类一般由Java源文件编译出Java字节码.class文件,然后经过类加载器ClassLoader加载使用。

字节码的数据主要由一个真实存在的.class记录,类加载器读取这个.class文件中的字节码数据。而动态代理,则是在运行时通过:Proxy.newProxyInstance在内存中直接生成类的字节码数据,然后创建此类实例对象返回。Java动态代理在内存中生成出来的字节码数据,我们可以写出到文件中去查看:

//生成NDK代理类:com.enjoy.lib.Proxy$0
byte[] bytes = ProxyGenerator.generateProxyClass("com.enjoy.lib.Proxy$0",
                new Class[]{NDK.class});
​
try {
    FileOutputStream fos = new FileOutputStream("/xxx/Proxy$0.class");
    fos.write(bytes);
    fos.close();
} catch (IOException e) {
    e.printStackTrace();
}

Android中没有ProxyGenerator,可以创建Java工程(使用JDK)

最后反编译查看Proxy.newProxyInstance创建的对象类型Proxy$0.class内容如下:

public final class Proxy$0 extends Proxy implements NDK {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;
    //InvocationHandler Proxy.newProxyInstance最后一个参数
    public Proxy$0(InvocationHandler var1) throws  {
        super(var1);
    }
​
    //......
​
    //在动态代理对象上调用ndk方法,通过InvocationHandler回调出去
    public final void ndk() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
​
​
    //静态代码块,加载就会找到对应的method,比如ndk方法
    static {
        try {
           //......
            m3 = Class.forName("com.xxx.NDK").getMethod("ndk");
           //......
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

更多Android面试题 可以详细Vx关注公众号:Android老皮 解锁          《2023最新Android中高级面试题汇总+解析》

目录

img

第一章 Java方面

  • Java基础部分
  • Java集合
  • Java多线程
  • Java虚拟机

img

第二章 Android方面

  • Android四大组件相关
  • Android异步任务和消息机制
  • Android UI绘制相关
  • Android性能调优相关
  • Android中的IPC
  • Android系统SDK相关
  • 第三方框架分析
  • 综合技术
  • 数据结构方面
  • 设计模式
  • 计算机网络方面
  • Kotlin方面

img

第三章 音视频开发高频面试题

  • 为什么巨大的原始视频可以编码成很小的视频呢?这其中的技术是什么呢?
  • 怎么做到直播秒开优化?
  • 直方图在图像处理里面最重要的作用是什么?
  • 数字图像滤波有哪些方法?
  • 图像可以提取的特征有哪些?
  • 衡量图像重建好坏的标准有哪些?怎样计算?

img

第四章 Flutter高频面试题

  • Dart部分
  • Flutter部分

img

第五章 算法高频面试题

  • 如何高效寻找素数
  • 如何运用二分查找算法
  • 如何高效解决雨水问题
  • 如何去除有序数组的重复元素
  • 如何高效进行模幂运算
  • 如何寻找最长回文子串

img

第六章 Andrio Framework方面

  • 系统启动流程面试题解析
  • Binder面试题解析
  • Handler面试题解析
  • AMS面试题解析