美文网首页
【Android面试题】2023最新面试专题:Java反射类加载

【Android面试题】2023最新面试专题:Java反射类加载

作者: 小城哇哇 | 来源:发表于2023-07-26 20:28 被阅读0次

7 反射为什么慢?(滴滴)

这道题想考察什么?

是否了解反射的代价

考察的知识点

反射

考生如何回答

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

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

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

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

  2. 需要检查方法可见性

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

  3. 需要校验参数

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

  4. 反射方法难以内联

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

  5. JIT 无法优化

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

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

这道题想考察什么?

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

考察的知识点

代理模式,反射

考生如何回答

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

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

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

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

代理.png
  • 抽象角色:
    • 指代理角色和真实角色对外提供的公共方法,一般为一个接口
  • 真实角色:
    • 需要实现抽象角色接口,定义了真实角色所要实现的业务逻辑,以便供代理角色调用。也就是真正的业

务逻辑在此。

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

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

静态代理

静态代理在进行一对一代理时,会出现时静态代理对象量多,可维护性差,而在一对多时,也会出现扩展能力差的问题。比如在进行开发时,同学们如果遇到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加载使用。

类加载.png

字节码的数据主要由一个真实存在的.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面试题】2023最新面试专题:Java反射类加载

      本文链接:https://www.haomeiwen.com/subject/itokpdtx.html