设计模式之原型模式(浅拷贝与深拷贝)

  • 模式定义

指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

  • 知识点

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
    }
}

现在我们需要通过拷贝来实现

  1. 实现Cloneable接口。
  2. 实现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 {
        /*Product clone = (Product) super.clone();
        BaseInfo clone1 = this.baseInfo.clone();
        clone.setBaseInfo(clone1);*/
        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

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值