Java 深拷贝与浅拷贝概念与代码实现
Posted 流楚丶格念
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java 深拷贝与浅拷贝概念与代码实现相关的知识,希望对你有一定的参考价值。
文章目录
拷贝概念
关于拷贝,简单来说就是创建一个和已知对象一模一样的对象。Java中的拷贝包括深拷贝和浅拷贝,可能日常编码过程中用的不多,但是这是一个面试经常会问的问题。
什么叫Java浅拷贝?
浅拷贝是会将对象的每个属性进行依次复制,但是当对象的属性值是引用类型时,实质复制的是其引用,当引用指向的值改变时也会跟着变化。
什么叫Java深拷贝?
深拷贝复制变量值,对于引用数据,则递归至基本类型后,再复制。深拷贝后的对象与原来的对象是完全隔离的,互不影响,对一个对象的修改并不会影响另一个对象。
Java浅拷贝和深拷贝的区别是什么?
通俗来讲浅拷贝的复制其引用,当引用指向的值改变时也会跟着变化;而深拷贝则是与原来的对象完全隔离,互不影响。
拷贝的实现
浅拷贝的实现
首先,我们定义一下需要拷贝的简单对象。
我们定义了一个Student学生类,包含name姓名,和age年龄,sex性别,而是另一个School类,包含schoolName学校名称和stuNums学生数量以及Student学生,其中Student并不是字符串,而是一个Student类。
学校对象
package com.yyl.javaprogram.copy;
public class School implements Cloneable
private String schoolName;
private int stuNums;
private Student stu;
public String getSchoolName()
return schoolName;
public void setSchoolName(String schoolName)
this.schoolName = schoolName;
public int getStuNums()
return stuNums;
public void setStuNums(int stuNums)
this.stuNums = stuNums;
public Student getStu()
return stu;
public void setStu(Student stu)
this.stu = stu;
@Override
protected Object clone() throws CloneNotSupportedException
return super.clone();
@Override
public String toString()
return "School" +
"schoolName='" + schoolName + '\\'' +
", stuNums=" + stuNums +
", stu=" + stu +
'';
学生对象:
package com.yyl.javaprogram.copy;
public class Student
private String name;
private int age;
private String sex;
public String getName()
return name;
public void setName(String name)
this.name = name;
public int getAge()
return age;
public void setAge(int age)
this.age = age;
public String getSex()
return sex;
public void setSex(String sex)
this.sex = sex;
@Override
public String toString()
return "Student" +
"name='" + name + '\\'' +
", age=" + age +
", sex='" + sex + '\\'' +
'';
这时,我们要进行赋值的原始类 School。下面我们产生一个 School对象,并调用其 clone 方法复制一个新的对象。
注意:调用对象的 clone 方法,必须要让类实现 Cloneable 接口,并且覆写 clone 方法。
package com.yyl.javaprogram.copy;
public class LightCopy
public static void main(String[] args) throws CloneNotSupportedException
// 创建初始的School对象
School s1 = new School();
s1.setSchoolName("燕山大学");
s1.setStuNums(20000);
Student stu1 = new Student();
stu1.setAge(18);
stu1.setName("玉如梦");
stu1.setSex("女");
s1.setStu(stu1);
// 调用重写的clone方法,clone出一个新的school---s2
School s2 = (School) s1.clone();
System.out.println("克隆s1出一个新的学校:s2");
System.out.println("s1: "+s1+" s1的hashcode:"+s1.hashCode()+" s1中stu1的hashcode:"+s1.getStu().hashCode());
System.out.println("s2: "+s2+" s2的hashcode:"+s2.hashCode()+" s2中stu1的hashcode:"+s2.getStu().hashCode());
//System.out.println(s1.getStu().getAge()==s2.getStu().getAge());
System.out.println("----------------------------");
System.out.println("修改克隆出来的对象");
Student student2 = s2.getStu();
student2.setAge(21);
student2.setName("曲华裳");
s2.setStu(student2);
s2.setSchoolName("YSU");
System.out.println("修改后两个学校学生一样,但是学校不是克隆的所以s2修改不影响s1");
System.out.println("s1: "+s1+" s1的hashcode:"+s1.hashCode()+" s1中stu1的hashcode:"+s1.getStu().hashCode());
System.out.println("s2: "+s2+" s2的hashcode:"+s2.hashCode()+" s2中stu1的hashcode:"+s2.getStu().hashCode());
System.out.println(s1.getStu().getAge()==s2.getStu().getAge());
我们查看输出的结果
结果分析:这里对School类选择了两个具有代表性的属性值:一个是引用传递类型(Student对象);另一个是字符串类型(学校名:属于常量)和基本数据类型(年龄)。
通过拷贝构造方法进行了浅拷贝,各属性值成功复制。其中,p1值传递部分的属性值发生变化时,p2不会随之改变;而引用传递部分属性值发生变化时,p2也随之改变。
基本数据类型是值传递,所以修改值后不会影响另一个对象的该属性值;引用数据类型是地址传递(引用传递),所以修改值后另一个对象的该属性值会同步被修改。
String类型非常特殊。首先,String类型属于引用数据类型,不属于基本数据类型,但是String类型的数据是存放在常量池中的,也就是无法修改的!也就是说,当将name属性从“燕山大学”改为“YSU"后,并不是修改了这个数据的值,而是把这个数据的引用从指向”燕山大学“这个常量改为了指向”YSU“这个常量。在这种情况下,另一个对象的name属性值仍然指向”燕山大学“不会受到影响。
要注意:如果在拷贝构造方法中,对引用数据类型变量逐一开辟新的内存空间,创建新的对象,也可以实现深拷贝。而对于一般的拷贝构造,则一定是浅拷贝。
深拷贝的实现
深拷贝的方式有很多种,此次我们介绍三种方式
- 方法一 构造函数
- 方法二 重载clone()方法
- 方法三Serializable序列化
方法一:构造函数
public static void constructorCopy()
Student student = new Student("小李", 21, "男");
School school = new School("xx大学", 100, student);
// 调用构造函数时进行深拷贝
School copySchool = new School(school.getSchoolName(),
school.getStuNums(),
new Student(student.getName(),
student.getAge(),
student.getSex()));
// 修改源对象的值
copySchool.getStudent().setSex("女");
// 检查两个对象的值不同
System.out.println(school.hashCode() == copySchool.hashCode());
结果输出二者为false
方法二:重载clone()方法
Object父类有个clone()的拷贝方法,不过它是protected类型的,我们需要重写它并修改为public类型。除此之外,子类还需要实现Cloneable接口来告诉JVM这个类是可以拷贝的。
让我们还是看之前的School代码,修改clone函数
package com.yyl.javaprogram.copy;
public class School implements Cloneable
private String schoolName;
private int stuNums;
private Student stu;
public String getSchoolName()
return schoolName;
public void setSchoolName(String schoolName)
this.schoolName = schoolName;
public int getStuNums()
return stuNums;
public void setStuNums(int stuNums)
this.stuNums = stuNums;
public Student getStu()
return stu;
public void setStu(Student stu)
this.stu = stu;
@Override
protected School clone() throws CloneNotSupportedException
School school = (School) super.clone();
school.stu = (Student) stu.clone();
return school;
@Override
public String toString()
return "School [schoolName=" + schoolName + ", stuNums=" + stuNums + ", stu=" + stu + "]";
Student修改clone函数
package com.yyl.javaprogram.copy;
public class Student implements Cloneable
private String name;
private int age;
private String sex;
public String getName()
return name;
public void setName(String name)
this.name = name;
public int getAge()
return age;
public void setAge(int age)
this.age = age;
public String getSex()
return sex;
public void setSex(String sex)
this.sex = sex;
@Override
public String toString()
return "Student [name=" + name + ", age=" + age + ", sex=" + sex + "]";
@Override
protected Student clone() throws CloneNotSupportedException
return (Student) super.clone();
这样再进行clone就是全部进行拷贝了
package com.yyl.javaprogram.copy;
public class DeepCopy
public static void main(String[] args) throws CloneNotSupportedException
Student student = new Student ();
student.setName("得到");
student.setAge(15);
student.setSex("男");
School school = new School ();
school.setSchoolName("kkk");
school.setStuNums(22);
school.setStu(student);
School school2 = school.clone();
// 检查两个对象的值不同
System.out.println(school.hashCode()==school2.hashCode());
方法三:Serializable序列化
我们看如下的代码
package com.yyl.javaprogram.copy;
import java.io.*;
public class People implements Serializable
private String name;
private Student student;
public People(String name, Student student)
this.name = name;
this.student = student;
public String getName()
return name;
public void setName(String name)
this.name = name;
public Student getAddress()
return student;
public void setAddress(Student address)
this.student = address;
public Object deepClone() throws Exception
// 序列化
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
// 反序列化
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();
注意 要使用序列化的方式来复制对象 对象需要继承Serializable接口,
接下来我们查看测试类
package com.yyl.javaprogram.copy;
public class DeepCopy
public static void main(String[] args) throws Exception
Student student = new Student ();
student.setName("得到");
student.setAge(15);
student.setSex("男");
People people = new People("ddd",student);
People people1 = (People) people.deepClone();
// 检查两个对象的值不同
System.out.println(people1.hashCode()==people1.hashCode());
结果如下:
通过比较people对象和克隆的people1对象的hashCode发现,也是不同的对象
以上是关于Java 深拷贝与浅拷贝概念与代码实现的主要内容,如果未能解决你的问题,请参考以下文章