淺談關于java中的深淺拷貝

一.淺拷貝(shallow copy)


?

?

1.如何實現淺拷貝?

Object類 是所有類的直接或間接父類,Object中存在clone方法,如下

protected native Object clone() throws CloneNotSupportedException;

如果想要使一個類的對象能夠調用clone方法 ,則需要實現Cloneable接口, 并重寫 clone方法:

public class Student implements Cloneable{private int sno ;private String name;//getter ,setter 省略
@Overridepublic Object clone() throws CloneNotSupportedException {Student s = null;try{s = (Student)super.clone();}catch (Exception e){e.printStackTrace();}return s;}}

?

現在測試clone方法:

@Testpublic void test04() throws CloneNotSupportedException {//創建Student對象Student s1 = new Student();s1.setSno(1);s1.setName("Rye");//通過clone 拷貝一個對象Student s2 = (Student)s1.clone();System.out.println("s1:"+s1);System.out.println("s2:"+s2);System.out.println("s1 == s2 ? ==> "+(s1 == s2));}

按照預期,克隆出的對象s2中的字段值應該與s1相同,但與s1對應的對象不在同一塊內存空間,結果如下:

s1:Student{sno=1, name='Rye'}
s2:Student{sno=1, name='Rye'}
s1 == s2 ? ==> false
View Code

此時如果修改 s1中的sno為2,那么會不會影響到s2中的sno呢?

//修改s1中的sno
s1.setSno(2);

結果如下:

s1:Student{sno=2, name='Rye'}
s2:Student{sno=1, name='Rye'}
View Code

?

此時看似已經完成了 copy, s1 與 s2有著自己不同的值,但如果為Student中新增了Teacher類型的成員變量,結果還是跟上面一樣嗎?讓我們改造下代碼:

public class Teacher {private int tno;private String name;//getter setter省略...  
}
public class Student  implements Cloneable{private int sno ;private String name;private Teacher teacher;//getter ,setter ,toString 省略...
@Overridepublic Object clone() throws CloneNotSupportedException {Student s = null;try{s = (Student)super.clone();}catch (Exception e){e.printStackTrace();}return s;}    
}

?

此時測試代碼如下:

    @Testpublic void test02() throws CloneNotSupportedException {Student student1 = new Student();student1.setSno(1);student1.setName("Rye");Teacher teacher = new Teacher();teacher.setTno(1);teacher.setName("LinTong");student1.setTeacher(teacher);Student student2 = (Student)student1.clone();System.out.println("student1:"+student1);System.out.println("student2:"+student2);System.out.println("student1 == student2 ? ==> "+ (student1 ==student2));System.out.println("student1.teacher == student2.teacher ? ==> "+ (student1.getTeacher() ==student2.getTeacher()));}

?

運行結果如下:

student1:Student{sno=1, name='Rye', teacher=Teacher{tno=1, name='LinTong'}}
student2:Student{sno=1, name='Rye', teacher=Teacher{tno=1, name='LinTong'}}
student1 == student2 ? ==> false
student1.teacher == student2.teacher ? ==> true
View Code

?

由此可見,此時經過clone生成的student2, 與 student1.二者中的teacher字段, 指向同一塊內存空間;

那么可能會有人問,這會有什么影響嗎??

我們拷貝的目的,更多的時候是希望獲得全新并且值相同的對象,操作原始對象或拷貝的新對象,對彼此之間互不影響;

此時我們修改student1中teacher的tno ,如下:

//修改teacher中的 tno值為2
student1.getTeacher().setTno(2);

再次運行test:

student1:Student{sno=1, name='Rye', teacher=Teacher{tno=2, name='LinTong'}}
student2:Student{sno=1, name='Rye', teacher=Teacher{tno=2, name='LinTong'}}
student1 == student2 ? ==> false
student1.teacher == student2.teacher ? ==> true
View Code

?

此時發現,student2中的teacher的tno ,也跟著變化了.

變化的原因是:通過student1執行clone時,基本類型會完全copy一份到student2對應對象內存空間中, 但是對于Teacher對象僅僅是copy了一份Teacher的引用而已.

而student1 與 student2的引用 指向的是同一塊堆內存,因此不論是通過student1或是student2修改teacher 都會影響另外一個;

通過圖會更直觀一些:

?

?

2.淺拷貝中引用類型的變量拷貝的是對象的引用 , 可通過如下思路解決:

Teacher類中也覆寫clone方法:

    @Overrideprotected Object clone() {Teacher teacher = null;try {teacher = (Teacher)super.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();}return teacher;}

?

修改Student中的clone方法,如下:

    @Overridepublic Object clone() {Student s = null;try{s = (Student)super.clone();Teacher t = (Teacher)this.teacher.clone();s.setTeacher(t);}catch (Exception e){e.printStackTrace();}return s;}

?

此時再次運行test:

student1:Student{sno=1, name='Rye', teacher=Teacher{tno=2, name='LinTong'}}
student2:Student{sno=1, name='Rye', teacher=Teacher{tno=1, name='LinTong'}}
student1 == student2 ? ==> false
student1.teacher == student2.teacher ? ==> false
View Code

?

由此可見,在copy Student的同時 將Teacher也進行了修改,如圖:

目前來看是滿足了我們的需求,但是如果Teacher類中,同樣也有別的引用類型 的成員變量呢?

那么就同樣需要一直覆寫clone方法,如果這個關系不是特多還可以接受,如果引用關系很復雜就會顯得代碼繁瑣;

此時應該使用序列化完成深度拷貝;

?

?

二.深拷貝(deep copy)


?

使用序列化完成深拷貝

深拷貝是利用對象流,將對象序列化,再反序列化得出新的對象. 因此首先需要實現序列化接口,如下:

public class Student implements Serializable{private static final long serialVersionUID = -2232725257771333130L;private int sno ;private String name;private Teacher teacher;
  //getter ,setter,toString()省略... }

Teacher也要實現序列化接口:

public class Teacher implements Serializable{private static final long serialVersionUID = 4477679176385287943L;private int tno;private String name;
  
 //getter ,setter,toString()省略...
}

?

工具方法:

  //工具方法public Object cloneObject(Object object) throws IOException, ClassNotFoundException {//將對象序列化ByteArrayOutputStream outputStream = new ByteArrayOutputStream();ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);objectOutputStream.writeObject(object);//將字節反序列化ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);Object obj = objectInputStream.readObject();return obj;}

?

測試類:

   public void test05() throws IOException, ClassNotFoundException {Student student1 = new Student();student1.setSno(1);student1.setName("Rye");Teacher teacher = new Teacher();teacher.setTno(1);teacher.setName("LinTong");student1.setTeacher(teacher);Student student2 = (Student)cloneObject(student1);//修改teacher中的 tno值為2student1.getTeacher().setTno(2);System.out.println("student1:"+student1);System.out.println("student2:"+student2);System.out.println("student1 == student2 ? ==> "+ (student1 ==student2));System.out.println("student1.teacher == student2.teacher ? ==> "+ (student1.getTeacher() ==student2.getTeacher()));}

?

如果Teacher類或者Student類沒有實現序列化接口,則執行時會報異常,如下:

java.io.NotSerializableException: com.example.test.Teacher

?

在都實現了Serializable接口的情況下,運行結果如下:

student1:Student{sno=1, name='Rye', teacher=Teacher{tno=2, name='LinTong'}}
student2:Student{sno=1, name='Rye', teacher=Teacher{tno=1, name='LinTong'}}
student1 == student2 ? ==> false
student1.teacher == student2.teacher ? ==> false
View Code

?

由此通過對象流的方式,成功完成了深度拷貝;

?


?

三.重寫clone方法 與 通過序列化 兩種拷貝方式比較:

clone方法:

優點:速度快,效率高

缺點:在對象引用比較深時,使用此方式比較繁瑣

?

通過序列化:

優點:非常簡便的就可以完成深度copy

缺點:由于序列化的過程需要跟磁盤打交道,因此效率會低于clone方式

?

如何抉擇?

實際開發中,根據兩種方式的優缺點進行選擇即可!

?

轉載于:https://www.cnblogs.com/lzzRye/p/9459465.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/397240.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/397240.shtml
英文地址,請注明出處:http://en.pswp.cn/news/397240.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

iOS開發-Protocol協議及委托代理(Delegate)傳值

前言:因為Object-C是不支持多繼承的,所以很多時候都是用Protocol(協議)來代替。Protocol(協議)只能定義公用的一套接口,但不能提供具體的實現方法。也就是說,它只告訴你要…

git 查看分支編碼_12個常用的Git命令,趕緊記一波!

今天齊姐簡單講下 Git 的實現原理,知其所以然才能知其然;并且梳理了日常最常用的 12 個命令,分為三大類分享給你。本文的結構如下:作者和開發原由Git 的數據模型常用命令資源推薦作者和開發原由Talk is cheap. Show me the code.這…

在域環境下搭建samba服務器

環境:samba:smbserver: 192.168.0.18AD:rise.com:192.168.0.37組:zixun xingzheng teacher class admin共享目錄:zixun xingzheng xueshu other一.安裝Samba服務器yum install -y samba二.把linux加入到ad中1.先…

Android NDK編程,引入第三方.so庫

android自帶的編譯工具NDK進行編譯時(非單純的調用第三方.so而是進行ndk編程),armeabi以及armeabi-v7a文件夾下的第三方so文件將會被刪除,只會產生編譯后的so文件,其他的so文件將無法引入,現在我們就來解決&#xff1a…

會做飯的機器人曰記_顏真卿《麻姑仙壇記》:蒼勁古樸,體態沉雄,氣象宏大...

《麻姑仙壇記》,全稱《有唐撫州南城縣麻姑山仙壇記》,或稱《麻姑山仙壇記》。顏真卿撰并書于大歷六年(771)四月。此碑有大、中、小三種刻本,且原石均佚,原拓佳本亦難得。大字本,字徑約5厘米&…

IBM服務器硬盤出現Other Error可能原因

除了確實物理等因素外,可能還因為:Other Errors的 很有可能也是固件(firmware)版本太低造成。 固件版本太低的話,硬盤自身有power safe模式,在硬盤長時間沒有I/O情況下,硬盤會自動斷電,而系統本身誤以為是硬…

怪異模式

眾所周知,HTML文檔結構可分為:文檔聲明<!DOCTYPE HTML>、HTML元素&#xff08;根元素/根標記/根標簽/祖先元素&#xff09;、head元素、body元素。 文檔聲明是用來通知瀏覽器&#xff0c;目前的文檔正使用哪個HTML版本&#xff0c;如果我們不寫文檔聲明<!DCOTYPE HTML…

Metro UI 菜單(Winform)

我有個項目需要要到菜單導航&#xff0c;就自己動作做了一個&#xff0c;感覺還可以&#xff0c;分享給大家。下載地址:http://files.cnblogs.com/files/dyj057/MetroUIMenu.zip 主要代碼&#xff1a; private void SetElements(){if (Elements null) return;int eWidth Bord…

echarts 山東地圖_用Python畫中國地圖,實現各省份數據可視化

第一步&#xff1a;安裝pyechartspyecharts是一款將python與echarts結合的強大的數據可視化工具&#xff0c;本文使用了0.1.9.4版本pip install pyecharts0.1.9.4第二步&#xff1a;讀取數據我的數據是在Excel表格里&#xff0c;如下圖&#xff1a;Execel數據使用xlrd(沒有就通…

mysql 中某個字段相同的數據拼接起來

2019獨角獸企業重金招聘Python工程師標準>>> mysql> select name, GROUP_CONCAT( age SEPARATOR ‘#’) from student group by name; ——————————————————— | name | GROUP_CONCAT( age SEPARATOR ‘#’) | ———————————————…

微信紅包系統架構的設計和優化分享

微信紅包系統架構的設計和優化分享 編者按&#xff1a;經過2014年一年的醞釀&#xff0c;2015微信紅包總量創下歷史新高&#xff0c;峰值1400萬次/秒&#xff0c;8.1億次每分鐘&#xff0c;微信紅包收發達10.1億次&#xff0c;系統整體運行平穩, 在這里我分享下微信紅包背后的技…

Jquery各版本下載

jquery-2.1.4 (注&#xff01;jquery-2.0以上版本不再支持IE 6/7/8) 百度引用地址 (推薦目前最穩定的&#xff0c;不會出現延時打不開情況) 百度壓縮版引用地址: <script src"http://libs.baidu.com/jquery/2.1.4/jquery.min.js"></script> 微軟壓縮版引…

python list方法操作_Python 列表(List)操作方法詳解

參考文獻來源于腳本之家列表是Python中最基本的數據結構&#xff0c;列表是最常用的Python數據類型&#xff0c;列表的數據項不需要具有相同的類型。列表中的每個元素都分配一個數字 - 它的位置&#xff0c;或索引&#xff0c;第一個索引是0&#xff0c;第二個索引是1&#xff…

FastDFS單機版安裝教程

安裝清單如下&#xff1a; 一、安裝FastDFS 1. 安裝libfastcommon 先解壓安裝包到目錄 # unzip libfastcommon-1.0.36.zip 安裝編譯工具及環境&#xff08;后面Nginx也會用到這些依賴環境&#xff09; # yum -y install gcc gcc gcc-c openssl openssl-devel pcre pcre-deve #…

【原創】Chrome最新版(53-55)再次爆出BUG!

2019獨角獸企業重金招聘Python工程師標準>>> 前言 今年十月份&#xff0c;我曾發布一篇文章《Chrome53 最新版驚現無厘頭卡死 BUG&#xff01;》&#xff0c;不過那個BUG在最新的 Chrome 54 中已經修正。 而今天即將發布的Chrome弱智BUG&#xff1a; 僅 Chrome 53 -…

ThinkPHP 發送post請求

function post($url, $paramarray()){ if(!is_array($param)){ throw new Exception("參數必須為array"); } $httph curl_init($url); curl_setopt($httph, CURLOPT_SSL_VERIFYPEER, 0); curl_setopt($httph, CURLOPT_SSL_VERIFYHOST, 1); curl_setopt($httph,CURLOP…

vue 打包路由報錯_Vue下路由History模式打包后頁面空白的解決方法

vue的路由在默認的hash模式下,默認打包一般不會有什么問題,不過hash模式由于url會帶有一個#,不美觀,而且在微信分享,授權登錄等都會有一些坑.所以history模式也會有一些應用場景.新手往往會碰到history模式打包后頁面一片空白的情況,而且沒有資源加載錯誤的報錯信息.這個其實仔…

leetcode-回文鏈表

請判斷一個鏈表是否為回文鏈表。 示例 1: 輸入: 1->2 輸出: false 示例 2: 輸入: 1->2->2->1 輸出: true進階&#xff1a;你能否用 O(n) 時間復雜度和 O(1) 空間復雜度解決此題&#xff1f; 思路&#xff1a;先遍歷鏈表&#xff0c;獲得長度。 把前半部分的鏈表逆置…

進程kswapd0與events/0消耗大量CPU的問題

http://www.nowamagic.net/librarys/veda/detail/2539 今天下午網站宕了兩次機&#xff0c;發工單給阿里云&#xff0c;發現原因是服務器的CPU 100%了。 重啟服務器后&#xff0c;使用 top 命令看看是哪些進程消耗那么大的 CPU 使用。盯了有好十幾分鐘&#xff0c;主要消耗 CPU…

索引器

namespace _03{ class Program { //請編寫一個類&#xff1a;ItcastClass,該類中有一個私有字段_names,數據類型為&#xff1a;字符串數組&#xff0c;長度為5&#xff0c;并且有5個默認的姓名。 //要求&#xff1a;為ItcastClass類編寫一個索引器&#xff0c;要求該索引器能夠…