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

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

  程式碼快照圖用箭頭指向引用,而實際上關於引用,可以簡單通俗的理解如下:對於語句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也間接的使得對象的值不可改變。