目录
一、浅拷贝
浅拷贝由对象实现cloneable接口和重写clone方法,然后调用一次内部不做改写的clone方法克隆出一个对象,如果源对象内部存在引用类型的成员变量,那么就说该克隆是浅克隆,即对于引用类型属性,只克隆引用,两个对象的引用指向同一块内存地址,即同一个对象。
示例代码:
class Friend{
private String name;
private String home;
public Friend(String name, String home) {
this.name = name;
this.home = home;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getHome() {
return home;
}
public void setHome(String home) {
this.home = home;
}
@Override
public String toString() {
return "Friend{" +
"name='" + name + '\'' +
", home='" + home + '\'' +
'}';
}
}
class Person implements Cloneable{
private String name;
private Integer age;
private Friend friend;
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Friend getFriend() {
return friend;
}
public void setFriend(Friend friend) {
this.friend = friend;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", friend=" + friend +
'}';
}
}
public class BaseOnClone {
public static void main(String[] args) throws CloneNotSupportedException {
Person person1 = new Person("张三", 18);
person1.setFriend(new Friend("f1", "西安"));
System.out.println("初始化的person:");
System.out.println(person1);
Person clonePerson = (Person) person1.clone();
System.out.println("克隆出来的person:");
System.out.println(clonePerson);
clonePerson.setName("tom");
clonePerson.setAge(20);
clonePerson.getFriend().setHome("延安");
System.out.println("修改克隆出来的person:");
System.out.println(clonePerson);
System.out.println("修改克隆出来的person之后,原来的person:");
System.out.println(person1);
}
}
执行结果如下:
从执行结果来看,对于克隆之后的clonePerson对象,当修改了它的age属性的值时,clonePerson的age的变化并不会影响到person1的age属性;当修改clonePerson对象的friend属性的home值时,person1也受到了影响。
这是因为通过浅拷贝复制对象时仅仅只是复制对象本身,包括基本属性,但该对象的属性引用其他对象时,该引用对象不会被复制,其拷贝出来的对象和原对象中属性引用的对象是同一个。因此可以使用深拷贝达到完全复制的目的,使得原对象和拷贝出来的对象之间互不影响。
二、深拷贝
1、基本介绍
进行深拷贝时,基本数据类型变量和引用类型变量指向的对象都会被复制,即针对引用类型的成员变量真正的复制一份,重新开辟空间保存,这样两个引用类型属性互不影响。
2、深拷贝的实现方式
(1)嵌套clone方法
该方法给引用数据类型的属性也实现Cloneable接口,然后重写clone方法。以上面的浅拷贝例子为例:只需要在Friend中也实现Cloneable接口,然后重写clone()方法,再对Person中的clone()方法进行修改即可。
1)在Friend中也实现Cloneable接口,然后重写clone()方法
class Friend implements Cloneable{
private String name;
private String home;
public Friend(String name, String home) {
this.name = name;
this.home = home;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getHome() {
return home;
}
public void setHome(String home) {
this.home = home;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Friend{" +
"name='" + name + '\'' +
", home='" + home + '\'' +
'}';
}
}
2) 对Person中的clone()方法进行修改
class Person implements Cloneable{
private String name;
private Integer age;
private Friend friend;
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Friend getFriend() {
return friend;
}
public void setFriend(Friend friend) {
this.friend = friend;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Person person = (Person) super.clone();
person.friend = (Friend) friend.clone();
return person;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", friend=" + friend +
'}';
}
}
3)完整代码
class Friend implements Cloneable{
private String name;
private String home;
public Friend(String name, String home) {
this.name = name;
this.home = home;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getHome() {
return home;
}
public void setHome(String home) {
this.home = home;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Friend{" +
"name='" + name + '\'' +
", home='" + home + '\'' +
'}';
}
}
class Person implements Cloneable{
private String name;
private Integer age;
private Friend friend;
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Friend getFriend() {
return friend;
}
public void setFriend(Friend friend) {
this.friend = friend;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Person person = (Person) super.clone();
person.friend = (Friend) friend.clone();
return person;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", friend=" + friend +
'}';
}
}
public class BaseOnClone {
public static void main(String[] args) throws CloneNotSupportedException {
Person person1 = new Person("张三", 18);
person1.setFriend(new Friend("f1", "西安"));
System.out.println("初始化的person:");
System.out.println(person1);
Person clonePerson = (Person) person1.clone();
System.out.println("克隆出来的person:");
System.out.println(clonePerson);
clonePerson.setName("tom");
clonePerson.setAge(20);
clonePerson.getFriend().setHome("延安");
System.out.println("修改克隆出来的person:");
System.out.println(clonePerson);
System.out.println("修改克隆出来的person之后,原来的person:");
System.out.println(person1);
}
}
执行结果如下:
从执行结果来看,在进行深拷贝的时候,无论是对基本数据类型 (age) 还是引用数据类型 (Friend) 进行修改,原对象 (Person) 都不会受到影响。
(2)序列化方式
如果当类中的属性存在数组(数组不能实现Cloneable接口)或者属性之间的关系比较复杂时,上面的方法都不能很好的实现深克隆了。序列化的方式是让每个类都实现Serializable接口,然后通过序列化和反序列化操作达到深克隆的目的。步骤如下:
- 使要序列化的对象和该对象的引用类型成员变量对象的类都实现Serializable接口,
- 创建一个ByteArrayOutputStream内存数组输出流,创建一个ObjectOutputStream序列化流,并传入内存数组输出流,使用序列化流的writeobject方法将要序列化的对象写入内部数组中
- 创建一个ByteArrayInputStream内存数组读取流,传入一个读取数据的数组,这个数组通过内存数组输出流的toByteArray方法获得,这个数组里面的数据其实就是已经被序列化成二进制数据的对象。
- 最后创建一个ObjectInputStream反序列化流,并传入内存数组读取流,使用反序列化流的readobject方法将数组中的对象的信息反序列化出来。由于反序列化出的对象就是一个新的对象,完成了深克隆。
注意:序列化时通过定义一个private static final long serialVersionUID固定要被序列化对象的版本号,且静态的成员和transient关键字修饰的成员不能被序列化。
代码如下:
import java.io.*;
class Friend implements Serializable {
private String name;
private String home;
public Friend(String name, String home) {
this.name = name;
this.home = home;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getHome() {
return home;
}
public void setHome(String home) {
this.home = home;
}
@Override
public String toString() {
return "Friend{" +
"name='" + name + '\'' +
", home='" + home + '\'' +
'}';
}
}
class Person implements Serializable{
private String name;
private Integer age;
private Friend friend;
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Friend getFriend() {
return friend;
}
public void setFriend(Friend friend) {
this.friend = friend;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", friend=" + friend +
'}';
}
}
public class BaseOnClone {
public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException {
Person person = new Person("张三", 18);
person.setFriend(new Friend("f1", "西安"));
System.out.println("初始化的person:");
System.out.println(person);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(person);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
Person clonePerson = (Person) ois.readObject();
System.out.println("克隆出来的person:");
System.out.println(clonePerson);
clonePerson.setName("tom");
clonePerson.setAge(24);
clonePerson.getFriend().setHome("长春");
System.out.println("修改克隆出来的person:");
System.out.println(clonePerson);
System.out.println("修改克隆出来的person之后,原来的person:");
System.out.println(person);
}
}
执行结果如下:
从执行结果来看,序列化的方式也实现了深拷贝。在使用序列化实现深拷贝时,将对象进行序列化之后写入流中,然后通过反序列化读取流,生成新对象,而新生成的对象和原对象之间互不影响。
三、总结
对于浅拷贝而言,在复制对象时仅仅复制对象本身,包括基本属性和引用类型,拷贝出来的对象和原对象中属性所引用的对象是同一个。
对于深拷贝而言,复制之后的对象和原对象之间互不影响。