Java static靜態修飾符詳解:靜態變數、靜態方法及靜態程式碼塊

2020-07-16 10:05:11
使用 static 修飾符修飾的屬性(成員變數)、常數和成員方法稱為靜態變數、常數和方法,它們統稱為靜態成員,歸整個類所有,不依賴於類的特定範例,被類的所有範例共用。只要這個類被載入,Java 虛擬機器就可以根據類名在執行時資料區的方法區內找到它們。

呼叫靜態成員的語法形式如下:
類名.靜態成員

靜態變數

實際上類的成員變數可以分為兩種:靜態變數(或稱為類變數),指被 static 修飾的成員變數;範例變數,指沒有被 static 修飾的成員變數。

靜態變數與範例變數的區別如下:
  • 對於靜態變數,執行時,Java 虛擬機器只為靜態變數分配一次記憶體,在載入類的過程中完成靜態變數的記憶體分配。在類的內部,可以在任何方法內直接存取靜態變數;在其他類中,可以通過類名存取該類中的靜態變數。
  • 對於範例變數,每建立一個範例,Java 虛擬機器就會為範例變數分配一次記憶體。在類的內部,可以在非靜態方法中直接存取範例變數;在本類的靜態方法或其他類中則需要通過類的範例物件進行存取。

靜態變數在類中的作用如下:
  • 靜態變數可以被類的所有範例共用,因此靜態變數可以作為範例之間的共用資料,增加範例之間的互動性。
  • 如果類的所有範例都包含一個相同的常數屬性,則可以把這個屬性定義為靜態常數型別,從而節省記憶體空間。例如,在類中定義一個靜態常數 PI。
public static double PI = 3.14159256;

例 1

建立一個帶靜態變數的類,然後在 main() 方法中存取該變數並輸出結果。
public class StaticVar {
    public static String str1 = "Hello";
    public static void main(String[] args) {
        String str2 = "World!";
        // 直接存取str1
        String accessVar1 = str1+str2;
        System.out.println("第 1 次存取靜態變數,結果為:"+accessVar1);
        // 通過類名存取str1
        String accessVar2 = StaticVar.str1+str2;
        System.out.println("第 2 次存取靜態變數,結果為:"+accessVar2);
        // 通過物件svt1存取str1
        StaticVar svt1 = new StaticVar();
        svt1.str1 = svt1.str1+str2;
        String accessVar3 = svt1.str1;
        System.out.println("第3次訪向靜態變數,結果為:"+accessVar3);
        // 通過物件svt2存取str1
        StaticVar svt2 = new StaticVar();
        String accessVar4 = svt2.str1+str2;
        System.out.println("第 4 次存取靜態變數,結果為:"+accessVar4);
    }
}
執行該程式後的結果如下所示。
第 1 次存取靜態變數,結果為:HelloWorld!
第 2 次存取靜態變數,結果為:HelloWorld!
第 3 次訪向靜態變數,結果為:HelloWorld!
第 4 次存取靜態變數,結果為:HelloWorld!World!
從執行結果可以看出,在類中定義靜態的屬性(成員變數),在 main() 方法中可以直接存取,也可以通過類名存取,還可以通過類的範例物件來存取。

注意:靜態變數是被多個範例所共用的。

靜態方法

與成員變數類似,成員方法也可以分為兩種:靜態方法(或稱為類方法),指被 static 修飾的成員方法;實體方法,指沒有被 static 修飾的成員方法。

靜態方法與實體方法的區別如下:
  • 靜態方法不需要通過它所屬的類的任何範例就可以被呼叫,因此在靜態方法中不能使用 this 關鍵字,也不能直接存取所屬類的範例變數和實體方法,但是可以直接存取所屬類的靜態變數和靜態方法。另外,和 this 關鍵字一樣,super 關鍵字也與類的特定範例相關,所以在靜態方法中也不能使用 super 關鍵字。
  • 在實體方法中可以直接存取所屬類的靜態變數、靜態方法、範例變數和實體方法。

例 2

建立一個帶靜態變數的類,新增幾個靜態方法對靜態變數的值進行修改,然後在 main() 方法中呼叫靜態方法並輸出結果。
public class StaticMethod {
    public static int count = 1;    // 定義靜態變數count
    public int method1() {    
        // 實體方法method1
        count++;    // 存取靜態變數count並賦值
        System.out.println("在靜態方法 method1()中的 count="+count);    // 列印count
        return count;
    }
    public static int method2() {    
        // 靜態方法method2
        count += count;    // 存取靜態變數count並賦值
        System.out.println("在靜態方法 method2()中的 count="+count);    // 列印count
        return count;
    }
    public static void PrintCount() {    
        // 靜態方法PrintCount
        count += 2;
        System.out.println("在靜態方法 PrintCount()中的 count="+count);    // 列印count
    }
    public static void main(String[] args) {
        StaticMethod sft = new StaticMethod();
        // 通過範例物件呼叫實體方法
        System.out.println("method1() 方法返回值 intro1="+sft.method1());
        // 直接呼叫靜態方法
        System.out.println("method2() 方法返回值 intro1="+method2());
        // 通過類名呼叫靜態方法,列印 count
        StaticMethod.PrintCount();
    }
}
執行該程式後的結果如下所示。
在靜態方法 method1()中的 count=2
method1() 方法返回值 intro1=2
在靜態方法 method2()中的 count=4
method2() 方法返回值 intro1=4
在靜態方法 PrintCount()中的 count=6
在該程式中,靜態變數 count 作為範例之間的共用資料,因此在不同的方法中呼叫 count,值是不一樣的。從該程式中可以看出,在靜態方法 method1() 和 PrintCount() 中是不可以呼叫非靜態方法 method1() 的,而在 method1() 方法中可以呼叫靜態方法 method2() 和 PrintCount()。

在存取非靜態方法時,需要通過範例物件來存取,而在存取靜態方法時,可以直接存取,也可以通過類名來存取,還可以通過範例化物件來存取。

靜態程式碼塊

靜態程式碼塊指 Java 類中的 static{} 程式碼塊,主要用於初始化類,為類的靜態變數賦初始值。靜態程式碼塊的特點如下:
  • 靜態程式碼塊類似於一個方法,但它不可以存在於任何方法體中。
  • Java 虛擬機器在載入類時會執行靜態程式碼塊,如果類中包含多個靜態程式碼塊,則 Java 虛擬機器將按它們在類中出現的順序依次執行它們,每個靜態程式碼塊只會被執行一次。
  • 靜態程式碼塊與靜態方法一樣,不能直接存取類的範例變數和實體方法,而需要通過類的範例物件來存取。

例 3

編寫一個 Java 類,在類中定義一個靜態變數,然後使用靜態程式碼塊修改靜態變數的值。最後在 main() 方法中進行測試和輸出。
public class StaticCode {
    public static int count = 0; 
    {
        count++;
        System.out.println("非靜態程式碼塊 count="+count);
    }
    static {
        count++;
        System.out.println("靜態程式碼塊 count="+count);
    }
    public static void main(String[] args) {
        System.out.println("*************** StaticCode1 執行 ***************");
        StaticCode sct1 = new StaticCode();
        System.out.println("*************** StaticCode2 執行 ***************");
        StaticCode sct2 = new StaticCode();
    }
}
如上述範例,為了說明靜態程式碼塊只被執行一次,特地新增了非靜態程式碼塊作為對比,並在主方法中建立了兩個類的範例物件。上述範例的執行結果為:
靜態程式碼塊 count=1
*************** StaticCode1 執行 ***************
非靜態程式碼塊 count=2
*************** StaticCode2 執行 ***************
非靜態程式碼塊 count=3