1. 程式人生 > >Java static關鍵字/靜態變數/靜態方法/final關鍵字

Java static關鍵字/靜態變數/靜態方法/final關鍵字

原文:www.weixueyuan.net/view/6003.html

http://www.weixueyuan.net/view/6004.html

靜態變數和靜態方法能夠通過類名來訪問,不需要建立一個類的物件來訪問該類的靜態成員,所以static修飾的成員又稱作類變數和類方法。靜態變數與例項變數不同,例項變數總是通過物件來訪問,因為它們的值在物件和物件之間有所不同。

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 指向不同的記憶體空間。

注意:類變數(class variables)用關鍵字 static 修飾,在類載入的時候,分配類變數的記憶體,以後再生成類的例項物件時,將共享這塊記憶體(類變數),任何一個物件對類變數的修改,都會影響其它物件。

關於靜態變數和靜態方法的總結

  • 一個類的靜態方法只能訪問靜態變數;
  • 一個類的靜態方法不能夠直接呼叫非靜態方法;
  • 如訪問控制權限允許,靜態變數和靜態方法也可以通過物件來訪問,但是不被推薦;
  • 靜態方法中不存在當前物件,因而不能使用 this,當然也不能使用 super;
  • 靜態方法不能被非靜態方法覆蓋;
  • 構造方法不允許宣告為 static 的;
  • 區域性變數不能使用static修飾。

靜態初始器(靜態塊)

塊是由大括號包圍的一段程式碼。靜態初始器(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

靜態匯入

用來匯入類的靜態變數和靜態方法。靜態匯入的好處是可以簡化一些操作,例如輸出語句 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

final關鍵字

具體規定如下:

  • final 修飾的類不能被繼承。
  • final 修飾的方法不能被子類重寫。
  • final 修飾的變數(成員變數或區域性變數)即成為常量,只能賦值一次。
  • final 修飾的成員變數必須在宣告的同時賦值,如果在宣告的時候沒有賦值,那麼只有 一次賦值的機會,而且只能在構造方法中顯式賦值,然後才能使用。
  • final 修飾的區域性變數可以只宣告不賦值,然後再進行一次性的賦值。

注意:

如果將引用型別(任何類的型別)的變數標記為 final,那麼該變數不能指向任何其它物件。但可以改變物件的內容,因為只有引用本身是 final 的。

如果變數被標記為 final,其結果是使它成為常數。想改變 final 變數的值會導致一個編譯錯誤。

final 也可以用來修飾類(放在 class 關鍵字前面),阻止該類再派生出子類。

方法也可以被 final 修飾,被 final 修飾的方法不能被覆蓋;變數也可以被 final 修飾,被 final 修飾的變數在建立物件以後就不允許改變它們的值了。一旦將一個類宣告為 final,那麼該類包含的方法也將被隱式地宣告為 final,但是變數不是。

被 final 修飾的方法為靜態繫結,不會產生多型(動態繫結),程式在執行時不需要再檢索方法表,能夠提高程式碼的執行效率。在Java中,被 static 或 private 修飾的方法會被隱式的宣告為 final,因為動態繫結沒有意義。

JVM 中的即時編譯器能夠實時監控程式的執行資訊,可以準確的知道類之間的繼承關係。如果一個方法沒有被覆蓋並且很短,編譯器就能夠對它進行優化處理,這個過程為稱為內聯(inlining)。例如,內聯呼叫 e.getName() 將被替換為訪問 e.name 變數。