Java 初始化块

【参考】《疯狂Java讲义》

目录

普通初始化块

初始化块对Java对象进行初始化操作

 一个类里面可以有多个初始化块

初始化块里的代码可包含任何可执行性语句

初始化块是Java类的一种成员

实例初始化块、声明实例变量指定的默认值的执行顺序

 编译后的初始化块

静态初始化块(类初始化块)

定义

 初始化块执行顺序问题

继承问题

 类初始化块和声明类变量时所指定的初始值的顺序

改正


普通初始化块

初始化块对Java对象进行初始化操作

如果没有new一个类的对象,那么这个类的初始化块不会执行:

public class Test {
	public static void main(String[] args) {
		System.out.println(">>>开始执行主函数");
	}
}

class A{
	{
		System.out.println(">>>执行A类的初始化块");
	}
}

输出结果:

>>>开始执行主函数

 可见,A类的初始化块没有执行,因为整个程序没有new一个A类的对象。


初始化块会在构造器之前执行,因为先加载类,才能构造对象:

public class Test {
	public static void main(String[] args) {
		System.out.println(">>>这是主函数里的一个语句");
		A a1 = new A();
	}
}

class A{
	{
		System.out.println(">>>执行A类的初始化块");
	}
	
	public A() {
		System.out.println(">>>执行A类的无参构造函数");
	}
}

输出结果:

>>>这是主函数里的一个语句
>>>执行A类的初始化块
>>>执行A类的无参构造函数


要new一个这个类的对象,才能执行这个类的初始化块,例如,改变上面程序主函数中输出语句和new语句的顺序:

public class Test {
	public static void main(String[] args) {
		A a1 = new A();
		System.out.println(">>>这是主函数里的一个语句");
	}
}

class A{
	{
		System.out.println(">>>执行A类的初始化块");
	}
	
	public A() {
		System.out.println(">>>执行A类的无参构造函数");
	}
}

输出结果:

>>>执行A类的初始化块
>>>执行A类的无参构造函数
>>>这是主函数里的一个语句


初始化块对Java对象进行初始化操作,所以,如果new了2个对象,那么会执行2次初始化块:

public class Test {
	public static void main(String[] args) {
		A a1 = new A();
		A a2 = new A();
	}
}

class A{
	{
		System.out.println(">>>执行A类的初始化块");
	}
	
	public A() {
		System.out.println(">>>执行A类的无参构造函数");
	}
}

 输出结果:

>>>执行A类的初始化块
>>>执行A类的无参构造函数
>>>执行A类的初始化块
>>>执行A类的无参构造函数


 一个类里面可以有多个初始化块

 一个类里面可以有多个初始化块,前面定义的初始化块先执行,后面定义的初始化块后执行:

public class Test {
	public static void main(String[] args) {
		A a1 = new A();
	}
}

class A{
	{
		System.out.println(">>>执行A类的第1个初始化块");
	}
	
	{
		System.out.println(">>>执行A类的第2个初始化块");
	}
	
	public A() {
		System.out.println(">>>执行A类的无参构造函数");
	}
}

输出结果:

>>>执行A类的第1个初始化块
>>>执行A类的第2个初始化块
>>>执行A类的无参构造函数


同样,如果new了2个对象:

public class Test {
	public static void main(String[] args) {
		A a1 = new A();
		A a2 = new A();
	}
}

class A{
	{
		System.out.println(">>>执行A类的第1个初始化块");
	}
	
	{
		System.out.println(">>>执行A类的第2个初始化块");
	}
	
	public A() {
		System.out.println(">>>执行A类的无参构造函数");
	}
}

 输出结果:

>>>执行A类的第1个初始化块
>>>执行A类的第2个初始化块
>>>执行A类的无参构造函数
>>>执行A类的第1个初始化块
>>>执行A类的第2个初始化块
>>>执行A类的无参构造函数

虽然Java允许一个类里定义2个实例初始化块,但这没有任何意义。因为实例初始化块是在创建Java对象时隐式执行的,而且它们总是全部执行,因此完全可以把多个实例初始化块合并成一个实例初始化块,从而可以让程序更加简洁,可读性更强。 


初始化块里的代码可包含任何可执行性语句

 初始化块里的代码可以包含任何可执行性语句,包括定义局部变量、调用其他对象的方法,以及使用分支、循环语句等。

public class Test {
	public static void main(String[] args) {
		A a1 = new A();
		System.out.println("new好的对象的score值:" + a1.score);
	}
}

class A{
	static String name = "JAVASE";
	int id = 3;
	int number;
	int score;
	
	{
		this.number = 10;
		showName();
		System.out.println("id = " + this.id);
		System.out.println("初始化块里面的score值:" + this.score);
		showScore();
	}
	
	public static void showName() {
		System.out.println("name = " + name);
	}
	
	public void showScore() {
		System.out.println(this.score);
	}
	
	public A() {
		score = 20;
	}
}

输出结果:

name = JAVASE
id = 3
初始化块里面的score值:0
0
new好的对象的score值:20

初始化块里的score值和new好的对象的score值,是因为先执行初始化块,再执行构造器,构造器给score赋值20。


初始化块是Java类的一种成员

初始化块是Java类里可出现的第4种成员(前面依次有成员变量、方法和构造器)。

初始化块虽然也是Java类的一种成员,但它没有名字,也就没有标识,因此无法通过类、对象来调用初始化块。实例初始化块只在创建Java对象时隐式执行,而且在构造器执行之前自动执行。类初始化则在类初始化阶段自动执行。

实例初始化块、声明实例变量指定的默认值的执行顺序

实例初始化块、声明实例变量指定的默认值都可认为是对象的初始化代码,它们的执行顺序与源程序中的排列顺序相同。


public class Test {
	public static void main(String[] args) {
		System.out.println(new A().id);
	}
}

class A{
	int id = 3;
	{
		this.id = 4;
	}
}

 输出结果:

4


交换一下顺序:


public class Test {
	public static void main(String[] args) {
		System.out.println(new A().id);
	}
}

class A{
	{
		this.id = 4;
	}
	int id = 3;
}

 输出结果:

3

 可见,实例初始化块、声明实例变量指定的默认值是按代码中的排序顺序执行的。


 编译后的初始化块

实际上实例初始化块是一个假象,使用javac命令编译Java类后,该Java类中的实例初始化块会消失—实例初始化块中代码会被“还原”到每个构造器中,且位于构造器所有代码的前面。


静态初始化块(类初始化块)

定义

如果定义初始化块时使用了static修饰符,则这个初始化块就变成了类初始化块,也被称为静态初始化块(实例初始化块负责对对象执行初始化,类初始化块则负责对类进行初始化)。类初始化块是类相关的,系统将在类初始化阶段执行类初始化块,而不是在创建对象时才执行。因此类初始化块总是比实例初始化块先执行。

同样,没有new对象,也都不执行:


public class Test {
	public static void main(String[] args) {
		System.out.println(">>>开始执行主函数");
	}
}

class A{
	{
		System.out.println(">>>执行A类的实例初始化块");
	}
	static {
		System.out.println(">>>执行A类的类初始化块(静态初始化块)");
	}
}

输出结果:

>>>开始执行主函数


new了对象会执行:


public class Test {
	public static void main(String[] args) {
		A a1 = new A();
	}
}

class A{
	{
		System.out.println(">>>执行A类的实例初始化块");
	}
	static {
		System.out.println(">>>执行A类的类初始化块(静态初始化块)");
	}
}

 输出结果:

>>>执行A类的类初始化块(静态初始化块)
>>>执行A类的实例初始化块


 初始化块执行顺序问题


public class Test {
	
	{
		System.out.println(">>>执行Test类的实例初始化块");
	}
	static {
		System.out.println(">>>执行Test类的类初始化块(静态初始化块)");
	}
	
	public static void main(String[] args) {
		System.out.println(">>>开始执行主函数");
	}
}

输出结果:

>>>执行Test类的类初始化块(静态初始化块)
>>>开始执行主函数



public class Test {
	public static void main(String[] args) {
		System.out.println(">>>开始执行主函数");
	}
	
	{
		System.out.println(">>>执行Test类的实例初始化块");
	}
	static {
		System.out.println(">>>执行Test类的类初始化块(静态初始化块)");
	}
}

 输出结果:

>>>执行Test类的类初始化块(静态初始化块)
>>>开始执行主函数


 类初始化块只会执行一次:


public class Test {
	public static void main(String[] args) {
		System.out.println(">>>开始执行主函数");
		Test t1 = new Test();
		Test t2 = new Test();
	}
	
	{
		System.out.println(">>>执行Test类的实例初始化块");
	}
	static {
		System.out.println(">>>执行Test类的类初始化块(静态初始化块)");
	}
}

输出结果:

>>>执行Test类的类初始化块(静态初始化块)
>>>开始执行主函数
>>>执行Test类的实例初始化块
>>>执行Test类的实例初始化块


继承问题


public class Test {
	public static void main(String[] args) {
		System.out.println(">>>开始执行主函数");
		Bottom t1 = new Bottom();
		Bottom t2 = new Bottom();
	}
}

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的无参构造器");
	}
}

class Bottom extends Mid{
	static {
		System.out.println(">>>Bottom的类初始化块");
	}
	{
		System.out.println(">>>Bottom的普通初始化块");
	}
	public Bottom() {
		System.out.println(">>>Bottom的无参构造器");
	}
}

输出结果:

>>>开始执行主函数
>>>Root的类初始化块
>>>Mid的类初始化块
>>>Bottom的类初始化块
>>>Root的普通初始化块
>>>Root的无参构造器
>>>Mid的普通初始化块
>>>Mid的无参构造器
>>>Bottom的普通初始化块
>>>Bottom的无参构造器
>>>Root的普通初始化块
>>>Root的无参构造器
>>>Mid的普通初始化块
>>>Mid的无参构造器
>>>Bottom的普通初始化块
>>>Bottom的无参构造器



public class Test {
	public static void main(String[] args) {
		System.out.println(">>>开始执行主函数");
		Bottom t1 = new Bottom();
		Bottom t2 = new Bottom();
	}
}

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) {
		System.out.println(">>>Mid的带参数构造器 : +" + msg);
	}
}

class Bottom extends Mid{
	static {
		System.out.println(">>>Bottom的类初始化块");
	}
	{
		System.out.println(">>>Bottom的普通初始化块");
	}
	public Bottom() {
		super("JAVAEE");
		System.out.println(">>>Bottom的无参构造器");
	}
}

 输出结果:

>>>开始执行主函数
>>>Root的类初始化块
>>>Mid的类初始化块
>>>Bottom的类初始化块
>>>Root的普通初始化块
>>>Root的无参构造器
>>>Mid的普通初始化块
>>>Mid的带参数构造器 : +JAVAEE
>>>Bottom的普通初始化块
>>>Bottom的无参构造器
>>>Root的普通初始化块
>>>Root的无参构造器
>>>Mid的普通初始化块
>>>Mid的带参数构造器 : +JAVAEE
>>>Bottom的普通初始化块
>>>Bottom的无参构造器


 类初始化块和声明类变量时所指定的初始值的顺序

类初始化块和声明类变量时所指定的初始值都是该类的初始化代码,它们的执行顺序与源程序中的排列顺序相同:


public class Test {
	public static void main(String[] args) {
		System.out.println(A.id);
	}
}

class A{
	static int id = 11;
	static {
		id = 22;
	}
}

输出结果:

22


如果换一下顺序:


public class Test {
	public static void main(String[] args) {
		System.out.println(A.id);
	}
}

class A{
	static {
		id = 22;
	}
	static int id = 11;
}

 输出结果:

11


改正

前面的说法有点小问题,不是只有new对象了才会执行类初始化块,如果主函数就在这个类里面,那么不管new不new,都会执行他的类初始化块,因为要执行这个类的主函数,要先加载这个类。

但还是要new一个对象才能执行普通初始化块。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值