Java 原型模式(克隆模式)

  Java 的设计模式有 23 种,前段时间小编已经介绍了单例模式,由于我们在学习 Spring 的时候在 bean 标签的学习中碰到了今天要讲的原型模式,那么小编就已本文来介绍下原型模式。

原型模式

  在java中我们知道通过new关键字创建的对象是非常繁琐的(类加载判断,内存分配,初始化等),在我们需要大量对象的情况下,原型模式就是我们可以考虑实现的方式。
  原型模式我们也称为克隆模式,即一个某个对象为原型克隆出来一个一模一样的对象,该对象的属性和原型对象一模一样。而且对于原型对象没有任何影响。原型模式的克隆方式有两种:浅克隆和深度克隆

浅克隆

  在浅克隆中,如果原型对象的成员变量是值类型,将复制一份给克隆对象;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。

  简单来说,在浅克隆中,当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制。
在这里插入图片描述

实现

被克隆的对象必须Cloneable,Serializable这两个接口
原型类

/**
 * 原型类:被克隆的类型
 */
public class User implements Cloneable, Serializable {
    
    private String name;
    
    private Date birth;
    
    private int age;

    /**
     * 实现克隆的方法
     * @return
     * @throws CloneNotSupportedException
     */
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
    //此处略过get和set方法
}

测试类

public static void main(String[] args) throws CloneNotSupportedException {
    Date date =  new Date(1231231231231l);
    User user = new User();
    user.setName("zmf");
    user.setAge(18);
    user.setBirth(date);
    System.out.println("----输出原型对象的属性------");
    System.out.println(user);
    System.out.println(user.getName());
    System.out.println(user.getBirth());
    // 克隆对象
    User user1 =(User) user.clone();
    // 修改原型对象中的属性
    date.setTime(123231231231l);
    System.out.println("原型对象修改后的属性:" + user.getBirth());

    // 修改参数
    user1.setName("知性人");
    System.out.println("-------克隆对象的属性-----");
    System.out.println(user1);
    System.out.println(user1.getName());
    System.out.println(user1.getBirth());
}

输出结果:

----输出原型对象的属性------
org.zmf.User@1b6d3586
zmf
Tue Jan 06 16:40:31 CST 2009
原型对象修改后的属性:Tue Nov 27 14:53:51 CST 1973
-------克隆对象的属性-----
org.zmf.User@14ae5a5
知性人
Tue Nov 27 14:53:51 CST 1973

说明:克隆后的 date 属性和原型对象修改后的 date 属性的结果一样 说明两个对象的Date的引用是同一个。由此可以说明,在浅克隆中,当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制。

深克隆

  在深克隆中,无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象,深克隆将原型对象的所有引用对象也复制一份给克隆对象。

  简单来说,在深克隆中,除了对象本身被复制外,对象所包含的所有成员变量也将复制。
在这里插入图片描述

深度克隆(deep clone)有两种实现方式,第一种是在浅克隆的基础上实现,第二种是通过序列化和反序列化实现,我们分别来介绍

第一种方式

在浅克隆的基础上实现
原型类

/**
 * 原型类:被克隆的类型
 * 深度克隆测试
 */
public class User2 implements Cloneable, Serializable {

    private String name;

    private Date birth;

    private int age;

    /**
     * 实现克隆的方法
     * 深度克隆(deep clone)
     */
    public Object clone() throws CloneNotSupportedException{
        Object object = super.clone();
        // 实现深度克隆(deep clone)
        User2 user = (User2)object;
        user.birth = (Date) this.birth.clone();
        return object;
    }
    //此处略过 get 和 set 方法
}

测试类:

public static void main(String[] args) throws CloneNotSupportedException {
    Date date =  new Date(1231231231231l);
    User2 user = new User2();
    user.setName("zmf");
    user.setAge(18);
    user.setBirth(date);
    System.out.println("----输出原型对象的属性------");
    System.out.println(user);
    System.out.println(user.getName());
    System.out.println(user.getBirth());
    // 克隆对象
    User2 user1 =(User2) user.clone();
    // 修改原型对象中的属性
    date.setTime(123231231231l);
    System.out.println("原型对象修改后的属性:" + user.getBirth());
    // 修改参数
    user1.setName("知性人");
    System.out.println("-------克隆对象的属性-----");
    System.out.println(user1);
    System.out.println(user1.getName());
    System.out.println(user1.getBirth());

}

测试结果:

----输出原型对象的属性------
org.zmf.User2@1b6d3586
zmf
Tue Jan 06 16:40:31 CST 2009
原型对象修改后的属性:Tue Nov 27 14:53:51 CST 1973
-------克隆对象的属性-----
org.zmf.User2@14ae5a5
知性人
Tue Jan 06 16:40:31 CST 2009

说明:根据测试得出克隆后的对象的属性并没有随着我们对原型对象Date属性的修改而改变,说明克隆对象的Date属性和原型对象的Date属性引用的不是同一个对象,实现的深度复制。

第二种方式:序列化和反序列化

  • 说明
    序列化: 把对象转换为字节序列的过程。
    反序列化: 把字节序列恢复为对象的过程。
public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException {
    Date date =  new Date(1231231231231L);
    User user = new User();
    user.setName("zmf");
    user.setAge(18);
    user.setBirth(date);
    System.out.println("-----原型对象的属性------");
    System.out.println(user);
    System.out.println(user.getName());
    System.out.println(user.getBirth());

    //使用序列化和反序列化实现深复制
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream(bos);
    oos.writeObject(user);
    byte[] bytes = bos.toByteArray();

    ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
    ObjectInputStream ois = new ObjectInputStream(bis);

    //克隆好的对象!
    User user1 = (User) ois.readObject();

    // 修改原型对象的值
    date.setTime(221321321321321L);
    System.out.println(user.getBirth());

    System.out.println("------克隆对象的属性-------");
    System.out.println(user1);
    System.out.println(user1.getName());
    System.out.println(user1.getBirth());
}

测试结果:

-----原型对象的属性------
org.zmf.User@1b6d3586
zmf
Tue Jan 06 16:40:31 CST 2009
Sat May 24 16:48:41 CST 8983
------克隆对象的属性-------
org.zmf.User@5f184fc6
zmf
Tue Jan 06 16:40:31 CST 2009

实现了和第一种实现方式相同的效果~实现了深度克隆

总结

实现对象克隆有两种方式:

 1). 实现Cloneable接口并重写Object类中的clone()方法;

 2). 实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值