文章目录
如果在网上搜索“类初始化顺序”,一般的答案为:
父类静态->子类静态->父类代码块->父类构造->子类代码块->子类构造
但这种简单的回答适合死记硬背但不适合理解
下面给出最详细的解释:
一、无继承关系(直接继承Object)的类加载过程:
1.静态成员是由类加载器加载的
静态成员是由类加载器加载的,而且类加载器只加载静态成员,不会加载实例成员与构造器
class Supper {
{
System.out.println("supper加载成员代码块");
}
String noStaticVar1 = "supper加载成员变量";
static String staticVar = "supper加载静态变量";
static{
System.out.println(staticVar);
System.out.println("supper加载静态代码块");
}
public Supper() {
System.out.println("supper加载空参构造器");
}
}
class TestClassLoad {
public static void main(String[] args) throws ClassNotFoundException {
ClassLoader supperClassLoader = Supper.class.getClassLoader(); // 通过反射获取类加载器
// supperClassLoader.loadClass("Supper"); 这种方法不会对类成员进行初始化
Class.forName("Supper",true,supperClassLoader); // 加载Supper类并对静态成员,true表示进行初始化
}
}
// 输出:
supper加载静态变量
supper加载静态代码块
supper加载静态代码块
2.代码块与变量按代码中出现的顺序进行初始化
这种说法可以看为main函数中的一个变量定义与一行语句,new 对象时先由加载器加载【静态变量/静态代码块】,然后实例化过程中加载【成员变量/成员代码块】
class Supper {
{
// System.out.println(noStaticVar); 编译报错:变量未定义
System.out.println("supper加载成员代码块");
}
String noStaticVar = "supper加载成员变量";
{
System.out.println(noStaticVar);
System.out.println("supper加载成员代码块1");
}
String noStaticVar1 = "supper加载成员变量1";
static String staticVar = "supper加载静态变量";
static{
System.out.println(staticVar);
// System.out.println(staticVar1); 编译报错:变量未定义
System.out.println("supper加载静态代码块");
}
static String staticVar1 = "supper加载静态变量";
static{
System.out.println("supper加载静态代码块1");
}
}
class TestClassLoad {
public static void main(String[] args) throws ClassNotFoundException {
new Supper();
}
}
// 输出
supper加载静态变量
supper加载静态代码块
supper加载静态代码块1
supper加载成员代码块
supper加载成员变量
supper加载成员代码块1
3.new对象时只会进入一个相应的构造器
而且静态成员只加载一次。
下面,变量与代码块我们只保留一个,然后我们new两个Supper,一个带餐一个不带参:
class Supper {
public String noStaticVar = "成员变量";
{
System.out.println("supper加载成员代码块");
}
static{
System.out.println("supper加载静态代码块");
}
public Supper() {
System.out.println("supper加载空参构造器");
}
public Supper(String noStaticVar) {
this.noStaticVar = noStaticVar;
System.out.println("supper加载有参构造器");
}
}
class TestClassLoad {
public static void main(String[] args) throws ClassNotFoundException {
new Supper();
System.out.println("*********");
new Supper("supper");
}
}
// 输出:
supper加载静态代码块
supper加载成员代码块
supper加载空参构造器
*********
supper加载成员代码块
supper加载有参构造器
二、继承关系下的类加载过程:
1.先加载父类静态成员,后加载本类静态成员
先只保留静态成员,只看类加载器做的事情:
class Supper {
static{
System.out.println("supper加载静态代码块");
}
}
class Sub extends Supper {
static {
System.out.println("Sub加载静态代码块");
}
}
class TestClassLoad {
public static void main(String[] args) throws ClassNotFoundException {
ClassLoader supperClassLoader = Sub.class.getClassLoader();
Class.forName("Sub",true,supperClassLoader);
}
}
// 输出:
supper加载静态代码块
Sub加载静态代码块
2.终极测试
import org.junit.Test;
public class JavaStudy {
static {
System.out.println("sdfbjahhdbojfasd");
}
@Test
public void lambda1() {
String[] strings = new String[0];
System.out.println(strings);
for (String string : strings) {
System.out.println(string);
}
}
}
class Supper {
public String noStaticVar = "supper成员变量";
{
System.out.println("supper加载非静态成员");
}
static{
System.out.println("supper加载成员");
}
public Supper() {
System.out.println("supper加载空参构造器");
}
public Supper(String noStaticVar) {
this.noStaticVar = noStaticVar;
System.out.println("supper加载有参构造器");
}
}
class Sub extends Supper {
public String noStaticVar = "Sub成员变量";
{
System.out.println("Sub加载非静态成员");
}
static {
System.out.println("Sub加载静态成员");
}
public Sub() {
System.out.println("Sub加载构造器");
}
public Sub(String noStaticVar) {
this.noStaticVar = noStaticVar;
System.out.println("Sub加载有参构造器");
}
}
class TestClassLoad {
public static void main(String[] args) throws ClassNotFoundException {
new Sub();
System.out.println("***********");
new Sub("sub");
}
}
// 输出
supper加载静态成员
Sub加载静态成员
supper加载非静态成员
supper加载空参构造器
Sub加载非静态成员
Sub加载空參构造器
***********
supper加载非静态成员
supper加载空参构造器
Sub加载非静态成员
Sub加载有参构造器
从输出结果看出:
1.new Sub()时,加载顺序为
- 父类静态成员 ->子类静态成员->父类非静态成员->父类 【空参】/ 【显式调用的】构造器 ->子类非静态成员 ->子类【空】参构造器
2.new Sub(“带参数”)时,加载顺序为,默认调用空参构造器
- 父类静态成员 ->子类静态成员->父类非静态成员->父类 【空参】/ 【显式调用的】构造器 ->子类非静态成员 ->子类【有】参构造器
也就是说,除了子类构造器不一样之外,所有内容完全一样
而且如果debug可以发现执行顺序如下(注意第5部):
注意:父类成员变量没有被加载
3.最后的结论
所以可以得出一个结论,各类成员的加载过程为:
1.类加载器
- 父类静态变量/静态代码块(定义的先后顺序)
- 子类静态变量/静态代码块(定义的先后顺序)
.2.子类构造器
- 父类成员代码块
- 父类构造器(默认调用空参构造器,可显示调用)
- 子类成员变量/成员代码块(定义的先后顺序)
- 子类构造器下的语句