设计模式-创建型之原型模式

本文详细介绍了Java中的原型模式,包括如何使用clone()方法实现对象的复制,以及浅克隆与深克隆的区别。通过代码实例展示了如何在不同情况下选择合适的克隆方式,以提高性能和简化对象创建过程。

模式动机

       在软件开发中,有时某些对象创建的过程比较复杂,而且需要频繁创建,那么我们可不可以直接Ctrl C再Ctrl V呢?答案是可以的,原型模式就是解决这类问题,它通过复制一个对象本身,从而克隆出多个与原型对象一模一样的对象出来。

       模式动机

模式定义

       用原型实例指定创建对象的种类,然后通过复制这个原型对象创建出新的对象,无须知道创建的细节,这便是原型模式。

Java中的克隆

  java中所有的类都继承与java.lang.Object,这个类中有一个本地方法clone(),可以将一个java对象复制一份,因此在java中可以使用这个方法来实现对象的克隆,java中的原型模式的实现也因此变得很简单。另外,要想使用clone(),必须实现Cloneable接口,表示这个java类是支持复制的,如果一个类没有实现这个接口,但却调用了clone()方法,那将会报出CloneNotSupportedException的异常。

模式结构

模式结构
       原型模式主要由以下两部分组成:
  抽象原型类:如Prototype
  具体原型类:如ConcretePrototypeA

代码实例

  抽象原型类

public interface Prototype {
    public Object copy();
}

  具体原型类

public class Email implements Prototype,Cloneable{
    private String title;
    @Override
    public Email copy() {
        try {
            return (Email) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
}

  客户端测试类

public class Client {
    public static void main(String[] args) {
        Email e1 = new Email();
        e1.setTitle("我是邮件一");
        Email e2 = e1.copy();
        System.out.println(e2.getTitle());
    }
}

  运行客户端测试类,输出为

我是邮件一

  至此,一个最简单的原型模式已经完成。

深克隆和浅克隆

       在上面的例子中,我们的原型对象Email非常简单,里面只有一个title属性,假如里面包含各种类型,甚至包含一个对象,此时又该如何克隆呢?此时,有两种不同的克隆方式,浅克隆和深克隆。
       
       浅克隆也叫浅度拷贝或者浅度复制:只复制原对象里的8种基本类型及其封装类和String类型,对于对象类型,不会复制,新对象中仍然是原来的引用。
       
       深克隆也叫深度拷贝或者深度复制:不仅复制对象里的8种基本类型及其封装类和String类型,同时也复制原对象类型,完全产生新对象。下面通过实例来说明:

  • 浅克隆
 public class Attachment {
    private String name;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
public class Email implements Prototype,Cloneable{
    private String title;
    private int intA;
    private Integer integerA;
    private Attachment attachment;//这里是另外的一个对象
    @Override
    public Email copy() {
        try {
            return (Email) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public int getIntA() {
        return intA;
    }
    public void setIntA(int intA) {
        this.intA = intA;
    }
    public Integer getIntegerA() {
        return integerA;
    }
    public void setIntegerA(Integer integerA) {
        this.integerA = integerA;
    }
    public Attachment getAttachment() {
        return attachment;
    }
    public void setAttachment(Attachment attachment) {
        this.attachment = attachment;
    }
}
public class Client {
    public static void main(String[] args) {
        Email e1 = new Email();
        e1.setTitle("我是title");
        e1.setIntA(1);
        e1.setIntegerA(new Integer(1));
        Attachment attachment1 = new Attachment();
        attachment1.setName("附件1");
        e1.setAttachment(attachment1);

        Email e2 = e1.copy();
        System.out.println(e1.getAttachment() == e2.getAttachment());//true,说明是同一个引用,并没有复制一个新的对象

        e1.setTitle("title发生改变");
        System.out.println(e1.getTitle());//title发生改变
        System.out.println(e2.getTitle());//我是title
        //克隆对象e2的title并没有发生改变,说明String类型是复制了的

        e1.setIntA(2);
        System.out.println(e1.getIntA());//2
        System.out.println(e2.getIntA());//1
        //克隆对象e2的intA并没有发生改变,说明int类型是复制了的

        e1.setIntegerA(new Integer(2));
        System.out.println(e1.getIntegerA());//2
        System.out.println(e2.getIntegerA());//1
        //克隆对象e2的integerA并没有发生改变,说明Integer类型是复制了的

        e1.getAttachment().setName("attachment的name发生改变");
        System.out.println(e1.getAttachment().getName());//attachment的name发生改变
        System.out.println(e2.getAttachment().getName());//attachment的name发生改变
        //e1修改了attachment的name,e2的attachment的name也跟着变了,说明attachment并没有被复制,e1的attachment跟e2的attachment仍然是同一个。说明对于对象类型,调用clone()方法,并不会被复制,这就是浅克隆
    }
}
  • 深克隆
 //这里跟上面略有不同,这里需要实现Serializable接口,让其可以序列化,因为克隆的时候需要写到流里面
 public class Attachment implements Serializable{
    private static final long serialVersionUID = 1L;
    private String name;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
//这里也要实现Serializable接口
public class Email implements Prototype,Serializable{
    private static final long serialVersionUID = 1L;
    private String title;
    private int intA;
    private Integer integerA;
    private Attachment attachment;
    @Override
    public Email copy() {
        try {
            //这里的克隆方法跟浅克隆就完全不一样了,浅克隆是直接调用clone()方法即可,而深度克隆需要将对象写入流,然后再从流中取出来

            //先把对象写入流
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(this);

            //然后把对象从流里取出来
            byte b[] = bos.toByteArray();
            ByteArrayInputStream bis = new ByteArrayInputStream(b);
            ObjectInputStream ois = new ObjectInputStream(bis);
            return (Email)ois.readObject();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public int getIntA() {
        return intA;
    }
    public void setIntA(int intA) {
        this.intA = intA;
    }
    public Integer getIntegerA() {
        return integerA;
    }
    public void setIntegerA(Integer integerA) {
        this.integerA = integerA;
    }
    public Attachment getAttachment() {
        return attachment;
    }
    public void setAttachment(Attachment attachment) {
        this.attachment = attachment;
    }

}
public class Client {
    public static void main(String[] args) {
        Email e1 = new Email();
        e1.setTitle("我是title");
        e1.setIntA(1);
        e1.setIntegerA(new Integer(1));
        Attachment attachment1 = new Attachment();
        attachment1.setName("附件1");
        e1.setAttachment(attachment1);

        Email e2 = e1.copy();
        System.out.println(e1.getAttachment() == e2.getAttachment());//false,说明不是同一个引用!!!而是一个全新的对象!!!

        e1.setTitle("title发生改变");
        System.out.println(e1.getTitle());//title发生改变
        System.out.println(e2.getTitle());//我是title
        //克隆对象e2的title并没有发生改变,说明String类型是复制了的

        e1.setIntA(2);
        System.out.println(e1.getIntA());//2
        System.out.println(e2.getIntA());//1
        //克隆对象e2的intA并没有发生改变,说明int类型是复制了的

        e1.setIntegerA(new Integer(2));
        System.out.println(e1.getIntegerA());//2
        System.out.println(e2.getIntegerA());//1
        //克隆对象e2的integerA并没有发生改变,说明Integer类型是复制了的

        e1.getAttachment().setName("attachment的name发生改变");
        System.out.println(e1.getAttachment().getName());//attachment的name发生改变
        System.out.println(e2.getAttachment().getName());//附件1
        //e1修改了attachment的name,e2的attachment的name却没有随之改变,依然保持原来的值"附件1",说明attachment也被复制了,是一个全新的对象,这就是深克隆
    }
}

       对比上面两种代码可以看出,浅克隆和深克隆主要有以下不同:

  • 浅克隆不会复制对象类型的属性,深克隆要复制
  • 实现方式不一样,浅克隆是调用clone(),深克隆是使用输入输出流;
  • 浅克隆要实现Cloneable接口,而深克隆不用,但深克隆中原型对象以及里面所包含的对象,要实现Serializable接口

模式优点

  • 当创建的对象比较复杂时,原型模式可以简化创建对象的过程,直接通过一个实例复制得到;
  • 浅克隆是调用clone()方法,这是一个本地(native)方法,直接操作的内存中的二进制流,深度克隆也是通过输入输出流进行复制,性能上比通过new要好,所以当复制对象较大时,通过原型模式可以大大提升性能。

总结

       主要讲了几点,一是java中的clone()方法,必须要实现Cloneable接口;二是深克隆和浅克隆,主要区别在于复制对象类型时,浅克隆不会复制,而深克隆会复制;三是模式的优点,简化创建过程,同时提高了性能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值