java構建器初始化

來源:互聯網
上載者:User

可考慮用構建器執行初始化進程。這樣便可在編程時獲得更大的靈活程度,因為我們可以在運行期調用方法和採取行動,從而“現場”決定初始化值。但要注意這樣一件事情:不可妨礙自動初始化的進行,它在構建器進入之前就會發生。因此,假如使用下述代碼:

class Counter {
int i;
Counter() { i = 7; }
// . . .

那麼i首先會初始化成零,然後變成7。對於所有基本類型以及物件控點,這種情況都是成立的,其中包括在定義時已進行了明確初始化的那些一些。考慮到這個原因,編譯器不會試著強迫我們在構建器任何特定的場所對元素進行初始化,或者在它們使用之前——初始化早已得到了保證(注釋⑤)。

⑤:相反,C++有自己的“構建器初始模組列表”,能在進入構建器主體之前進行初始化,而且它對於對象來說是強制進行的。參見《Thinking in C++》。

1. 初始化順序
在一個類裡,初始化的順序是由變數在類內的定義順序決定的。即使變數定義大量遍佈於方法定義的中間,那些變數仍會在調用任何方法之前得到初始化——甚至在構建器調用之前。例如:

 

//: 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  }} ///:~

在Card中,Tag對象的定義故意到處散布,以證明它們全都會在構建器進入或者發生其他任何事情之前得到初始化。除此之外,t3在構建器內部得到了重新初始化。它的輸入結果如下:
 

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


因此,t3控制代碼會被初始化兩次,一次在構建器調用前,一次在調用期間(第一個對象會被丟棄,所以它後來可被當作垃圾收掉)。從表面看,這樣做似乎效率低下,但它能保證正確的初始化——若定義了一個過載的構建器,它沒有初始化t3;同時在t3的定義裡並沒有規定“預設”的初始化方式,那麼會產生什麼後果呢?

2. 待用資料的初始化
若資料是靜態(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();    t2.f2(1);    t3.f3(1);  }  static Table t2 = new Table();  static Cupboard t3 = new Cupboard();} ///:~


Bowl允許我們檢查一個類的建立過程,而Table和Cupboard能建立散佈於類定義中的Bowl的static成員。注意在static定義之前,Cupboard先建立了一個非static的Bowl b3。它的輸出結果如下:

 

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


static初始化只有在必要的時候才會進行。如果不建立一個Table對象,而且永遠都不引用Table.b1或Table.b2,那麼static Bowl b1和b2永遠都不會建立。然而,只有在建立了第一個Table對象之後(或者發生了第一次static訪問),它們才會建立。在那以後,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) 執行構建器。正如第6章將要講到的那樣,這實際可能要求進行相當多的操作,特別是在涉及繼承的時候。

3. 明確進行的靜態初始化
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) } ///:~


在標記為(1)的行內訪問static對象c1的時候,或在行(1)標記為注釋,同時(2)行不標記成注釋的時候,用於Cups的static初始化模組就會運行。若(1)和(2)都被標記成注釋,則用於Cups的static初始化進程永遠不會發生。

4. 非靜態執行個體的初始化
針對每個對象的非靜態變數的初始化,Java 1.1提供了一種類似的文法格式。下面是一個例子:

 

//: 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();  }} ///:~


大家可看到執行個體初始化從句:

 

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

它看起來與靜態初始化從句極其相似,只是static關鍵字從裡面消失了。為支援對“匿名內部類”的初始化(參見第7章),必須採用這一文法格式。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.