JAVA 面试突击复习整理 反射和动态代理

反射

1:反射概念 :
jdk 自带功能,主要优点 可以在java 运行期间获取、检测和调用对象的属性和方法。
编译和运行之间的区别;
编译时刻加载类是静态加载类,运行时刻加载类是动态加载类
2:反射使用场景:springIoc springboot 注入bean;数据库链接池 com.mysql.jdbc.Driver
3:获取反射的几种方式:
假设有个Foo 对象;

  1. Class c1 = Foo.class
  2. Class c2 = foo.getClass();
  3. Class.forName(“类的全路径”)
    获取无参数构造方法
    Foo foo = (Foo)c1.newInstance();

4:反射的基本使用: 调用静态方法,调用公共方法,调用私有方法
使用

  1. 调用静态方法
    Class c1 = Class.forName(“类的全路径”)
    Method method = c1.getMethod(“静态方法的名字”)
    method.invoke(c1);
  2. 调用公共方法
    Class c1 = Class.forName(“类的全路径”)
    Object instance = c1.newInstance();
    Method method = c1.getMethod(“公共方法的名字”)
    method.invoke(instance);
  3. 调用私有方法
    Class c1 = Class.forName(“类的全路径”)
    Object instance = c1.newInstance();
    Method method3 = c1.getDeclaredMethod(“私有方法”");
    method3.setAccessible(true);
    method3.invoke(instance);

动态代理

1: 动态代理:一句话,中间商,中介 ,帮干活并且不赚差价;
2:使用场景: spring 编程中的AOP 依赖注入@Autowired 和事务注解 @Transactional
3:动态代理和反射的关系 jdk 中自带的动态代理是通过反射实现的,还有cglib

jdk 代理实现的过程:
注意: JDK Proxy 只能代理实现接口的类(即使是 extends 继承类也是不可以代理的)
首先要有被代理的类 (需要实现接口)
其次需要代理类
接着代理类实现 InvocationHandler 接口 重写invoke 方法;
method.invoke(target, args); 实现调用代理方法
并且实现一个getInstance方法使用
Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
获取代理对象

interface Animal {
    void eat();
}
class Dog implements Animal {
    @Override
    public void eat() {
        System.out.println("The dog is eating");
    }
}
class Cat implements Animal {
    @Override
    public void eat() {
        System.out.println("The cat is eating");
    }
}

// JDK 代理类
class AnimalProxy implements InvocationHandler {
    private Object target; // 代理对象
    public Object getInstance(Object target) {
        this.target = target;
        // 取得代理对象
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("调用前");
        Object result = method.invoke(target, args); // 方法调用
        System.out.println("调用后");
        return result;
    }
}

public static void main(String[] args) {
    // JDK 动态代理调用
    AnimalProxy proxy = new AnimalProxy();
    Animal dogProxy = (Animal) proxy.getInstance(new Dog());
    dogProxy.eat();
}

cglib 代理实现的过程
注意:cglib 底层是通过子类继承被代理对象的方式实现动态代理的,因此代理类不能是最终类(final),否则就会报错 java.lang.IllegalArgumentException: Cannot subclass final class xxx。
首先要有被代理的类
其次代理类实现MethodInterceptor 接口
重写里面的intercept方法
调用代理方法是methodProxy.invokeSuper(0,objects)
并且实现一个getInstance方法使用

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.12</version>
</dependency>
class Panda {
    public void eat() {
        System.out.println("The panda is eating");
    }
}
class CglibProxy implements MethodInterceptor {
    private Object target; // 代理对象
    public Object getInstance(Object target) {
        this.target = target;
        Enhancer enhancer = new Enhancer();
        // 设置父类为实例类
        enhancer.setSuperclass(this.target.getClass());
        // 回调方法
        enhancer.setCallback(this);
        // 创建代理对象
        return enhancer.create();
    }
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("调用前");
        Object result = methodProxy.invokeSuper(o, objects); // 执行方法调用
        System.out.println("调用后");
        return result;
    }
}
public static void main(String[] args) {
    // cglib 动态代理调用
    CglibProxy proxy = new CglibProxy();
    Panda panda = (Panda)proxy.getInstance(new Panda());
    panda.eat();
}

1:动态代理解决了什么问题
答:首先它是一个代理机制,如果熟悉设计模式中的代理模式,我们会知道,代理可以看作是对调用目标的一个包装,这样我们对目标代码的调用不是直接发生的,而是通过代理完成,通过代理可以让调用者与实现者之间解耦。比如进行 RPC 调用,通过代理,可以提供更加友善的界面;还可以通过代理,做一个全局的拦截器。
2.动态代理和反射的关系是什么?
答:反射可以用来实现动态代理,但动态代理还有其他的实现方式,比如 ASM(一个短小精悍的字节码操作框架)、cglib 等。
3.JDK 原生动态代理和 cglib 有什么区别?
答:JDK 原生动态代理和 cglib 区别如下:

JDK 原生动态代理是基于接口实现的,不需要添加任何依赖,可以平滑的支持 JDK 版本的升级;
cglib 不需要实现接口,可以直接代理普通类,需要添加依赖包,性能更高。

4:为什么 JDK 原生的动态代理必须要通过接口来完成?
这是由于 JDK 原生设计的原因,来看动态代理的实现方法 newProxyInstance() 的源码:

/**
 * ......
 * @param   loader the class loader to define the proxy class
 * @param   interfaces the list of interfaces for the proxy class to implement
 * ......
 */ 
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
    throws IllegalArgumentException
{
// 省略其他代码

来看前两个参数的声明:

loader:为类加载器,也就是 target.getClass().getClassLoader()
interfaces:接口代理类的接口实现列表
看了上面的参数说明,我们就明白了,要使用 JDK 原生的动态只能通过实现接口来完成。

总结
JDK 原生动态代理是使用反射实现的,但动态代理的实现方式不止有反射,还可以是 ASM(一个短小精悍的字节码操作框架)、cglib(基于 ASM)等。其中 JDK 原生的动态代理是通过接口实现的,而 cglib 是通过子类实现的,因此 cglib 不能代理最终类(final)。而反射不但可以反射调用静态方法,还可以反射调用普通方法和私有方法,其中调用私有方法时要设置 setAccessible 为 true。

部分转载
https://gitbook.cn/gitchat/column/5d493b4dcb702a087ef935d9/topic/5d4e136669004b174cd0013d

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
建议阅读本文档的方式 本文档提供详细的目录,建议大家使用电脑阅读。如果大家用手机阅读的话,可以下载一个不错的PDF阅读器,比如 很多人常用的福昕PDF阅读器。 本文档提供详细的目录,大家可以根据自己的实际需要选择自己薄弱的知识章节阅读。 前言 不论是校招还是社招都避免不了各种面试、笔试,如何去准备这些东西就显得格外重要。不论是笔试还是面试都是有 章可循的,我这个“有章可循”说的意思只是说应对技术面试是可以提前准备。 运筹帷幄之后,决胜千里之外!不打毫无准备的仗,我觉得大家可以先从下面几个方面来准备面试: 1. 自我介绍。(你可千万这样介绍:“我叫某某,性别,来自哪里,学校是那个,自己爱干什么”,记住:多说点简 历上没有的,多说点自己哪里比别人强!) 2. 自己面试中可能涉及哪些知识点、那些知识点是重点。 3. 面试中哪些问题会被经常问到、面试中自己改如何回答。(强烈不推荐背题,第一:通过背这种方式你能记住多 少?能记住多久?第二:背题的方式的学习很难坚持下去!) 4. 自己的简历该如何写。 “80%的offer掌握在20%的人手中” 这句话也不是不无道理的。决定你面试能否成功的因素中实力固然占有很大一部 分比例,但是如果你的心态或者说运气不好的话,依然无法拿到满意的 offer。运气暂且不谈,就拿心态来说,千万 不要因为面试失败而气馁或者说怀疑自己的能力,面试失败之后多总结一下失败的原因,后面你就会发现自己会越来 越强大。 另外,大家要明确的很重要的几点是: 1. 写在简历上的东西一定要慎重,这可能是面试官大量提问的地方; 2. 大部分应届生找工作的硬伤是没有工作经验或实习经历; 3. 将自己的项目经历完美的展示出来非常重要。 笔主能力有限,如果有不对的地方或者和你想法不同的地方,敬请雅正、不舍赐教。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值