關於static關鍵字及其初始化
阿新 • • 發佈:2018-12-04
什麼是static
通常來說,建立類之後,只有執行new來建立物件時,資料儲存空間才被分配,這個類的方法才能被外界呼叫。但是有兩種特殊情況用這個方法不能解決:
- 只想為某特定域分配單一儲存空間,而不去考慮是否建立物件或建立多少物件。
- 希望方法不於包含它的類的任何物件關聯。即:即使沒有建立物件,也可以呼叫這個方法。
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的類,總結一下物件的建立過程:
- 構造器(constructor)實際上是static靜態方法。首次建立型別為Dog的物件時,或者Dog類的靜態方法/靜態域被首次訪問時,Java直譯器必須查詢類路徑,定位Dog.class檔案。
- 載入Dog.class並建立Class物件,此時有關靜態初始化的所有動作都會執行。靜態初始化只在Class物件首次載入時進行一次。
- 用new Dog()建立物件時,首先在堆上為Dog物件分配足夠的儲存空間。
- 這塊儲存空間被清零,自動將Dog物件所有基本資料型別設定成預設值,引用設定成null.
- 執行出現於欄位定義處的初始化動作。
- 執行構造器。
參考:《Thinking in Java 4th Edition》第二章、第五章。