初始化块和构造器的比较、总结

本文详细解析了Java中的初始化块概念,包括静态初始化块与普通初始化块的区别及其执行时机。通过具体示例,展示了不同初始化块在类继承过程中的执行顺序。

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

初始化块分为:静态初始化块和普通初始化块。

在定义初始化块时使用了static修饰符,静态初始化块(也叫做类初始化块);否则,就是普通初始化块。

与静态方法、非静态方法的理解其实类似,普通初始化块负责对对象执行初始化,类初始化块负责对类执行初始化。

为了更好地区分,上一段代码:

package object_down;

class Root{
	static {
		System.out.println("Root的静态初始化块");
	}
	{
		System.out.println("Root的普通初始化块");
	}
	public Root(){
		System.out.println("Root的无参数的构造器");
	}
}
class Mid extends Root{
	static {
		System.out.println("Mid的静态初始化块");
	}
	{
		System.out.println("Mid的普通初始化块");
	}
	public Mid() {
		System.out.println("Mid的无参数的构造器");
	}
	public Mid(String msg) {
		this(); //this()函数会在末尾进行解释,如果此处不太理解this()函数的意思的话,请跳转末尾
		System.out.println("Mid的带参数构造器,其参数值为:"+msg);
	}
}
class Leaf extends Mid{
	static {
		System.out.println("Leaf的静态初始化块");
	}
	{
		System.out.println("Leaf的普通初始化块");
	}
	public Leaf() {
		super("大学物理");
		System.out.println("执行Leaf的构造器");
	}
}
public class Test {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		new Leaf();
		new Leaf();
	}

}

运行结果见下:

Root的静态初始化块
Mid的静态初始化块
Leaf的静态初始化块
Root的普通初始化块
Root的无参数的构造器
Mid的普通初始化块
Mid的无参数的构造器
Mid的带参数构造器,其参数值为:大学物理
Leaf的普通初始化块
执行Leaf的构造器
Root的普通初始化块
Root的无参数的构造器
Mid的普通初始化块
Mid的无参数的构造器
Mid的带参数构造器,其参数值为:大学物理
Leaf的普通初始化块
执行Leaf的构造器

为啥生成两个Leaf()对象,却要执行那么多的函数呢?

答案见下:

第一次创建一个Leaf()对象时,因为系统中还不存在Leaf()类,因此需要先加载并初始化Leaf类。

而初始化Leaf类其实也是一个蛮麻烦的过程:初始化Leaf类需要先执行其顶层父类的静态初始化块(因为是对类初始化的嘛!),再执行其直接父类的静态初始化块,最后才执行Leaf本身的静态初始化块。

一旦Leaf类初始化成功后,Leaf类在JVM中将一直存在,因此当第二次创建Leaf实例时,无需再次对Leaf类进行初始化。

每次创建一个Leaf对象时,都需要先执行最顶层的父类的初始化块,构造器,然后再执行其父类的初始化块、构造器……最后才执行Leaf的初始化块,构造器。(注意:创建对象时执行的初始化块是普通初始化块,它负责对对象执行初始化)

最后,上面代码中有一段中有一个this()函数,在此作以下解释:

package test_test;

import java.util.*;

public class Test {
    private int x;
    private int y;
    private int z;
   
    public Test(int x, int y) {
        this.x = x;
        this.y = y;
        System.out.println("Test的含有两个参数的构造器");
    }

    public Test(int x, int y, int z) {
        this(x,y);
        this.z = z;
        System.out.println("Test的含有三个参数的构造器");
    }
    
    public static void main(String []args) {
    	new Test(100,10);
    	System.out.println("-------------------------");
    	new Test (1, 2,3);
    }

}

输出结果:

Test的含有两个参数的构造器
-------------------------
Test的含有两个参数的构造器
Test的含有三个参数的构造器
this()方法只能用在构造函数中,同时为了避免操作对象时对象还未构建成功,需要this()和super()的调用在第一行实现【以此来创建对象】,防止异常。
<think>嗯,用户想了解Java构造器初始化过程。首先,我需要回忆一下Java中对象初始化的基本步骤。记得类加载对象实例化是两个不同的阶段,类初始化发生在首次使用类时,而对象初始化则是每次创建实例时进行的。 构造器的主要作用是初始化对象的状态。构造器内部可以调用其他构造器,比如通过this(),但必须在第一行。同时,实例变量初始化静态初始化也有不同的执行顺序。静态的东西是在类加载的时候处理的,而实例变量初始化则是在对象创建时,按照代码中的顺序执行。 然后要考虑父类子类的初始化顺序。Java中,子类的构造器会隐式或显式地调用父类的构造器,通常是super(),确保父类先初始化。如果父类没有无参构造器,子类必须显式调用父类的有参构造器,否则会编译错误。 接下来是实例变量的默认值显式初始化。比如,int变量默认是0,对象引用默认是null,但如果在声明时赋了值,比如int a = 5,这个赋值操作会在构造器执行之前完成。实例初始化中的代码也会在构造器之前执行,合并到构造器中。 可能用户还会想知道构造器重载的情况,如通过不同的参数列表来创建对象,以及构造器中异常处理的问题。不过用户的问题主要集中在初始化过程,所以应该重点放在执行顺序各个部分的相互作用上。 还需要注意静态变量静态初始化的执行时机,它们是在类初始化阶段执行的,早于任实例的创建。而实例初始化变量初始化则是在每次创建对象时执行,顺序是按照代码中的出现顺序,然后才是构造器中的代码。 可能用户遇到的问题包括构造器循环调用、初始化顺序导致的NullPointerException,或者继承中的构造器调用错误。比如,子类构造器没有正确调用父类构造器,导致父类未初始化,从而引发问题。 最后,结合参考内容里的《深入理解Java虚拟机》Java 101的链接,可以确认类初始化涉及准备阶段初始化阶段,而对象实例化则涉及分配内存、设置默认值、执行实例初始化代码构造器。这样整理下来,应该能覆盖用户的需求,并解答可能的疑问。</think># Java构造器初始化过程详解 Java构造器初始化对象创建的核心流程,涉及类加载、内存分配、父类构造链调用等多个关键步骤。以下是分阶段说明: ### 一、类加载阶段(首次使用类时) 1. **静态成员初始化** - 执行顺序:按代码书写顺序处理静态变量静态代码 $$static\ int\ count = 0;$$ $$static\ \{ System.out.println("Static\ block"); \}$$ 2. **类验证准备** - 为静态变量分配内存空间,设置默认值(如int为0,对象引用为null) ### 二、对象实例化阶段(每次new操作) 1. **内存分配** - 根据类信息在堆中分配对象内存空间 - 设置对象头信息(包括类型指针、GC信息等) 2. **成员初始化** - 执行顺序: (1) 实例变量默认初始化(基本类型默认值,引用类型null) (2) 实例变量显式初始化(声明时的赋值) (3) 实例初始化代码 ```java class MyClass { int a = initValue(); // 显式初始化 { System.out.println("实例初始化"); } // 初始化 } ``` 3. **构造器执行** - 父类构造器优先调用(通过`super()`显式或隐式调用) - 当前类构造器剩余代码执行 $$子类构造器 \rightarrow 父类构造器 \rightarrow Object构造器$$ ### 三、构造器链示例 ```java class Parent { Parent() { System.out.println("Parent构造器"); } } class Child extends Parent { Child() { // 隐式调用super() System.out.println("Child构造器"); } } ``` 执行顺序:Parent静态 → Child静态 → Parent实例 → Parent构造器 → Child实例 → Child构造器[^1] ### 四、特殊场景处理 1. **构造器重载** - 通过`this()`调用同类其他构造器(必须作为第一条语句) ```java public Rectangle() { this(0, 0); // 调用双参数构造器 } ``` 2. **final字段初始化** - final实例变量必须在构造器完成前初始化 - 三种初始化方式:声明时、初始化构造器 3. **异常处理** - 构造器可以抛出异常,但需要调用者处理 - 构造失败的对象不会被垃圾回收器特殊处理 ### 五、初始化顺序总结 $$类加载 \Rightarrow 静态初始化 \Rightarrow 内存分配 \Rightarrow 默认初始化 \Rightarrow 显式初始化 \Rightarrow 构造器执行$$
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值