Java程序初始化顺序

Java程序初始化顺序

引言

今天在课上复习了Java的初始化顺序,一直有点疑惑,搞不明白,所以打算写下来,记录一下。

重点

先说一下Java程序初始化的顺序:
父类静态变量>父类静态代码块>子类静态变量>子类静态代码块>父类非静态变量>父类非静态代码块>父类构造器>子类非静态变量>子类非静态代码块>子类构造器。

Java程序初始化一般遵循3个原则

  • 1.静态对象(变量)先于非静态对象(变量)初始化。其中静态对象(变量)只初始化一次,因为static在jvm中只有一块区域存储,方法区(Method Area),他之所以被称为静态是因为从程序创建到死亡他的地址值都不会改变,他只在class类对象初次加载时初始化,因此static只需要初始化一次,而非静态对象(变量)可能会初始化很多次。

  • 2.如果类之间存在继承关系,那么父类优先于子类进行初始化。

  • 3.按照成员变量的定义顺序进行初始化。即使变量定义散布于方法之中,他们依然在任何方法(包括构造函数)被调用前先初始化。

接下来我们先来看来《Thinking in Java 》的代码示例:


/*
 * copy from 《Thinking in Java》
 */
class Tag{
    Tag(int marker){
        System.out.println("Tag("+marker+")");
    }
}

class Card{
    Tag t1=new Tag(1);//Before constructor
    Card(){
        //in the constructor
        System.out.println("Card()");
        t3=new Tag(33); //Reinitialize t3
    }
    Tag t2=new Tag(2);//After constructor
    void f(){
        System.out.println("f()");
    }
    Tag t3=new Tag(3);//at the end
}

public class OrderOfInitialization{
    public static void main(String[] args){
        Card t=new Card();
        t.f();//shows that construction is done
    }
}

在Card中,Tag对象的实例化故意到处散布,in order to 使得他们都在构造器进入或者发生其它任何事情之前得到初始化。除此之外,t3在构造器内部得到了重新的初始化。

下面我们来看看运行结果:
我是图片

如果想在定义的同时进行初始化,采取的方法与非静态值表面看起来是相同的。但由于static值只有一个存储区域,所以无论创建了多少个对象,都必然会遇到何时对那个存储区域进行初始化的问题。下面来看一段《Thinking in Java 》代码:


class Bowl{
    Bowl(int marker){
        System.out.println("Bowl("+marker+")");
    }
    void f(int marker){
        System.out.println("f("+marker+")");
    }
}

class Table{
    //静态对象
    static Bowl b1=new Bowl(1);
    //构造器
    Table(){
        System.out.println("Table()");
        b2.f(1);
    }
    //方法
    void f2(int marker){
        System.out.println("f2("+marker+")");
    }
    //静态对象
    static Bowl b2=new Bowl(2);
}

class Cupboard{
    //非静态对象
    Bowl b3=new Bowl(3);
    //静态对象
    static Bowl b4=new Bowl(4);
    //构造器
    Cupboard(){
        System.out.println("Cupboard()");
        b4.f(2);
    }
    //非静态方法
    void f3(int marker){
        System.out.println("f3("+marker+")");
    }
    //静态对象
    static Bowl b5=new Bowl(5);
}

注意在static定义之前,Cupboard已经实例化了一个非静态的b3.


public class StaticInitialization {
    public static void main(String[] args){
        System.out.println("Creating new Cupboard() in main");
        new Cupboard();
        System.out.println("Creating new Cupboard() in main");
        new Cupboard();
        t2.f2(1);
        t3.f3(3);
    }
    static Table t2=new Table();
    static Cupboard t3=new Cupboard();
}

大家拿出纸笔来算算会输出个什么个结果吗,你的思路是否还清晰呢,还是像笔者一样已经云里雾里了,好了不卖关子了,我们来看看输出的结果:
我是图片

是否和你在纸上的答案一样呢?

分析

我们来剖析一下这个程序:

  • 首先看main方法,

    • main方法里首先会初始化静态对象t2,t3,
    • 在jvm的方法区里创建一个t2的静态对象,指向Table类,
    • 然后在创建一个t3的静态对象指向Cupboard.
  • 然后程序继续运行到Table类里,

    • 初始化静态对象b1,跳转到Bowl类,执行Bowl的构造器,打印输出Bowl(1),
    • 进入bowl类,初始化静态对象b2,跳转到Bowl类,执行Bowl的构造器,打印输出Bowl(2),
    • 回到Table类,执行Table类的构造器,打印输出Table(),
    • 执行b2.f(1),打印输出f(1),此时,静态对象t2的任务已经完成了。
  • 接下来是t3的任务,进入Cupboard类,

    • 初始化静态对象b4,跳转到Bowl类,打印输出Bowl(4),
    • 初始化静态对象b5,跳转到Bowl类,打印输出Bowl(5),
    • 初始化非静态对象b3,跳转到Bowl类,打印输出Bowl(3),
    • 然后执行Cupboard的构造器,打印输出Cupboard(),
    • next,执行b4.f(2),打印输出f(2),此时静态对象t3的任务已经完成了。
  • 回到main函数里,

    • 执行非静态变量,打印输出一个Creating new Cupboard() in main,
    • 在new一个Cupboard,初始化非静态对象b3,跳转到Bowl类,打印输出Bowl(3),
    • 然后执行Cupboard的构造器,打印输出Cupboard(),
    • next,执行b4.f(2),打印输出f(2),
    • 然后继续打印一个Creating new Cupboard() in main,
    • 初始化非静态对象b3,跳转到Bowl类,打印输出Bowl(3),
    • 然后执行Cupboard的构造器,打印输出Cupboard(),
    • next,执行b4.f(2),打印输出f(2)。
  • 最后执行t2.f2(1),t2指向的是Table类,所以打印输出f2(1),同理,t3.f3(3)打印输出f3(3).

总结

到此为止,这段代码已经被我们解剖的清清楚楚了,所以对Java的初始化顺序应该印象更深了吧!代码本身不难。但是有点绕口,有点复杂,只要自己敲一遍,理解一下,就能记住了。今天的笔记就到此为止!