什么是不可變類
1. 不可變類是指類的實體一經創建完成,這個實體的內容就不會改變,
2. Java中的String和八個基本型別的包裝類(Integer, Short, Byte, Long, Double, Float,Boolean,Char)都是不可變類
3.不可變類 vs 不可變變數:
二者是不一樣的,
不可變類是指類的實體內容不會改變,考慮如下代碼:
1 String s = "ABC"; 2 s = "BCD" 3 System.out.println("s:"+s); 4 //output s:BCD
在line 2中我們對s變數進行了再次賦值,實際上是又創建了一個值為"BCD"的String 物件,并將s指向它,變化的是s指向的記憶體地址(或者簡單的叫指標),值為"ABC" 與值為"BCD"的兩個String 物件是沒有變的,
不可變變數是用Final關鍵字修飾的變數,考慮如下代碼:
1 final String s = "ABC"; 2 s = "BCD" //此行報錯,不能給final變數賦值 3 System.out.println("s:"+s);
我們將s變數用final關鍵字修飾,這時在s被初始化之后,就無法在line 2 再次給它賦值了,也就是說我們沒辦法改變final變數指向的記憶體地址,
如何實作一個不可變類
1. 所有的成員變數宣告為private final,防止初始化后被修改
2. 類宣告為final,禁止繼承,其實是防止類中的方法被重寫
3. 不為成員變數提供setter方法
4. 如果類中包含可變物件,比如一個成員變數是陣列,或者其他可變類,那么要有如下操作:
1)在構造方法中,如果構造方法會傳入可變物件,我們要使用這個物件的copy來初始化我們的成員變數,而不是直接使用傳入的物件,因為傳入的是指標,傳入的物件在外面可能會被修改,如果直接參考的話會導致我們的成員變數也間接被修改,
2) 在回傳這些可變物件的getter方法中,回傳物件的copy,而不是直接回傳該物件(或者叫該物件的參考/指標)
實體:
1 package org.adeline.learning; 2 3 import java.util.Arrays; 4 5 public final class Immutable { 6 private final int vInt; 7 private final String vStr; 8 private final int[] vArr; 9 10 public Immutable(int vInt, String vStr, int[] vArr) { 11 this.vInt = vInt; 12 this.vStr = vStr; 13 //this.vArr = vArr; //不正確 14 this.vArr = vArr.clone();//使用傳入陣列的copy初始化 15 } 16 17 public int[] getVArr() { 18 //return vArr; //不正確 19 return vArr.clone(); //回傳陣列的copy 20 } 21 22 public static void main(String[] args) { 23 int[] arr = new int[]{3,4}; 24 Immutable im = new Immutable(1,"2", arr); 25 int[] arr1 = im.getVArr(); 26 Arrays.stream(arr1).forEach((e) -> {System.out.println(e);}); 27 arr[0] = 33; 28 arr[1] = 44; 29 Arrays.stream(arr1).forEach((e) -> {System.out.println(e);}); 30 } 31 32 }
下面探討一下為何類也需要宣告為final. 考慮如下代碼:
1 public class ImmutableChild extends Immutable{ 2 private int[] vArr; 3 public ImmutableChild(int vInt, String vStr, int[] vArr) { 4 super(vInt, vStr, vArr); 5 this.vArr = vArr; 6 } 7 @Override 8 public int[] getVArr() { //父類中的方法被重寫,回傳的是子類中的vArr 9 return vArr; 10 } 11 12 public static void main(String[] args) { 13 Immutable imNG = new ImmutableChild(1,"2", new int[]{3,4}); 14 imNG.getVArr()[0] = 33; 15 imNG.getVArr()[1] = 44; 16 Arrays.stream(imNG.getVArr()).forEach(e -> {System.out.println(e);});//output 33,44 17 } 18 }
我們把上面Immutable 類的final 宣告去掉,ImmutableChild繼承了Immutable類,重寫了getVArr方法,回傳自己的成員變數陣列vArr,而這個子類里面的vArr是可變的,在main方法里面初始化時我們給其賦值{3,4},可以看到后面我們改成了{33,44}.
在使用中,任何一個接受Immutable實體的地方都可以接受其子類ImmutableChild實體,并將它作為一個不可變的實體來操作,而實際上它是可變的,這樣就有可能出錯,
所以把不可變類宣告為final是為了防止惡意繼承,或者繼承中考慮不周密導致的問題,
不可變類的優點與用途
1. 執行緒安全,省去了加鎖的程序,因為物件內容不可變就不用擔心執行緒同時對物件做修改
2. 拷貝效率高,當類不可變時, 拷貝其物件只需拷貝指標即可,而不用拷貝物件本身,不用擔心會被修改
3. 可以作為HashMap的key,類不可變保證了Hashcode的穩定性,
當然,也要注意不可變類在使用程序中可能出現的記憶體浪費問題,比如大家都知道的最好不要用許多"+"連接String
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/505323.html
標籤:其他
上一篇:傳輸層
