Java程序员从笨鸟到菜鸟(五)JVM类继承关系的初始化顺序

非继承关系的初始化顺序:

代码块:

package demo;

public class Student {
	static StudentClass sam = new StudentClass("静态成员sam初始化");
	StudentClass sam1 = new StudentClass("普通成员sam1初始化");
	static {
		System.out.println("Student 静态代码块");
	}
	
	{
		System.out.println("Student 构造代码块");
	}
	
	StudentClass sam2 = new StudentClass("普通成员sam2初始化");
	
	public Student() {
		System.out.println("Student 构造方法");
	}
	
	public static void main(String[] args) {
		System.out.println("我是main方法");
		
		System.out.println("第一个主类对象:");
		Student s1 = new Student();
		
		System.out.println("第二个主类对象:");
		Student s2 = new Student();
		
		System.out.println("两个主类成员的静态变量:");
		System.out.println("第一个主类 静态成员s1.sam:" + s1.sam);
		System.out.println("第二个主类 静态成员s2.sam:" + s2.sam);
	}
	
}

class StudentClass {
	String s;
	public StudentClass() {
		System.out.println("StudentClass默认构造函数被调用");
	}
	public StudentClass(String s) {
		super();
		this.s = s;
		System.out.print(s);
	}
	@Override
	public String toString() {
		return "StudentClass [s=" + s + "]";
	}
	
}

执行结果:

 

静态成员sam初始化
Student 静态代码块
我是main方法
第一个主类对象:
普通成员sam1初始化
Student 构造代码块
普通成员sam2初始化
Student 构造方法
第二个主类对象:
普通成员sam1初始化
Student 构造代码块
普通成员sam2初始化
Student 构造方法
两个主类成员的静态变量:
第一个主类 静态成员s1.sam:StudentClass [s=静态成员sam初始化]
第二个主类 静态成员s2.sam:StudentClass [s=静态成员sam初始化]

由输出结果分析可知,类加载初始化顺序为:

1. 静态成员、静态代码块。

2. 普通成员、构造代码块、构造方法

有多个静态成员或静态代码块、多个普通成员变量的初始化顺序和声明顺序一致,类的静态成员和静态代码块在类加载中是最先进行初始化的,并且只进行一次。

初始化顺序图示:

继承关系的初始化顺序:

代码块:

package demo;

public class Demo {
	public static void main(String[] args) {
               System.out.println("我是main方法");
               Father fa = new Son();
	}
}

class Father {
	{
		System.out.println("父类   构造代码块");
	}
	static Sample staticSam1 = new Sample("父类   静态成员staticSam1初始化");
	static {
		System.out.println("父类   静态代码块1");
	}
	Sample sam1 = new Sample("父类   普通成员sam1初始化");
	static Sample staticSam2 = new Sample("父类   静态成员staticSam2初始化");
	static {
		System.out.println("父类   静态代码块2");
	}
	
	Father() {
		System.out.println("父类   默认构造函数被调用");
	}
	Sample sam2 = new Sample("父类   普通成员sam2初始化");
}

class Son extends Father {
	static {
		System.out.println("子类   静态代码块1");
	}
	{
		System.out.println("子类   构造代码块1");
	}
	static Sample staticSamS1 = new Sample("子类   静态成员staticSamS1初始化");
	static {
		System.out.println("子类   静态代码块2");
	}
	
	Son() {
		System.out.println("子类   默认构造函数被调用");
	}
	Sample sam1 = new Sample("子类   普通成员sam1初始化");
	static Sample staticSamS2 = new Sample("子类   静态成员staticSamS2初始化");
	{
		System.out.println("子类   构造代码块2");
	}
}

class Sample {
	public Sample() {
		super();
		System.out.println("Sample默认构造函数被调用");
	}

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

执行结果:

我是main方法
父类   静态成员staticSam1初始化
父类   静态代码块1
父类   静态成员staticSam2初始化
父类   静态代码块2
子类   静态代码块1
子类   静态成员staticSamS1初始化
子类   静态代码块2
子类   静态成员staticSamS2初始化
父类   构造代码块
父类   普通成员sam1初始化
父类   普通成员sam2初始化
父类   默认构造函数被调用
子类   构造代码块1
子类   普通成员sam1初始化
子类   构造代码块2
子类   默认构造函数被调用

由输出结果可分析出,执行顺序:

1. 父类的静态成员和静态代码块。

2. 子类的静态成员和静态代码块。

3. 父类的普通成员和构造代码块。

4. 父类的构造函数。

5. 子类的普通成员和构造代码块。

6. 子类的构造函数。

初始化顺序图:

类加载时机:

只有在五种主动引用的情况下才会触发类初始化:

 

  1. 遇到new(使用new 关键字实例化一个对象)、getstatic(读取一个类的静态字段)、putstatic或者invokestatic(设置一个类的静态字段)这4条指令的时候,如果累没有进行过初始化。则需要先触发其初始化。

  2. 使用java.lang.reflect包中的方法对类进行反射调用的时候,如果类没有初始化,则需要先触发其初始化。

  3. 当初始化一个类的时候,如果其父类没有初始化,则需要先触发其父类的初始化

  4. 程序启动需要触发main方法的时候,虚拟机会先触发这个类的初始化

  5. 当使用jdk1.7的动态语言支持的时候,如果一个java.lang.invoke.MethodHandler实例最后的解析结果为REF_getStatic、REF_pusStatic、REF_invokeStatic的方法句柄(句柄中包含了对象的实例数据和类型数据,句柄是访问对象的一种方法。句柄存储在堆中),并且句柄对应的类没有被初始化,那么需要先触发这个类的初始化。

 

备注:静态内部类和非静态内部类一样,都是在被调用时才会被加载

原文博客传送门:http://blog.csdn.net/u011080472/article/details/51330114

版权声明:欢迎转载, 转载请保留原文链接。https://mp.csdn.net/postedit/79373524

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值