设计模式——原型模式

这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。

在java中,克隆有两种,深克隆和浅克隆。在完成设计模式讲解之后会进行深克隆和浅克隆的学习。

Java提供了一个标记接口——Cloneable,实现该接口完成标记,在JVM中具有这个标记的对象才有可能被拷贝。如果不实现该接口,克隆对象会抛出CloneNotSupportedException异常。

  1. 原型类 Prototype:声明一个克隆自己的接口。
  2. 具体原型类 Realizetype:实现原型类的 clone() 方法,实现克隆自己的操作。它是可被复制的对象,可以有多个。
  3. 访问类 PrototypeTes:使用具体原型类中的 clone() 方法来复制新的对象。

 实现:浅克隆和深克隆

若要改变一个对象,但同时不想改变调用者的对象,就要针对该对象制作一个本地副本。这里我们讨论的是对象,如果是基本数据类型,就很简单了,只需要重新定义一个变量名称然后赋值即可。如果是一个对象,可能有些人说了,我直接new一个新的对象就可以了,这确实是一种解决方式,可是有一些在开发中需要使用的对象经过若干逻辑其中的属性早已经不再是初始值了。如果new不行,那么我重新声明一个然后使用=进行赋值,对象如果使用"="赋值,那么两个对象在内存中会指向同一个地址,最终结果就是新对象所做的任何改动都会影响原对象中的值。制作本地副本简单的一种方式就是使用Java自身提供的clone()方法,该方法位于Object类中,权限修饰符是protected。Clone就是克隆的意思,即制作一个一模一样的副本。克隆有最常见的有两种方式即:深克隆和浅克隆,有时也叫做深复制和浅复制。

浅克隆

被克隆对象中所有变量的值都含有和原来对象相同的值,请记住这里所说的是值都是相同的。如果原对象中的变量是基本数据类型的,该变量会复制一份跟克隆对象相同的对象。则会将该引用类型的地址复制一份给克隆对象,原对象和克隆对象中该成员变量的指向相同的内存地址。也就是说如果修改克隆对象中引用类型的值,同样会影响原对象中引用类型的值,反之原对象修改引用类型的值,克隆对象中引用类型值也会跟随着改变。

在Java中通过Object的clone()方法默认实现的就是浅克隆。

例子:

public class Area {
    private String country;

    private String area;

    public Area(String country, String area) {
        this.country = country;
        this.area = area;
    }

    public String getCountry() {
        return country;
    }

    public void setCountry(String country) {
        this.country = country;
    }

    public String getArea() {
        return area;
    }

    public void setArea(String area) {
        this.area = area;
    }
}
//实现cloneale
public class Rapper implements Cloneable{
    private String name;
    // 一个引用
    private Area area;

    public Rapper(String name, Area area) {
        this.name = name;
        this.area = area;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Area getArea() {
        return area;
    }

    public void setArea(Area area) {
        this.area = area;
    }
    // 浅克隆重写object方法

    @Override
    protected Rapper clone() throws CloneNotSupportedException {
        return (Rapper) super.clone();
    }

    @Override
    public String toString() {
        return "Rapper{" +
                "name='" + name + '\'' +
                ", area=" + area +
                '}';
    }
}

 结果

Rapper{name='王以太', area=com.prototype.Area@4b67cf4d}
Rapper{name='王以太', area=com.prototype.Area@4b67cf4d}
false
true

从运行结果看,我们能看出:

  1. Rapper类源对象rapper和克隆对象clone虽然内容一致,但两个对象的地址不一致。
  2. Area类源对象和克隆对象不但内容一样,地址也一样。他們指向同一个地址。 

此时如果对clone后的Area类进行修改,源Area也会被修改

public class Main {
    public static void main(String[] args) {
        Area area = new Area("china", "chongqing");
        Rapper rapper = new Rapper("王以太", area);
        System.out.println(rapper);
        try {
            Rapper clone = rapper.clone();
            clone.getArea().setRegion("sichuan");
            System.out.println(clone);
            System.out.println(rapper == clone);
            System.out.println(rapper.getArea() == area);
            System.out.println(rapper.getArea().getRegion());
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

    }
}

结果

Rapper{name='王以太', area =chongqing}
Rapper{name='王以太', area =sichuan}
false
true
sichuan

 如果要解决浅克隆在Area方法中实现Clonable接口并且重写clone方法即可。

public class Area implements Cloneable{
    private String country;

    @Override
    protected Area clone() throws CloneNotSupportedException {
        return (Area) super.clone();
    }

    private String region;

    public Area(String country, String region) {
        this.country = country;
        this.region = region;
    }

    public String getCountry() {
        return country;
    }

    public void setCountry(String country) {
        this.country = country;
    }

    public String getRegion() {
        return region;
    }

    public void setRegion(String region) {
        this.region = region;
    }
}
//实现cloneale
public class Rapper implements Cloneable{
    private String name;
    // 一个引用
    private Area area;

    public Rapper(String name, Area area) {
        this.name = name;
        this.area = area;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Area getArea() {
        return area;
    }

    public void setArea(Area area) {
        this.area = area;
    }
    // 浅克隆重写object方法

    @Override
    protected Rapper clone() throws CloneNotSupportedException {
        Rapper rapper = (Rapper) super.clone();
        rapper.area = rapper.area.clone();
        return rapper;
    }

    @Override
    public String toString() {
        return "Rapper{" +
                "name='" + name + '\'' +
                ", area =" + area.getRegion() +
                '}';
    }
}

结果

Rapper{name='王以太', area =chongqing}
Rapper{name='王以太', area =sichuan}
false
true
chongqing

深克隆

序列化方式实现

public class Area implements Serializable {
    private String country;

    private String region;

    public Area(String country, String region) {
        this.country = country;
        this.region = region;
    }

    public String getCountry() {
        return country;
    }

    public void setCountry(String country) {
        this.country = country;
    }


    public String getRegion() {
        return region;
    }

    public void setRegion(String region) {
        this.region = region;
    }
}
//实现cloneale
public class Rapper implements Cloneable, Serializable {
    private String name;
    // 一个引用
    private Area area;

    public Rapper(String name, Area area) {
        this.name = name;
        this.area = area;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Area getArea() {
        return area;
    }

    public void setArea(Area area) {
        this.area = area;
    }
    // 浅克隆重写object方法

    @Override
    protected Rapper clone() throws CloneNotSupportedException {
        Rapper rapper = null;
        try { // 将该对象序列化成流,因为写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。所以利用这个特性可以实现对象的深拷贝
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(this);
            // 将流序列化成对象
            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            rapper = (Rapper) ois.readObject();
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
        return rapper;
    }

    @Override
    public String toString() {
        return "Rapper{" +
                "name='" + name + '\'' +
                ", area =" + area.getRegion() +
                '}';
    }
}

 结果

Rapper{name='王以太', area =chongqing}
Rapper{name='王以太', area =sichuan}
false
true
chongqing

可以看到,序列化后进行整体克隆并没有因为clone类修改而改变 

参考:

23种设计模式——原型模式-CSDN博客

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值