java对象克隆详解
创始人
2024-05-24 07:25:45
0

概述: 当我们new一个对象时,其中的属性就会被初始化, 那么想要保存刚开始初始化的值就靠clone方法来实现, 平时我们最常见的是一个对象的引用指向另一个对象,并不是创建了两个对象.

 Person p1 = new Person(100,"jim");Person p2 = p1;System.out.println(p1==p2);//true

克隆肯定是创建了两个对象

 Person p1 = new Person(100,"jim");Person p2 =p1.clone();//克隆的新对象System.out.println(p1==p2);//false

克隆分为浅克隆(ShallowClone)和深克隆(DeepClone)。

在 Java 语言中,数据类型分为值类型(基本数据类型)和引用类型,值类型包括 int、double、byte、boolean、char 等简单数据类型,引用类型包括类、接口、数组等复杂类型。基本类型的值可以直接复制,引用类型只能复制引用地址。所以浅克隆和深克隆的主要区别在于是否支持引用类型的成员变量的复

制.

浅克隆

在浅克隆中,如果原型对象的成员变量是值类型,将复制一份给克隆对象;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。简单来说,在浅克隆中,当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制。

实现方式:

1.在 Java 语言中,通过覆盖 Object 类的 clone()方法可以实现浅克隆。

2.在 spring 框架中提供 BeanUtils.copyProperties(source,target);

这里我们主要演示通过重写object中clone方法来实现

1.首先定义一个类(需要被克隆的类)

public class Person implements  Cloneable{int num;String name;Address address;public Person() {}public Person(int num, String name) {this.num = num;this.name = name;}public int getNum() {return num;}public void setNum(int num) {this.num = num;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Address getAddress() {return address;}public void setAddress(Address address) {this.address = address;}@Overrideprotected Person clone() throws CloneNotSupportedException {Person person = (Person)super.clone();return person;}@Overridepublic String toString() {return "Person{" +"num=" + num +", name='" + name + '\'' +", address=" + address +'}';}
}

2.可以看到Person类关联着Address类,也写出来

public class Address  {String  address;public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}@Overridepublic String toString() {return "Address{" +"address='" + address + '\'' +'}';}
}

3.写一个Test类进行测试

public class Test {public static void main(String[] args) throws CloneNotSupportedException {Address address = new Address();address.setAddress("汉中");Person p1 = new  Person(100,"jim");p1.setAddress(address);Person p2 =p1.clone();p2.setName("tom");address.setAddress("西安");System.out.println(p1); // jim   西安System.out.println(p2);// tom    西安}
}

首先看name属性,p1的name为jim,克隆出另一个对象p2,将name改成了tom,因为是两个对象,所以输出的结果分别都不同.

再看关联对象, 首先将有汉中信息的address加入到了p1中,所以目前p1中的address是汉中,经过克隆出p2后,其实对于address来说只克隆了地址,所以说p1和p2指向的都是同一个address,所以都是汉中,再经过一次修改成了西安,所以都是西安.

所以说浅克隆只是克隆了引用类型变量的地址.

深克隆

在深克隆中,无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象,深克隆将原型对象的所有引用对象也复制一份给克隆对象。简单来说,在深克隆中,除了对象本身被复制外,对象所包含的所有成员变量也将复制

在 Java 语言中,如果需要实现深克隆,可以通过覆盖 Object 类的 clone()方法实现,也可以通过序列化(Serialization)等方式来实现。序列化就是将对象写到流的过程,写到流中的对象是原有对象的一个拷贝,而原对象仍然存在于内存中。通过序列化实现的拷贝不仅可以复制对象本身,而且可以复制其引用的成员对象,因此通过序列化将对象写到一个流中,再从流里将其读出来,可以实现深克隆。需要注意的是能够实现序列化的对象其类必须实现Serializable 接口,否则无法实现序列化操作

1.重写Object类中的clone方法

1.首先定义一个类(需要被克隆的类),相比于上面的浅克隆增加了一行克隆address对象的代码

public class Person implements  Cloneable{int num;String name;Address address;public Person() {}public Person(int num, String name) {this.num = num;this.name = name;}public int getNum() {return num;}public void setNum(int num) {this.num = num;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Address getAddress() {return address;}public void setAddress(Address address) {this.address = address;}@Overrideprotected Person clone() throws CloneNotSupportedException {Person person = (Person)super.clone();person.address = (Address)address.clone();   //深度复制  联同person中关联的对象也一同克隆.return person;}@Overridepublic String toString() {return "Person{" +"num=" + num +", name='" + name + '\'' +", address=" + address +'}';}
}

2.Address写出来,相比于上面浅克隆多了一个重写Object类的clone方法

public class Address  {String  address;public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}@Overridepublic String toString() {return "Address{" +"address='" + address + '\'' +'}';}@Overrideprotected Address clone() throws CloneNotSupportedException {return (Address)super.clone();}
}

3.还是Test测试类

public class Test {public static void main(String[] args) throws CloneNotSupportedException {Address address = new Address();address.setAddress("汉中");Person p1 = new  Person(100,"jim");p1.setAddress(address);Person p2 =p1.clone();p2.setName("tom");address.setAddress("西安");System.out.println(p1); // jim   西安System.out.println(p2);// tom    汉中}
}

这次的结果会有所不同, 因为深克隆不仅克隆了自己, 还克隆了关联着的类的对象, 所以说原来的p1存储的汉中被克隆在了p2中,而最后一行更改为西安的是原来的address对象, p2中已经克隆了原来的address并且保存了下来

2.序列化(Serialization)的方式

如果需要被克隆的类中关联的其他类的对象太多, 那么继续用深克隆的话需要耗费大量的时间去一个一个克隆关联着的对象, 而序列化的方式可以将该类中所有关联的对象化成流从而高校的进行克隆.

1.还是创建一个关联着被克隆的类的对象

public class Address  implements Serializable {String  address;public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}@Overridepublic String toString() {return "Address{" +"address='" + address + '\'' +'}';}}

2.Person类,里面写一个自己的序列化方式的克隆方法

public class Person implements Serializable {int num;String name;Address address;public Person() {}public Person(int num, String name) {this.num = num;this.name = name;}public int getNum() {return num;}public void setNum(int num) {this.num = num;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Address getAddress() {return address;}public void setAddress(Address address) {this.address = address;}/*** 自定义克隆方法* @return*/public Person myclone() {Person person = null;try { // 将该对象序列化成流,因为写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。所以利用这个特性可以实现对象的深拷贝ByteArrayOutputStream baos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(baos);oos.writeObject(this);// 将流序列化成对象ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());ObjectInputStream ois = new ObjectInputStream(bais);person = (Person) ois.readObject();} catch (IOException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}return person;}@Overridepublic String toString() {return "Person{" +"num=" + num +", name='" + name + '\'' +", address=" + address +'}';}
}

3.Test类测试

public class Test {public static void main(String[] args) throws CloneNotSupportedException {Address address = new Address();address.setAddress("汉中");Person p1 = new  Person(100,"jim");p1.setAddress(address);Person p2 =p1.myclone();p2.setName("tom");address.setAddress("西安");System.out.println(p1);jim   西安System.out.println(p2);tom   汉中}
}

所以说, 得看具体情况进行选择

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
一帆风顺二龙腾飞三阳开泰祝福语... 本篇文章极速百科给大家谈谈一帆风顺二龙腾飞三阳开泰祝福语,以及一帆风顺二龙腾飞三阳开泰祝福语结婚对应...
美团联名卡审核成功待激活(美团... 今天百科达人给各位分享美团联名卡审核成功待激活的知识,其中也会对美团联名卡审核未通过进行解释,如果能...