01-初始化顺序

一、 前言  

      数据的初始化顺序在编写高效率的代码的时候有着决定性的作用! 作为一个java开发者,弄懂这快内容是最基本的。

二、初始化顺序 

      在一个类中,初始化的顺序是由变量在类中定义的顺序决定的,即使变量定义大量分布在方法定义的中间,那些变量仍会在调用任何方法之前得到初始化--甚至在构造方法执行之前 。

      那么通过下面这个例子来看看: 

      

//: OrderOfInitialization.java
// Demonstrates initialization order.
// When the constructor is called, to create a
// Tag object, you'll see a message:
class Tag {
    Tag(int marker) {
        System.out.println("Tag(" + marker + ")");
    }
}

class Card {
    Tag t1 = new Tag(1); // Before constructor

    Card() {
        // Indicate we're in the constructor:
        System.out.println("Card()");
        t3 = new Tag(33); // Re-initialize t3
    }

    Tag t2 = new Tag(2); // After constructor

    void f() {
        System.out.println("f()");
    }

    Tag t3 = new Tag(3); // At end
}

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

} // /:~

 

       代码执行的结果: 

      

Tag(1)
Tag(2)
Tag(3)
Card()
Tag(33)
f()

    在Card 中,Tag 对象的定义故意到处散布,以证明它们全都会在构建器进入或者发生其他任何事情之前得到初始化。除此之外,t3 在构建器内部得到了重新初始化因此,t3 句柄会被初始化两次,一次在构建器调用前,一次在调用期间(第一个对象会被丢弃,所以它后来可被当作垃圾收掉)。从表面看,这样做似乎效率低下,但它能保证正确的初始化——若定义了一个过载的构建器,它没有初始化t3;同时在t3 的定义里并没有规定“默认”的初始化方式,那么会产生异常。

 

三、静态数据的初始化

      若数据是静态的(static),那么同样的事情就会发生;如果它属于一个基本类型(主类型),而且未对其初始化,就会自动获得自己的标准基本类型初始值;如果它是指向一个对象的句柄,那么除非新建一个对象,并将句柄同它连接起来,否则就会得到一个空值(NULL)。如果想在定义的同时进行初始化,采取的方法与非静态值表面看起来是相同的。但由于static 值只有一个存储区域,所以无论创建多少个对象,都必然会遇到何时对那个存储区域进行初始化的问题。下面这个例子可将这个问题说更清楚一些:

//: StaticInitialization.java
// Specifying initial values in a
// class definition.
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);
}

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();  //re-init b3
        t2.f2(1);
        t3.f3(1);
    }

    static Table t2 = new Table();
    static Cupboard t3 = new Cupboard();
} // /:~

    输出的结果: 

Bowl(1)
Bowl(2)
Table()
f(1)
Bowl(4)
Bowl(5)
Bowl(3)
Cupboard()
f(2)
Creating new Cupboard() in main
Bowl(3)
Cupboard()
f(2)
Creating new Cupboard() in main
Bowl(3)
Cupboard()
f(2)
f2(1)
f3(1)

 

              从上面可以发现: 初始化的顺序是首先static(如果它们尚未由前一次对象创建过程初始化,初始化过后就不再初始化,要和非static区分),接着是非static 对象。大家可从输出结果中找到相应的证据

      下面做一个小结对象的创建过程: 

      请考虑一个名为Dog 的类:

    (1) 类型为Dog 的一个对象首次创建时,或者Dog 类的static 方法/static 字段首次访问时,Java 解释器必须找到Dog.class(在事先设好的类路径里搜索)。
    (2) 找到Dog.class 后(它会创建一个Class 对象),它的所有static 初始化模块都会运行。因此,static 初始化仅发生一次——在Class 对象首次载入的时候。
    (3) 创建一个new Dog()时,Dog 对象的构建进程首先会在内存堆(Heap)里为一个Dog 对象分配足够多的存储空间。
    (4) 这种存储空间会清为零,将Dog 中的所有基本类型设为它们的默认值(零用于数字,以及boolean 和char 的等价设定)。
    (5) 进行属性定义时发生的所有初始化都会执行(上面的第一个例子)。
    (6) 执行构造方法。

      掌握对象的创建过程  那么初始化的顺序也就了然于心了。 下面再看几个例子

四、明确进行的静态初始化

      Java 允许将其他static 初始化工作划分到类内一个特殊的“static 构建从句”(有时也叫作“静态块”)里。如:

class Spoon {
  static int i;
  static {
    i = 47;
    }
// . . .
尽管看起来象个方法,但它实际只是一个static 关键字,后面跟随一个方法主体。与其他static 初始化一样,这段代码仅执行一次——首次生成那个类的一个对象时,或者首次访问属于那个类的一个static 成员时(即便从未生成过那个类的对象)。例如:

//: ExplicitStatic.java
// Explicit static initialization
// with the "static" clause.
class Cup {
    Cup(int marker) {
        System.out.println("Cup(" + marker + ")");
    }

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

class Cups {
    static Cup c1;
    static Cup c2;
    static {
        c1 = new Cup(1);
        c2 = new Cup(2);
    }

    Cups() {
        System.out.println("Cups()");

    }
}

public class ExplicitStatic {
    public static void main(String[] args) {
        System.out.println("Inside main()");
        Cups.c1.f(99); // (1)
    }

    static Cups x = new Cups(); // (2)
    static Cups y = new Cups(); // (2)
} // /:~

 

     执行结果:

Cup(1)
Cup(2)
Cups()
Cups()
Inside main()
f(99)

 

      在执行static Cups y = new Cups(); // (2)  时没有重复打印 Cup(1) Cup(2)  表示静态代码块只执行了一次.

 

五、普通代码块的初始化

      

//: Mugs.java
// Java 1.1 "Instance Initialization"
class Mug {
    Mug(int marker) {
        System.out.println("Mug(" + marker + ")");
    }

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

public class Mugs {
    Mug c1;
    Mug c2;
    {
        c1 = new Mug(1);
        c2 = new Mug(2);
        System.out.println("c1 & c2 initialized");
    }

    Mugs() {
        System.out.println("Mugs()");
    }

    public static void main(String[] args) {
        System.out.println("Inside main()");
        Mugs x = new Mugs();
    }
} // /:~

    执行的结果:

Inside main()
Mug(1)
Mug(2)
c1 & c2 initialized
Mugs()

 

    优先执行普通代码块

 

六、 其他

       我们习惯将代码块分为四类: 

    静态代码块 static { //...}

           普通代码块    在一个方法中定义的代码块

           同步代码块   synchronized {//...}

           构造代码块   在类中定义的代码块

在执行顺序上 优先执行静态代码块 其次是构造代码块 再次构造方法

posted @ 2014-04-11 18:08  廖凯林  阅读(726)  评论(0)    收藏  举报