栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

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

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

  • 模式定义

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

  • 知识点

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

 

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/318386.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号