1. 程式人生 > >程式碼快照圖與可變不可變

程式碼快照圖與可變不可變

  首先講一下程式碼快照圖,在軟體多維檢視中屬於執行時檢視、時刻檢視、程式碼檢視。它實際上是表示某一時刻程式碼中各變數的實際情況。

  程式碼快照圖用箭頭指向引用,而實際上關於引用,可以簡單通俗的理解如下:對於語句new Hero(),代表建立了一個Hero物件但是也僅僅是建立了一個物件,沒有辦法訪問它為了訪問這個物件,會使用引用來代表這個物件:Hero h = new Hero(),h這個變數是Hero型別,又叫做引用,=的意思指的h這個引用代表右側建立的物件“代表” 。在面向物件裡,又叫做“指向”。

  引用有多個,但是物件只有一個。物件就像 "房產", 引用就像"房產證",房產證的影印件可以有多張,但是真正的"房產" 只有這麼一處。在這個例子裡,所有引用都指向了同一個物件。

  物件值是一個用其型別標記的橢圓。當我們想顯示更多細節時,我們在其中寫上欄位名稱,並用箭頭指出它們的值,這些欄位可以包括其宣告的型別。可變物件用單圈表示,不可變物件用雙圈表示。那麼什麼是可變不可變物件呢?首先我們針對資料型別來說:

  不可變資料型別: 當該資料型別的對應變數的值發生了改變,那麼它對應的記憶體地址也會發生改變,對於這種資料型別,就稱不可變資料型別。其中基本資料型別都是不可變資料型別,例如int,如果一個int型別的資料發生改變,那麼它指向了記憶體中的另一個地址,但是需要注意的是java快取了所有-128-127的值。
  可變資料型別 :當該資料型別的對應變數的值發生了改變,那麼它對應的記憶體地址不發生改變,對於這種資料型別,就稱可變資料型別,當可變資料型別改變時它實際上是更改了記憶體中的內容

  比如我們熟知的String和StringBuilder,前者是不可變資料型別,後者是可變資料型別。當我們改變他們的值時,String實際上會再建立一個新的物件,原來的物件則會被垃圾處理器回收。而後者則會在原有的基礎上進行修改。

  將可變與不可變的範圍擴大到所有物件,即討論可變與不可變。我們如果想建立一個自己的不可變類的話,需要做到以下幾點:所有成員都是private final不提供對成員的改變方法,確保所有的方法不會被過載。其實這個final也不是必要的,此外如果我們想提供修改方法,那可以使用防禦式拷貝,即建立一個新的修改後的物件傳給呼叫者。還有一件很重要的事就是我們不能對外洩露內部引用,否則外部仍然能修改內部值,這一點也是可以通過防禦式拷貝來避免的。

  這裡還要特別說明一點,也是我經常容易弄錯的一個知識點,就是用final修飾修飾物件引用時,並不是讓物件不可變,這一點和基本資料型別不太一樣,基本資料型別用final修飾後就不可以修改,而物件則是引用無法修改,這個怎麼解釋呢?

  其實很好理解,因為物件資料型別裡面儲存的是引用而不是真實值!換種說法來說,引用物件的地址不能變,但是它裡面的內容可以變。比如用final修飾的StringBuilder,如果改變他的值是可以的。

package hetest;

public class fianltest {
    
    public static void main(String args[])
    {
        final StringBuilder s=new StringBuilder("a");
        s.append("b");
        System.out.print(s);
        
    }
}

  上面這一段程式碼執行之後就會輸出結果ab,但是如果換成String就會報錯。具體原因上面也說了,就是final修飾物件的引用不可變,但是String是不可變物件,一旦改變他的值引用就能改變,所以加上final也間接的使得物件的值不可改