博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JAVA反射中Class类源码分析及常见面试题
阅读量:4150 次
发布时间:2019-05-25

本文共 13728 字,大约阅读时间需要 45 分钟。

这篇博客主要是来分析下java反射机制实现的核心类Class

public class Test {    public static void main(String[] args) {        Class clazz = String.class;        Constructor[] constructors = clazz.getConstructors();    }}

jvm通过ClassLoader把类的class字节码文件加载到方法内存当中

以上是通过一个Class对象获取到某个具体类当中的构造方法,我们来看下实现方式

准备工作,先看下Class当中经常用到的内部类,和属性

private static boolean useCaches = true; //是否使用RedefineClasses作为缓存数据    // reflection data that might get invalidated when JVM TI RedefineClasses() is called    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; } }    // 保存一个弱引用的对象 private volatile transient SoftReference
> reflectionData; // Incremented by the VM on each call to JVM TI RedefineClasses() // 用来判断和ReflectionData当中redefinedCount的值是否相同,如果不同说明缓存当中的数据失效 private volatile transient int classRedefinedCount = 0;
@CallerSensitive    public Constructor
[] getConstructors() throws SecurityException { checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true); //判断是否有访问权限 return copyConstructors(privateGetDeclaredConstructors(true)); //获取到声明的构造方法,并拷贝一份构造方法 }

接下来我们来看下获取构造方法的实现代买privateGetDeclaredConstructors(true)

private Constructor
[] privateGetDeclaredConstructors(boolean publicOnly) { checkInitted(); Constructor
[] res; ReflectionData
rd = reflectionData(); if (rd != null) { res = publicOnly ? rd.publicConstructors : rd.declaredConstructors; if (res != null) return res; } // No cached value available; request value from VM if (isInterface()) { @SuppressWarnings("unchecked") Constructor
[] temporaryRes = (Constructor
[]) new Constructor
[0]; res = temporaryRes; } else { res = getDeclaredConstructors0(publicOnly); } if (rd != null) { if (publicOnly) { rd.publicConstructors = res; } else { rd.declaredConstructors = res; } } return res; }

1、checkInitted() 来判断是否配置了useCached属性,通过sun.reflect.noCaches来配置,如果配置的话,将useCached改为配置的属性

2、reflectionData()获取缓存当中的数据,如果缓存当中有数据,直接返回缓存当中的构造方法

3、如果缓存当中没有数据,就从JVM当中读取数据

4、如果是接口类型,直接生成一个数组长度为0的Constructor数组,因为接口没有构造方法

5、如果不是接口类型,从JVM当中获取getDeclaredConstructors0 是native方法

6、最后更新缓存reflectionData当中的构造方法

接下来,我们来看下reflectionData()方法,如何获取缓存数据,主要是延迟创建,并缓存数据

private ReflectionData
reflectionData() { SoftReference
> reflectionData = this.reflectionData; int classRedefinedCount = this.classRedefinedCount; ReflectionData
rd; if (useCaches && reflectionData != null && (rd = reflectionData.get()) != null && rd.redefinedCount == classRedefinedCount) { return rd; } // else no SoftReference or cleared SoftReference or stale ReflectionData // -> create and replace new instance return newReflectionData(reflectionData, classRedefinedCount); }

1、首先获取当前reflectionData

2、如果可以使用缓存,并且缓存当中的数据不为null,而且缓存没有失效,(缓存当中redefinedCount等于classRedefinedCount的值),直接返回缓存当中的reflectionData

3、如果以上都不是的话,就创建新的reflectionData,并保存到缓存当中去

我们来看下newReflectionData方法的实现

private ReflectionData
newReflectionData(SoftReference
> oldReflectionData, int classRedefinedCount) { if (!useCaches) return null; while (true) { ReflectionData
rd = new ReflectionData<>(classRedefinedCount); // try to CAS it... if (Atomic.casReflectionData(this, oldReflectionData, new SoftReference<>(rd))) { return rd; } // else retry oldReflectionData = this.reflectionData; classRedefinedCount = this.classRedefinedCount; if (oldReflectionData != null && (rd = oldReflectionData.get()) != null && rd.redefinedCount == classRedefinedCount) { return rd; } } }

1、首先,还是判断是否使用缓存,如果不使用,直接返回

2、使用while+CAS方式更新数据,创建一个新的ReflectionData,如果更新成功直接返回,否则进入3

3、获取到旧的reflectionData和classRedefinedCount的值,如果旧的值不为null, 并且缓存未失效,说明其他线程更新成功了,直接返回

Constructor.newInstance()实现

在上面获取到Constructor之后,通过newInstance的方式获取到实例方法

public T newInstance(Object ... initargs)        throws InstantiationException, IllegalAccessException,               IllegalArgumentException, InvocationTargetException    {        if (!override) {            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {                Class
caller = Reflection.getCallerClass(); checkAccess(caller, clazz, null, modifiers); } } if ((clazz.getModifiers() & Modifier.ENUM) != 0) throw new IllegalArgumentException("Cannot reflectively create enum objects"); ConstructorAccessor ca = constructorAccessor; // read volatile if (ca == null) { ca = acquireConstructorAccessor(); } @SuppressWarnings("unchecked") T inst = (T) ca.newInstance(initargs); return inst; }

1、判断语言级别的访问权限是否被覆盖,如果没有被覆盖需要检查是否有访问权限

2、判断是否为枚举类型,如果是枚举类型不能通过反射创建

3、获取constructorAccessor,如果为null, 通过acquireConstructorAccessor获取

4、通过ConstructorAccessor创建实例对象

接下来来看下如果构建ConstructorAccessor

private ConstructorAccessor acquireConstructorAccessor() {        // First check to see if one has been created yet, and take it        // if so.        ConstructorAccessor tmp = null;        if (root != null) tmp = root.getConstructorAccessor();        if (tmp != null) {            constructorAccessor = tmp;        } else {            // Otherwise fabricate one and propagate it up to the root            tmp = reflectionFactory.newConstructorAccessor(this);            setConstructorAccessor(tmp);        }        return tmp;    }

1、先判断是否已经创建了ConstructorAccessor,如果已经创建了,直接返回获取就可以

2、通过reflectionFactory来创建一个新的ConstructorAccessor

3、设置当前Class对象当中的ConstructorAccessor对象

接下来通过RelectionFactory来创建ConstructorAccessor方法的实现

public ConstructorAccessor newConstructorAccessor(Constructor
var1) { checkInitted(); Class var2 = var1.getDeclaringClass(); if(Modifier.isAbstract(var2.getModifiers())) { return new InstantiationExceptionConstructorAccessorImpl((String)null); } else if(var2 == Class.class) { return new InstantiationExceptionConstructorAccessorImpl("Can not instantiate java.lang.Class"); } else if(Reflection.isSubclassOf(var2, ConstructorAccessorImpl.class)) { return new BootstrapConstructorAccessorImpl(var1); } else if(noInflation && !ReflectUtil.isVMAnonymousClass(var1.getDeclaringClass())) { return (new MethodAccessorGenerator()).generateConstructor(var1.getDeclaringClass(), var1.getParameterTypes(), var1.getExceptionTypes(), var1.getModifiers()); } else { NativeConstructorAccessorImpl var3 = new NativeConstructorAccessorImpl(var1); DelegatingConstructorAccessorImpl var4 = new DelegatingConstructorAccessorImpl(var3); var3.setParent(var4); return var4; } }

1、checkInitted来判断一些配置参数是否设置

2、如果是抽象类的构造方法,会创建一个InstantiationExeceptionConstructorAccessorImpl,也是个ConstructorAccessor的一个实现,不过他的newInstance会直接抛出异常

3、如果当前构造方法等于Class对象直接抛出异常

4、如果当前类是ConstructorAccessorImpl的一个子类,构建一个BootstrapConstructorAccessorImpl的类

5、判断是否启用inflation如果启用了,并且当前类不是匿名内部类,使用MethodAccessorGenerator来生成ConstructorAccessor

6、否则就通过NativeConstructorAccessorImpl的方式来构建,当然如果构建次数超过了inflationThreshold(15)就是用MethodAccessorGenerator方式来构建。

接下来我们看下关于Method的构建和调用

首先我们来看获取指定的Method对象方法

public Method getDeclaredMethod(String name, Class
... parameterTypes) throws NoSuchMethodException, SecurityException { checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true); Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes); if (method == null) { throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes)); } return method; }

1、判断当前method是否有访问权限

2、privateGetDeclaredMethods获取到当前所声明的所有方法

3、根据当前传递的方法名和参数类型从生命的方法中找到匹配的方法,并返回

接下来我们先来看privateGetDeclaredMethods方法的实现

private Method[] privateGetDeclaredMethods(boolean publicOnly) {        checkInitted();        Method[] res;        ReflectionData
rd = reflectionData(); if (rd != null) { res = publicOnly ? rd.declaredPublicMethods : rd.declaredMethods; if (res != null) return res; } // No cached value available; request value from VM res = Reflection.filterMethods(this, getDeclaredMethods0(publicOnly)); if (rd != null) { if (publicOnly) { rd.declaredPublicMethods = res; } else { rd.declaredMethods = res; } } return res; }

1、同样的判断当前是否有配置信息,读取是否从缓存中获取数据

2、如果缓存当中有数据直接拿缓存当中的数据返回

3、如果没有缓存数据,从JVM当中读取数据,getDeclaredMethods0(publicOnly) 使用native方式读取methods数组对象

4、将缓存当中的数据更新

接下来来看下关于Method的invoke方法,总体上和构造方法的newInstance很类似

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; // read volatile if (ma == null) { ma = acquireMethodAccessor(); } return ma.invoke(obj, args); }

主要是创建MethodAccessor方法,首先要获取到MethodAccessor,基于上面对构造方法的分析我们直接到创建MethodAccessor的方法中去看

public MethodAccessor newMethodAccessor(Method var1) {        checkInitted();        if(noInflation && !ReflectUtil.isVMAnonymousClass(var1.getDeclaringClass())) {            return (new MethodAccessorGenerator()).generateMethod(var1.getDeclaringClass(), var1.getName(), var1.getParameterTypes(), var1.getReturnType(), var1.getExceptionTypes(), var1.getModifiers());        } else {            NativeMethodAccessorImpl var2 = new NativeMethodAccessorImpl(var1);            DelegatingMethodAccessorImpl var3 = new DelegatingMethodAccessorImpl(var2);            var2.setParent(var3);            return var3;        }    }

默认情况下我们设置的noInflation=false,所以我们主要看通过创建的DelegationMehtodAccessorImpl,我们来看下它的实现方法

class DelegatingMethodAccessorImpl extends MethodAccessorImpl {    private MethodAccessorImpl delegate;    DelegatingMethodAccessorImpl(MethodAccessorImpl var1) {        this.setDelegate(var1);    }    public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException {        return this.delegate.invoke(var1, var2);    }    void setDelegate(MethodAccessorImpl var1) {        this.delegate = var1;    }}

可以看到这个类的实现是通过里面的代理对象delegate来实现的,从上面方法我们可以看到,代理对象是NativeMethodAccessorImpl

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);    }

这里可以看到会判断numInvocations的次数和inflationThreshold的大小,inflationThreshod默认是15,可以通过配置文件修改,如果调用次数超过了设定值,那么久还是通过MethodAccessorGenerator的方式来创建MethodAccessorImpl

这里需要注意下,通过MethodAccessorGenerator的方式生成,速度要比Native的快,但是第一次执行非常耗时,而Native方式执行比较耗时,但是第一次执行并不耗时,所以配置inflation和inflationThreshold是为了在启动时间和执行时间上面做一个考量。

通过sun.reflect.noInflation和sun.reflect.inflationThreshold来进行配置。

关于面试

以下三种获取Class对象的方式有什么不同?

1、new Object().getClass 2、Object.class 3、 Class.forName("java.util.String")

我们给出一个代码实例

public class Test {    static {        System.out.println("静态代码块");    }    {        System.out.println("动态代码块");    }    public Test(){        System.out.println("构造方法");    }    public static void main(String[] args) {        Class
clazz1 = Test.class; System.out.println("-----------"); try{ Class
clazz2 = Class.forName("com.reflect.Test"); }catch (ClassNotFoundException e){ e.printStackTrace(); } System.out.println("-----------"); Class
clazz3 = new Test().getClass(); }}

看下打印结果

静态代码块----------------------动态代码块构造方法

分别来分析以下,通过Test.class的方式只执行了静态代码块,通过Class.forName形式的话,也只执行静态代码块,而通过构造方法去执行,如果已经执行过静态代码块将不会执行静态代码块,如果没有执行过静态代码块,需要执行静态代码块,动态代码块,及构造方法

静态代码块只执行一次,因此在测试每个构造Class的方法时,建议不要像上面那样去执行,最好是分别执行测试,这一点我发现好多博客对Object.class和Class.forName方式的分析和执行结果其实是错误的,两者都是只执行静态代码块。

转载地址:http://tlvti.baihongyu.com/

你可能感兴趣的文章
Spring MVC 教程,快速入门,深入分析
查看>>
Android 的source (需安装 git repo)
查看>>
Commit our mod to our own repo server
查看>>
LOCAL_PRELINK_MODULE和prelink-linux-arm.map
查看>>
Simple Guide to use the gdb tool in Android environment
查看>>
Netconsole to capture the log
查看>>
Build GingerBread on 32 bit machine.
查看>>
How to make SD Card world wide writable
查看>>
Detecting Memory Leaks in Kernel
查看>>
Linux initial RAM disk (initrd) overview
查看>>
Timestamping Linux kernel printk output in dmesg for fun and profit
查看>>
There's Much More than Intel/AMD Inside
查看>>
CentOS7 安装MySQL 5.6.43
查看>>
使用Java 导入/导出 Excel ----Jakarta POI
查看>>
本地tomcat 服务器内存不足
查看>>
IntelliJ IDAE 2018.2 汉化
查看>>
基于S5PV210的uboot移植中遇到的若干问题记录(一)DM9000网卡移植
查看>>
Openwrt源码下载与编译
查看>>
我和ip_conntrack不得不说的一些事
查看>>
Linux 查看端口使用情况
查看>>