java中的class到底是个什么东西?

年轻时,也曾被这个问题困扰多时,直到去看open jdk的源代码,才彻底搞懂了。

为什么这个问题,当时的我,一直搞不懂呢? 因为在java里,到处有class的身影,比如:

  • .class文件 - 磁盘上的字节码文件;
  • Class对象 - 运行时JVM内存中的元数据对象;
  • class关键字 - 源码中定义类的语法。

要想彻底搞懂java中的class,则上面的三个就必须完全搞懂,另外这三者还是递进关系来的:

编译成.class字节码 --> JVM加载后生成Class对象。

其中最难啃的,就是Class对象。


Class对象到底是何方神圣

在正式开始之前,我需要先分享一个理解知识的重要方法,就是:

有什么问题要解决?

你得先知道,到底存在什么问题需要被去解决,而各路神仙的解决方案又是什么,然后方能彻底理解这块的知识。

比如你看哈,我最近在写了一篇技术内容:

Java NIO到底是个什么东西?

我并没有一来就开始介绍NIO,而是通过高可靠的网络服务需要解决什么问题,然后慢慢引导出设计方案,最终才到JAVA的NIO是如何解决这些问题的。

这样子就能很好的理解这块的知识,也比较容易记得牢。

好,我们现在依然采用这个方法,从问题入手,然后慢慢引导出解决方案,以及java是如何解决的。

从类型内省谈起

软件系统发展到一定阶段,必然会遇到一个架构层面的根本问题:程序能否在运行时"看清"自己的结构?

这不是什么虚头巴脑的东西,而是实实在在的工程需求,一种纯技术的需求,比如说:

  • JSON序列化框架:拿到一个对象,需要知道它有哪些字段,才能把它转成 JSON 字符串;
  • 依赖注入容器(Spring):需要知道构造函数里声明了什么参数,才能把对应的 Bean 自动塞进去;
  • 半ORM和ORM框架(MyBatis/Hibernate):需要知道对象字段的类型,才能把它映射到数据库的列;
  • RPC:方法签名是什么,参数/返回值怎么编解码,才能远程调用。

这个能力在编程语言设计中有一个专门的术语:类型内省(Introspection)

面对这个问题,不同的编程语言选择了完全不同的技术设计方案。

各种编程语言是如何处理类型信息的

各种编程语言,针对这个问题,给出的解决方案是截然不同的。

设计思路一:极简主义(C/C++),编译即销毁

C++ 追求极致的性能 。代码编译成机器码后,绝大部分类型信息都被抹除,只剩下内存地址和指令。

虽然 C++ 也有 RTTI(运行时类型识别),但非常弱。所以 C++ 要实现序列化,往往需要手写代码或者依赖编译期的宏,无法像 Java 那样动态自查。

设计思路二:折中主义(Go),自带说明书

Go 语言没有虚拟机,编译出来的也是二进制文件。但它在编译时,会把类型的元数据(字段名、Tag等)打包写进二进制文件的特定段(Section)里。

在运行时,通过特定的接口(reflect包)去读取这份内置说明书。这是一种很好的平衡,既保留了原生编译的高性能,又提供了一定的动态能力。不得不说,GO语言的设计者,还是挺天才的。

设计思路三:全动态主义(Java/C#) ,Java 选择了最重但也最灵活的方案:它把类型的定义信息视为一种对象。

读到这里的时候,你就应该能意识到:

编译之后,运行时还能保留多少可供程序访问类型元数据?访问成本是多少?

是大多数程序语言要考虑的问题。为了帮助你理解接下来的内容,你需要再理解一个东西,就是所谓的元数据

很多人第一次看到MetaData/元数据会懵,其实它没那么难理解。

元数据 = 描述数据的数据。你可以把它理解成说明书

举个最直观的例子:

  • 你写的User对象,里面有id、name、age这些 ,这种叫数据
  • 但这个对象有哪些字段、字段叫什么、字段类型是什么、有哪些方法、有哪些注解、构造器参数是什么, 这些是描述****User**** 的信息 ,这就叫元数据
  • 再强调一下,元数据不是你的业务数据,它是结构信息,说明书信息。

那什么叫【类型的元数据?其实就是专门描述一个类型(类)长什么样的那份说明书,包括:

  • 类名、包名
  • 父类、实现了哪些接口
  • 有哪些字段(字段名/字段类型/修饰符)
  • 有哪些方法(方法名/参数类型/返回值/异常)
  • 有哪些注解、泛型签名(部分)、访问权限等等

在 Java 里,这份类型说明书的入口就是:java.lang.Class**** 对象。

你平时写的User.classobj.getClass(),拿到的就是这本说明书:

Java 复制代码
Class<?> clazz = User.class;

// 类名:com.xxx.User
System.out.println(clazz.getName());
// 字段数量
System.out.println(clazz.getDeclaredFields().length); 
// 方法数量
System.out.println(clazz.getDeclaredMethods().length); 

所以这句话就很好理解了:

编译之后,运行时还能保留多少可供程序访问的类型元数据?

大白话讲就是:

编译完以后,运行时还能不能拿到这本说明书(也就是 Class 里能不能读到字段、方法、注解等信息),并且读它要付出多大代价。

Java的解决方案:Class对象是桥梁

好,现在可以开始回答Java 中的Class 到底是什么这个问题了。

在 java虚拟机JVM 的视角里,内存被分为了两个大块:

  1. 元数据(Metaspace):这里存放着类的说明信息,这是给虚拟机用的,Java 代码无法直接访问;
  2. 堆(Heap) :这里存放着我们在代码里new 出来的各种实例。

java.lang.Class对象,就是 JVM 在加载类时,特意在Heap里暴露的一个访问入口

User.class 被加载到内存时,JVM 做了两件事:

  1. 解析字节码,将类的结构信息(元数据)存入元空间(Metaspace)
  2. 在**堆(Heap)**中创建一个Class<User> 的实例对象。

这个Class对象内部持有指向元空间的指针。它就像是一个中间人:

  • 它对外(Java代码)暴露方法(getName(),getFields());
  • 它对内(JVM)通过指针去元空间查询真正的数据结构。

所以,Java 中所有的反射操作(Reflection),本质上都是在和这个Class 对象对话,通过它间接操控底层的类型信息。

如果你还是理解不了的话,我们可以用苹果手机来做一个比喻:

我们平时new出来的对象(Object),是工厂生产出来的具体产品 ,比如一台台 iPhone,而与之对应的Class对象,就是这台 iPhone 的设计图纸

因此所谓的反射,就是程序在运行的时候,拿着手里的产品(Object),反向去找它的图纸(Class),看清楚内部构造后,再对产品进行修改或复制。

因此反射中的【】字不是随便起的,是有真实的具体含义的。

当我们正确理解Class对象后,class关键字和.class文件,就可以简单用几句话来描述了。

在JAVA中的类,它是有不同形态的。

  • 静态形态 :源代码里的class 关键字;
  • 传输形态 :编译后的.class 字节码文件;
  • 运行时形态 :堆内存中的java.lang.Class 对象。

因此最关键的,还是需要理解好Class对象,其他的就自然而然能理解了。

小结

希望这篇内容能帮助到你,真正的理解java的Class对象,如果有疑问的,可以在评论区留言。


最近在知乎出了

  • 「应付6000万会员的秒杀系统专栏」
  • 「几亿用户,百万并发的C端商品系统实战」
  • 「技术团队DDD领域驱动设计三年落地实战」
  • 「应付亿级用户规模的支付系统代码实战」
  • 「应付亿级用户的会员体系代码实战」

专栏,感兴趣的可以订阅一下。至于知识星球的,可以搜:

  • 老码头的技术浮生录

它是一个能实际帮你解决难题的星球。有问题的,找知心的Sam哥,支持无限次语音一对一解决你遇到的难题。「另外后续我新写的所有对外的付费专栏,在星球内都是免费的,且可以拿到所有源代码。」

当前星球里免费看的专栏是:

  • 「应付6000万会员的秒杀系统专栏」
  • 「几亿用户,百万并发的C端商品系统实战」
  • 「技术团队DDD领域驱动设计三年落地实战」
  • 「应付亿级用户规模的支付系统代码实战」
  • 「应付亿级用户的会员体系代码实战」

知识星球内后续将推出20+个付费专栏,覆盖电商全链路:

选购线 用户会员营销线 中后台
购物车服务 营销系统 订单系统
商品服务 用户系统 支付系统
菜单服务 结算服务

从前台选购到中后台结算,星球成员全部免费,后续新增也不额外收费。

我的知乎账号:

  • SamDeepThinking
相关推荐
swordbob1 小时前
Spring 3 级缓存解决循环依赖
java·spring
摇滚侠1 小时前
SpringMVC 入门到实战 获取请求参数 25-32
java·spring·intellij-idea
咖啡八杯1 小时前
【无标题】
java·后端·设计模式
mqiqe1 小时前
面试题-MyBatis 面试篇
java·面试·mybatis
摇滚侠1 小时前
SpringMVC 入门到实战 @RequestMapping 14-24
java·spring
云烟成雨TD1 小时前
Spring AI Alibaba 1.x 系列【80】可观测集成
java·人工智能·spring
资深流水灯工程师1 小时前
PySide6 QMainWindow与QWidget秒解
开发语言·python
Filwaod2 小时前
MCP 接入模式对比:Agent - Gateway - 业务项目 vs Agent - Adapter - 业务项目
java·agent·mcp
kuonyuma2 小时前
MyBatis入门·注解操作
java·spring boot·mysql·spring·mybatis