1. 程式人生 > >關於static關鍵字及其初始化

關於static關鍵字及其初始化

什麼是static

通常來說,建立類之後,只有執行new來建立物件時,資料儲存空間才被分配,這個類的方法才能被外界呼叫。但是有兩種特殊情況用這個方法不能解決:

  1. 只想為某特定域分配單一儲存空間,而不去考慮是否建立物件或建立多少物件。
  2. 希望方法不於包含它的類的任何物件關聯。即:即使沒有建立物件,也可以呼叫這個方法

static關鍵字可以滿足這兩個需要。即使從未建立某個類的任何物件,也可以呼叫其static方法或訪問static域。

static作用於欄位

static作用於某個欄位時,會改變資料建立的方式,其本質是:一個static欄位對每個類來說都只有一份儲存空間

,而非static欄位則是對每個物件都有一個儲存空間。

class StaticTest {
  static int i = 47;
}

class Static {
  public static void main(String[] args) {
    StaticTest st1 = new StaticTest();
    StaticTest st2 = new StaticTest();
    System.out.println("st1:" + st1.i + " st2:" + st2.i);
    StaticTest.i++;
    System.out.
println("st1Increment:" + st1.i + " st2Increment:" + st2.i); } } /* output: st1:47 st2:47 st1Increment:48 st2Increment:48 *///:~

在示例中,即使建立了兩個StaticTest物件,StaticTest.i也只有一份儲存空間(即:st1.i 和st2.i 指向同一個儲存空間)。
這也是為什麼我們可以通過類名去直接引用靜態成員:

StaticTest.i++;

如上使用類名是引用static變數的首選方式(另一種方式是通過物件定位,如st1.i),因為它強調了變數的static結構,某些情況下為編譯器進行優化提供了更好的機會。

static作用於方法

static作用於方法時,不像其作用於欄位時有那麼大的差別。static方法的重要用法是 在不建立任何物件的前提下就可以呼叫static方法。這一點對定義main()方法十分重要,因為它是執行一個應用時的切入點。

靜態資料的初始化

在類的內部,即使變數定義散佈於方法定義之間,它們仍舊會在任何方法(包括構造器)被呼叫之前得到初始化。

public class ExplicitStatic {
  public static void main(String[] args) {
    System.out.println("Inside main");
    Cups.cup1.f(99); //(1)
  }
  // static Cups cups1 = new Cups(); //(2)
  // static Cups cups2 = new Cups(); //(2)
}

class Cups {
  static Cup cup1;
  static Cup cup2;
  static {
    cup1 = new Cup(1);
    cup2 = new Cup(2);
  }
  Cups() {
    System.out.println("Cups()");
  }
}

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

此時(執行(1)時)的輸出為:

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

當註釋掉(1),執行(2)時,輸出為:

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

可見,初始化的順序是:先靜態物件,後非靜態物件,且靜態初始化動作只進行一次

假設有名為Dog的類,總結一下物件的建立過程:

  1. 構造器(constructor)實際上是static靜態方法。首次建立型別為Dog的物件時,或者Dog類的靜態方法/靜態域被首次訪問時,Java直譯器必須查詢類路徑,定位Dog.class檔案。
  2. 載入Dog.class並建立Class物件,此時有關靜態初始化的所有動作都會執行。靜態初始化只在Class物件首次載入時進行一次
  3. 用new Dog()建立物件時,首先在堆上為Dog物件分配足夠的儲存空間。
  4. 這塊儲存空間被清零,自動將Dog物件所有基本資料型別設定成預設值,引用設定成null.
  5. 執行出現於欄位定義處的初始化動作。
  6. 執行構造器。

參考:《Thinking in Java 4th Edition》第二章、第五章。