深入解析Java new運算子

2020-07-16 10:05:16
”new“在 Java 中意思是”新的“,可以說是 Java 開發者最常用的關鍵字。在 Java 中 new 的操作往往意味著在記憶體中開闢新的空間,這個記憶體空間分配在記憶體的堆區。
堆是用來存放由 new 建立的物件和陣列,即動態申請的記憶體都存放在堆區。棧是用來存放在方法中定義的一些基本型別的變數和物件的參照變數。
Java 中一般使用 new 來建立物件,它可以動態地為一個物件分配地址。它的通用格式如下:

classname obj = new classname( );

其中,obj 是建立的物件,classname 是類的名字,類名後邊的( )指明了類的構造方法。構造方法定義了當建立一個物件時要進行的操作。

下面我們通過 String 這個類舉例說明。
public class Test {
    public static void main(String[] args) {
        String a = "C語言中文網";
        String b = new String("C語言中文網");
        String c = "C語言中文網";
        String d = new String("C語言中文網");
        System.out.println(a == b);
        System.out.println(a == c);
        System.out.println(d == b);
        System.out.println(a);
        a = "Java";
        System.out.println(a);
    }
}
輸出結果為:

false
true
false
C語言中文網
Java


不同方式定義字串時堆和棧的變化:
  1. String a; 只是在棧中建立了一個 String 類的物件參照變數  a。
  2. String a = "C語言中文網";在棧中建立一個 String 類的物件參照變數  a,然後查詢棧中有沒有存放“C語言中文網”,如果有則直接指向“C語言中文網",如果沒有,則將”C語言中文網“存放進棧,再指向。
  3. String a = new String("C語言中文網");不僅在棧中建立一個 String 類的物件參照變數 a,同時也在堆中開闢一塊空間存放新建的 String 物件“C語言中文網”,變數 a 指向堆中的新建的 String 物件”C語言中文網“。

==用來比較兩個物件在堆區存放的地址是否相同。根據上面的輸出結果,我們可以看出:
  • 使用 new 運算子建立的 String 物件進行==操作時,兩個地址是不同的。這就說明,每次物件進行 new 操作後,系統都為我們開闢堆區空間,雖然值是一樣,但是地址卻是不一樣的。
  • 當我們沒有使用 new 運算子的時候,系統會預設將這個變數儲存在記憶體的棧區。如果變數的值存放在棧中,使用==比較時,比較的是具體的值。如果變數的值存放在堆中,使用==比較時,比較的是值所在的地址。因此在變數 a 與變數 c 進行==操作的時候,返回 true,因為變數 a 和變數 c 比較的是具體的值,即“C語言中文網”。
  • 在改變變數 a 的值後(如 a = "Java"),再次輸出時,我們發現輸出的結果是”Java“。事實上原來的那個“C語言中文網”在記憶體中並沒有清除掉,而是在棧區的地址發生了改變,這次指向的是”Java“所在的地址。

注意:如果需要比較兩個使用 new 建立的物件具體的值,則需要通過“equal()”方法去實現,這樣才是比較參照型別變數具體值的正確方式。


這時,你可能想知道為什麼對整數或字元這樣的簡單變數不使用 new 運算子。答案是 Java 的簡單型別不是作為物件實現的。出於效率的考慮,它們是作為“常規”變數實現的。

物件有許多屬性和方法,這使得 Java 對物件的處理不同於簡單型別。Java 在處理物件和處理簡單型別時開銷不同,Java 能更高效地實現簡單型別。當然,如果你希望完全使用物件型別,那麼 Java 也提供了簡單型別的物件版本,也就是包裝類。

大家一定要明白,new 運算子是在執行期間為物件分配記憶體的,這使得記憶體的分配更加靈活和高效,你的程式在執行期間可以根據實際情況來合理地分配記憶體。但是,記憶體是有限的,因此 new 有可能由於記憶體不足而無法給一個物件分配記憶體。如果出現這種情況,就會發生執行時異常。

對於本教學中的範例程式,你不必擔心記憶體不足的情況,但是在實際的程式設計中你必須考慮這種可能性。