java类变量与实例变量的初始化顺序

我们一共考虑4个部分,类变量的初始化,static静态代码块,实例变量的初始化,非static代码块。
理解类变量的初始化和static静态代码块先需要了解java的类加载器的运行机制。
参考https://blog.csdn.net/qq_31156277/article/details/80188110这篇博文讲的非常详细,这里仅仅简要的说一说:
Java虚拟机中类加载的全过程,即 加载、验证、准备、解析和初始化
在这里插入图片描述
一、加载
加载是类装载的第一步,首先通过class文件的路径读取到二进制流,并解析二进制流将里面的元数据(类型、常量等)载入到方法区,在java堆中生成对应的java.lang.Class对象。
二、连接
连接分3部分:验证、准备、解析

2.1验证:判断class文件的合法性

2.2准备:
准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。这些变量会被置为默认的初始值,比如0,false等。
例如一个类变量的定义为:

public static int i=123;

这里的 i 会被初始化为0,而非123,置为123的操作会在后面执行。
若是同时被final修饰的类变量,则会在此阶段就会被设置成指定的值
例如下面的一个被final修饰的类变量,他会被直接设置为123

public static final int value=123

而实例变量此时并没有被分配内存,他们会在对象实例化的时候随对象一起被分配在java堆中。

2.3解析:解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程

三、初始化
类初始化阶段是类加载过程的最后一步,到了初始化阶段,才真正开始执行类中定义的Java程序代码。
在初始化阶段,开始执行类变量的赋值语句和static{}静态代码块
在准备阶段,变量已经赋过一次系统要求的初始值,而在初始化阶段,则根据程序员通过程序制定的主观计划去初始化类变量和其他资源。例如前文定义的类变量 i ,这里会被置为123。

public static int i=123;

这里有个细节
编译器会自动收集类中的所有类变量的赋值动作静态语句块(static{}代码块),编译器收集的顺序是由语句在源文件中出现的顺序所决定的,静态语句块中只能访问到定义在静态语句块之前的变量,定义在它之后的变量,在前面的静态语句块可以赋值,但是不能访问
下面例子中,j先从0被置为8,再被置为9

package test;
public class test {
	static{
		j=8;//正常通过
		//System.out.println(j);//编译显示错误,不能访问它
	}
	static int j=9;
	static{
		System.out.println(j);//打印出的j = 9
	}
	public static void main(String[] args) {
		new test();
	}
}

PS:<clinit>()方法与类的构造函数(或者说实例构造器<init>()方法)不同,它不需要显式地调用父类构造器,虚拟机会保证在子类的<clinit>()方法执行之前,父类的<clinit>()方法已经执行完毕。因此在虚拟机中第一个被执行的<clinit>()方法的类肯定是java.lang.Object。由于父类的<clinit>()方法先执行,也就意味着父类中定义的静态语句块要优先于子类的变量赋值操作-----说人话,即父类的静态成员(静态语句块)初始化早于子类的静态成员(静态语句块)的初始化

四、使用
在初始化完成后,进入代码的执行阶段,程序会从main()函数进入,一直执行到new语句创建类的对象(每一个新的实例),在创建时会对类的实例变量分配内存并置为默认的初始化值 0 或 false 等。
一旦虚拟机完成了为新的对象分配内存和为实例变量初始化为默赋予正确认的初始值后,接下来就会为执行实例变量的初始化赋值语句与非static{}代码块
下面的例子中,i 会先被置为0再被置为8

public int i=8;

与初始化过程类似的,所有实例变量的赋值动作和非static{}代码块,执行的的顺序是由语句在源文件中出现的顺序所决定的,非static{}代码块中只能访问到定义在非static{}代码块之前的变量,定义在它之后的变量,在前面的非static{}代码块可以赋值,但是不能访问
当我们定义了一个构造方法时,他会在上述操作执行后再执行构造方法内的操作,与代码顺序无关
eg:

public class test {
	test(){//这里是构造方法
		i=3;
		System.out.println(i);//打印出i=3
	}
	{
		i=4;//可以赋值
		//System.out.println("i="+i);//编译显示错误,不能访问
	}
	int i;
	{
		System.out.println(i);//打印出i=9
	}
	public static void main(String[] args) {
		new test();
	}

}

上述例子中,i 从最初的默认初始值0,被置为4,然后被置为9,最后被构造方法置为3

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值