关于Java中的klass和class

 更新时间:2023年08月30日 09:44:36   作者:云川之下  
这篇文章主要介绍了关于Java中klass和class的区别,vm加载的字节码,也就是.class文件,被加载到方法区里面,叫Kclass,是一个C++对象,含有类的信息、虚方法表等,需要的朋友可以参考下

Java的klass和class

vm加载的字节码,也就是.class文件,被加载到方法区里面,叫Kclass,是一个C++对象,含有类的信息、虚方法表等

下文中说的mirror是指,JVM在加载了字节码后,在堆里创建的Class对象,这个对象和方法区的Kclass相互指向,也就是说我们可以通过Kclass找到这个对象

我们new 一个对象,对象头里面会有一个指针,指向方法区的Kclass

new Object().getClass()流程, 对象头里面的指针–>方法区kClass–>堆Class对象

反射也是拿到堆里的Class对象

所有我们比较两个对象类型是否相等,实际上就是比较两个Class对象是否一样

本节课,我们深入地理解一下反射中使用的Class类,Method类,Field类这三个类。

其中,重点中的重点是Class类和Class对象。这个概念太容易混淆。

我在写这篇文章之前,先在网上搜了一下,发现没有一篇文章是能讲得很清楚的。

确实很少有文章能讲明白

知乎上有很多大牛,关于Class对象也曾留下过只言片语,但又不够系统,因为对于这些进行JVM开发的牛人而言,Class对象是一个不屑于说的问题。但就是在这样的,开发者有点糊涂,大牛觉得不重要的地方,才容易产生知识的死角。而且网上有很多错误的概念,以讹传讹,更加容易让新手们搞不清楚。

先给结论,每一个Java类都有一个伴生的Class对象。

详细解释一下,定义这样一个类:

class Main {
}

那么当这个类所在的文件被加载,更准确地说,这个类被ClassLoader加载到JVM中的时候,Hotspot虚拟机会为这个类在虚拟机内部创建一个叫做Klass的数据结构:

class Klass : public Metadata {
  friend class VMStructs;
 protected:
  // note: put frequently-used fields together at start of klass structure
  // for better cache behavior (may not make much of a difference but sure won't hurt)
  enum { _primary_super_limit = 8 };
  jint        _layout_helper;
  juint       _super_check_offset;
  Symbol*     _name;
  Klass*      _secondary_super_cache;
  // Array of all secondary supertypes
  Array<Klass*>* _secondary_supers;
  // Ordered list of all primary supertypes
  Klass*      _primary_supers[_primary_super_limit];
  // java/lang/Class instance mirroring this class
  oop       _java_mirror;
  // Superclass
  Klass*      _super;
  // First subclass (NULL if none); _subklass->next_sibling() is next one
  Klass*      _subklass;
  // Sibling link (or NULL); links all subklasses of a klass
  Klass*      _next_sibling;
  Klass*      _next_link;
  // The VM's representation of the ClassLoader used to load this class.
  // Provide access the corresponding instance java.lang.ClassLoader.
  ClassLoaderData* _class_loader_data;
  jint        _modifier_flags;  // Processed access flags, for use by Class.getModifiers.
  AccessFlags _access_flags;    // Access flags. The class/interface distinction is stored here.
  // Biased locking implementation and statistics
  // (the 64-bit chunk goes first, to avoid some fragmentation)
  jlong    _last_biased_lock_bulk_revocation_time;
  markOop  _prototype_header;   // Used when biased locking is both enabled and disabled for this type
  jint     _biased_lock_revocation_count;
  TRACE_DEFINE_KLASS_TRACE_ID;
  // Remembered sets support for the oops in the klasses.
  jbyte _modified_oops;             // Card Table Equivalent (YC/CMS support)
  jbyte _accumulated_modified_oops; // Mod Union Equivalent (CMS support)
....
}

这个类的完整定义,大家可以去看hotspot/src/share/vm/oops/klass.hpp, 我们这里就不再多列了。这些属性已经足够我们讲解的了。

如果Main class被加载,那么虚拟机内部就会为它创建一个 Klass ,它的 _name 属性就是字符串 “Main”。

_primary_supers 代表了这个类的父类。比如,我们看IOException, 是Exception的子类,而Exception又是Throwable的子类。

那么,如果你去看IOException的 _primary_supers 属性就会发现,它是这样的:[Throwable, Exception, IOException],后面5位为空。

其他的属性我们先不看,以后有时间会慢慢再来讲。

今天重点说一下oop,这个我猜是ordinary object pointer的缩写,到底是什么的缩写,其实我也不确定。

但我能确定的是,这种类型代表是一个真正的Java对象。比如说:

Main m = new Main();

这行语句里创建的 m 在JVM中,就是一个oop,是一个普通的Java对象,而Main在JVM里则是一个Klass。

大家理清了这里面的关系了吗?我建议没看懂的,再多看一遍。一般地来说,我不是很鼓励新手学习JVM源代码。但是有一些核心概念,如果能加以掌握的话,还是有利于快速掌握概念的本质的。

好了。说了这么多,才刚来到我们今天的主题: java_mirror 。不起眼的一行:

 // java/lang/Class instance mirroring this class
  oop       _java_mirror;

注释说得很清楚了,这个属性代表的就是本class的Class对象。举例来说,如果JVM加载了Main这个类,那么除了为Main创建了一个名为"Main"的Klass,还默默地背后创建一个object,并且把这个object 挂到了 Klass 的 _java_mirror 属性上了。

那我们通过Java代码能不能访问到这个背后的对象呢?你肯定已经猜到了,当然能啊,这就是Main的class对象啊。我们上节课已经有两种写法来访问它了啊:

Class m = Main.class;
Class m = Class.forName("Main");

这两种方法都能访问到Main的Class object,也就是 Klass 上那个不起眼的 _java_mirror 。那么这个_java_mirror上定义的 newInstance 方法,其实最终也是通过JVM中的方法来创建真正的对象:

JVM_ENTRY(jobject, JVM_NewInstanceFromConstructor(JNIEnv *env, jobject c, jobjectArray args0))
  JVMWrapper("JVM_NewInstanceFromConstructor");
  oop constructor_mirror = JNIHandles::resolve(c);
  objArrayHandle args(THREAD, objArrayOop(JNIHandles::resolve(args0)));
  oop result = Reflection::invoke_constructor(constructor_mirror, args, CHECK_NULL);
  jobject res = JNIHandles::make_local(env, result);
  if (JvmtiExport::should_post_vm_object_alloc()) {
    JvmtiExport::post_vm_object_alloc(JavaThread::current(), result);
  }
  return res; 
JVM_END

这个函数到这里我们就不再往下追了,只要知道JVM可以通过 java_mirror 找到真正的 Klass ,然后再用这个 Klass 创建一个真正的对象就可以了。

到此这篇关于关于Java中的klass和class的文章就介绍到这了,更多相关Java的klass和class内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java实现Redis分布式锁的三种方案汇总

    Java实现Redis分布式锁的三种方案汇总

    setnx、Redisson、RedLock 都可以实现分布式锁,从易到难得排序为:setnx < Redisson < RedLock,本文为大家整理了三种方法的实现,希望对大家有所帮助
    2023-11-11
  • Fluent Mybatis学习之Update语法实践

    Fluent Mybatis学习之Update语法实践

    Fluent MyBatis是一个MyBatis的增强工具,没有对mybatis做任何修改。本篇文章将详细介绍对Fluent Mybatis中的update语法进行验证。代码具有一定价值,感兴趣的小伙伴可以学习一下
    2021-11-11
  • 实例详解SpringMVC入门使用

    实例详解SpringMVC入门使用

    大家好,本篇文章主要讲的是实例详解SpringMVC入门使用,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下,方便下次浏览
    2021-12-12
  • SpringBoot读取多环境配置文件的几种方式

    SpringBoot读取多环境配置文件的几种方式

    这篇文章主要给大家介绍了SpringBoot读取多环境配置文件的几种方式,文章通过代码示例介绍的非常详细,具有一定的参考价值,需要的朋友可以参考下
    2023-10-10
  • Spring中@Scope注解用法解析

    Spring中@Scope注解用法解析

    这篇文章主要介绍了Spring中@Scope注解用法解析,@Scope注解主要作用是调节Ioc容器中的作用域,在Spring IoC容器中主要有以下五种作用域,需要的朋友可以参考下
    2023-11-11
  • java Aop实现自动填充字段值示例

    java Aop实现自动填充字段值示例

    这篇文章主要为大家介绍了Aop实现自动填充字段值示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-08-08
  • Mybatis如何开启控制台打印sql语句

    Mybatis如何开启控制台打印sql语句

    在SpringBoot与Mybatis整合开发中,开启控制台SQL语句打印是一个常见需求,有助于调试与性能优化,方法一:在Mybatis配置文件mybatis-config.xml中添加设置;方法二:在SpringBoot配置文件application.yml或properties中
    2024-11-11
  • SpringBoot静态函数无法自动注入Bean的原因分析与解决方案

    SpringBoot静态函数无法自动注入Bean的原因分析与解决方案

    在 Spring Boot 项目中,开发者常遇到一个典型问题:在静态方法或静态变量中尝试使用 @Autowired 注入 Bean 时,始终得到 null 值,本文将深入剖析这一问题的根源,并提供多种可靠解决方案,需要的朋友可以参考下
    2025-08-08
  • Java中的WeakHashMap详解

    Java中的WeakHashMap详解

    这篇文章主要介绍了Java中的WeakHashMap详解,WeakHashMap可能平时使用的频率并不高,但是你可能听过WeakHashMap会进行自动回收吧,下面就对其原理进行分析,需要的朋友可以参考下
    2023-09-09
  • Springboot+Mybatis+logback打印sql脚本日志实现过程

    Springboot+Mybatis+logback打印sql脚本日志实现过程

    在SpringBoot项目中,通过配置`application.yml`和`logback-spring.xml`文件,可以在控制台和日志文件中打印SQL脚本日志,关键在于配置logback-spring.xml中的`name`属性为mapper类所在路径
    2026-01-01

最新评论