<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
一般情況下,物件通過new 關鍵字建立,首先會在堆上給物件分配空間,然後執行建構函式進行一系列的初始化,在分配的記憶體空間上為一眾屬性賦值;完成初始化後再將堆區物件的參照傳遞給棧區,最終參與程式的執行。
呼叫Java.lang.Class
或者java.lang.reflect.Constructor
類的newInstance()
實體方法。
它分為深拷貝和淺拷貝,通過呼叫物件的 clone
方法,進行物件的克隆(拷貝)
我們可以看到 clone 方法的定義:
native
方法protected
修飾,那麼,它的存取許可權就只有它的子類或者[同包:java.lang.*];為了測試 clone 我定義了兩個類:
使用者資訊:UserBean
package com.bokerr.canaltask.po; import lombok.AllArgsConstructor; import lombok.Data; import java.io.Serializable; @Data @AllArgsConstructor public class UserBean implements Cloneable, Serializable { private static final long serialVersionUID = 2022010901L; /** 基本型別、不可變型別 */ private int age; private int sex; private String name; private String home; /** 參照型別 */ private SubInfo subInfo; public UserBean(){} /*** * 重寫 clone 為 public 讓任意物件都有 clone的存取許可權 * */ @Override public UserBean clone(){ UserBean clone = null; try{ clone = (UserBean) super.clone(); } catch (CloneNotSupportedException e){ e.printStackTrace(); } return clone; } }
附加資訊類:SubInfo
package com.bokerr.canaltask.po; import lombok.AllArgsConstructor; import lombok.Data; import java.io.Serializable; @Data @AllArgsConstructor public class SubInfo implements Cloneable, Serializable { private static final long serialVersionUID = 2022010902L; /** * SubInfo 類的屬性都是基本型別、不可變物件(String) * */ private String work; private Integer salary; private int idNum; public SubInfo(){} /** * 此處通過public 方法對外提供物件clone方法存取許可權 * */ public SubInfo selfClone(){ try{ return (SubInfo) clone(); } catch (CloneNotSupportedException e){ e.printStackTrace(); } return null; } /*@Override public SubInfo clone(){ try{ return (SubInfo) super.clone(); } catch (CloneNotSupportedException e){ e.printStackTrace(); } return null; }*/ }
我們需要知道,單純呼叫一個物件的clone方法,它進行的是:"淺表複製",當物件的屬性都是基本型別或者不可變(final)型別時是沒有問題的;但是存在可變物件參照時,對它的拷貝並不是一個深層的拷貝,它只拷貝該物件的參照,這樣就會造成原物件和克隆物件的修改,都會反映到該參照物件上。
關於淺拷貝看如下測試程式碼:
package com.bokerr.canaltask.workerrun; import com.bokerr.canaltask.po.SubInfo; import com.bokerr.canaltask.po.UserBean; public class ExecuteTest { public static void main(String[] args){ UserBean userBean1 = new UserBean(); userBean1.setAge(25); userBean1.setSex(1); userBean1.setName("bokerr"); userBean1.setHome("貴州銅仁"); SubInfo subInfo1 = new SubInfo(); subInfo1.setIdNum(3423); subInfo1.setSalary(Integer.valueOf(15000)); subInfo1.setWork("coder"); userBean1.setSubInfo(subInfo1); System.out.println("userBean-orign:" + userBean1); UserBean userBean2 = userBean1.clone(); userBean2.setHome("貴州貴陽"); userBean2.setAge(26); SubInfo subInfo2 = userBean2.getSubInfo(); subInfo2.setSalary(Integer.valueOf(25000)); subInfo2.setWork("manager"); subInfo2.setIdNum(100002); System.out.println("######################"); System.out.println("userBean-orign:" + userBean1); System.out.println("userBean-clone:" + userBean2); } }
UserBean
的 clone
方法定義如下,我們可以看見它只呼叫了super.clone 而對 super.clone 的返回值沒做任何修改:
@Override public UserBean clone(){ UserBean clone = null; try{ clone = (UserBean) super.clone(); } catch (CloneNotSupportedException e){ e.printStackTrace(); } return clone; }
輸出如下,結合測試code,我們可以發現,克隆得到的物件對 SubInfo 的修改同樣體現到了原物件參照的 SubInfo 物件上,因為呼叫 super.clone 只是一個 "淺表複製"
userBean-orign:UserBean(age=25, sex=1, name=bokerr, home=貴州銅仁, subInfo=SubInfo(work=coder, salary=15000, idNum=3423)) ###################### userBean-orign:UserBean(age=25, sex=1, name=bokerr, home=貴州銅仁, subInfo=SubInfo(work=manager, salary=25000, idNum=100002)) userBean-clone:UserBean(age=26, sex=1, name=bokerr, home=貴州貴陽, subInfo=SubInfo(work=manager, salary=25000, idNum=100002))
深拷貝生成的物件,必須擁有完全獨立的物件記憶體空間,拷貝物件和原物件上的修改,都不會影響對方;
前邊提到通過super.clone
呼叫 Object
上的clone
方法實際上進行的只是一個淺拷貝
為了實現深拷貝我們則必須修改 UserBean 的clone 方法:
/*** * 重寫 clone 為 public 讓任意物件都有 clone的存取許可權 * */ @Override public UserBean clone(){ UserBean clone = null; try{ clone = (UserBean) super.clone(); /** SubInfo.selfClone() 提供SubInfo物件clone()方法許可權 */ /** 克隆可變參照物件 SubInfo,並賦值給 super.clone() 返回的:UserBean 完成深拷貝 */ SubInfo cloneSub = this.subInfo.selfClone(); clone.setSubInfo(cloneSub); } catch (CloneNotSupportedException e){ e.printStackTrace(); } return clone; }
實際上除此之外,測試程式碼一成不變,然後我們來看現在的輸出,可以發現對克隆物件的參照物件:SubInfo 的修改,並未使原物件的SubInfo變化
userBean-orign:UserBean(age=25, sex=1, name=bokerr, home=貴州銅仁, subInfo=SubInfo(work=coder, salary=15000, idNum=3423)) ###################### userBean-orign:UserBean(age=25, sex=1, name=bokerr, home=貴州銅仁, subInfo=SubInfo(work=coder, salary=15000, idNum=3423)) userBean-clone:UserBean(age=26, sex=1, name=bokerr, home=貴州貴陽, subInfo=SubInfo(work=manager, salary=25000, idNum=100002))
此時問題來了:你可能會說假如我的物件進行了多層參照呢,且參照了多個物件該怎麼辦呢?那我只能一個一個去重寫 clone 方法麼?
是的你如果使用 clone 方法可能,你確實需要那樣去處理。
假如,存在如下以物件A為根節點的參照關係:
A -> B C -> E -> F G -> G D -> H I -> J -> K
我相信處理深拷貝的人會瘋掉的。。。。
那麼有更省事的方法麼? 當然有,那就是下一節提到的反序列化。
Serializable
介面的類才能被序列化,官方建議自定義一個SerialversionUID
,若使用者沒有自定義SerialversionUID那麼會生成預設值;序列化和反序列化就是通過對比其SerialversionUID
來進行的,一旦SerialversionUID不匹配,反序列化就無法成功java.io.NotSerializableException]
static
和transient
修飾的變數不會被序列化,可以理解為:static是類屬性存在於方法區而不在堆區;transient常用於修飾涉及安全的資訊,它只能和Serializable介面一起使用,比如:使用者密碼,不應該讓它序列化之後在網路上傳輸。參考程式碼:
package com.bokerr.canaltask.workerrun; import com.bokerr.canaltask.po.NoCloneInfo; import com.bokerr.canaltask.po.SubInfo; import com.bokerr.canaltask.po.UserBean; import org.apache.commons.lang3.SerializationUtils; import java.util.Arrays; public class ExecuteTest { public static void main(String[] args){ UserBean userBean1 = new UserBean(); userBean1.setAge(25); userBean1.setSex(1); userBean1.setName("bokerr"); userBean1.setHome("貴州銅仁"); SubInfo subInfo1 = new SubInfo(); subInfo1.setIdNum(3423); subInfo1.setSalary(Integer.valueOf(15000)); subInfo1.setWork("coder"); userBean1.setSubInfo(subInfo1); System.out.println("序列化前" + userBean1); /** 物件序列化為二進位制位元組序列 */ byte[] serializeBytes = SerializationUtils.serialize(userBean1); /** 反序列化還原為Java物件 */ UserBean userBeanSer = SerializationUtils.deserialize(serializeBytes); userBeanSer.getSubInfo().setSalary(800000); userBeanSer.getSubInfo().setWork("CTO"); System.out.println("反序列化" + userBeanSer); System.out.println(userBean1 == userBeanSer); } }
輸出:
序列化前UserBean(age=25, sex=1, name=bokerr, home=貴州銅仁, subInfo=SubInfo(work=coder, salary=15000, idNum=3423))
反序列化UserBean(age=25, sex=1, name=bokerr, home=貴州銅仁, subInfo=SubInfo(work=CTO, salary=800000, idNum=3423))
false
我們可以發現最終輸出了:subInfo.work=CTO subInfo.salary=800000
,對反序列化得到的物件參照的SubInfo物件的修改,並未影響到原物件,所以可以通過反序列化進行物件的深拷貝。
PS*有一說一:lombok 是真的好用,雖然 set、get 方法可以自動生成,但是用 lombok後明顯程式碼更簡潔了 ;
commons-lang3
它是apache的一個工具包,我用的序列化工具來自它。
可能有小夥伴不瞭解,我還是貼一下Maven依賴吧,雖然我知道大家都人均大佬了。
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> <scope>compile</scope> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.12.0</version> </dependency>
到此這篇關於JAVA 物件建立與物件克隆的文章就介紹到這了,更多相關JAVA 物件的建立與克隆內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45