標籤:
static 修飾符能夠與變數、方法一起使用,表示是“靜態”的。
靜態變數和靜態方法能夠通過類名來訪問,不需要建立一個類的對象來訪問該類的靜態成員,所以static修飾的成員又稱作類變數和類方法。靜態變數與執行個體變數不同,執行個體變數總是通過對象來訪問,因為它們的值在對象和對象之間有所不同。
請看下面的例子:
- public class Demo {
- static int i = 10;
- int j;
- Demo() {
- this.j = 20;
- }
- public static void main(String[] args) {
- System.out.println("類變數 i=" + Demo.i);
- Demo obj = new Demo();
- System.out.println("執行個體變數 j=" + obj.j);
- }
- }
運行結果:
類變數 i=10
執行個體變數 j=20
static 的記憶體配置
靜態變數屬於類,不屬於任何獨立的對象,所以無需建立類的執行個體就可以訪問靜態變數。之所以會產生這樣的結果,是因為編譯器只為整個類建立了一個靜態變數的副本,也就是只分配一個記憶體空間,雖然有多個執行個體,但這些執行個體共用該記憶體。執行個體變數則不同,每建立一個對象,都會分配一次記憶體空間,不同變數的記憶體相互獨立,互不影響,改變 a 對象的執行個體變數不會影響 b 對象。
請看下面的代碼:
- public class Demo {
- static int i;
- int j;
- public static void main(String[] args) {
- Demo obj1 = new Demo();
- obj1.i = 10;
- obj1.j = 20;
-
- Demo obj2 = new Demo();
-
- System.out.println("obj1.i=" + obj1.i + ", obj1.j=" + obj1.j);
- System.out.println("obj2.i=" + obj2.i + ", obj2.j=" + obj2.j);
- }
- }
運行結果:
obj1.i=10, obj1.j=20
obj2.i=10, obj2.j=0
注意:靜態變數雖然也可以通過對象來訪問,但是不被提倡,編譯器也會產生警告。
上面的代碼中,i 是靜態變數,通過 obj1 改變 i 的值,會影響到 obj2;j 是執行個體變數,通過 obj1 改變 j 的值,不會影響到 obj2。這是因為 obj1.i 和 obj2.i 指向同一個記憶體空間,而 obj1.j 和 obj2.j 指向不同的記憶體空間,請看:
圖1 靜態變數記憶體配置
注意:static 的變數是在類裝載的時候就會被初始化。也就是說,只要類被裝載,不管你是否使用了這個static 變數,它都會被初始化。
小結:類變數(class variables)用關鍵字 static 修飾,在類載入的時候,分配類變數的記憶體,以後再產生類的執行個體對象時,將共用這塊記憶體(類變數),任何一個對象對類變數的修改,都會影響其它對象。外部有兩種訪問方式:通過對象來訪問或通過類名來訪問。
靜態方法
靜態方法是一種不能向對象實施操作的方法。例如,Math 類的 pow() 方法就是一個靜態方法,文法為 Math.pow(x, a),用來計算 x 的 a 次冪,在使用時無需建立任何 Math 對象。
因為靜態方法不能操作對象,所以不能在靜態方法中訪問執行個體變數,只能訪問自身類的靜態變數。
以下情形可以使用靜態方法:
- 一個方法不需要訪問對象狀態,其所需參數都是通過顯式參數提供(例如 Math.pow())。
- 一個方法只需要訪問類的靜態變數。
讀者肯定注意到,main() 也是一個靜態方法,不對任何對象進行操作。實際上,在程式啟動時還沒有任何對象,main() 方法是程式的入口,將被執行並建立程式所需的對象。
關於靜態變數和靜態方法的總結:
- 一個類的靜態方法只能訪問靜態變數;
- 一個類的靜態方法不能夠直接調用非靜態方法;
- 如存取控制許可權允許,靜態變數和靜態方法也可以通過對象來訪問,但是不被推薦;
- 靜態方法中不存在當前對象,因而不能使用 this,當然也不能使用 super;
- 靜態方法不能被非靜態方法覆蓋;
- 構造方法不允許聲明為 static 的;
- 局部變數不能使用static修飾。
靜態方法舉例:
- public class Demo {
- static int sum(int x, int y){
- return x + y;
- }
- public static void main(String[] args) {
- int sum = Demo.sum(10, 10);
- System.out.println("10+10=" + sum);
- }
- }
運行結果:
10+10=20
static 方法不需它所屬的類的任何執行個體就會被調用,因此沒有 this 值,不能訪問執行個體變數,否則會引起編譯錯誤。
注意:執行個體變數只能通過對象來訪問,不能通過類訪問。
靜態初始器(靜態塊)
塊是由大括弧包圍的一段代碼。靜態初始器(Static Initializer)是一個存在於類中、方法外面的靜態塊。靜態初始器僅僅在類裝載的時候(第一次使用類的時候)執行一次,往往用來初始化靜態變數。
範例程式碼:
- public class Demo {
- public static int i;
- static{
- i = 10;
- System.out.println("Now in static block.");
- }
- public void test() {
- System.out.println("test method: i=" + i);
- }
- public static void main(String[] args) {
- System.out.println("Demo.i=" + Demo.i);
- new Demo().test();
- }
- }
運行結果是:
Now in static block.
Demo.i=10
test method: i=10
靜態匯入
靜態匯入是 Java 5 的新增特性,用來匯入類的靜態變數和靜態方法。
一般我們匯入類都這樣寫:
- import packageName.className; // 匯入某個特定的類
或
- import packageName.*; // 匯入包中的所有類
而靜態匯入可以這樣寫:
- import static packageName.className.methonName; // 匯入某個特定的靜態方法
或
- import static packageName.className.*; // 匯入類中的所有靜態成員
匯入後,可以在當前類中直接用方法名調用靜態方法,不必再用 className.methodName 來訪問。
對於使用頻繁的靜態變數和靜態方法,可以將其靜態匯入。靜態匯入的好處是可以簡化一些操作,例如輸出語句 System.out.println(); 中的 out 就是 System 類的靜態變數,可以通過 import static java.lang.System.*; 將其匯入,下次直接調用 out.println() 就可以了。
請看下面的代碼:
- import static java.lang.System.*;
- import static java.lang.Math.random;
- public class Demo {
- public static void main(String[] args) {
- out.println("產生的一個隨機數:" + random());
- }
- }
運行結果:
產生的一個隨機數:0.05800891549018705
9.Java static關鍵字以及Java靜態變數和靜態方法