Java 构造器初始化

可以用构造器来进行初始化,但是成员变量的自动初始化是在构造器被调用之前发生的,即成员变量的自动初始化不能被阻止

如以下例子:

public class Counter {
	int count;
	Counter(){
		count = 7;
	}
}
count首先是为0,当调用其构造方法后,才使得count为7.


1.初始化顺序

在类的内部,变量定义的先后顺序决定了其初始化顺序。但变量的初始化操作会在调用任何方法之前完成,包括构造方法,哪怕变量的定义散布于方法定义之间。

如以下例子:

class Window{
	Window(int i){
		System.out.println("Window("+i+")");
	}
}

class House{
	Window window1 = new Window(1);
	House(){
		System.out.println("----I'm contructor method----");
	}
	Window window2 = new Window(2);
	void f(){
		System.out.println("-----I'm f()-----");
	}
	Window window3 = new Window(3);
}

public class Demo {
	
	public static void main(String[] args) {
		House house = new House();
		house.f();
	}
}
当运行这个代码时,其结果为:

Window(1)
Window(2)
Window(3)
----I'm contructor method----
-----I'm f()-----
由此可以看出,Window的几个对象均在House的构造器和f()方法之前便已完成初始化。

2、静态数据的初始化
无论创建多少对象,静态数据都只占据一份存储域。static关键字不能应用于局部变量,因此它只能作用于域。

可以通过一个例子来看静态数据的初始化是如何进行的:

package com.hd.demo;

class Bowl{
	
	Bowl(int i){
		System.out.println("Bowl("+i+")");
	}
	
	void f(int i){
		System.out.println("f("+i+")");
	}
}

class Table{
	static Bowl bolw1 = new Bowl(1);
	Table(){
		System.out.println("----I'm Table contructor method----");
	}
	
	void f(){
		System.out.println("-----I'm f() method in Table-----");
	}
	static Bowl bolw2 = new Bowl(2);
}

class Cupboard{
	Bowl bowl3 = new Bowl(3);
	static Bowl bowl4 = new Bowl(4);
	Cupboard(){
		System.out.println("----I'm Cupboard contructor method----");
		bowl4.f(1);
	}
	void f(){
		System.out.println("-----I'm f() method in Cupboard-----");
	}
	static Bowl bowl5 = new Bowl(5);
}

public class StaticDemo {
	public static void main(String[] args) {
		System.out.println("creating new Cupboard() in main");
		new Cupboard();
		System.out.println("creating new Cupboard() in main");
		new Cupboard();
		table.f();
		cupboard.f();
	}
	static Table table = new Table();
	static Cupboard cupboard = new Cupboard();
}
其运行结果如下:
Bowl(1)
Bowl(2)
----I'm Table contructor method----
Bowl(4)
Bowl(5)
Bowl(3)
----I'm Cupboard contructor method----
f(1)
creating new Cupboard() in main
Bowl(3)
----I'm Cupboard contructor method----
f(1)
creating new Cupboard() in main
Bowl(3)
----I'm Cupboard contructor method----
f(1)
-----I'm f() method in Table-----
-----I'm f() method in Cupboard-----
以上代码有几个需要注意的地方,一个是StaticDemo中在main方法的下面定义了静态数据成员table和cupboard。它们会在main方法执行之前先进行初始化。另一个是Cupboard类中,先定义了一个非静态的bowl3成员。最后就是在main方法中又new了Cupboard对象。

通过以上运行结果可以得出以下结果:

1)、静态初始化只有在必要的时候才会进行,如果不创建Table对象,那么bowl1和bowl2就不会创建。

2)、静态数据只有在第一次被访问时才会进行初始化,此后便不再被初始化。比如,在main方法中new Cupboard,初始化了非静态成员bowl3,而静态成员bowl4和bowl5不再被初始化。

3)、初始化的顺序是先静态对象,后是非静态对象。

3、显示的静态初始化

先看一段代码:

class Cup{
	Cup(int mark){
		System.out.println("Cup("+mark+")");
	}
	
	void f(int mark){
		System.out.println("f("+mark+")");
	}
}

class Cups{
	static Cup cup1 ;
	static Cup cup2 ;
	
	static {
		cup1 = new Cup(1);
		cup2 = new Cup(2);
	}
}



public class ExplicitStatic {
	public static void main(String[] args) {
		Cups.cup1.f(1);	  //(1)
	}
	//static Cups cups1 = new Cups();  //(2)
	//static Cups cups2 = new Cups();  //(3)
}
其运行结果如下:

Cup(1)
Cup(2)
f(1)
假设注释掉(1)的代码,解除(2)或(3)的代码,那么结果就会为:

Cup(1)
Cup(2)
如果同时解除(2)和(3)的代码,其运行结果都是一样的。因为上面已经提到,静态数据只有在第一次被访问时才会初始化,此后便不再初始化。

4、非静态实例初始化

先看一段代码:

class Mug{
	Mug(int i){
		System.out.println("Mug("+i+")");
	}
	
}

public class Mugs {
	Mug mug1;
	Mug mug2;
	{
		mug1 = new Mug(1);
		mug2 = new Mug(2);
	}
	
	Mugs(){
		System.out.println("无参构造器");
	}
	Mugs(int i){
		System.out.println("有参构造器");
	}
	
	public static void main(String[] args) {
		new Mugs();
		new Mugs(1);
	}
}
运行结果:

Mug(1)
Mug(2)
无参构造器
Mug(1)
Mug(2)
有参构造器
可以看到,因为少了一个static关键字,因此,每new一个对象,mug1和mug2都会初始化一次,并且也是在构造器执行之前完成。


写这篇博客是因为在《Java思想编程》中看到专门讲构造器初始化的知识,感觉自己有很多不懂的地方,看了两三遍才明白。现在再总结一下,算是加深印象吧。







<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 构造器执行$$
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值