(整理)java基础——深浅拷贝

1. 深拷贝浅拷贝概念

浅拷贝(Shallow Copy):

①对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。因为是两份不同的数据,所以对其中一个对象的该成员变量值进行修改,不会影响另一个对象拷贝得到的数据。
②对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值

此处输入图片的描述

深拷贝 (Deep Copy)

对于深拷贝来说,不仅要复制对象的所有基本数据类型的成员变量值,还要为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象

此处输入图片的描述

2. 浅拷贝实现方法

2.1 通过构造方法实现浅拷贝

拷贝构造方法指的是该类的构造方法参数为该类的对象。使用拷贝构造方法可以很好地完成浅拷贝,直接通过一个现有的对象创建出与该对象属性相同的新的对象。

代码参考如下:

public class Main {
public static void main(String [] args){
Message message=new Message();
Age age=new Age();
age.number=17;
message.sex="男";
message.age=age;
Person person1=new Person("Tom",message);
Person person2=new Person(person1);
person1.printMessage();
person2.printMessage();

person1.name="Jack";
message.sex="女";
person1.printMessage();
person2.printMessage();

}
}

class Person implements Cloneable{
public String name;
public Message message;
public Person(String name,Message message){
this.name=name;
this.message=message;
}
public Person(Person person){
this.name=person.name;
this.message=person.message;
}
public void printMessage(){
System.out.println("name:"+name+", Message.sex:"+message.sex+", Message.age:"+message.age.number+", hashcode:"+this.hashCode());
}
@Override
public Object clone(){
try {
return super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
}
class Message{
public String sex;
public Age age;
}
class Age{
public int number;
}

运行结果:
name:Tom, Message.sex:男, Message.age:17, hashcode:796684896
name:Tom, Message.sex:男, Message.age:17, hashcode:1935637221
name:Jack, Message.sex:女, Message.age:17, hashcode:796684896
name:Tom, Message.sex:女, Message.age:17, hashcode:1935637221

2.2 通过clone()方法实现

Object类是类结构的根类,其中有一个方法为protected Object clone() throws CloneNotSupportedException,这个方法就是进行的浅拷贝。有了这个浅拷贝模板,我们可以通过调用clone()方法来实现对象的浅拷贝。

但是需要注意:
1、Object类虽然有这个方法,但是这个方法是受保护的(被protected修饰),所以我们无法直接使用。
2、使用clone方法的类必须实现Cloneable接口,否则会抛出异常CloneNotSupportedException。对于这两点,我们的解决方法是,在要使用clone方法的类中重写clone()方法,通过super.clone()调用Object类中的原clone方法。

示例代码:

public class Main {
public static void main(String [] args){
Message message=new Message();
Age age=new Age();
age.number=17;
message.sex="男";
message.age=age;
Person person1=new Person("Tom",message);
Person person2=(Person)person1.clone();
person1.printMessage();
person2.printMessage();

person1.name="Jack";
message.sex="女";
person1.printMessage();
person2.printMessage();

}
}

class Person implements Cloneable{
public String name;
public Message message;
public Person(String name,Message message){
this.name=name;
this.message=message;
}
public void printMessage(){
System.out.println("name:"+name+", Message.sex:"+message.sex+", Message.age:"+message.age.number+", hashcode:"+this.hashCode());
}

@Override
public Object clone(){
try {
return super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
}
class Message{
public String sex;
public Age age;
}
class Age{
public int number;
}

运行结果:
name:Tom, Message.sex:男, Message.age:17, hashcode:796684896
name:Tom, Message.sex:男, Message.age:17, hashcode:1935637221
name:Jack, Message.sex:女, Message.age:17, hashcode:796684896
name:Tom, Message.sex:女, Message.age:17, hashcode:1935637221

3. 浅拷贝实现方法

3.1 通过重写clone方法来实现深拷贝

与通过重写clone方法实现浅拷贝的基本思路一样,只需要为对象图的每一层的每一个对象都实现Cloneable接口并重写clone方法,最后在最顶层的类的重写的clone方法中调用所有的clone方法即可实现深拷贝。简单的说就是:每一层的每个对象都进行浅拷贝=深拷贝。

代码实现:

public class Main {
public static void main(String [] args){
Message message=new Message();
Age age=new Age();
age.number=17;
message.sex="男";
message.age=age;
Person person1=new Person("Tom",message);
Person person2=(Person) person1.clone();
person1.printMessage();
person2.printMessage();

age.number=18;
person1.name="Jack";
message.sex="女";
person1.printMessage();
person2.printMessage();

}
}

class Person implements Cloneable{
public String name;
public Message message;
public Person(String name,Message message){
this.name=name;
this.message=message;
}
public void printMessage(){
System.out.println("name:"+name+", Message.sex:"+message.sex+", Message.age:"+message.age.number+", hashcode:"+this.hashCode());
}
@Override
public Object clone(){
try {
Person person=(Person) super.clone();
person.message=(Message)message.clone();
return person;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
}
class Message implements Cloneable{
public String sex;
public Age age;

@Override
public Object clone(){
try {
Message message=(Message) super.clone();
message.age=(Age)age.clone();
return message;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
}
class Age implements Cloneable{
public int number;

@Override
public Object clone(){
try {
return super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
}

运行结果:
name:Tom, Message.sex:男, Message.age:17, hashcode:796684896
name:Tom, Message.sex:男, Message.age:17, hashcode:1935637221
name:Jack, Message.sex:女, Message.age:18, hashcode:796684896
name:Tom, Message.sex:男, Message.age:17, hashcode:1935637221

3.2 通过对象序列化实现深拷贝

对于属性数量比较多、层次比较深的类而言,每个类都要重写clone方法太过繁琐。
将对象序列化为字节序列后,默认会将该对象的整个对象图进行序列化,再通过反序列即可完美地实现深拷贝。

代码实现:

import java.io.*;

public class Main {
public static void main(String [] args) throws IOException, ClassNotFoundException {
Message message=new Message();
Age age=new Age();
age.number=17;
message.sex="男";
message.age=age;
Person person1=new Person("Tom",message);

//写入流
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
objectOutputStream.writeObject(person1);
//从流中取出
ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
Person person2=(Person)objectInputStream.readObject();
person1.printMessage();
person2.printMessage();

age.number=18;
person1.name="Jack";
message.sex="女";
person1.printMessage();
person2.printMessage();
}
}

class Person implements Serializable{
public String name;
public Message message;
public Person(String name,Message message){
this.name=name;
this.message=message;
}
public void printMessage(){
System.out.println("name:"+name+", Message.sex:"+message.sex+", Message.age:"+message.age.number+", hashcode:"+this.hashCode());
}
}
class Message implements Serializable{
public String sex;
public Age age;

}
class Age implements Serializable{
public int number;

}

运行结果:
name:Tom, Message.sex:男, Message.age:17, hashcode:895947612
name:Tom, Message.sex:男, Message.age:17, hashcode:1165897474
name:Jack, Message.sex:女, Message.age:18, hashcode:895947612
name:Tom, Message.sex:男, Message.age:17, hashcode:1165897474
-------------本文结束感谢您的阅读-------------