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