- 模式定义
指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
- 知识点
1.Cloneable接口/Object#clone方法 详解
2.浅拷贝/深拷贝
3.序列化机制实现深拷贝
- 应用场景
当代码不应该依赖于需要复制的对象的具体类时,请使用Prototype模式。
- 有点
1.可以不耦合具体类的情况下克隆对象
2.避免重复的初始化代码
3.更方便的构建复杂对象
- 示例:
我们有一个类Product
class Product{
private String part1;
private String part2;
private Integer part3;
private Integer part4;
public String getPart1() {
return part1;
}
public void setPart1(String part1) {
this.part1 = part1;
}
public String getPart2() {
return part2;
}
public void setPart2(String part2) {
this.part2 = part2;
}
public Integer getPart3() {
return part3;
}
public void setPart3(Integer part3) {
this.part3 = part3;
}
public Integer getPart4() {
return part4;
}
public void setPart4(Integer part4) {
this.part4 = part4;
}
public Product(String part1, String part2, Integer part3, Integer part4) {
this.part1 = part1;
this.part2 = part2;
this.part3 = part3;
this.part4 = part4;
}
@Override
public String toString() {
return "Product{" +
"part1='" + part1 + ''' +
", part2='" + part2 + ''' +
", part3=" + part3 +
", part4=" + part4 +
'}';
}
}
我们在创建一群比较类似的对象时,以往就是不停的new
public class ProtoTypeTest {
public static void main(String[] args) {
Product product = new Product("part1","part2",3,4);
//...new
}
}
现在我们需要通过拷贝来实现
- 实现Cloneable接口。
- 实现Cloneable接口的clone方法。
class Product implements Cloneable{
private String part1;
private String part2;
private Integer part3;
private Integer part4;
public String getPart1() {
return part1;
}
public void setPart1(String part1) {
this.part1 = part1;
}
public String getPart2() {
return part2;
}
public void setPart2(String part2) {
this.part2 = part2;
}
public Integer getPart3() {
return part3;
}
public void setPart3(Integer part3) {
this.part3 = part3;
}
public Integer getPart4() {
return part4;
}
public void setPart4(Integer part4) {
this.part4 = part4;
}
public Product(String part1, String part2, Integer part3, Integer part4) {
this.part1 = part1;
this.part2 = part2;
this.part3 = part3;
this.part4 = part4;
}
@Override
protected Product clone() throws CloneNotSupportedException {
return ((Product) super.clone());
}
@Override
public String toString() {
return "hashcode:"+super.hashCode()+"|Product{" +
"part1='" + part1 + ''' +
", part2='" + part2 + ''' +
", part3=" + part3 +
", part4=" + part4 +
'}';
}
}
我们来看一下clone的对象
public class ProtoTypeTest {
public static void main(String[] args) throws CloneNotSupportedException {
Product product = new Product("part1","part2",3,4);
//...new
Product clone = product.clone();
System.out.println("original: "+product);
System.out.println("clone: "+clone);
}
}
通过hashcode我们发现这是clone出来的对象是与原对象不相关的新对象(没有依赖关系),clone的底层实现是native修饰的,说明是虚拟机帮我们来做的。
现在我们对象中全部是八中原生类型及其包装类型,string,bigInteger等都是属于不可变类型(immutable),所以我们可以用上面的方式进行实现,没有问题。但是如果对象中有引用类型就会有问题,如下:
class baseInfo{
private String companyName;
public baseInfo(String companyName) {
this.companyName = companyName;
}
public String getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
@Override
public String toString() {
return super.hashCode()+"|baseInfo{" +
"companyName='" + companyName + ''' +
'}';
}
}
class Product implements Cloneable{
private String part1;
private String part2;
private Integer part3;
private Integer part4;
private baseInfo baseInfo;
public String getPart1() {
return part1;
}
public void setPart1(String part1) {
this.part1 = part1;
}
public String getPart2() {
return part2;
}
public void setPart2(String part2) {
this.part2 = part2;
}
public Integer getPart3() {
return part3;
}
public void setPart3(Integer part3) {
this.part3 = part3;
}
public Integer getPart4() {
return part4;
}
public void setPart4(Integer part4) {
this.part4 = part4;
}
public baseInfo getbaseInfo() {
return baseInfo;
}
public void setbaseInfo(baseInfo baseInfo) {
this.baseInfo = baseInfo;
}
public Product(String part1, String part2, Integer part3, Integer part4, baseInfo baseInfo) {
this.part1 = part1;
this.part2 = part2;
this.part3 = part3;
this.part4 = part4;
this.baseInfo = baseInfo;
}
@Override
protected Product clone() throws CloneNotSupportedException {
return ((Product) super.clone());
}
@Override
public String toString() {
return super.hashCode()+"Product{" +
"part1='" + part1 + ''' +
", part2='" + part2 + ''' +
", part3=" + part3 +
", part4=" + part4 +
", baseInfo=" + baseInfo +
'}';
}
}
我们来测试一下:
public class ProtoTypeTest {
public static void main(String[] args) throws CloneNotSupportedException {
baseInfo baseInfo = new baseInfo("xxx");
Product product = new Product("part1","part2",3,4,baseInfo);
//...new
Product clone = product.clone();
System.out.println("original: "+product);
System.out.println("clone: "+clone);
product.getbaseInfo().setCompanyName("yyyy");
System.out.println("original: "+product);
System.out.println("clone: "+clone);
}
}
可以看到clone对象和原始对象中的baseInfo用的是同一个对象,当我们对原始对象的baseInfo进行修改后,发现clone对象和原始对象的baseInfo都修改了,这个时候说明我们的拷贝出问题了,原始对象和拷贝对象有了关系,产生了依赖,怎么解决呢?
我们有两种方式进行解决:
一是我们对被引用对象也实现Cloneable接口,然后修改引用对象的clone方法,直接在引用对象中的clone方法中实现对引用对象的拷贝,如下
class baseInfo implements Cloneable{
private String companyName;
public baseInfo(String companyName) {
this.companyName = companyName;
}
public String getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
@Override
protected baseInfo clone() throws CloneNotSupportedException {
return ((baseInfo) super.clone());
}
@Override
public String toString() {
return super.hashCode()+"|baseInfo{" +
"companyName='" + companyName + ''' +
'}';
}
}
class Product implements Cloneable{
private String part1;
private String part2;
private Integer part3;
private Integer part4;
private baseInfo baseInfo;
public String getPart1() {
return part1;
}
public void setPart1(String part1) {
this.part1 = part1;
}
public String getPart2() {
return part2;
}
public void setPart2(String part2) {
this.part2 = part2;
}
public Integer getPart3() {
return part3;
}
public void setPart3(Integer part3) {
this.part3 = part3;
}
public Integer getPart4() {
return part4;
}
public void setPart4(Integer part4) {
this.part4 = part4;
}
public baseInfo getbaseInfo() {
return baseInfo;
}
public void setbaseInfo(baseInfo baseInfo) {
this.baseInfo = baseInfo;
}
public Product(String part1, String part2, Integer part3, Integer part4, baseInfo baseInfo) {
this.part1 = part1;
this.part2 = part2;
this.part3 = part3;
this.part4 = part4;
this.baseInfo = baseInfo;
}
@Override
protected Product clone() throws CloneNotSupportedException {
Product clone = (Product) super.clone();
baseInfo clone1 = this.baseInfo.clone();
clone.setbaseInfo(clone1);
return clone;
}
@Override
public String toString() {
return super.hashCode()+"Product{" +
"part1='" + part1 + ''' +
", part2='" + part2 + ''' +
", part3=" + part3 +
", part4=" + part4 +
", baseInfo=" + baseInfo +
'}';
}
}
这时候我们再看一下运行结果,就发现clone出来的对象和原始对象已经没有依赖关系:
如果我们的baseInfo中还有引用对象,那我们必须对改对象也实现cloneable接口。这就是深拷贝。
注意数组类型已实现cloneable接口,但是数组类型clone出来的是浅拷贝而不是深拷贝。
我们在拷贝的时候要注意浅拷贝和深拷贝,如果类中有引用类型时我们就要进行递归式的实现cloneable接口。
对于引用对象比较复杂的情况我们还有另一种方式来实现:序列化方式实现深拷贝,首先原始对象和其包含的引用对象都要实现序列化接口,然后通过流进行序列化操作,这种操作不推荐使用,会非常耗性能,具体如下:
class baseInfo implements Cloneable,Serializable{
private static final long serialVersionUID = 42L;
private String companyName;
public baseInfo(String companyName) {
this.companyName = companyName;
}
public String getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
@Override
protected baseInfo clone() throws CloneNotSupportedException {
return ((baseInfo) super.clone());
}
@Override
public String toString() {
return super.hashCode()+"|baseInfo{" +
"companyName='" + companyName + ''' +
'}';
}
}
class Product implements Cloneable,Serializable{
private static final long serialVersionUID = 42L;
private String part1;
private String part2;
private Integer part3;
private Integer part4;
private baseInfo baseInfo;
public String getPart1() {
return part1;
}
public void setPart1(String part1) {
this.part1 = part1;
}
public String getPart2() {
return part2;
}
public void setPart2(String part2) {
this.part2 = part2;
}
public Integer getPart3() {
return part3;
}
public void setPart3(Integer part3) {
this.part3 = part3;
}
public Integer getPart4() {
return part4;
}
public void setPart4(Integer part4) {
this.part4 = part4;
}
public baseInfo getbaseInfo() {
return baseInfo;
}
public void setbaseInfo(baseInfo baseInfo) {
this.baseInfo = baseInfo;
}
public Product(String part1, String part2, Integer part3, Integer part4, baseInfo baseInfo) {
this.part1 = part1;
this.part2 = part2;
this.part3 = part3;
this.part4 = part4;
this.baseInfo = baseInfo;
}
@Override
protected Product clone() throws CloneNotSupportedException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try (ObjectOutputStream oos = new ObjectOutputStream(bos)){
oos.writeObject(this);
} catch (IOException e) {
e.printStackTrace();
}
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
try (ObjectInputStream ois = new ObjectInputStream(bis);){
Product object =(Product) ois.readObject();
return object;
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e){
e.printStackTrace();;
}
return null;
}
@Override
public String toString() {
return super.hashCode()+"Product{" +
"part1='" + part1 + ''' +
", part2='" + part2 + ''' +
", part3=" + part3 +
", part4=" + part4 +
", baseInfo=" + baseInfo +
'}';
}
}
我们看一下运行结果:也是可以的
- 源码中的应用
org.springframework.beans.factory.support.AbstractBeanDefinition
java.util.Arrays



