https://blog.csdn.net/qq_31339141/article/details/90549335
Drawing 2025-07-27 15.20.21.excalidraw
⚠ Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this document. ⚠ You can decompress Drawing data with the command palette: ‘Decompress current Excalidraw file’. For more info check in plugin settings under ‘Saving’
Excalidraw Data
Text Elements
Java 程序执行有三个阶段
代码阶段/编译阶段
class Cat{ // Cat.java
String name
Cat(){}
public hi(){}…
}javac编译
Cat.class 字节码文件
String name
Cat(){}
pubic hi(){}
…Runtime 运行阶段
Cat cat = new Cat();
new 了对象导致类加载Class类阶段(加载阶段)
在堆,生成Class类对象
成员变量 Filed[] fields
构造器
成员方法…
使用类加载器(ClassLoader)
调用loadClass()进行类加载cat对象/堆
该对象知道他是属于哪个Class对象得到Class对象后
就可以创建对象,调用方法,操作属性。。。方法区
生成类的字节码二进制文件
Java 的类加载阶段是 JVM Runtime 执行过程中的一部分,而不是与 Runtime 分离的独立阶段。
Link to original
Class类
Class也是类,因此也继承Object类
Class类对象不是new出来的,而是系统创建的(通过上图发现,是通过 ClassLoader的loadClass方法创建的)
对于传统 new 出来的对象
Cat cat = new Cat();会调用
// ClassLoader类
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}如果通过反射创建Class 对象
// ....
Object classfullpath = "com.laohei.reflection.Player"
Class<?> aClass = Class.forName((String) classfullpath); // 传入 类的全限定名(fully qualified class name)也会调用 ClassLoader类的loadClass()方法
对于某个类的Class对象,在内存中只有一份,因为类只加载一次(上图)
Class<?> aClass1 = Class.forName("com.laohei.reflection.Player");
Class<?> aClass2 = Class.forName("com.laohei.reflection.Player");
// aClass1.hashCode() == aClass2.hashCode() 创建一个Class对象以后,通过Class对象可以完整地得到一个类的完整结构(通过一系列APl)
类的字节码二进制数据,是放在方法区的,有的地方称为类的元数据(包括方法代码变量名,方法名,访问权限等等)
类加载
classloading process.excalidraw
⚠ Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this document. ⚠ You can decompress Drawing data with the command palette: ‘Decompress current Excalidraw file’. For more info check in plugin settings under ‘Saving’
Excalidraw Data
Text Elements
java source code
Java bytecode file
using “javac” compile it
executed by JVM
class loading process
Loading
Linking
verifacation
Preparation
Resolution
Initialization
memory layout: after a class was loaded by the JVM
Method Area
(or Metaspace in modern JVMs)stored java bytecodes and
binary dataStores instances (objects)
created from the class.Heap
The ‘class’ object used for
Link to original
reflection is also here
Loading: 将类的class文件读入内存,并为之创建一个java.lang.Class对象。此过程由类加载器完成。
Linking: 将类的二进制数据合并到JRE中。
Initialization: JVM负责对类进行初始化,这里主要是指静态成员(can be controlled by programmers)
加载阶段
JVM在该阶段的主要目的是将字节码从不同的数据源(可能是class文件、也可能是jar包,甚至网络)转化为二进制字节流加载到内存中,并生成一个代表该类的java.lang.Class对象
连接阶段-验证
- 目的是为了确保CIss文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
- 包括:文件格式验证(是否以魔数oxcafebabe开头)、元数据验证、字节码验证和符号引用验证
- 可以考虑使用-Xverify:none 参数来关闭大部分的类验证措施,缩短虚拟机类加载的时间。
比如创建一个对象后
public static void main(String[] args){
new Dog();
// 追源码会看到,系统生成一个 SecurityManager
// System.getSecurityManager();通过这个静态方法调用
}其对应的.class文件的开头为(以二进制格式打开)
cafe babe
// …
连接阶段-准备
JVM会在该阶段对静态变量,分配内存并默认初始化(对应数据类型的默认初始值,如O、OL、null、false等)。这些变量所使用的内存都将在方法区中进行分配
class Aclass {
// 属性-成员变量-字段
// 分析类加载的链接阶段-准备 属性是如何处理
// 1. n1 是实例属性, 不是静态变量,因此在准备阶段,是不会分配内存
// 2. n2 是静态变量,分配内存 n2 是默认初始化 0 ,而不是20
// 3. n3 是static final 是常量, 他和静态变量不一样, 因为一旦赋值就不变 n3 = 30
public int n1 = 10;
public static int n2 = 20;
public static final int n3 = 30;
}链接阶段-解析
虚拟机将常量池内的符号引用替换为直接引用的过程
Initialization(初始化)
- 到初始化阶段,才真正开始执行类中定义的Java程序代码,此阶段是执行
<clinit>()方法的过程。 <clinit>()方法是由编译器按语句在源文件中出现的顺序,依次自动收集类中的所有静态变量的赋值动作和静态代码块中的语句,并进行合并。- 虚拟机会保证一个类的
<clinit>()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的<clinit>()方法,其他线程都需要阻塞等待,直到活动线程执行<clinit>()方法完毕
class A{
static{
System.out.println("A静态代码块");
num = 300;
}
static int num = 100;
public A (){
System.out.println("A Constructor");
}
public static void main(String[] args){
new A();
//1. 加载A类,并生成 B的class对象
//2. 链接阶段-准备 num = 0
//3. 初始化阶段
// 依次自动收集类中的所有静态变量的赋值动作和静态代码块中的语句,并合并
/*
clinit() {
System.out.println("B 静态代码块被执行");
//num = 300;
num = 100;
}
合并: num = 100
*/
// 4. 执行A类的构造器,创建对象
}
}https://blog.csdn.net/qq_31339141/article/details/90549335
获取Class
// 三种方式:
// ClassLoader将类装载入内存,但不进行类的初始化
// 即类完成了加载阶段被加载进JVM内存创建了Class
Class clazz = Test.class;
// ClassLoader将类装载入内存,并进行类的初始化
// 即类完成了加载阶段并加载进JVM内存创建了Class,并且完成了类初始化阶段
Class clazz = Class.forName("com.example.Test");
// 返回类对象运行时真正所指的对象、所属类型的Class对象
Class clazz = new Test().getClass();通过Class获取obj
通过 Class 得到你加载的类的对象实列
xxx.class获取obj
Class.forName()获取obj
补充 by ChatGPT
会触发类初始化的情况
1.1 创建类的实例
new MyClass();说明:实例化对象前必须先初始化类。
1.2 访问类的静态字段或方法(非 final 常量)
MyClass.staticField;
MyClass.staticMethod();⚠️ 如果是
final static编译期常量,不会触发初始化。
1.3 反射调用(如 Class.forName)
Class.forName("com.example.MyClass");1.4 子类初始化时,先初始化父类
new SubClass(); // 会先初始化父类1.5 JVM 启动时,指定的主类(包含 main 方法)
java com.example.MyApp