7 反射为什么慢?(滴滴)
这道题想考察什么?
是否了解反射的代价
考察的知识点
反射
考生如何回答
以反射方法为例,在使用反射时,首先需要获得对应的 Class 对象,然后获取需要反射执行的 Method 对象,调用 invoke 方法。
Java 反射效率低主要原因是:
-
在反射执行方法时,参数需要拆装箱3
invoke 方法的参数是 Object[] 类型,当方法参数是基本数据类型时候,需要在此转化成 对应的包装类型,如 int需要转换为Integer ,也就会额外生成Integer 对象。接着还需要将这些参数封装为Object数组。
-
需要检查方法可见性
反射时每次调用都必须检查方法的可见性,比如反射private方法时必须设置
setAccessible(true)
,在执行时,invoke方法需要根据作用域与flag进行检查 -
需要校验参数
反射时也必须检查每个实际参数与形式参数的类型匹配性
-
反射方法难以内联
正常调用的方法能够进行内联优化,而反射将本来允许内联的方法变得复杂,无法内联
-
JIT 无法优化
反射涉及到动态加载 ,无法进行优化
8 动态代理是什么?如何实现?
这道题想考察什么?
面试者对设计模式中的代理模式掌握情况,是否了解并能合理运用静态代理与动态代理,知道两者的区别;动态代理原理与其所涉及到的知识点
考察的知识点
代理模式,反射
考生如何回答
代理模式,属于结构型模式。使用一个类代表另一个类的功能,在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。 比如生活中常见的中介与租房,租房者不需要直接与房东交互,房东把房屋租赁交给中介代理。代理模式的目的有:
(1)通过引入代理对象的方式来间接访问目标对象,防止直接访问目标对象给系统带来的不必要复杂性;
(2)通过代理对象对访问进行控制;
代理模式一般会有三个角色:
![](https://img.haomeiwen.com/i27607674/9776801d320379fe.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加载使用。
![](https://img.haomeiwen.com/i27607674/fc5801e7a1b349b7.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());
}
}
}
最后
整理不易,白嫖太易,关注哇哇,以上均可分享哦~
网友评论