IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 万字解析Java反射原理(源码) -> 正文阅读

[Java知识库]万字解析Java反射原理(源码)

一、反射是什么?

学习反射,首先要知道"反"是什么意思,"正"又是什么。

对于 Java 来说,已知类型创建对象是"正":Object o = new Object(); ,编译期间已知要创建的对象 o 的静态类型为 java.lang.Object。

运行期间根据全限定名或对象获取类型信息是"反":例如 Class clazz = Class.forName("xxx");,编译期间无法得知目标类型。只能在运行期间根据给出的类的全限定名获取类型信息。

Java 为了让我们能够使用反射在运行期间检查类型信息,将类型信息抽象为了多个类。除了 Class 类用于描述类信息外,还有 Method 类描述方法,Field 类描述字段,Constructor 类描述构造器。

反射能干的事情不止于此,获得以上几种类型的实例后,还能动态加载类、创建对象,动态调用方法,动态修改字段的值,获取父类信息,获取接口信息,获取注解信息等等…

反射的功能强大,应用于很多工具及框架:Spring Ioc ;BeanUtils 内省工具;反射机制也应用在多种设计模式中:工厂模式,动态代理模式等。

1.1暴力反射

我们可以通过反射动态获取类中私有的字段或方法,这违背了 private 的语义。所以我们需要暴力点,让他没屁放。例如以下代码:

Object o = clazz.newInstance();
Field aPrivateField = clazz.getDeclaredField("aPrivateField");
//设置当前对象不需要被检查访问权限
aPrivateField.setAccessible(true);
aPrivateField.set(o,123);
System.out.println(aPrivateField.get(o));//123

暴力反射原理

在这里插入图片描述

Constructor 与 Method 类继承 Executable 抽象类,继承 AccessibleObject 类,其子类还有 Field 字段类

Executable 类:提供了许多展示方法信息的函数例如:sharedToString,sharedToGenericString,getName,getModifiers,getParameters。

AccessibleObject 类:

The AccessibleObject class is the base class for Field, Method and
 * Constructor objects.  It provides the ability to flag a reflected
 * object as suppressing default Java language access control checks
 * when it is used.  The access checks--for public, default (package)
 * access, protected, and private members--are performed when Fields,
 * Methods or Constructors are used to set or get fields, to invoke
 * methods, or to create and initialize new instances of classes,
 * respectively.

AccessibleObject类是字段、方法和构造函数对象的基类。
它提供了在使用反射对象时将其标记为抑制默认Java语言访问控制检查的能力。
当Fields、Methods或Constructors分别用于设置或获取字段、调用方法或创建和初始化类的新实例时,
执行访问检查(对于公共、默认()访问、保护和私有成员)

当我们通过反射获取 Field,Method,Constructor 对象时,都要经过访问检查 checkAccess(),那么暴力反射的原理就是获得权限,通过访问检查站。

setAccessible(true) 源码

public void setAccessible(boolean flag) throws SecurityException {
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) sm.checkPermission(ACCESS_PERMISSION);
    setAccessible0(this, flag);
}

二、反射核心类 Class

当 JVM 加载任一类型时,都会在堆中同时创建一个 Class 实例作为该类型的类型信息的访问入口。Class 将类型信息缓存,并向外提供获取类型信息的方法。

至于 Class 实例是如何创建的?;当 JVM 第一次加载 Class 类时是否会同时创建一个 Class 实例?;这些有趣的问题,在文末与获取 Class 实例一起来探讨。

三、反射数据类 ReflectionData

是 Class 类的私有静态内部类,作为 Class 的实例成员,存储类型信息。

private volatile transient SoftReference<ReflectionData<T>> reflectionData;
private static class ReflectionData<T> {
    volatile Field[] declaredFields;
    volatile Field[] publicFields;
    volatile Method[] declaredMethods;
    volatile Method[] publicMethods;
    volatile Constructor<T>[] declaredConstructors;
    volatile Constructor<T>[] 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;
    }
}

以获取构造器为例:

public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
    throws NoSuchMethodException, SecurityException {
    checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
    return getConstructor0(parameterTypes, Member.DECLARED);
}

private Constructor<T> getConstructor0(Class<?>[] parameterTypes,
                                        int which) throws NoSuchMethodException
    {
        Constructor<T>[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC));
        for (Constructor<T> constructor : constructors) {
            if (arrayContentsEq(parameterTypes,
                                constructor.getParameterTypes())) {
                return getReflectionFactory().copyConstructor(constructor);
            }
        }
        throw new NoSuchMethodException(getName() + ".<init>" + argumentTypesToString(parameterTypes));
    }

Constructor<T>[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC));

privateGetDeclaredConstructors() 方法返回构造器数组。

为什么不直接从成员变量 reflectionData 中获取呢,而是通过一个方法?

private Constructor<T>[] privateGetDeclaredConstructors(boolean publicOnly) {
    checkInitted();
    Constructor<T>[] res;
    ReflectionData<T> 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<T>[] temporaryRes = (Constructor<T>[]) new Constructor<?>[0];
        res = temporaryRes;
    } else {
        res = getDeclaredConstructors0(publicOnly);
    }
    if (rd != null) {
        if (publicOnly) {
            rd.publicConstructors = res;
        } else {
            rd.declaredConstructors = res;
        }
    }
    return res;
}

方法中,首先判断 reflectionData() 方法返回的对象是否为空,又套娃了一层!再进入该方法:

private ReflectionData<T> reflectionData() {
    SoftReference<ReflectionData<T>> reflectionData = this.reflectionData;
    int classRedefinedCount = this.classRedefinedCount;
    ReflectionData<T> 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);
}

这么多层套娃,都是由于 reflectionData 在Class 类中以软引用的形式存在。

软引用 SoftReference()<>,当 JVM 内存不足时,立刻被当做垃圾回收。

既然 reflectionData 有可能会被当做垃圾回收,那么套娃就解释的通了。

if (useCaches &&
        reflectionData != null &&
        (rd = reflectionData.get()) != null &&
        rd.redefinedCount == classRedefinedCount) {
        return rd;
    }

由于每次回收再创建,可能导致版本不一致。所以在 Class 类中,有 classRedefinedCount 字段与 ReflectionData 中的 redefinedCount 相对应。如果不同,则代表不是一个版本,那么就重新创建并替换 -> newReflectionData():

private ReflectionData<T> newReflectionData(SoftReference<ReflectionData<T>> oldReflectionData,
                                            int classRedefinedCount) {
    if (!useCaches) return null;

    while (true) {
        ReflectionData<T> 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;
        }
    }
}

使用 Atomic.casReflectionData(),使用 CAS(CompareAndSwap),失败重试直至替换新的 ReflectionData。

确定 ReflectionData 后,返回 Constructor[]。

四、反射获取构造器

  1. public Constructor[] getConstructors(): 获取 public 修饰的构造方法
  2. public Constructor[] getDeclaredConstructors():获取类中所有构造方法
  3. public Constructor getConstructor(Class... parameterTypes) 根据参数列表信息在 public 修饰的构造方法中寻找
  4. public Constructor getDeclaredConstructor(Class... parameterTypes)根据参数列表信息在类的所有构造方法中寻找

getConstructor(参数列表) 为例

public Constructor<T> getConstructor(Class<?>... parameterTypes)
    throws NoSuchMethodException, SecurityException {
    checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
    return getConstructor0(parameterTypes, Member.PUBLIC);
}

private Constructor<T> getConstructor0(Class<?>[] parameterTypes,
                                        int which) throws NoSuchMethodException
    {
    //获取 public 修饰的构造方法
        Constructor<T>[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC));
        for (Constructor<T> constructor : constructors) {
            if (arrayContentsEq(parameterTypes,
                                constructor.getParameterTypes())) {
                return getReflectionFactory().copyConstructor(constructor);
            }
        }
        throw new NoSuchMethodException(getName() + ".<init>" + argumentTypesToString(parameterTypes));
    }


最终返回的是 getReflectionFactory().copyConstructor(constructor);,一个 constructor 的 copy,为什么是 copy 呢?往下看

4.1 Constructor.newInstance()

通过构造方法对象创建该类型的实例

Constructor declaredConstructor = Object.class.getConstructor();
//        declaredConstructor.setAccessible(true);
        declaredConstructor.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;
}

Constructor.newInstance() 实际上是调用 ConstructorAccessor.newInstance()。ConstructorAccessor 是当前构造方法的访问器。

在 Constructor 类中有名为 root 的字段:

// For sharing of ConstructorAccessors. This branching structure
// is currently only two levels deep (i.e., one root Constructor
// and potentially many Constructor objects pointing to it.)
//
// If this branching structure would ever contain cycles, deadlocks can
// occur in annotation code.
private Constructor<T>      root;

我们提供反射获取的每个 Constructor 对象都是当前构造方法的根对象 root 的 copy,目的是为了共享根对象的 ConstructorAccessor 对象:构造方法访问器,newInstance() 方法的真正实现类。原因是 ConstructorAccessor 比较重量级,每当 getConstructor() 时去创建新的访问器很消耗系统资源。

为什么不直接返回 Class 对象保存的 Constructor 对象呢?而是每次 copy 一个?

原因在 Constructor::copy() 方法中说明了:

copy()

Constructor<T> copy() {
    // This routine enables sharing of ConstructorAccessor objects
    // among Constructor objects which refer to the same underlying
    // method in the VM. (All of this contortion is only necessary
    // because of the "accessibility" bit in AccessibleObject,
    // which implicitly requires that new java.lang.reflect
    // objects be fabricated for each reflective call on Class
    // objects.)
    if (this.root != null)
        throw new IllegalArgumentException("Can not copy a non-root Constructor");

    Constructor<T> res = new Constructor<>(clazz,
                                           parameterTypes,
                                           exceptionTypes, modifiers, slot,
                                           signature,
                                           annotations,
                                           parameterAnnotations);
    //指向根构造方法对象
    res.root = this;
    // Might as well eagerly propagate this if already present
    //共享构造方法访问器
    res.constructorAccessor = constructorAccessor;
    return res;
}
This routine enables sharing of ConstructorAccessor objects among Constructor objects which refer to 
the same underlying method in the VM. (All of this contortion is only necessary because of the "accessibility" bit in 
AccessibleObject, which implicitly requires that new java.lang.reflect objects be fabricated for each reflective call 
on Class objects.) 
    
这个例程允许在引用VM中相同底层方法的构造函数对象之间共享ConstructorAccessor对象。(所有这些扭曲只是因为AccessibleObject中的“可访问性”
位而必要,它隐式地要求为Class对象的每个反射调用构造新的java.lang.reflect.Constructor对象。)

由于 AccessibleObject 的要求,我们每次通过反射获得类的 Field、Constructor、Method 对象时,都要创建新的对象。为了避免每次创建对象都要创建对应的 xxxAccessor 对象,所以对根对象进行 copy。

可见每次我们通过 getConstuctor() 获取构造器对象时,都是新创建一个返回给我们。所以我们如果需要频繁获取,大可以将该对象缓存,避免重复创建。

Constructor.newInstance() 重点

if (ca == null) {
        ca = acquireConstructorAccessor();
    }
    @SuppressWarnings("unchecked")
    T inst = (T) ca.newInstance(initargs);
    return inst;

那么根对象的访问器 Accessor 是一起创建的吗,还是怎样?不能光靠猜测,还是得查看源码。发现 newInstance() 方法获取的 ConstructorAccessor 对象 ca,是通过调用 acquireConstructorAccessor() 获得的,那么进入该方法:

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

通过源码可以发现,ConstructorAccessor 并不是与 Constructor 根对象一起创建的。Java 为了性能而考虑,决定将访问器设置为延迟创建。当第一次使用 newInstance() 时再创建:reflectionFactory.newConstructorAccessor(this);

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

主要代码就是几个 if else,撇去中间两个看不懂的懒得看的 elseif,前面两个判断当前类是否是抽象类、Class 类。不能进行实例化。最后一个判断就是正常情况了,我们可以看到创建了两个访问器具体1实现类的实例 1.NativeConstructorAccessorImpl 2.DelegatingConstructorAccessorImpl

DelegatingConstructorAccessorImpl 如名字一样 Delegating 代理,这个访问器实现类,是为了给真正的业务类做代理

DelegatingConstructorAccessorImpl(ConstructorAccessorImpl var1) {
    this.setDelegate(var1);
}

public Object newInstance(Object[] var1) throws InstantiationException, IllegalArgumentException, InvocationTargetException {
    return this.delegate.newInstance(var1);
}

NativeConstructorAccessorImpl 真正的业务类,使用本地方法 newInstance0 创建对象并返回

NativeConstructorAccessorImpl(Constructor<?> var1) {
    this.c = var1;
}

public Object newInstance(Object[] var1) throws InstantiationException, IllegalArgumentException, InvocationTargetException {
    if (++this.numInvocations > ReflectionFactory.inflationThreshold() && !ReflectUtil.isVMAnonymousClass(this.c.getDeclaringClass())) {
        ConstructorAccessorImpl var2 = (ConstructorAccessorImpl)(new MethodAccessorGenerator()).generateConstructor(this.c.getDeclaringClass(), this.c.getParameterTypes(), this.c.getExceptionTypes(), this.c.getModifiers());
        this.parent.setDelegate(var2);
    }

    return newInstance0(this.c, var1);
}

void setParent(DelegatingConstructorAccessorImpl var1) {
    this.parent = var1;
}

private static native Object newInstance0(Constructor<?> var0, Object[] var1) throws InstantiationException, IllegalArgumentException, InvocationTargetException;

在 newInstance 方法中,有一个莫名其妙的条件:

++this.numInvocations > ReflectionFactory.inflationThreshold()

经过各种跳转,发现这行代码的意思就是:

只用本地方法 newInstance0 15次,15次以后改为用 MethodAccessorGenerator.generateConstructor() 生成的访问器实例来创建对象。

不用猜,肯定还是 Java 为了性能而考虑。我虽然不是太懂其中的原理,但是我会百度啊~ 百度查到大佬(RednaxelaFX)说: https://www.iteye.com/blog/rednaxelafx-548536

newInstance() 一个是Java实现的,另一个是native code实现的(newInstance0)。
Java实现的版本在初始化时需要较多时间,但长久来说性能较好;native版本正好相反,启动时相对较快,但运行时间长了之后速度就比不过Java版了。
这是HotSpot的优化方式带来的性能特性,同时也是许多虚拟机的共同点:跨越native边界会对优化有阻碍作用,它就像个黑箱一样让虚拟机难以分析也将其内联,于是运行时间长了之后反而是托管版本的代码更快些。
*

为了权衡两个版本的性能,Sun的JDK使用了“inflation”的技巧:让Java方法在被反射调用时,开头若干次使用native版,等反射调用次数超过阈值时则生成一个专用的MethodAccessor实现类,生成其中的invoke()方法的字节码,以后对该Java方法的反射调用就会使用Java版。

这个 15 次也不是一定的。可以通过 JVM 启动参数进行修改:-Dsun.reflect.inflationThreshold=xxx

ps:类似的 Integer 等含有缓存池的包装类的最大范围 high 也可以提供 JVM 启动参数进行修改。

要动态生成对象,首先要动态加载其类型。generateConstructor() 生成的访问器实例是由 DelegatingClassLoader 来加载的。

为什么要使用 DelegatingClassLoader 加载动态生成的访问器类呢?

我们知道,类的声明周期的最后一步是类的卸载。类的卸载的前提是,加载这个类的类加载器被回收。如果不用 DelegatingClassLoader 来加载,而是使用启动类 / 扩展 / 应用程序类加载器。那么类型就不太可能会被卸载,一直占着内存,浪费资源。

generateConstructor() 使用 DelegatingClassLoader 加载类型源码:

generateConstructor() -> generate() ->

return (MagicAccessorImpl)ClassDefiner.defineClass(var13, var17, 0, var17.length, var1.getClassLoader()).newInstance();

->

static Class<?> defineClass(String var0, byte[] var1, int var2, int var3, final ClassLoader var4) {
    ClassLoader var5 = (ClassLoader)AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
        public ClassLoader run() {
            return new DelegatingClassLoader(var4);
        }
    });
    return unsafe.defineClass(var0, var1, var2, var3, var5, (ProtectionDomain)null);
}

五、反射获取方法

  1. public Method[] getMethods(): 获取所有可见的方法
  2. public Method[] getDeclaredMethods() :获取所有的方法,无论是否可见
  3. public Method getMethod(String name, Class… parameterTypes) 根据方法名称及参数列表获取可见的方法
  • public Method getDeclaredMethod(String name, Class… parameterTypes) 根据方法名称及参数列表获取指定方法

同 getConstructor() 原理相同,每次反射获取时都是新创建一个实例并返回。该实例共享根Method 对象 root 的方法访问器 MethodAccessor。

5.1 Method.invoke()

测试代码

public class Father{
    public void test(){}
}

Class clazz = Class.forName("Father")
Object obj = clazz.getConstructor(null).newInstance();
Method testMethod = clazz.getMethod("test",null);
testMethod.invoke(obj,null);

Method.invoke() 原理

与 Constructor.newInstance() 同理 Method.invoke -> MethodAccessor.invoke,默认小于 15 次 native code 实现,大于 15 次 java 实现。

invoke() 调用路径:

method.invoke -> methodAccessor.invoke -> DelegatingMethodAccessorImpl.invoke -> NativeMethodAccessorImpl.invoke —大于15次–> MethodAccessorImpl.invoke(动态生成)

六、反射获取变量

  1. public Field[] getFields():获取所有 public 修饰的字段信息。
  2. public Field[] getDeclaredFields():获取所有的字段信息
  3. public Field getField(String name):通过字段名称在 public 修饰的字段集合中寻找
  4. public Field getDeclaredField(String name) :通过字段名称在所有字段信息中寻找

6.1 Field.set() / Field.get()

Field.set() -> FieldAccessor.set()。

FieldAccessor 有许多实现类:都是使用 Unsafe 类之间操作内存,修改字段的值

在这里插入图片描述

char c = '1';字段为例,Accessor 具体实现类:UnsafeCharacterFieldAccessorImpl

public void set(Object var1, Object var2) throws IllegalArgumentException, IllegalAccessException {
    this.ensureObj(var1);
    if (this.isFinal) {
        this.throwFinalFieldIllegalAccessException(var2);
    }

    if (var2 == null) {
        this.throwSetIllegalArgumentException(var2);
    }

    if (var2 instanceof Character) {
        //使用 unsafe 直接操作工作内存,修改基本数据类型的值
        unsafe.putChar(var1, this.fieldOffset, (Character)var2);
    } else {
        this.throwSetIllegalArgumentException(var2);
    }
}

volatile char c = '1';字段为例,Accessor 具体实现类:UnsafeQualifiedCharacterFieldAccessorImpl

public void set(Object var1, Object var2) throws IllegalArgumentException, IllegalAccessException {
    this.ensureObj(var1);
    if (this.isReadOnly) {
        this.throwFinalFieldIllegalAccessException(var2);
    }

    if (var2 == null) {
        this.throwSetIllegalArgumentException(var2);
    }

    if (var2 instanceof Character) {
        //直接操作**主内存**,修改值后,对所有线程可见
        unsafe.putCharVolatile(var1, this.fieldOffset, (Character)var2);
    } else {
        this.throwSetIllegalArgumentException(var2);
    }
}

Object o = new Object();字段为例,Accessor 具体实现类:UnsafeObjectFieldAccessorImpl

public void set(Object var1, Object var2) throws IllegalArgumentException, IllegalAccessException {
    this.ensureObj(var1);
    if (this.isFinal) {
        this.throwFinalFieldIllegalAccessException(var2);
    }

    if (var2 != null && !this.field.getType().isAssignableFrom(var2.getClass())) {
        this.throwSetIllegalArgumentException(var2);
    }

    //使用 unsafe 直接操作工作内存,指针指向新的对象
    unsafe.putObject(var1, this.fieldOffset, var2);
}

Field.get() -> FieldAccessor.get()。

char c = '1';字段为例,访问器实现类:UnsafeCharacterFieldAccessorImpl

省略...
public boolean getBoolean(Object var1) throws IllegalArgumentException {
    throw this.newGetBooleanIllegalArgumentException();
}

public byte getByte(Object var1) throws IllegalArgumentException {
    throw this.newGetByteIllegalArgumentException();
}

public char getChar(Object var1) throws IllegalArgumentException {
    this.ensureObj(var1);
    return unsafe.getChar(var1, this.fieldOffset);
}
省略...

七、获得 Class 对象的方法

new 不可行

7.1 私有构造方法

/*
 * Private constructor. Only the Java Virtual Machine creates Class objects.
 * This constructor is not used and prevents the default constructor being
 * generated.
 */
private Class(ClassLoader loader) {
    // Initialize final field for classLoader.  The initialization value of non-null
    // prevents future JIT optimizations from assuming this final field is null.
    classLoader = loader;
}

因为构造方法是私有的,我们不能以 Class clazz = new Class<>();。方式获得 Class 对象

Class 类的构造方法只能由 JVM 调用,只有 JVM 能创建 Class 对象,所以即使我们通过暴力反射的方式尝试,也无济于事。

Constructor declaredConstructor = Class.class.getDeclaredConstructor(ClassLoader.class);
declaredConstructor.setAccessible(true);
declaredConstructor.newInstance();

//output
Exception in thread "main" java.lang.SecurityException: Cannot make a java.lang.Class constructor accessible

7.2 Object.class

我们可以通过类名.class的形式,获取该类型对应的 Class 对象。

甚至我们可以这样:int.classlong.class

public void test(){
        System.out.println(int.class);
    }

Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: getstatic     #34                 // Field java/lang/Integer.TYPE:Ljava/lang/Class;
         6: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
         9: return

反编译一下,我们可以看到,int.class 实际上就是 Integet.TYPE。

对于所有基本数据类型,其包装类都有 TYPE 静态属性,对应 基本数据类型.class。

7.3 o.getClass() 重点

通过对象获取 class 对象。原理:对象在jvm中的存在形式

7.4 Class.forName("java.lang.Object")重点

Class.forName("")源码
public static Class<?> forName(String className)
                throws ClassNotFoundException {
        Class<?> caller = Reflection.getCallerClass();
        return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
    }
---------------------------------
测试代码
public class ReflectTest {
    public static void main(String[] args) throws ClassNotFoundException {
        Class clazz = Class.forName("mysql.cj.jdbc.Driver");
    }
}

以这种方式获得 Class 对象,forName() 方法的第二个参数默认为 true。表示初始化该类。

我们知道,类的初始化阶段,是由 JVM 调用该类的 < clinit>() 方法。其中包括静态变量赋目标值,调用静态代码块。

相信大多数人接触到这行代码都是从 jdbc 开始的,当时只是知道调用 Class.forName("mysql.cj.jdbc.Driver");就可以连接数据库了。是因为 Driver 类在静态代码块中加载数据库驱动,所以只需要 Class.forName() 初始化该类就可以。

7.5 Class.forName("xxx",true,ClassLoader);

public static Class<?> forName(String name, boolean initialize,
                                   ClassLoader loader)
        throws ClassNotFoundException
    {
        Class<?> caller = null;
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            // Reflective call to get caller class is only needed if a security manager
            // is present.  Avoid the overhead of making this call otherwise.
            caller = Reflection.getCallerClass();
            if (sun.misc.VM.isSystemDomainLoader(loader)) {
                ClassLoader ccl = ClassLoader.getClassLoader(caller);
                if (!sun.misc.VM.isSystemDomainLoader(ccl)) {
                    sm.checkPermission(
                        SecurityConstants.GET_CLASSLOADER_PERMISSION);
                }
            }
        }
        return forName0(name, initialize, loader, caller);
    }

7.6 ClassLoader.loadClass(“xxx”)

Class<?> aClass = Test.class.getClassLoader().loadClass("java.lang.Object");

动态加载目标类的第二种方式,这种方式更加简洁。与 Class.forName() 不同在于,不初始化该类,也就不会调用静态代码块,不会为静态变量赋值。

八、JVM 第一次加载 Class 类时是否会同时创建一个 Class 实例?

我们知道在类加载阶段,JVM 读取静态类型信息保存至方法区,同时为该类型创建一个保存类型信息的对象。如果是 Class 对象,那么当 JVM 第一次加载 Class 类时是否会创建一个 Class 实例吗?

但是 Class 类正处于加载阶段,又怎么会跳过中间的链接阶段、实例化阶段。直接创建 Class 对象并分配内存呢?这个问题看似无解,实际上:

在 JVM 中,对象的存在形式是 Oop-Klass 模型。Oop 表示对象,Klass 表示类型。

在这里插入图片描述

当一个类第一次被加载时,同时创建的 class 对象其实本质上就是 Klass (C++)对象,这个对象的创建不需要加载 Class 类。

我们通过各种方式获得类型对应的 Class 对象时,由 JVM 在堆中创建 class 对象。并让 instanceKlass 对象中的 _java_mirror 指针指向该对象。例如 getClass() 方法获取类型对应的 class 对象的流程如下:

对象 -> instanceKlass -> class

Class 也是个普通的 Java 类。当 JVM 第一次加载 Class 类时,在方法区中为 Class 类创建对应的 instanceKlass 对象。并不会出现本节标题的问题。

`

参考:
假笨说-从一起GC血案谈到反射原理
RednaxelaFX-关于反射调用方法的一个log
Yilun Fan-Java反射原理简析

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-07-13 17:19:45  更:2021-07-13 17:20:23 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/2 9:03:09-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码