java反射机制

2 篇文章 0 订阅
2 篇文章 0 订阅

前言碎语

刚开始学习时其实是跳过了这么一个知识点的(因为确定难懂又暂时没什么用),后面听说了在框架中反射是基本的原理,我就又滚回来了(出来混迟早要还的,深以为然)

反射机制是什么?

反射机制是能在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取信息的以及动态调用对象的方法的功能成为java反射机制

反射是为了动态的加载java类,使得程序在编译时不需要知道某些类的具体信息,只有在运行的时候根据输入的类(补全了某些类的信息),来动态的加载该类,并运行其中的方法。

反射机制有什么用?

看过一篇博客,举了这么一个通俗易懂的例子:
两个程序员A和B一起工作,因为工作的原因,两个人的任务是分开完成的,同时也是为了保证工程进度;但是程序员A的任务中需要用到程序员B的代码,那么如何在保证A的任务能够进行下去同时又能保证A和B一起推进任务呢?
这时就需要用到了java反射的机制。按照上面的说法,我们可以在A的代码中先对所需要B中的某个类进行**代理使用,这样可以保证A的程序编译通过;然后在程序运行的时候,通过某种方式(传参数)来获取到真正想要调用的类。这样在程序运行时就会使用到该类的动态代理对象,从而完成任务。

反射机制的原理

看了上面的介绍以,我就在想这是怎么实现的(感觉这个反射是为了骗过编译器啊….)

首先说一下动态加载,我们知道jvm在运行java程序前会先加载所使用到的类进行编译,而有的类是在编译时期不知道的,只有在运行的时候才会加载,此谓动态加载。在实现动态加载类时,又有一个动态的代理机制在里面:
所谓动态代理,就是程序在运行的时候,对于一个接口和实现类,可以由JVM生成一个代理对象来帮助你使用接口或类中的方法(而不需要显式的去实例化一个类的对象)
这样我们可以在程序中直接使用代理对象,完成操作。

反射机制的常规用法

对于编译时知道类的信息的情况就不说了,上一篇Class里面有涉及,具体使用时查API文档就好,这里说编译时期不知道类的信息的情况

  • 定义一个动态代理类,该类必须实现InvocationHandler接口
class DynamicProxy implements InvocationHandler
{
    // 这个是要代理的对象
    private Object subject;

    // 构造方法,给要代理的对象赋初值
    public DynamicProxy(Object subject)
    {
        this.subject = subject;
    }

    @Override
    public Object invoke(Object object, Method method, Object[] args)
            throws Throwable
    {
        // 在代理真实对象前我们可以添加一些自己的操作
        // blablabla... 

        // 当代理对象调用真实对象的方法时,其会自动的跳转到代理对象的invoke方法来进行调用
        method.invoke(subject, args);

        // 在代理真实对象后也可以添加一些操作
        // blablabla...

        return null;
    }

}
Class testClass = Class.forName(str);// str可以以字符串的形式传入
  • 创建一个动态代理对象并开始使用被代理对象中的方法;
 // 需要代理的真实对象
        Object realObject = new Object();

        // 将真实对象传入,最后是通过代理对象来调用其方法的
        InvocationHandler handler = new DynamicProxy(realObject);

        /*
         * 通过Proxy的newProxyInstance方法来创建我们的代理对象
         * 第一个参数 handler.getClass().getClassLoader() ,我们这里使用handler这个类的ClassLoader对象来加载代理对象
         * 第二个参数realObject.getClass().getInterfaces(),这里为代理对象提供的接口是真实对象所实现的接口,表示要代理的是该真实对象,这样就能调用这组接口中的方法了
         * 第三个参数handler, 将这个代理对象关联到了上方的 InvocationHandler 这个对象上
         */
        Object obj = (Object)Proxy.newProxyInstance(handler.getClass().getClassLoader(), realObject.getClass().getInterfaces(), handler);

        // obj.xxx(); // 这样使用代理对象来直接调用被代理类的方法

这里有需要注意的地方:Proxy的newProxyInstance方法的第二个参数,意思是代理对象去实现了被代理对像的接口,这样代理对象才可以去使用被代理对象实现接口或继承类中的方法。同时如果打印出代理对象的obj.getClass().getName()会显示$proxy0,因为这是JVM自动动态生成的代理对象(与使用时自己初始化代理对象不是一个意思,初始化主要是为了传入被代理对象),这是一种固定的命名方式。

使用反射的利弊

优点
反射提高了程序的灵活性以及扩展性,降低了耦合性(依赖关系),提高了程序的适应能力;允许程序在不知道具体类的信息的情况下,创建和控制任何类的对象。

缺点

  • 性能问题:使用反射时是一种解释操作,用于字段和方法接入时要远慢于直接代码(这事必然啊,总要能理解吧),所以反射机制主要应用于灵活性和扩展性要求很高的系统框架上,普通程序不建议使用
  • 模糊程序内部逻辑:反射绕过了源代码的技术会带来后期的维护问题,毕竟看反射代码更难

总结

其实当我们使用编译器,在对象后面加上.时,编译器会自动列出该对象中的所有属性以及方法,这里就用到了这个原理,并称之为java类的自审,可以探知到类的基本结构。

上面的内容都是我在学习时自己提出的问题,越来越感觉在学一个知识点时主动挖掘它的原理是重要的(虽然有的并不需要也并不会想到),开始萌发要看看JVM底层原理的书的念头了….

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值