最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java Clone深拷貝與淺拷貝的兩種實(shí)現(xiàn)方法

 更新時(shí)間:2018年10月22日 14:33:07   作者:李學(xué)凱  
今天小編就為大家分享一篇關(guān)于Java Clone深拷貝與淺拷貝的兩種實(shí)現(xiàn)方法,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧

1.首先,你要知道怎么實(shí)現(xiàn)克隆:實(shí)現(xiàn)Cloneable接口,在bean里面重寫(xiě)clone()方法,權(quán)限為public。
2.其次,你要大概知道什么是地址傳遞,什么是值傳遞。
3.最后,你要知道你為什么使用這個(gè)clone方法。

先看第一條,簡(jiǎn)單的克隆代碼的實(shí)現(xiàn)。這個(gè)也就是我們?cè)跊](méi)了解清楚這個(gè)Java的clone的時(shí)候,會(huì)出現(xiàn)的問(wèn)題。

看完代碼,我再說(shuō)明這個(gè)時(shí)候的問(wèn)題。

先看我要克隆的學(xué)生bean的代碼:

package com.lxk.model;
/**
 * 學(xué)生類(lèi):有2個(gè)屬性:1,基本屬性-String-name;2,引用類(lèi)型-Car-car。
 * <p>
 * Created by lxk on 2017/3/23
 */
public class Student implements Cloneable {
 private String name;
 private Car car;
 public String getName() {
 return name;
 }
 public void setName(String name) {
 this.name = name;
 }
 public Car getCar() {
 return car;
 }
 public void setCar(Car car) {
 this.car = car;
 }
 @Override
 public String toString() {
 return "Student{" +
 "name='" + name + '\'' +
 ", car=" + car +
 '}';
 }
 @Override
 public Student clone() {
 Student student = null;
 try {
 student = (Student) super.clone();
 } catch (CloneNotSupportedException ignored) {
 System.out.println(ignored.getMessage());
 }
 return student;
 }
}

學(xué)生內(nèi)部引用了Car這個(gè)bean

package com.lxk.model;
import java.util.List;
public class Car implements Comparable<Car> {
 private String sign;
 private int price;
 private List<Dog> myDog;
 private List<String> boys;
 public Car() {
 }
 public Car(String sign, int price) {
 this.sign = sign;
 this.price = price;
 }
 public Car(String sign, int price, List<Dog> myDog) {
 this.sign = sign;
 this.price = price;
 this.myDog = myDog;
 }
 public Car(String sign, int price, List<Dog> myDog, List<String> boys) {
 this.sign = sign;
 this.price = price;
 this.myDog = myDog;
 this.boys = boys;
 }
 public String getSign() {
 return sign;
 }
 public void setSign(String sign) {
 this.sign = sign;
 }
 public int getPrice() {
 return price;
 }
 public void setPrice(int price) {
 this.price = price;
 }
 public List<Dog> getMyDog() {
 return myDog;
 }
 public void setMyDog(List<Dog> myDog) {
 this.myDog = myDog;
 }
 public List<String> getBoys() {
 return boys;
 }
 public void setBoys(List<String> boys) {
 this.boys = boys;
 }
 @Override
 public int compareTo(Car o) {
 //同理也可以根據(jù)sign屬性排序,就不舉例啦。
 return this.getPrice() - o.getPrice();
 } 
 @Override
 public String toString() {
 return "Car{" +
 "sign='" + sign + '\'' +
 ", price=" + price +
 ", myDog=" + myDog +
 ", boys=" + boys +
 '}';
 }
}

最后就是main測(cè)試類(lèi)

package com.lxk.findBugs;
import com.lxk.model.Car;
import com.lxk.model.Student;
/**
 * 引用傳遞也就是地址傳遞需要注意的地方,引起的bug
 * <p>
 * Created by lxk on 2017/3/23
 */
public class Bug2 {
 public static void main(String[] args) {
 Student student1 = new Student();
 Car car = new Car("oooo", 100);
 student1.setCar(car);
 student1.setName("lxk");
 //克隆完之后,student1和student2應(yīng)該沒(méi)關(guān)系的,修改student1不影響student2的值,但是完之后發(fā)現(xiàn),你修改car的值,student2也受影響啦。
 Student student2 = student1.clone();
 System.out.println("學(xué)生2:" + student2);//先輸出student2剛剛克隆完之后的值,然后在修改student1的相關(guān)引用類(lèi)型的屬性值(car)和基本屬性值(name)
 car.setSign("X5");
 student1.setName("xxx");
 System.out.println("學(xué)生2:" + student2);//再次輸出看修改的結(jié)果
 }
}

之后就該是執(zhí)行的結(jié)果圖了:

對(duì)上面執(zhí)行結(jié)果的疑惑,以及解釋說(shuō)明:

我們可能覺(jué)得自己在bean里面實(shí)現(xiàn)clone接口,重寫(xiě)了這個(gè)clone方法,那么學(xué)生2是經(jīng)由學(xué)生1clone,復(fù)制出來(lái)的,
那么學(xué)生1和學(xué)生2,應(yīng)該是毫不相干的,各自是各自,然后,在修改學(xué)生1的時(shí)候,學(xué)生2是不會(huì)受影響的。

但是結(jié)果,不盡人意。從上圖執(zhí)行結(jié)果可以看出來(lái),除了名字,這個(gè)屬性是沒(méi)有被學(xué)生1影響,關(guān)于car的sign屬性已經(jīng)因?yàn)閷W(xué)生1的變化而變化,這不是我希望的結(jié)果。

可見(jiàn),這個(gè)簡(jiǎn)單的克隆實(shí)現(xiàn)也僅僅是個(gè)“淺克隆”,也就是基本類(lèi)型數(shù)據(jù),他是會(huì)給你重新復(fù)制一份新的,但是引用類(lèi)型的,他就不會(huì)重新復(fù)制份新的。引用類(lèi)型包括,上面的其他bean的引用,list集合,等一些引用類(lèi)型。

那么怎么實(shí)現(xiàn)深克隆呢?

對(duì)上述代碼稍作修改,如下:
學(xué)生bean的clone重寫(xiě)方法如下所示:

 @Override
 public Student clone() {
 Student student = null;
 try {
 student = (Student) super.clone();
 if (car != null) {
 student.setCar(car.clone());
 }
 } catch (CloneNotSupportedException ignored) {
 System.out.println(ignored.getMessage());
 }
 return student;
 }

然后還要Car類(lèi)實(shí)現(xiàn)cloneable接口,復(fù)寫(xiě)clone方法:

 @Override
 public Car clone() {
 Car car = null;
 try {
 car = (Car) super.clone();
 if (myDog != null) {
 car.setMyDog(Lists.newArrayList(myDog));
 }
 if (boys != null) {
 car.setBoys(Lists.newArrayList(boys));
 }
 } catch (CloneNotSupportedException ignored) {
 System.out.println(ignored.getMessage());
 }
 return car;
 }

主測(cè)試代碼不動(dòng),這個(gè)時(shí)候的執(zhí)行結(jié)果如下:

可以看到,這個(gè)時(shí)候,你再修改學(xué)生1的值,就不會(huì)影響到學(xué)生2的值,這才是真正的克隆,也就是所謂的深克隆。

怎么舉一反三?

可以看到,這個(gè)例子里面的引用類(lèi)型就一個(gè)Car類(lèi)型的屬性,但是實(shí)際開(kāi)發(fā)中,除了這個(gè)引用其他bean類(lèi)型的屬性外,可能還要list類(lèi)型的屬性值用的最多。

那么要怎么深克隆呢,就像我在Car bean類(lèi)里面做的那樣,把所有的引用類(lèi)型的屬性,都在clone一遍。那么你在最上層調(diào)用這個(gè)clone方法的時(shí)候,他就是真的深克隆啦。

我代碼里面那么判斷是為了避免空指針異常。當(dāng)然,這個(gè)你也得注意咯。

注意 重寫(xiě)clone方法的時(shí)候,里面各個(gè)屬性的null的判斷哦。

上面的是override clone()方法來(lái)實(shí)現(xiàn)深克隆的。如果你這個(gè)要克隆的對(duì)象很復(fù)雜的話(huà),你就不得不去每個(gè)引用到的對(duì)象去復(fù)寫(xiě)這個(gè)clone方法,這個(gè)太啰嗦來(lái),改的地方,太多啦。

還有個(gè)方法就是使用序列化來(lái)實(shí)現(xiàn)這個(gè)深拷貝

 /**
 * 對(duì)象的深度克隆,此處的對(duì)象涉及Collection接口和Map接口下對(duì)象的深度克隆
 * 利用序列化和反序列化的方式進(jìn)行深度克隆對(duì)象
 *
 * @param object 待克隆的對(duì)象
 * @param <T> 待克隆對(duì)象的數(shù)據(jù)類(lèi)型
 * @return 已經(jīng)深度克隆過(guò)的對(duì)象
 */
 public static <T extends Serializable> T deepCloneObject(T object) {
 T deepClone = null;
 ByteArrayOutputStream baos = null;
 ObjectOutputStream oos = null;
 ByteArrayInputStream bais = null;
 ObjectInputStream ois = null;
 try {
 baos = new ByteArrayOutputStream();
 oos = new ObjectOutputStream(baos);
 oos.writeObject(object);
 bais = new ByteArrayInputStream(baos
  .toByteArray());
 ois = new ObjectInputStream(bais);
 deepClone = (T)ois.readObject();
 } catch (IOException | ClassNotFoundException e) {
 e.printStackTrace();
 } finally {
 try {
 if(baos != null) {
  baos.close();
 }
 } catch (IOException e) {
 e.printStackTrace();
 }
 try {
 if(oos != null) {
  oos.close();
 }
 } catch (IOException e) {
 e.printStackTrace();
 }
 try{
 if(bais != null) {
  bais.close();
 }
 } catch (IOException e) {
 e.printStackTrace();
 }
 try{
 if(ois != null) {
  ois.close();
 }
 } catch (IOException e) {
 e.printStackTrace();
 }
 }
 return deepClone;
 }

具體的使用如下:

 /**
 * 使用序列化來(lái)實(shí)現(xiàn)深拷貝簡(jiǎn)單。但是,所涉及到的所有對(duì)象都的實(shí)現(xiàn)序列化接口。
 */
 private static void cloneBySerializable() {
 Student student1 = new Student();
 Car car = new Car("oooo", 100, Lists.newArrayList(new Dog("aaa", true, true)));
 student1.setCar(car);
 student1.setName("lxk");
 Student student2 = deepCloneObject(student1);
 System.out.println("學(xué)生2:" + student2);
 car.setSign("X5");
 car.setMyDog(null);
 student1.setName("xxx");
 System.out.println("學(xué)生2:" + student2);
 }

實(shí)現(xiàn)的效果,還是和上面的一樣的,但是這個(gè)就簡(jiǎn)單多來(lái),只需要給涉及到的每個(gè)引用類(lèi)型,都去實(shí)現(xiàn)序列化接口就好啦。

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)腳本之家的支持。如果你想了解更多相關(guān)內(nèi)容請(qǐng)查看下面相關(guān)鏈接

相關(guān)文章

  • SpringSecurity rememberme功能實(shí)現(xiàn)過(guò)程解析

    SpringSecurity rememberme功能實(shí)現(xiàn)過(guò)程解析

    這篇文章主要介紹了SpringSecurity rememberme功能實(shí)現(xiàn)過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-03-03
  • Java介紹多線(xiàn)程計(jì)算階乘實(shí)現(xiàn)方法

    Java介紹多線(xiàn)程計(jì)算階乘實(shí)現(xiàn)方法

    這篇文章主要為大家詳細(xì)介紹了Java多線(xiàn)程計(jì)算階乘的實(shí)現(xiàn),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • java實(shí)現(xiàn)后臺(tái)返回base64圖形編碼

    java實(shí)現(xiàn)后臺(tái)返回base64圖形編碼

    這篇文章主要介紹了java實(shí)現(xiàn)后臺(tái)返回base64圖形編碼,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • Java實(shí)現(xiàn)計(jì)算一個(gè)月有多少天和多少周

    Java實(shí)現(xiàn)計(jì)算一個(gè)月有多少天和多少周

    這篇文章主要介紹了Java實(shí)現(xiàn)計(jì)算一個(gè)月有多少天和多少周,本文直接給出實(shí)例代碼,需要的朋友可以參考下
    2015-06-06
  • IDEA的.idea文件夾和iml文件使用方式

    IDEA的.idea文件夾和iml文件使用方式

    這篇文章主要介紹了IDEA的.idea文件夾和iml文件使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • 如何通過(guò)一張圖搞懂springBoot自動(dòng)注入原理

    如何通過(guò)一張圖搞懂springBoot自動(dòng)注入原理

    這篇文章主要給大家介紹了關(guān)于如何通過(guò)一張圖搞懂springBoot自動(dòng)注入原理的相關(guān)資料,文中通過(guò)圖文以及實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2022-02-02
  • Java中IO的NIO通道解析

    Java中IO的NIO通道解析

    這篇文章主要介紹了Java中IO的NIO通道解析,NIO 提供了與傳統(tǒng) BIO 模型中的 Socket 和 ServerSocket 相對(duì)應(yīng)的 SocketChannel 和 ServerSocketChannel 兩種不同的套接字通道實(shí)現(xiàn),需要的朋友可以參考下
    2024-01-01
  • 深入理解Java原生的序列化機(jī)制

    深入理解Java原生的序列化機(jī)制

    Java 提供了一種對(duì)象序列化的機(jī)制,該機(jī)制中,一個(gè)對(duì)象可以被表示為一個(gè)字節(jié)序列,該字節(jié)序列包括該對(duì)象的數(shù)據(jù)、有關(guān)對(duì)象的類(lèi)型的信息和存儲(chǔ)在對(duì)象中數(shù)據(jù)的類(lèi)型。下面小編和大家來(lái)一起學(xué)習(xí)一下吧
    2019-06-06
  • SpringBoot使用WebJars統(tǒng)一管理靜態(tài)資源的方法

    SpringBoot使用WebJars統(tǒng)一管理靜態(tài)資源的方法

    這篇文章主要介紹了SpringBoot使用WebJars統(tǒng)一管理靜態(tài)資源的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-12-12
  • Java中的@builder建造者模式詳細(xì)解析

    Java中的@builder建造者模式詳細(xì)解析

    這篇文章主要介紹了Java中的@builder建造者模式詳細(xì)解析,使用 @Builder 注解可以簡(jiǎn)化手動(dòng)編寫(xiě)建造者模式的代碼,使代碼更加簡(jiǎn)潔易讀,它可以自動(dòng)生成鏈?zhǔn)秸{(diào)用的方法來(lái)設(shè)置對(duì)象的屬性,并且可以在需要時(shí)進(jìn)行可選屬性的設(shè)置,需要的朋友可以參考下
    2024-01-01

最新評(píng)論

汤阴县| 金塔县| 呈贡县| 依安县| 浦江县| 桐柏县| 延长县| 教育| 东宁县| 洛川县| 盐城市| 京山县| 元阳县| 拉萨市| 广元市| 年辖:市辖区| 乐业县| 石狮市| 河西区| 花莲县| 南靖县| 彩票| 株洲县| 诸城市| 海南省| 灵台县| 社旗县| 荆州市| 阜平县| 宜君县| 佛山市| 辉南县| 隆安县| 资阳市| 老河口市| 高台县| 临澧县| 鹤峰县| 桃园县| 永宁县| 建湖县|