java反射技术强制类型转换_网易高薪架构师技术心得:Java反射机制的核心原理

反射是Java的高级特性之一,在底层框架中被频繁的使用。比如:JDBC中的加载数据库驱动程序,Spring框架中加载bean对象,以及态代理,这些都使用到反射,因为我们要想理解一些框架的底层原理,Java反射是我们必须要掌握的。

本文来自网易资深工程师的内部分享,为你解析Java反射必备知识点!

37e6e70c46a49c9725d9e78bf735629a.png

自己整理的Java架构学习视频和大厂项目底层知识点,需要的同学欢迎私信我【资料】发给你~一起学习进步!

Java反射

【一】简介

  • 允许运行时的Java程序获取自身信息,同时操作类或对象的内部属性,最通俗易懂的解释,就是让你根据一个String来得到你要的实体对象
  • 功能:运行时判断对象所属类运行时构造类的对象运行时判断类的属性和方法运行时调用任意一个类的方法

下面是相关的四个类,包含了我们定义一个类所能用到的所有属性和方法。

  • Class
  • Field
  • Method
  • Constructor

【二】常用方法

  1. 获取Class对象Class类中的静态方法forName直接获取某一个对象的 class调用某个对象的 getClass() 方法
  2. 反射获取类的属性getFields():Field[] - 只能获取public的字段,包括父类的**getDeclaredFields():Field[] **- 只能获取自己声明的各种字段,包括public,protected,private
  3. 获取方法:getMethod(String,Class>...):Method - 根据方法名和参数获取方法getDeclaredMethods():Method[]
  4. invoke():调用包装在当前Method对象中的方法Method和NativeMethodAccessorImpl类中的invoke()方法:public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { if (!override) { if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) { Class> caller = Reflection.getCallerClass(); checkAccess(caller, clazz, obj, modifiers); } } MethodAccessor ma = methodAccessor; if (ma == null) { ma = acquireMethodAccessor(); } return ma.invoke(obj, args); } public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException { if (++this.numInvocations > ReflectionFactory.inflationThreshold() && !ReflectUtil.isVMAnonymousClass(this.method.getDeclaringClass())) { MethodAccessorImpl var3 = (MethodAccessorImpl)(new MethodAccessorGenerator()).generateMethod(this.method.getDeclaringClass(), this.method.getName(), this.method.getParameterTypes(), this.method.getReturnType(), this.method.getExceptionTypes(), this.method.getModifiers()); this.parent.setDelegate(var3); } return invoke0(this.method, var1, var2); }

在上述方法中,有两点是比较重要的:

  1. 根据Class对象获取Method方法时,参数:方法名和参数的CLass类型
  2. 调用method.invoke(obj, args)

【三】原理探究

如果想要理解反射,就首先要对Java类加载的方式有了解。

我们知道,我们写的Java文件会被编译成.class文件(字节码文件),才能在JVM中执行,是通过类加载器进行加载,

java.lang.ClassLoader#loadClass(java.lang.String, boolean)是加载一个类的核心方法,

 protected Class> loadClass(String name, boolean resolve)        throws ClassNotFoundException    {        synchronized (getClassLoadingLock(name)) {            // First, check if the class has already been loaded            Class> c = findLoadedClass(name);            if (c == null) {                long t0 = System.nanoTime();                try {                    if (parent != null) {                        c = parent.loadClass(name, false);                    } else {                        c = findBootstrapClassOrNull(name);                    }                } catch (ClassNotFoundException e) {                    // ClassNotFoundException thrown if class not found                    // from the non-null parent class loader                }                if (c == null) {                    // If still not found, then invoke findClass in order                    // to find the class.                    long t1 = System.nanoTime();                    c = findClass(name);                    // this is the defining class loader; record the stats                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);                    sun.misc.PerfCounter.getFindClasses().increment();                }            }            if (resolve) {                resolveClass(c);            }            return c;        }    }

主要逻辑分为3步

  1. 检查是否已加载,如果已加载则直接返回
  2. 遵循双亲委派模式,先调用父类加载器加载器
  3. 如果父类加载器加载失败,调用findClass加载

当我们打开findClass方法时,会不会有些诧异,直接抛了个异常,这是因为ClassLoader是一个抽象类,是无法通过new创建一个对象的,也就是说,你无法通过ClassLoader类直接加载.class文件,只能写一个子类继承ClassLoader,然后覆盖findClass方法,定义自己的加载逻辑。

  protected Class> findClass(String name) throws ClassNotFoundException {        throw new ClassNotFoundException(name);    }

在自定义加载逻辑的时候,通常的做法是,读取一个字节数组byte[] b,然后调用ClassLoader的defineClass()方法,于是返回了一个Class对象

  private native Class> defineClass0(String name, byte[] b, int off, int len,                                         ProtectionDomain pd);    private native Class> defineClass1(String name, byte[] b, int off, int len,                                         ProtectionDomain pd, String source);    private native Class> defineClass2(String name, java.nio.ByteBuffer b,                                         int off, int len, ProtectionDomain pd,                                         String source);

Class类的内部类 ReflectionData,里面的内容与xxx.class文件中的内容映射,作用就是缓存从JVM中读取类的如下属性数据。同时由于一个类中包含的内容信息量过大,所以被拆成了四个类,也就是上面提到的- Class、Field、Method、Constructor

 private static class ReflectionData {        volatile Field[] declaredFields;        volatile Field[] publicFields;        volatile Method[] declaredMethods;        volatile Method[] publicMethods;        volatile Constructor[] declaredConstructors;        volatile Constructor[] publicConstructors;        // Intermediate results for getFields and getMethods        volatile Field[] declaredPublicFields;        volatile Method[] declaredPublicMethods;        volatile Class>[] interfaces;        // Value of classRedefinedCount when we created this ReflectionData instance        final int redefinedCount;        ReflectionData(int redefinedCount) {            this.redefinedCount = redefinedCount;        }    }

看到这里,你掌握Java反射知识点了吗?有任何问题欢迎留言!

来源:网易工程师-韩虹莹


整理总结不易,如果觉得这篇文章有意思的话,欢迎转发、收藏,给我一些鼓励~

有想看的内容或者建议,敬请留言!

最近利用空余时间整理了一些精选Java架构学习视频和大厂项目底层知识点,需要的同学欢迎私信我发给你~一起学习进步!有任何问题也欢迎交流~

Java日记本,每日存档超实用的技术干货学习笔记,每天陪你前进一点点~

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值