前置知识
- 初始化块:类中直接用 {} 定义,每一次创建对象时执行
- 静态代码块:用 staitc 声明,JVM加载类时执行,仅执行一次
Java 程序初始化顺序
对于静态变量、静态初始化块、变量、初始化块、构造器,它们初始化顺序流程如下文字描述,示意图如下
静态变量、静态代码块 > 变量、初始化块 > 构造器
静态变量和静态代码块两个的初始化顺序是靠它们俩的位置决定的
单个类的初始化
单个类的初始化
public class Example {
public static String STR = "静态成员变量";
public String variable = "普通成员变量";
static {
System.out.println(STR);
System.out.println("静态代码块");
}
{
System.out.println(variable);
System.out.println("初始化块");
}
public Example(){
System.out.println("构造器");
}
public static void main(String[] args) {
System.out.println("Java 程序初始化顺序");
Example a = new Example();
}
}
结果
静态成员变量
静态代码块
Java 程序初始化顺序
普通成员变量
初始化块
构造器
调换静态成员变量和静态代码块顺序
静态成员变量在先,并做初始化动作
public class Example {
public static Example example1 = new Example();
public static Example example2 = new Example();
{
System.out.println("构造块");
}
static {
System.out.println("静态块");
}
public Example(){
System.out.println("构造器");
}
public static void main(String[] args) {
System.out.println("Java初始化顺序");
Example example = new Example();
}
}
结果
构造块 ---->(example1实例)
构造器 ---->(example1实例)
构造块 ---->(example2实例)
构造器 ---->(example2实例)
静态块
Java初始化顺序
构造块 ---->(example实例)
构造器 ---->(example实例)
静态成员变量在后,并做初始化动作
public class Example {
public Example(){
System.out.println("构造器");
}
{
System.out.println("构造块");
}
static {
System.out.println("静态块");
}
public static Example example1 = new Example();
public static Example example2 = new Example();
public static void main(String[] args) {
System.out.println("Java初始化顺序");
Example example = new Example();
}
}
结果
静态块
构造块 ---->(example1实例)
构造器 ---->(example1实例)
构造块 ---->(example2实例)
构造器 ---->(example2实例)
Java初始化顺序
构造块 ---->(example实例)
构造器 ---->(example实例)
静态变量在前,并不做初始化
public class Example {
public static Example example1;
public static Example example2;
{
System.out.println("构造块");
}
static {
System.out.println("静态块");
}
public Example(){
System.out.println("构造器");
}
public static void main(String[] args) {
System.out.println("Java初始化顺序");
Example example = new Example();
}
}
结果
静态块
Java初始化顺序
构造块 ---->(example实例)
构造器 ---->(example实例)
具有继承关系的初始化
父类
public class Father {
public static String F_STR = "父类-静态成员变量";
public String f_variable = "父类-普通成员变量";
static {
System.out.println(F_STR);
System.out.println("父类-静态代码块");
}
{
System.out.println(f_variable);
System.out.println("父类-初始化块");
}
public Father(){
System.out.println("父类-构造器");
}
}
子类
public class Subclass extends Father{
public static String S_STR = "子类-静态成员变量";
public String s_variable = "子类-普通成员变量";
static {
System.out.println(S_STR);
System.out.println("子类-静态代码块");
}
{
System.out.println(s_variable);
System.out.println("子类-初始化块");
}
public Subclass(){
System.out.println("子类-构造器");
}
public static void main(String[] args) {
System.out.println("Java 程序初始化顺序");
Subclass subclass = new Subclass();
}
}
结果
父类-静态成员变量
父类-静态代码块
子类-静态成员变量
子类-静态代码块
Java 程序初始化顺序
父类-普通成员变量
父类-初始化块
父类-构造器
子类-普通成员变量
子类-初始化块
子类-构造器
初始化说明
- 访问子类的main(),于是装载器就会寻找已经编译的子类class文件。在装载的过程中,装载器注意到它有一个基类,于是它再装载基类。不管创不创建基类对象,这个过程总会发生。如果基类还有基类,那么第二个基类也会被装载,依此类推
- 执行根基类的static初始化,然后是下一个派生类的static初始化,依此类推。这个顺序非常重要,因为派生类的
static初始化
有可能要依赖基类成员的正确初始化 - 当所有必要的类都已经装载结束,开始执行main()方法体,并用new Subclass()创建对象
- 类Subclass存在父类,则调用父类的构造函数,可以使用super来指定调用哪个构造函数
- 基类的构造过程以及构造顺序,同派生类的相同。首先基类中各个变量按照字面顺序进行初始化,然后执行基类的构造函数的其余部分
- 对子类成员数据按照它们声明的顺序初始化,执行子类构造函数的其余部分
不会初始化子类情况
不会初始化子类的情况
public class Father {
public static int number = 123;
public static final int number2 = 456;
static{
System.out.println("父类-静态代码块");
}
{
System.out.println("父类-构造块");
}
public Father(){
System.out.println("父类-构造器");
}
}
class Subclass extends Father {
static{
System.out.println("子类-静态代码块");
}
{
System.out.println("子类-构造块");
}
public Subclass(){
System.out.println("子类-构造器");
}
}
class Test {
public static void main(String[] args) {
System.out.println(Subclass.number);
System.out.println("-------------------------");
System.out.println(Subclass.number2);
System.out.println("-------------------------");
Subclass[] s = new Subclass[10];
}
}
结果
父类-静态代码块
123
-------------------------
456
-------------------------