深入Java深淺拷貝、immutable、unmodifiable
- 建議:函數調用的時候,調用方傳給被調用方的參數,如果在調用之后還會被修改,那么調用方應該給被調用方傳一個當時的拷貝,深拷貝,否則:
- 可能被調用方是異步執行的,如果調用函數之后,參數發生了修改,那么被調用方執行的時候,看到的就是被修改之后的數據,這將導致嚴重、隱蔽、非必現的BUG,而這種BUG是最讓人頭疼的
- 可能被調用方會修改傳入的參數,這就導致函數執行完畢之后,調用方看到的數據發生了非預期的變化,這同樣會導致嚴重、隱蔽的BUG </ul> </li>
- 深拷貝:這里需要弄清楚深淺拷貝的區別,用“=”號給非基本類型賦值,均是淺拷貝,例如List,以下代碼就是淺拷貝: </ul>
List<Integer> list1 = new ArrayList<>(); list1.add(1); List<Integer> list2 = list1; list1.add(2);
代碼執行完畢之后,list2將包含整數1和2。
而以下代碼則是深拷貝:List<Integer> list1 = new ArrayList<>(); list1.add(1); List<Integer> list2 = new ArrayList<>(list1); list1.add(2);
代碼執行完畢之后,list2將只包含整數1,不包含整數2。
- Immutable對象:上述情形如果遇到immutable對象,即不可變對象,其實是不需要深拷貝的(不僅不需要,還應該杜絕拷貝,因為純屬浪費)。但是前提是對象是真正的immutable。反面例子為: </ul>
public class NonStrictlyImmutable { private final List<Integer> mList = new ArrayList<>();
public List<Integer> getList() { return mList; } }</pre>mList成員設置為了private final,NonStrictlyImmutable對象實例化完成后mList所引用的實際對象也不可再被改變,然而mList這個List的元素確是可以改變的,nonStrictlyImmutable.getList().add(1)并不會報編譯錯誤,而這一行代碼卻實實在在改變了nonStrictlyImmutable對象的值!<br />
而如果getList()函數不直接返回mList引用,創建一個副本,或者使其不可被改變,則可以達到”嚴格意義上的“immutable。例如:
public class NonStrictlyImmutable { private final List<Integer> mList = new ArrayList<>();
public List<Integer> getList() { // 以下兩種方式都可以,各有優劣 // return new ArrayList<>(mList); // return Collections.unmodifiableList(mList); return Collections.unmodifiableList(mList); } }</pre><br />
上面兩種方式各有優劣:前者允許對新獲取到的副本進行修改操作而不會拋出異常,但會把底層數組數據創建多份;后者不會創建多份底層數組數據,但是如果對getList()返回的引用進行修改操作,將會拋出異常;見仁見智。
- unmodifiable:unmodifiable不等于immutable,所以對于前面提到的建議做法,直接傳入unmodifiable是不對的,因為這樣只能阻止被調用方修改傳入數據后導致調用方出錯,并不能阻止調用方修改后導致被調用方出錯。unmodifiable并未拷貝底層數組數據,而是實現了另外一個List的實現類,該類的修改操作均拋出異常。 </ul> https://github.com/Piasy/notes/blob/master/misc/copy.md
本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!