摘要:前言在中關(guān)于對象的拷貝我們大致可以分為兩種,一種是淺拷貝也叫引用拷貝,另外一種是深拷貝也稱值拷貝。深拷貝根據(jù)上面的分析,淺拷貝是無法去完成含有除基本數(shù)據(jù)類型之外對象的拷貝的。擴(kuò)展深拷貝除了上述實現(xiàn)方式外,也可以用序列化來實現(xiàn)。
前言
在Java中關(guān)于對象的拷貝我們大致可以分為兩種,一種是淺拷貝(也叫引用拷貝),另外一種是深拷貝(也稱值拷貝)。
示例我相信絕大多數(shù)程序員Ctrl+C、Ctrl+V都玩的很溜,我也一樣哈。工作周報我覺得大家在熟悉不過了吧。以我自身寫周報為例子,為了節(jié)省自己的時間(主要還是自己懶),我基本都是Ctrl+C、Ctrl+V別人寫好的周報格式進(jìn)行現(xiàn)改。一人一份工作周報,總不能有兩個人的周報一字不差、一模一樣的吧。或多或少還是有點出入的,比如改下標(biāo)題,發(fā)件人,工作內(nèi)容等等。
淺拷貝 定義:被復(fù)制的對象所有的變量都含有與原來對象相同的值,所有的對其他對象的引用都仍然指向原來的對象(即原始的對象和其副本引用同一個對象)。
先來看下程序代碼
/** * 工作周報類 * @author zhh * @date 2017-08-23 上午11:03:47 */ class Report implements Cloneable { private String title; // 標(biāo)題 private String sender; // 發(fā)送者 private String receiver; // 接收者 private ArrayListcontent; //內(nèi)容 public Report(String title, String sender, String receiver) { this.title = title; this.sender = sender; this.receiver = receiver; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getSender() { return sender; } public void setSender(String sender) { this.sender = sender; } public String getReceiver() { return receiver; } public void setReceiver(String receiver) { this.receiver = receiver; } public ArrayList getContent() { return content; } public void setContent(ArrayList content) { this.content = content; } public void print() { System.out.println(this); } @Override public Report clone() { Report msg = null; try { msg = (Report) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return msg; } @Override public String toString() { return "Report [title=" + title + ", sender=" + sender + ", receiver=" + receiver + ", content=" + content + "]"; } }
舉個例子,我這里拷貝下公司UI小姐姐的周報模板現(xiàn)改現(xiàn)賣,像標(biāo)題,發(fā)送者,內(nèi)容什么的大致還是得改改。寫個主方法測試下。
public static void main(String[] args) { Report report = new Report("yg工作周報", "yg", "boss"); ArrayListcontent = new ArrayList<>(); content.add("1.參加研討會議, 確定需求"); content.add("2.設(shè)計首頁的icon以及界面"); report.setContent(content); Report report1 = report.clone(); report1.setTitle("zhh工作周報"); report1.setSender("zhh"); ArrayList content1 = report1.getContent(); content1.set(1, "2.搭建基礎(chǔ)的后臺框架"); report1.setContent(content1); System.out.println("----------UI小姐姐工作周報--------------"); report.print(); System.out.println("----------我的工作周報--------------"); report1.print(); }
程序運行的結(jié)果如下:
----------UI小姐姐工作周報-------------- Report [title=yg工作周報, sender=yg, receiver=boss, content=[1.參加研討會議, 確定需求, 2.搭建基礎(chǔ)的后臺框架]] ----------我的工作周報-------------- Report [title=zhh工作周報, sender=zhh, receiver=boss, content=[1.參加研討會議, 確定需求, 2.搭建基礎(chǔ)的后臺框架]]
雖然執(zhí)行沒有發(fā)生什么異常,但其結(jié)果顯然是不對的。UI小姐姐跟我干的活一樣啦?老板看了顯然也是一臉懵逼的。搞不好我也就被UI小姐姐給頂替掉了(開個玩笑哈)。
但為什么會這樣呢?克隆以后為什么我的工作內(nèi)容把UI的工作內(nèi)容替換了呢?而其他標(biāo)題、發(fā)送者卻沒有這種情況。
我們知道在Java當(dāng)中Object類是所有類的頂級父類,而其clone方法只會拷貝對象中的基本數(shù)據(jù)類型,對于數(shù)組、容器對象、引用對象等都不會拷貝。程序Report類自寫的clone方法中 msg = (Report) super.clone(); 就是調(diào)用了Object類的clone方法。String類雖然也是引用類型,但由于其的特殊性(final 類),雖然復(fù)制的引用,但是修改值的時候并沒有改變被復(fù)制對象的值;而ArrayList復(fù)制的僅僅是引用,導(dǎo)致原本引用和副本引用指向同一對象,所以上述代碼修改任意一個對象的content都會影響另外一個。
根據(jù)上面的分析,淺拷貝是無法去完成含有除基本數(shù)據(jù)類型之外對象的拷貝的。
定義:被復(fù)制的對象所有的變量都含有與原來對象相同的值,所有的對其他對象的引用也都指向復(fù)制過的新的對象(即原始的對象和其副本引用不同對象)。
這里我們自己實現(xiàn)深拷貝,讓原始對象和其副本對象指向不同對象。
其實代碼和淺拷貝大致相同,差別只是在Report類重寫的clone方法,這里我就多帶帶拿出來寫下了。
... @Override public Report clone() { Report msg = null; try { msg = (Report) super.clone(); // 將引用對象 content 也 clone下 msg.content = (ArrayList) this.content.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return msg; } ...
測試的主方法同淺拷貝,不做修改直接運行,運行的結(jié)果如下:
----------UI小姐姐工作周報-------------- Report [title=yg工作周報, sender=yg, receiver=boss, content=[1.參加研討會議, 確定需求, 2.設(shè)計首頁的icon以及界面]] ----------我的工作周報-------------- Report [title=zhh工作周報, sender=zhh, receiver=boss, content=[1.參加研討會議, 確定需求, 2.搭建基礎(chǔ)的后臺框架]]
這里你可以看到,拷貝后兩者的內(nèi)容之間并沒有相互的影響。
擴(kuò)展深拷貝除了上述實現(xiàn)方式外,也可以用序列化來實現(xiàn)。
/** * 序列化實現(xiàn)深拷貝 * @author zhh * @date 2017-08-23 下午1:04:44 */ class Report implements Serializable { private static final long serialVersionUID = -760030405417987698L; private String title; // 標(biāo)題 private String sender; // 發(fā)送者 private String receiver; // 接收者 private ArrayListcontent; //內(nèi)容 public Report(String title, String sender, String receiver) { this.title = title; this.sender = sender; this.receiver = receiver; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getSender() { return sender; } public void setSender(String sender) { this.sender = sender; } public String getReceiver() { return receiver; } public void setReceiver(String receiver) { this.receiver = receiver; } public ArrayList getContent() { return content; } public void setContent(ArrayList content) { this.content = content; } public void print() { System.out.println(this); } @Override public Report clone() { ByteArrayOutputStream bos = null; ObjectOutputStream oos = null; ByteArrayInputStream bis = null; ObjectInputStream ois = null; try { // 1.將對象序列化成流 bos = new ByteArrayOutputStream(); oos = new ObjectOutputStream(bos); oos.writeObject(this); // 2.將流序列化成對象 bis = new ByteArrayInputStream(bos.toByteArray());; ois = new ObjectInputStream(bis); return (Report) ois.readObject(); } catch (Exception e) { e.printStackTrace(); return null; } } @Override public String toString() { return "Report [title=" + title + ", sender=" + sender + ", receiver=" + receiver + ", content=" + content + "]"; } }
測試的主方法同淺拷貝,不做修改直接運行,運行的結(jié)果如下:
----------UI小姐姐工作周報-------------- Report [title=yg工作周報, sender=yg, receiver=boss, content=[1.參加研討會議, 確定需求, 2.設(shè)計首頁的icon以及界面]] ----------我的工作周報-------------- Report [title=zhh工作周報, sender=zhh, receiver=boss, content=[1.參加研討會議, 確定需求, 2.搭建基礎(chǔ)的后臺框架]]
事實證明,用序列化來實現(xiàn)對象的深拷貝也是可行的。主要原因是在對象序列化流的過程當(dāng)中,寫在流里面的是對象的一個拷貝,而原本的對象仍然存在堆內(nèi)。
序列化實現(xiàn)深拷貝過程中,我們實現(xiàn)了Serializable這個空接口,來標(biāo)明Report類可序列化。
這里要說一下 為什么要給 serialVersionUID 賦值
舉個例子,當(dāng)對象序列化存到硬盤上后,比方我修改了這個對象的屬性,那么在反序列化的過程就會出現(xiàn)異常。
一旦我們給 serialVersionUID 賦值,當(dāng)序列化和反序列化的 serialVersionUID 相同的時候,中間過程修改對象屬性就不會像上面拋出異常,而是以屬性的對應(yīng)類型賦默認(rèn)值(如String類型默認(rèn)為null,int類型默認(rèn)為0等等)
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/67832.html
摘要:展開語法木易楊通過代碼可以看出實際效果和是一樣的。木易楊可以看出,改變之后的值并沒有發(fā)生變化,但改變之后,相應(yīng)的的值也發(fā)生變化。深拷貝使用場景木易楊完全改變變量之后對沒有任何影響,這就是深拷貝的魔力。木易楊情況下,轉(zhuǎn)換結(jié)果不正確。 一、賦值(Copy) 賦值是將某一數(shù)值或?qū)ο筚x給某個變量的過程,分為下面 2 部分 基本數(shù)據(jù)類型:賦值,賦值之后兩個變量互不影響 引用數(shù)據(jù)類型:賦址,兩個...
摘要:說明外層數(shù)組拷貝的是實例說明元素拷貝是引用深拷貝在堆中重新分配內(nèi)存,并且把源對象所有屬性都進(jìn)行新建拷貝,拷貝后的對象與原來的對象完全隔離,互不影響。中的方法可以實現(xiàn)深拷貝,源碼原理也是遞歸使用淺拷貝。 1.淺拷貝 當(dāng)把數(shù)組或?qū)ο蠛唵钨x值給其他變量的時候,實際上進(jìn)行的是淺拷貝,淺拷貝是拷貝引用,只是將拷貝后的引用指向同一個對象實例,彼此間的操作還會互相影響。 分為兩種情況:直接拷貝源對象...
摘要:而大多數(shù)實際項目中,我們想要的結(jié)果是兩個變量初始值相同互不影響。所以就要使用到拷貝分為深淺兩種深淺拷貝的區(qū)別淺拷貝只復(fù)制一層對象的屬性,而深拷貝則遞歸復(fù)制了所有層級。 為什么會用到淺拷貝和深拷貝 首先來看一下如下代碼 let a = b = 2 a = 3 console.log(a) console.log(b) let c = d = [1,2,3] let e = f = {a:...
摘要:而大多數(shù)實際項目中,我們想要的結(jié)果是兩個變量初始值相同互不影響。所以就要使用到拷貝分為深淺兩種深淺拷貝的區(qū)別淺拷貝只復(fù)制一層對象的屬性,而深拷貝則遞歸復(fù)制了所有層級。 為什么會用到淺拷貝和深拷貝 首先來看一下如下代碼 let a = b = 2 a = 3 console.log(a) console.log(b) let c = d = [1,2,3] let e = f = {a:...
摘要:而大多數(shù)實際項目中,我們想要的結(jié)果是兩個變量初始值相同互不影響。所以就要使用到拷貝分為深淺兩種深淺拷貝的區(qū)別淺拷貝只復(fù)制一層對象的屬性,而深拷貝則遞歸復(fù)制了所有層級。 為什么會用到淺拷貝和深拷貝 首先來看一下如下代碼 let a = b = 2 a = 3 console.log(a) console.log(b) let c = d = [1,2,3] let e = f = {a:...
閱讀 3285·2023-04-25 20:35
閱讀 3632·2019-08-30 15:54
閱讀 2017·2019-08-30 15:43
閱讀 2200·2019-08-29 15:14
閱讀 1906·2019-08-29 11:17
閱讀 3400·2019-08-26 13:36
閱讀 722·2019-08-26 10:15
閱讀 2861·2019-08-23 15:41