浅拷贝(shadow copy)和深拷贝(deep copy)
在设计模式中的原型模式中需要用到浅拷贝和深拷贝
浅拷贝: 拷贝基本属性,对于引用类型只拷贝其引用
深拷贝: 不仅拷贝基本属性,对于引用类型中的基本类型也拷贝一份
实现clone步骤:
1.实现Cloneable
接口
2.重写Object
类型的clone
方法
Cloneable
接口:
public interface Cloneable {
}
浅拷贝
//Address.java
public class Address implements Cloneable{
public Address(String addrName) {
this.addrName = addrName;
}
private String addrName;
public String getAddrName() {
return addrName;
}
public void setAddrName(String addrName) {
this.addrName = addrName;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Object obj = super.clone();
return obj;
}
@Override
public String toString() {
return "Address{" +
"addrName='" + addrName + '\'' +
'}';
}
}
//Horse.java
public class Horse implements Cloneable{
private String name;
private Date birthday;
private Address address;
public Horse(String name, Date birthday, Address address) {
System.out.println("constructor invoked...");
this.name = name;
this.birthday = birthday;
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Object sheep = super.clone();
return sheep;
}
@Override
public String toString() {
return "Horse{" +
"name='" + name + '\'' +
", birthday=" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(birthday) +
", address=" + address +
'}';
}
}
测试:
@Test
public void cloneTest() throws CloneNotSupportedException, InterruptedException {
Address address = new Address("上海");
Horse horse = new Horse("来福", new Date(), address);
System.out.println("horse: "+horse);
Horse cloneHorse = (Horse) horse.clone();
System.out.println("clonehorse: "+cloneHorse);
Thread.sleep(10000);
horse.setBirthday(new Date());
address.setAddrName("上海乐业");
horse.setName("来福-2");
System.out.println("after change...");
System.out.println("horse: "+horse);
System.out.println("clonehorse: "+cloneHorse);
}
结果:
constructor invoked...
horse: Horse{name='来福', birthday=2018-03-19 10:50:56, address=Address{addrName='上海'}}
clonehorse: Horse{name='来福', birthday=2018-03-19 10:50:56, address=Address{addrName='上海'}}
after change...
horse: Horse{name='来福-2', birthday=2018-03-19 10:51:06, address=Address{addrName='上海乐业'}}
clonehorse: Horse{name='来福', birthday=2018-03-19 10:50:56, address=Address{addrName='上海乐业'}}
- 可以发现使用clone方式创建对象,不会调用构造函数
- 引用类型Address随着原对象改变而改变
- 但是为什么Date类型没有改变
查看Date源码发现Date中的clone方法是实现的深拷贝:
//Date.java
public Object clone() {
Date d = null;
try {
d = (Date)super.clone();
if (cdate != null) {
d.cdate = (BaseCalendar.Date) cdate.clone();
}
} catch (CloneNotSupportedException e) {} // Won't happen
return d;
}
深拷贝实现对象创建
1.对引用类型进行一次拷贝,只需要将Horse.java
中clone
方法换成
@Override
protected Object clone() throws CloneNotSupportedException {
Horse horse = (Horse) super.clone();
horse.address = (Address) this.address.clone();
return horse;
}
测试案例不变,测试结果:
constructor invoked...
horse: Horse{name='来福', birthday=2018-03-19 10:59:05, address=Address{addrName='上海'}}
clonehorse: Horse{name='来福', birthday=2018-03-19 10:59:05, address=Address{addrName='上海'}}
after change...
horse: Horse{name='来福-2', birthday=2018-03-19 10:59:15, address=Address{addrName='上海乐业'}}
clonehorse: Horse{name='来福', birthday=2018-03-19 10:59:05, address=Address{addrName='上海'}}
可以看到clonehorse
中address
属性没有随着horse
改变而改变
2.使用流(Stream)实现深拷贝
注意:使用流来实现深拷贝的时候对象不用实现Cloneable
接口,但对象必须实现Serializable
接口
public class Address implements Serializable{
public Address(String addrName) {
this.addrName = addrName;
}
private String addrName;
public String getAddrName() {
return addrName;
}
public void setAddrName(String addrName) {
this.addrName = addrName;
}
@Override
public String toString() {
return "Address{" +
"addrName='" + addrName + '\'' +
'}';
}
}
//Horse.java
public class Horse implements Serializable{
private String name;
private Date birthday;
private Address address;
public Horse(String name, Date birthday, Address address) {
System.out.println("constructor invoked...");
this.name = name;
this.birthday = birthday;
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "Horse{" +
"name='" + name + '\'' +
", birthday=" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(birthday) +
", address=" + address +
'}';
}
}
实现深拷贝工具类:
public class HorseCloneObject {
public static <T>T clone(T object){
T cloneObj = null;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(object);
ByteArrayInputStream bis = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
cloneObj = (T) ois.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return cloneObj;
}
}
测试
@Test
public void cloneTest() throws InterruptedException {
Address address = new Address("上海");
Horse horse = new Horse("来福", new Date(), address);
System.out.println("horse: "+horse);
Horse cloneHorse = HorseCloneObject.clone(horse);
System.out.println("clonehorse: "+cloneHorse);
Thread.sleep(10000);
horse.setBirthday(new Date());
address.setAddrName("上海乐业");
horse.setName("来福-2");
System.out.println("after change...");
System.out.println("horse: "+horse);
System.out.println("clonehorse: "+cloneHorse);
}
结果:
constructor invoked...
horse: Horse{name='来福', birthday=2018-03-19 11:04:58, address=Address{addrName='上海'}}
clonehorse: Horse{name='来福', birthday=2018-03-19 11:04:58, address=Address{addrName='上海'}}
after change...
horse: Horse{name='来福-2', birthday=2018-03-19 11:05:08, address=Address{addrName='上海乐业'}}
clonehorse: Horse{name='来福', birthday=2018-03-19 11:04:58, address=Address{addrName='上海'}}
可以看到同样实现了深拷贝对象的功能