JDK_实例(类的加载顺序)

本文通过一个具体的Java示例,详细介绍了Java中类的初始化流程,包括静态成员与静态初始化块的执行时机,以及对象创建过程中初始化块与构造方法的调用顺序。同时展示了对象销毁时的调用流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

父类

package book.oo.initorder;

public class Parent {
	private int ix = 50;

	private static int iz = getNext(30);
	{
		System.out.println("Parent的初始化块");
		int x = 100;
		int y = getNext(100);
	}
	static {
		System.out.println("Parent的静态初始化块");
		int sx = 100;
		int sy = getNext(100);
	}
	public Parent() {
		System.out.println("Parent的构造方法被调用");
	}
	public void display() {
		System.out.println("Parent的display方法被调用");
		System.out.print("ix=" + this.ix);
		System.out.println("; iz=" + iz);
		dispA();
	}
	public static void dispA() {
		System.out.println("Parent的dispA()被调用");
	}
	public static int getNext(int base) {
		System.out.println("Parent的getNext(int base)被调用");
		return ++base;
	}
	/**
	 * 当Java在进行垃圾回收时,会调用对象的finalize方法
	 */
	protected void finalize() {
		System.out.println("Parent的销毁方法被调用");
	}
}

 

 

子类

package book.oo.initorder;

public class Child extends Parent {
	{
		System.out.println("Child的初始化块");
		int bx = 100;
	}
	static {
		System.out.println("Child的静态初始化块");
	}
	public Child() {
		super();
		System.out.println("Child的构造方法被调用");
	}
	public static void dispB(){
		System.out.println("Child的dispB()被调用");
	}
	/**
	 * 当Java在进行垃圾回收时,会调用对象的finalize方法
	 */
	protected void finalize() {
		System.out.println("Child的销毁方法被调用");
		super.finalize();
	}
}

 

 

测试类

package book.oo.initorder;

public class ClassInitOrderTest {

	public static void main(String[] args) {
		System.out.println("不new对象,访问静态方法时的输出:");
		Child.dispB();
		System.out.println();
		System.out.println("new对象,访问非静态方法时的输出:");
		new Child().display();
		//通知虚拟机进行垃圾回收
		System.gc();
	}

//保留main方法的前2行程序,将后面的注释,得到的输出结果是如下:
//	不new对象,访问静态方法时的输出:
//	Parent的静态初始化块
//	Parent的getNext(int base)被调用
//	Parent的getNext(int base)被调用
//	Child的静态初始化块
//	Child的dispB()被调用
	
//将main方法的前3行程序注释,保留后面的程序,得到的输出结果如下:
//	new对象,访问非静态方法时的输出:
//	Parent的静态初始化块
//	Parent的getNext(int base)被调用
//	Parent的getNext(int base)被调用
//	Child的静态初始化块
//	Parent的初始化块
//	Parent的getNext(int base)被调用
//	Parent的构造方法被调用
//	Child的初始化块
//	Child的构造方法被调用
//	Parent的display方法被调用
//	ix=50; iz=31
//	Parent的dispA()被调用
//	Child的销毁方法被调用
//	Parent的销毁方法被调用
	
//	总结:
//	1、虚拟机在首次加载Java类时,会对静态初始化块、静态成员变量、静态方法进行一次初始化
//	2、只有在调用new方法时才会创建类的实例
//	3、类实例创建过程:按照父子继承关系进行初始化,首先执行父类的初始化块部分,然后是父类的构造方法;再执行
//	   本类继承的子类的初始化块,最后是子类的构造方法
//	4、类实例销毁时候,首先销毁子类部分,再销毁父类部分

}

 

### Java 类加载过程概述 Java 虚拟机通过类加载器来管理的加载。整个加载流程遵循严格的顺序,确保程序能够正确运行并维护安全性[^1]。 #### 类加载的主要阶段 1. **加载 (Loading)** 这一阶段负责找到二进制字节流并将之转换成方法区内的数据结构。对于 `Bootstrap` 类加载器而言,主要处理来自 JDK 核心库(如 rt.jar 文件)中的文件。当启动应用程序时,即使是在单线程环境中也会自动触发对基础(例如 Object String 的 .class 文件)的加载操作[^2]。 2. **验证 (Verification)** 验证步骤旨在保证Class文件的合法性以及不会危害到虚拟机的安全性。此过程中会检查诸如元数据信息、字节码校验等内容以确认其符合 JVM 规范的要求。 3. **准备 (Preparation)** 在这一环节里为静态变量分配内存空间,并设置默认初始值;需要注意的是此时并不会赋予实际业务逻辑所指定的具体数值。 4. **解析 (Resolution)** 解析工作涉及将常量池里的符号引用转化为直接引用。这一步骤使得各个型的成员之间建立起联系,从而支持后续的方法调用等功能实现。 5. **初始化 (Initialization)** 终于来到了最后一个关键性的时刻——初始化。这里会对之前已经完成准备工作后的静态字段赋上真正的起始值,并执行 `<clinit>()` 方法块内的代码片段。如果某个含有 main 函数,则该函数也在此期间被执行[^3]。 #### 双亲委派模型 为了保障系统的稳定性安全性,JVM采用了一种称为“双亲委派”的模式来进行类加载: - 当收到一个类加载请求时,子类加载器首先委托给父级加载器尝试加载; - 如果父级无法完成加载任务(即在其命名空间中找不到对应的),则允许子代继续尝试直至最底层的应用程序级别加载器为止。 - Bootstrap 是位于顶端的父亲节点,它只负责加载核心API相关的资源文件[^4]。 ```mermaid graph TD; A[Java 应用程序入口] --> B(启动); B --> C{查找 Class}; C -->|未缓存| D[加载]; D --> E[链接]; E --> F[初始化]; subgraph "加载" direction LR G[读取字节码] H[创建 java.lang.Class 实例] I[存储至方法区内存区域] G --> H --> I end subgraph "链接 Linking" J[验证 Verification] K[准备 Preparation] L[解析 Resolution] J --> K --> L end style A fill:#f96,stroke:#333,stroke-width:4px style B fill:#bbf,stroke:#000,stroke-width:2px ``` 上述图解展示了从应用启动到具体实例化之间的全过程,其中包含了五个重要组成部分:启动、查找、加载、链接初始化。特别强调了在加载部分如何获取字节码并通过一系列内部机制最终形成可使用的对象表示形式。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值