在学习C++时,老师说过值传递与引用传递。Java中只有值传递。今天我们就来讨论一下什么是浅拷贝与深拷贝。
先写一个例子给大家:
创建一个消费者类,其中包含属性,构造方法与get set方法.
public class Buyer {
String name;
int Level;
char sex;
double price;
public Buyer(String name,int level, char sex, double price) {
this.name = name;
this.Level = level;
this.sex = sex;
this.price = price;
}
对这个类进行创建工作:
public class BuyeTest {
public static void main(String[] args) {
Buyer buyter1 = new Buyer("小张",13,'男',100.3);
Buyer buyter2 = buyter1;
buyter1.setLevel(10);
//打印两个对象
System.out.println(buyter1);
System.out.println(buyter2);
}
}
image.png
结果在我们意料之内,只创建了一个对象,两个变量同时指向同一个地址,所以一改全改.
那有什么办法能够不创建对象,然而得到多个独立的对象呢?答案是有的,那就是拷贝.在实现拷贝之前要实现一个接口:
image.png
在object类中有clone()方法,我们只需要在子类中调用clone()方法即可,那么我们来试一试。
调用父类的clone方法
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
再次使用clone方法拷贝对象
public class BuyeTest {
public static void main(String[] args) throws CloneNotSupportedException {
Buyer buyter1 = new Buyer("小张",13,'男',100.3);
Buyer buyter2 = (Buyer) buyter1.clone();
buyter1.setLevel(10);
System.out.println(buyter1);
System.out.println(buyter2);
}
}
结果:
image.png
符合预期效果,浅拷贝完成,为什么说他是浅拷贝呢?已经是两个独立的对象了呀?其实并不是。
浅拷贝
- 对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。因为是两份不同的数据,所以对其中一个对象的该成员变量值进行修改,不会影响另一个对象拷贝得到的数据。
- 对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值。
举个例子:
我们创建一个商品类,包含属性,构造方法与get set 方法
public class Product {
String name;
double proPrice;
public Product(String name, double proPrice) {
this.name = name;
this.proPrice = proPrice;
}
}
- 商品类与消费者类关联
public class Buyer implements Cloneable{
String name;
int Level;
char sex;
double price;
Product product;
public Buyer(String name,int level, char sex, double price,Product product) {
this.name = name;
this.Level = level;
this.sex = sex;
this.price = price;
this.product = product;
}
}
用同样方法进行复制对象:
public class BuyeTest {
public static void main(String[] args) throws CloneNotSupportedException {
// 创建Product类
Product product = new Product("饼干", 15.3);
Buyer buyter1 = new Buyer("小张", 13, '男', 100.3, product);
Buyer buyter2 = (Buyer) buyter1.clone();
buyter1.getProduct().setName("蛋糕");
System.out.println(buyter1);
System.out.println(buyter2);
}
}
image.png
修改了其中一个对象的值,另一个也随之改变,并非完全独立.
使用深拷贝来解决这个问题:
- 方式1重写clone方法,对下一层对象进行clone
Product类中实现父类的clone方法
@Override
public String toString() {
return "Product [name=" + name + ", proPrice=" + proPrice + "]";
}
Buyer类中重写clone方法
@Override
protected Object clone() throws CloneNotSupportedException {
Buyer buyter = (Buyer)super.clone();
Product product = (Product)buyter.getProduct().clone();
buyter.setProduct(product);
return buyter;
}
再次运行结果
image.png
方法1 能够解决问题,但是一旦遇到复杂的对象,递归遍历对象图就会消耗大量时间空间.
- 方法2:对象序列化,类都要实现Serializable接口,如果又不需要序列化的属性使用transient关键字
//方法2
protected Object deepClone() throws Exception {
// 将Buyer转换为字节流(序列化)
ByteArrayOutputStream bos = new ByteArrayOutputStream(); // 字节数组输出流
ObjectOutputStream oos = new ObjectOutputStream(bos); // 对象输出流(用于实现序列化)
oos.writeObject(this);
// 将字节流转换为新的Buyer(反序列化)
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); // 创建字节数组输入流,传入"序列化后的字节流"
ObjectInputStream ois = new ObjectInputStream(bis); // 对象输入流(用于实现反序列化)
Buyer buyer = (Buyer) ois.readObject();
return buyer;
}
测试
public static void main(String[] args) throws Exception {
// 创建Product类
Product product = new Product("饼干", 15.3);
Buyer buyter1 = new Buyer("小张", 13, '男', 100.3, product);
// Buyer buyter2 = (Buyer) buyter1.clone();
// buyter1.getProduct().setName("蛋糕");
// System.out.println(buyter1);
// System.out.println(buyter2);
Buyer buyter3 = (Buyer) buyter1.deepClone();
buyter3.getProduct().setName("水果");
System.out.println(buyter1);
System.out.println(buyter3);
结果
image.png
网友评论