Java类和对象初始化顺序

Java类和对象在初始化时主要按照以下顺序进行:

父类静态变量和静态块(这两者的初始化顺序按照定义顺序来) --> 子类静态变量和静态块 --> 父类普通成员和块 --> 父类构造函数 --> 子类普通成员和块 --> 子类构造函数

个人理解:父类静态变量静态块 和 子类静态变量静态块 的初始化应该叫做类的初始化,而其他四步应该叫做类相关对象的初始化。因为后四步与对象密切相关,没有对象的访问后四步就不会出现,这个在后面的例子中可以看出来。有时候可能只会激发类的初始化,而没有对象初始化步骤。

基本代码:

本文中的例子都基于以下代码,各个例子稍有改动。

public class Sample {
	Sample(String s) {
		System.out.println(s);
	}

	Sample() {
		System.out.println("Sample默认构造函数被调用");
	}
}
public class Parent {
	static int x = 1;
	
	static Sample sam_pa = new Sample("父类静态成员sam_pa初始化");
	Sample sam1_pa=new Sample("父类sam1_pa成员初始化");
	static {
		System.out.println("父类static块2 执行");
	}
	
	Parent() {
		System.out.println("父类默认构造函数调用");
	}
	Parent(String s) {
		System.out.println("父类字符串构造函数调用");
	}
	
	static {
		System.out.println("父类static块1 执行");
	}
	
	{
		System.out.println("父类块1执行");
	}
	
	Sample sam2_pa=new Sample("父类sam2_pa成员初始化");
	
	{
		System.out.println("父类块2执行");
	}
}
public class Child extends Parent{
	static final int y = 2;
	
	static {
		System.out.println("子类static块1 执行");
	}
	Sample sam1_ch=new Sample("子类sam1_ch成员初始化");
	static Sample sam_ch = new Sample("子类静态成员sam_ch初始化");
	
	Child() {
		System.out.println("子类默认构造函数调用");
	}
	
	static {
		System.out.println("子类static块2 执行");
	}
	Sample sam2_ch=new Sample("子类sam2_ch成员初始化");
}
public class StartClass {
	public static void main(String[] args){
		new Child();
	}
}

尝试1:

以StartClass的main函数为入口启动程序。并查看打印结果:

父类静态成员sam_pa初始化
父类static块2 执行
父类static块1 执行


子类static块1 执行
子类静态成员sam_ch初始化
子类static块2 执行


父类sam1_pa成员初始化
父类块1执行
父类sam2_pa成员初始化
父类块2执行
父类默认构造函数调用


子类sam1_ch成员初始化
子类sam2_ch成员初始化
子类默认构造函数调用

结果完全符合上面所讲的初始化顺序。

尝试2:

将Child类改为:

public class Child extends Parent{
	static final int y = 2;
	
	static {
		System.out.println("子类static块1 执行");
	}
	Sample sam1_ch=new Sample("子类sam1_ch成员初始化");
	static Sample sam_ch = new Sample("子类静态成员sam_ch初始化");
	
	Child() {
		//super("string");
		System.out.println("子类默认构造函数调用");
	}
	
	static {
		System.out.println("子类static块2 执行");
	}
	Sample sam2_ch=new Sample("子类sam2_ch成员初始化");
	
	public static void main(String[] args){
		System.out.println("子类main方法---程序入口");
		new Child();
	}
}

增加了main函数。并从Child的main函数启动程序。并查看打印结果:

父类静态成员sam_pa初始化
父类static块2 执行
父类static块1 执行


子类static块1 执行
子类静态成员sam_ch初始化
子类static块2 执行


子类main方法---程序入口


父类sam1_pa成员初始化
父类块1执行
父类sam2_pa成员初始化
父类块2执行
父类默认构造函数调用


子类sam1_ch成员初始化
子类sam2_ch成员初始化
子类默认构造函数调用

奇怪的事情发生了,main函数中的第一句怎么出现在了中间,相当尴尬的位置。。。其实这是很正确的。因为当调用main函数时,JVM虚拟机发现这个main函数同时也是Child类的static成员,因而激发了Child类的初始化过程,要初始化Child类,那么先初始化Parent类,因此出现了打印结果的前6行,然后进入main函数,打印第7行。new Child()激发对象的初始化过程,就有了后面的所有行。

尝试3:

将StarClass的main函数改为:

public class StartClass {
	public static void main(String[] args){
		System.out.println(Child.x);
		System.out.println(Child.y);
	}
}

打印结果为:

父类静态成员sam_pa初始化
父类static块2 执行
父类static块1 执行
1
2

对Child.x的访问,实际上激发了父类的类初始化过程,因为x为父类的static变量。对Child.y的访问,不会激发任何初始化过程,因为Child.y是常量。

尝试4:

将Child的默认构造函数改为:

Child() {
		super("string");
		System.out.println("子类默认构造函数调用");
	}

打印结果为:

父类静态成员sam_pa初始化
父类static块2 执行
父类static块1 执行


子类static块1 执行
子类静态成员sam_ch初始化
子类static块2 执行


子类main方法---程序入口


父类sam1_pa成员初始化
父类块1执行
父类sam2_pa成员初始化
父类块2执行
父类字符串构造函数调用


子类sam1_ch成员初始化
子类sam2_ch成员初始化
子类默认构造函数调用

如果在构造函数中调用父类指定构造函数,那么父类默认构造函数将不会执行。


以上就是通过实践证明的java初始化顺序,但实际上初始化顺序的内容远不止这些,比如interface及其相关成员在整个初始化过程中的位置在哪里,我们这里还没有涉及到。那就等我们在实际中遇到问题时再来实践证明吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值