Java基础(Java类的初始化顺序)

Posted 半卷清詞

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java基础(Java类的初始化顺序)相关的知识,希望对你有一定的参考价值。

Java类的初始化顺序

知道Java 类初始化过程有利于我们对其运行过程的理解。
首先:每个类的编译代码都存在于它自己的独立的文件中,该文件只在需要使用程序代码时才会被加载。通常加载发生在创建类的第一个对象时或者该类的静态资源被访问时。
另外,定义为static 类型的代码只会被初始化一次。
另外,构造器方法实际上是static的,是隐式的static声明。

首先说结果:
加载顺序:该类的顶级父类的静态代码块 -> 顶级父类的静态成员变量 -> 父类的静态代码块 -> 父类的静态成员变量 -> ... -> 当前类的静态代码块 -> 当前类的静态成员变量 -> 顶级父类构造代码块初始化 -> ... -> 当前类构造代码块初始化 -> 顶级父类构造器初始化 -> ... -> 当前类构造器初始化 -> 运行业务代码...

一、静态资源被访问时的加载的情况

package cn.tobin.cls;

class TestClassInitSuper {

private int i;
protected int j;

TestClassInitSuper() {
    System.out.println("i : " + i + "; j : " + j);
    System.out.println("TestClassInitSuper 构造器已运行");
}

{
    i = 0;
    j = 1;
    System.out.println("TestClassInitSuper构造代码块已运行 ");
}

static {
    System.out.println("TestClassInitSuper静态代码块已运行 ");
}

private static String SUPER_STATIC_STR = printStr("TestClassInitSuper.SUPER_STATIC_STR 已初始化");

private static void parentStaticTest1() {
    System.out.println("TestClassInitSuper parentStaticTest1静态方法已运行");
}

static String printStr(String str) {
    System.out.println(str);
    return str;
}

}

public class TestClassInit extends TestClassInitSuper {

private int k = 2;

private MemberObject member = new MemberObject();

{
    System.out.println("TestClassInit构造代码块已运行 ");
}

static {
    System.out.println("TestClassInit静态代码块已运行 ");
}

public TestClassInit() {
    System.out.println("TestClassInit.k : " + k);
    System.out.println("TestClassInit 构造器已运行");
}

public static String STATIC_STR = printStr("TestClassInit 静态成员变量STATIC_STR已初始化");

private static void subStaticTest1() {
    System.out.println("TestClassInit subStaticTest1静态方法已运行");
}


public void test() {
    System.out.println("test() 方法运行");
}


public static void main(String[] args) {
    // 只调用静态资源
    System.out.println("--- 只调用静态资源情况下的加载 ---");
    System.out.println(TestClassInit.STATIC_STR);
    System.out.println("--- 第二次加载TestClassInit ---");
    System.out.println(TestClassInit.STATIC_STR);
    System.out.println("--- 调用完成 ---");
}

}

package cn.tobin.cls;

public class MemberObject {

public MemberObject() {
    System.out.println("初始化了引用成员对象 MemberObject");
}

}

再创建一个类单独用于测试:

package cn.tobin.cls;

public class TestInit {

public static void main(String[] args) {
    // 只调用静态资源
    System.out.println("--- 只调用静态资源情况下的加载 ---");
    System.out.println(TestClassInit.STATIC_STR);
    System.out.println("--- 第二次加载TestClassInit ---");
    System.out.println(TestClassInit.STATIC_STR);
    System.out.println("--- 调用完成 ---");
}

}

运行结果:

--- 只调用静态资源情况下的加载 ---
TestClassInitSuper静态代码块已运行
TestClassInitSuper.SUPER_STATIC_STR 已初始化
TestClassInit静态代码块已运行
TestClassInit 静态成员变量STATIC_STR已初始化
TestClassInit 静态成员变量STATIC_STR已初始化
--- 第二次加载TestClassInit ---
TestClassInit 静态成员变量STATIC_STR已初始化
--- 调用完成 ---

Process finished with exit code 0

在这个测试中,如果把该main 方法写在TestClassInit类中,结果就可能不太一样了:

TestClassInitSuper静态代码块已运行
TestClassInitSuper.SUPER_STATIC_STR 已初始化
TestClassInit静态代码块已运行
TestClassInit 静态成员变量STATIC_STR已初始化
--- 只调用静态资源情况下的加载 ---
TestClassInit 静态成员变量STATIC_STR已初始化
--- 第二次加载TestClassInit ---
TestClassInit 静态成员变量STATIC_STR已初始化
--- 调用完成 ---

Process finished with exit code 0

原因很简单,在加载到main方法之前,当前类(TestClassInit)首先被加载,然后再执行main 方法。

二、创建对象时的加载顺序
再创建一个类单独测试,第一次创建对象和第二次创建对象的情况:
package cn.tobin.cls;

public class TestInit2 {

public static void main(String[] args) {
    TestClassInit testObject = new TestClassInit();
    testObject.test();
    System.out.println("--- 第二次加载TestClassInit ---");
    TestClassInit testObject2 = new TestClassInit();
    testObject2.test();
}

}

运行结果:

TestClassInitSuper静态代码块已运行
TestClassInitSuper.SUPER_STATIC_STR 已初始化
TestClassInit静态代码块已运行
TestClassInit 静态成员变量STATIC_STR已初始化
TestClassInitSuper构造代码块已运行
i : 0; j : 1
TestClassInitSuper 构造器已运行
初始化了引用成员对象 MemberObject
TestClassInit构造代码块已运行
TestClassInit.k : 2
TestClassInit 构造器已运行
test() 方法运行
--- 第二次加载TestClassInit ---
TestClassInitSuper构造代码块已运行
i : 0; j : 1
TestClassInitSuper 构造器已运行
初始化了引用成员对象 MemberObject
TestClassInit构造代码块已运行
TestClassInit.k : 2
TestClassInit 构造器已运行
test() 方法运行

Process finished with exit code 0

说明:父类优于基类优先初始化,每个类内部静态代码块,静态成员都只初始化一次,类中成员变量优于构造代码块,构造器之前,静态初始化之后初始化,并且每次加载都重新初始化一次。

以上是关于Java基础(Java类的初始化顺序)的主要内容,如果未能解决你的问题,请参考以下文章

java父子类的初始化顺序--个人总结

Java基础 - 静态代码块

IT十八掌作业_java基础第五天_静态代码块类的继承和接口

Java 的类加载顺序

Java类的初始化顺序

JAVA的初始化顺序: