Java中的对象拷贝主要分为:浅拷贝(Shallow Copy)、深拷贝(Deep Copy)。
Java中的数据类型分为基本数据类型和引用数据类型。对于这两种数据类型,在进行赋值操作、用作方法参数或返回值时,会有值传递和引用(地址)传递的差别。
1.通过构造方法实现浅拷贝
package copy;
/**
* @author zhouxuan
* @since 2019/5/22
*/
public class ShallowCopy {
public static void main(String[] agrs){
Stock stock = new Stock(100);
Car p1 = new Car("Audi",stock);
Car p2 = new Car(p1);
System.out.println("修改前:");
System.out.println("p1:" + p1.toString());
System.out.println("p2:" + p2.toString());
//修改基本数据类型数据
p1.setBrand("Porches");
//修改引用类型数据
stock.setStock(999);
System.out.println("修改后:");
System.out.println("p1:" + p1.toString());
System.out.println("p2:" + p2.toString());
}
}
class Car {
private String brand;
private Stock stock;
public Car(String brand, Stock stock) {
this.brand = brand;
this.stock = stock;
}
public Car(Car car){
this.brand = car.brand;
this.stock = car.stock;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public Stock getStock() {
return stock;
}
public void setStock(Stock stock) {
this.stock = stock;
}
@Override
public String toString() {
return "brand='" + brand + '\'' +
", stock=" + stock;
}
}
class Stock{
private int stock;
public Stock(int stock) {
this.stock = stock;
}
public int getStock() {
return stock;
}
public void setStock(int stock) {
this.stock = stock;
}
@Override
public String toString() {
return ""+this.getStock();
}
}
运行结果如下:
修改前:
p1:brand='Audi', stock=100
p2:brand='Audi', stock=100
修改后:
p1:brand='Porches', stock=999
p2:brand='Audi', stock=999
从运行结果我们可以看出:
对于基本数据类型(字符串类型)的修改,p1的修改并不会影响p2。
对于引用类型的修改,p2的修改导致了p1中的数据也随之变化。
2.通过重写clone()接口实现浅拷贝
所有类的都有一个Obeject根类,Obeject中提供了clone()方法:
protected native Object clone() throws CloneNotSupportedException;
但我们不能直接调用clone()方法,需要实现Cloneable接口,否则会抛出CloneNotSupportedException异常。我们可以在类中重写clone()方法,实现浅拷贝
public class ShallowCopyClone {
public static void main(String[] agrs) {
Price price = new Price(100);
Computer p1 = new Computer(price, 999);
Computer p2 = (Computer) p1.copy();
System.out.println("修改前");
System.out.println(p1.toString());
System.out.println(p2.toString());
p1.setStock(20);
price.setPrice(123);
System.out.println("修改后");
System.out.println(p1.toString());
System.out.println(p2.toString());
}
}
class Computer implements Cloneable{
private Price price;
private int stock;
public Computer(Price price, int stock) {
this.price = price;
this.stock = stock;
}
public Computer(Computer computer) {
this.price = computer.price;
this.stock = computer.stock;
}
public Price getPrice() {
return price;
}
public void setPrice(Price price) {
this.price = price;
}
public int getStock() {
return stock;
}
public void setStock(int stock) {
this.stock = stock;
}
@Override
public String toString() {
return "price=" + price.getPrice() +
", stock='" + stock ;
}
public Object copy() {
Object obj = null;
try {
obj = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return obj;
}
}
class Price{
private int price;
public Price(int price) {
this.price = price;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
运行结果:
修改前:
price=100, stock='999
price=100, stock='999
修改后:
price=123, stock='20
price=123, stock='999
深拷贝将对所有对象开辟新的内存空间而不是引用。
如A对象包含B对象,则会对A、B对象开辟新的内存空间实现拷贝。此时修改拷贝前后对象不会互相造成影响。
1.通过序列化和反序列化实现深拷贝
需要拷贝的类实现Serializable接口,否则会抛出NotSerializableException异常。
public class DeepCopy {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Category category = new Category(1);
Watch p1 = new Watch("high", category);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
//将p1序列化
oos.writeObject(p1);
oos.flush();
//反序列化
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
Watch p2 = (Watch) ois.readObject();
System.out.println("修改前:");
System.out.println(p1.toString());
System.out.println(p2.toString());
System.out.println("修改后:");
p1.setLevel("low");
category.setCategory(2);
System.out.println(p1.toString());
System.out.println(p2.toString());
}
}
class Watch implements Serializable{
private String level;
private Category category;
public Watch(Watch watch) {
this.level = watch.level;
this.category = watch.category;
}
public Watch(String level, Category category) {
this.level = level;
this.category = category;
}
public String getLevel() {
return level;
}
public void setLevel(String level) {
this.level = level;
}
public Category getCategory() {
return category;
}
public void setCategory(Category category) {
this.category = category;
}
@Override
public String toString() {
return "Watch{" +
"level='" + level + '\'' +
", category=" + category +
'}';
}
}
class Category implements Serializable{
private int category;
public Category(int category) {
this.category = category;
}
public int getCategory() {
return category;
}
public void setCategory(int category) {
this.category = category;
}
@Override
public String toString() {
return "Category{" +
"category=" + category +
'}';
}
}
运行结果:
修改前:
Watch{level='high', category=Category{category=1}}
Watch{level='high', category=Category{category=1}}
修改后:
Watch{level='low', category=Category{category=2}}
Watch{level='high', category=Category{category=1}}