1.出现的原因
Java 中的数据类型分为基本数据类型和引用数据类型。对于这两种数据类型,在进行赋值操作、用作方法参数或返回值时,会有值传递和引用(地址)传递的差别。
2.浅拷贝
2.1 介绍
浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。
如果属性是基本类型,拷贝的就是基本类型的值;
如果属性是内存地址(引用类型),拷贝的就是内存地址 。因此如果其中一个对象改变了这个地址的值,就会影响到另一个对象。
即默认拷贝构造函数只是对对象进行浅拷贝复制(逐个成员依次拷贝),即只复制对象空间而不复制资源。
2.2 实现
实现对象拷贝的类,需要实现 Cloneable 接口,并覆写 clone() 方法。
Object类是类结构的根类,其中有一个方法为protected Object clone() throws CloneNotSupportedException,这个方法就是进行的浅拷贝。有了这个浅拷贝模板,我们可以通过调用clone()方法来实现对象的浅拷贝。
但是需要注意:
1、Object类虽然有这个方法,但是这个方法是受保护的(被protected修饰),所以我们无法直接使用。
2、使用clone方法的类必须实现Cloneable接口,否则会抛出异常CloneNotSupportedException。对于这两点,我们的解决方法是,在要使用clone方法的类中重写clone()方法,通过super.clone()调用Object类中的原clone方法。
2.3 示例代码
public class b1112 {
public static void main(String[] args) {
To to1=new To();
To to2=new To();
Toy a=new Toy(100,to1);
Toy b=(Toy)a.clone();
a.setPrice(200);//修改基本类型
a.setName(to1);//修改引用类型
System.out.println("a:"+a.getName()+","+a.getPrice());
System.out.println("b:"+b.getName()+","+b.getPrice());//price不变,name发生了变化
/*
输出
a:a1112.To@1b6d3586,200
b:a1112.To@1b6d3586,100
*/
}
}
class Toy implements Cloneable{
private int price;//基本类型
private To name;//引用类型
public Toy(int price,To name){
this.price=price;
this.name=name;
}
@Override
public Object clone() {
//浅拷贝
try {
return super.clone();
} catch (CloneNotSupportedException e) {
return null;
}
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public To getName() { return name;}
public void setName(To name) {
this.name = name;
}
}
class To{
}
3.深拷贝
3.1 介绍
深拷贝,在拷贝引用类型成员变量时,为引用类型的数据成员另辟了一个独立的内存空间,实现真正内容上的拷贝。
3.2 特点
(1) 对于基本数据类型,和浅拷贝一样拷贝的是值。
(2) 对于引用类型,比如数组或者类对象,深拷贝会新建一个对象空间,然后拷贝里面的内容,所以它们指向了不同的内存空间,互不影响。**
(3) 对于有多层对象的,每个对象都需要实现 Cloneable并重写 clone() 方法,进而实现了对象的串行层层拷贝。
(4) 深拷贝相比于浅拷贝速度较慢并且花销较大。
3.3 使用
对于引用类型的成员变量,需要实现 Cloneable 并重写 clone() 方法。
3.4 示例代码
public class a1112 {
public static void main(String[] args) {
To to1=new To();
To to2=new To();
Toy a=new Toy(100,to1);
Toy b=(Toy)a.clone();
a.setPrice(200);//修改基本类型
a.setName(to1);//修改引用类型
System.out.println("a:"+a.getName()+","+a.getPrice());
System.out.println("b:"+b.getName()+","+b.getPrice());
}
/*
输出:
a:a1112.To@1b6d3586,200
b:a1112.To@4554617c,100
*/
}
class Toy implements Cloneable{
private int price;
private To name;
public Toy(int price,To name){
this.price=price;
this.name=name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public To getName() { return name;}
public void setName(To name) {
this.name = name;
}
@Override
public Object clone() {
//深拷贝
try {
// 直接调用父类的clone()方法
Toy toy= (Toy) super.clone();
toy.setName((To)name.clone());
return toy;
} catch (CloneNotSupportedException e) {
return null;
}
}
}
class To implements Cloneable{
@Override
public Object clone(){
try {
return super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
}