Bootstrap

JAVA中的初始化块

前言

李刚老师《JAVA疯狂讲义》第5版,第5章学习笔记。

1.什么是初始化块

初始化块是类的第4种成员(成员变量、方法、构造器),与构造器作用有些类似,用于对JAVA对象进行初始化操作。例如:

public class Animal {
	//定义初始化块
	{
		int a = 6;
		if(a>4) {
			System.out.println("Animal初始化块,局部变量a的值大于4");
		}
		System.out.println("Animla的初始化块");
	}
	//再定义一个初始化块
	{
		System.out.println("Animal的第二个初始化块");
	}
	//定义构造器
	public Animal() {
		System.out.println("Animal的无参构造器");
	}
}
public class Demo01 { 
	public static void main(String[] args) {
		Animal a = new Animal();
	}
}

上述代码输出结果为:

Animal初始化块,局部变量a的值大于4
Animla的初始化块
Animal的第二个初始化块
Animal的无参构造器

可见,JAVA创建对象时,首先执行初始化块。如果一个类中定义了多个初始化块,则按照顺序执行(这其实没有什么意义,因为初始化块都可以合并为一个)。并且,初始化块在构造器之前执行。

2.初始化块与构造器

从原理上讲,初始化块其实是一个假象,经过编译后,初始化块还是放到构造器中执行的。

实际使用时,二者的区别是,初始化块是一段固定的代码,它无法接受任何参数,对一个类的所有对象进行的初始化处理完全相同;而构造器则是可以带参数的,对不同的对象,参数不同,可以进行不同的初始化处理。

3.静态初始化块

与静态方法、静态成员变量类似,在初始化块之前加上static就变成了静态初始化块。
普通初始化块是对象的,负责初始化具体对象;静态初始化块是类的,负责初始化类。
与静态方法相同,静态初始化块不能访问非静态方法,非静态成员变量(因为静态初始化块是在类加载的时候就执行了的,这个时候可能还没有类的对象,就没办法执行非静态方法、非静态成员变量)

4.初始化块执行顺序

JAVA中,创建一个对象时,若是第一次创建该类的对象,则会首先加载并初始化该类,因此会首先执行其所有父类的静态初始化块(按照顺序执行,首先是最顶层父类,然后是下一层,层层向下执行),最后是其本身的静态初始化块。
一旦该对象创建成功,该类在虚拟机中将一直存在,第二次创建该类的对象时,就无需加载与初始化类。会依次执行所有父类的初始化块、构造器,最后是自己类的初始化块、构造器。
其中,类的静态初始化块、静态成员变量的声明都可以看作是类的初始化代码,执行顺序依代码顺序而定。

代码举例如下:

class Person {
	static {
		System.out.println("我是Person类的静态初始化块");
	}
	 {
		System.out.println("我是Person类的普通初始化块");
	}
	 public Person() {
		 System.out.println("我是Person类的无参构造器");
	 }
}
class Police extends Person{
	static {
		System.out.println("我是Police类的静态初始化块");
	}
	{
		System.out.println("我是Police类的普通初始化块");
	}
	 public Police() {
		 System.out.println("我是Police类的无参构造器");
	 }
	 public Police(int a) {
		 //调用自己的无参构造器
		 this();
		 System.out.println("我是Police类的有参构造器,参数为:"+a);
	 }
}
class SWAT extends Police{
	static {
		System.out.println("我是SWAT的静态初始化块");
	}
	{
		System.out.println("我是SWAT的普通初始化块");
	}
	public SWAT() {
		//调用直接父类的有参构造器
		super(10);
		System.out.println("我是SWAT的无参构造器");
	}
}
public class Demo01 { 
	public static void main(String[] args) {
		new SWAT();
	}
}

上述代码创建了三个类,Person,Police,SWAT。其中,Person是Police的直接父类,Police是SWAT的直接父类。
输出结果为:

我是Person类的静态初始化块
我是Police类的静态初始化块
我是SWAT的静态初始化块
我是Person类的普通初始化块
我是Person类的无参构造器
我是Police类的普通初始化块
我是Police类的无参构造器
我是Police类的有参构造器,参数为:10
我是SWAT的普通初始化块
我是SWAT的无参构造器

因为这是第一次使用SWAT这个类,因此需要初始化类,因此会首先依次执行Person类的静态初始化块、Police类的静态初始化块和SWAT类的静态初始化块。
随后,创建对象时,则会依次执行各个父类的初始化块,构造器。首先是Person类的普通初始化块、无参构造器,然后是Police类的普通初始化块、无参构造器、有参构造器(因为SWAT的无参构造器中调用了Police的有参构造器(super(10)),而Police的有参构造器中调用了Police的无参构造器(this())),最后是SWAT自己的普通初始化块、无参构造器。

修改一下代码

public class Demo01 { 
	public static void main(String[] args) {
		new SWAT();
		new SWAT();
	}
}

输出将增加:

我是Person类的普通初始化块
我是Person类的无参构造器
我是Police类的普通初始化块
我是Police类的无参构造器
我是Police类的有参构造器,参数为:10
我是SWAT的普通初始化块
我是SWAT的无参构造器

因为该类已经被加载过了,因此,各个类的静态初始化块均不会被重新运行。

;