深拷贝&浅拷贝
Java中变量有两种类型:基本类型和引用类型
基本类型的变量保存原始值,即它代表的值就是数值本身 引用类型的变量保存引用值,"引用值"指向内存空间的地址,代表了某个对象的引用,而不是对象本身,对象本身存放在这个引用值所表示的地址的位置
注:String类型通过常量赋值时相当于基本数据类型,通过new关键字创建对象时便是引用数据类型
浅拷贝
1. 浅拷贝介绍
浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。即默认拷贝构造函数只是对对象进行浅拷贝复制(逐个成员依次拷贝),即只复制对象空间而不复制资源。
2. 浅拷贝特点
(1) 对于基本数据类型的成员对象,因为基础数据类型是值传递的,所以是直接将属性值赋值给新的对象。基础类型的拷贝,其中一个对象修改该值,不会影响另外一个。 (2) 对于引用类型,比如数组或者类对象,因为引用类型是引用传递,所以浅拷贝只是把内存地址赋值给了成员变量,它们指向了同一内存空间。改变其中一个,会对另外一个也产生影响。
3. 浅拷贝的实现
public class Subject {
private String name;
public Subject(String name) {
this.name = name;
}
public String toString() {
return String.format("[Subject: %d, name: %s]", this.hashCode(), this.name);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Student implements Cloneable{
//基础数据类型
private int age;
private String name;
//引用类型
private Subject subject;
public Student(int age,String name,Subject subject){
this.age=age;
this.name=name;
this.subject=subject;
}
/**
* 浅拷贝,重写clone()
* @return
*/
public Object clone(){
try {
//直接调用父类的clone()方法
return super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
public String toString(){
return String.format("{Student:%d, subject:%s, name:%s, age:%d}",this.hashCode(),this.subject.toString(),this.name,this.age);
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Subject getSubject() {
return subject;
}
public void setSubject(Subject subject) {
this.subject = subject;
}
}
测试
public class Test {
public static void main(String[] args) {
Subject subjectA=new Subject("yuwen");
Student studentA=new Student(10,"stuA",subjectA);
Student studentB= (Student) studentA.clone();
studentB.setAge(12);
studentB.setName("stuB");
Subject subjectB=studentB.getSubject();
subjectB.setName("shuxue");
System.out.println("studentA: "+studentA.toString());
System.out.println("studentB: "+studentB.toString());
}
}
结果
studentA: {Student:1896277646, subject:[Subject: 2128227771, name: shuxue], name:stuA, age:10}
studentB: {Student:396180261, subject:[Subject: 2128227771, name: shuxue], name:stuB, age:12}
1.深拷贝介绍
深拷贝,在拷贝引用类型成员变量时,为引用类型的数据成员另辟了一个独立的内存空间,实现真正内容上的拷贝。
2. 深拷贝特点
(1) 对于基本数据类型的成员对象,因为基础数据类型是值传递的,所以是直接将属性值赋值给新的对象。基础类型的拷贝,其中一个对象修改该值,不会影响另外一个(和浅拷贝一样)。 (2) 对于引用类型,比如数组或者类对象,深拷贝会新建一个对象空间,然后拷贝里面的内容,所以它们指向了不同的内存空间。改变其中一个,不会对另外一个也产生影响。 (3) 对于有多层对象的,每个对象都需要实现 Cloneable 并重写 clone() 方法,进而实现了对象的串行层层拷贝。 (4) 深拷贝相比于浅拷贝速度较慢并且花销较大。
3.深拷贝的实现
public class Subject implements Cloneable{
private String name;
public Subject(String name) {
this.name = name;
}
/**
* 重写clone(),每个对象都调用父类的clone() 方法
* @return
*/
public Object clone(){
try {
return super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
public String toString() {
return String.format("[Subject: %d, name: %s]", this.hashCode(), this.name);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Student implements Cloneable{
//基础数据类型
private int age;
private String name;
//引用类型
private Subject subject;
public Student(int age, String name, Subject subject){
this.age=age;
this.name=name;
this.subject=subject;
}
/**
* 深拷贝,重写clone(),每个引用类型的变量都调用 各自重写的clone()
* @return
*/
public Object clone(){
Student student=null;
try {
student= (Student) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
student.subject= (Subject) subject.clone();
return student;
}
public String toString(){
return String.format("{Student:%d, subject:%s, name:%s, age:%d}",this.hashCode(),this.subject.toString(),this.name,this.age);
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Subject getSubject() {
return subject;
}
public void setSubject(Subject subject) {
this.subject = subject;
}
}
测试
public class Test {
public static void main(String[] args) {
Subject subjectA= new Subject("yuwen");
Student studentA=new Student(10,"stuA",subjectA);
Student studentB= (Student) studentA.clone();
studentB.setAge(12);
studentB.setName("stuB");
Subject subjectB=studentB.getSubject();
subjectB.setName("shuxue");
System.out.println("studentA: "+studentA.toString());
System.out.println("studentB: "+studentB.toString());
}
}
结果
studentA: {Student:1896277646, subject:[Subject: 2128227771, name: yuwen], name:stuA, age:10}
studentB: {Student:396180261, subject:[Subject: 625576447, name: shuxue], name:stuB, age:12}
深拷贝后,不管是基础数据类型还是引用类型的成员变量,修改其值都不会相互造成影响。
关于clone()
JDK API的说明文档解释这个方法将返回Object对象的一个拷贝。
和 new 的区别
共同点:都是分配内存,对象都是指向不同的内存地址 不同点:new创建一个对象,clone复制一个对象。new是返回的新对象,而调用clone()方法时,拷贝对象已经包含了一些原来对象的信息,而不是对象的初始信息
在Object类的clone()是一个native方法,native方法的效率一般来说都是远高于Java中的非native方法。这也解释了为 什么要用Object中clone()方法而不是先new一个类,然后把原始对象中的信息赋到新对象中,虽然这也实现了clone功能
为什么要实现Cloneable接口
Cloneable接口是不包含任何方法的,其实这个接口仅仅是一个标志,而且这个标志也仅仅是针对 Object类中clone()方法的,如果实现clone()方法的类没有实现Cloneable接口,并调用了Object的clone()方法(也就是调用了 super.clone()方法),那么Object的clone()方法就会抛出CloneNotSupportedException异常
浅拷贝
对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,此为浅拷贝
深拷贝
对基本数据类型进行值传递,对引用数据类型,会对引用指向的对象进行拷贝,此为深拷贝。也就是在clone()方法对其内的引用类型的变量再进行一次 clone()
参考

浙公网安备 33010602011771号