Java设计模式之原型模式

文章介绍了原型模式中的浅克隆和深克隆概念,展示了在Java中如何通过实现Cloneable接口进行浅克隆,以及如何使用序列化解决深克隆的问题。浅克隆只复制对象的引用,深克隆则复制所有引用对象。

目录

概述

结构

实现

创建具体原型类

测试

使用场景

浅克隆问题解析

创建具体原型类

测试

使用深克隆解决


概述

用一个已经创建出来的实例作为对象,通过复制该原型对象创建出一个和原型对象相同的新对象。

结构

  • 抽象原型类:规定了具体原型对象必须实现clone()【克隆】方法。
  • 具体原型类:实现抽象原型类的clone()方法,他是可被复制的对象。
  • 访问类:使用具体原型类中的clone()方法来复制新的对象。

 

实现

原型模式的克隆分为浅克隆与深克隆

浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。

深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。

在Java中Object类中提供的clone()方法实现的克隆,Cloneable 接口是抽象原型类,实现了Cloneable接口的的类是具体原型类。

创建具体原型类

public class Realizetype implements Cloneable {
    public Realizetype(){
        System.out.println("通过无参构造器创建出一个具体对象");
    }

    //重写Cloneable接口中的clone()方法,由于是克隆出来的对象,那么返回值一定是Realizetype
    @Override
    protected Realizetype clone() throws CloneNotSupportedException {
        System.out.println("通过克隆创建出一个对象");
        return (Realizetype) super.clone();
    }
}

测试

public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {
        //创建出原型对象
        Realizetype realizetype = new Realizetype();

        Realizetype clone = realizetype.clone();
        System.out.println("创建出克隆对象,并且原型对象与克隆对象地址比较结果为"+(clone==realizetype));
    }
}

运行结果为: 

通过无参构造器创建出一个具体对象

通过克隆创建出一个对象

创建出克隆对象,并且原型对象与克隆对象地址比较结果为false

由输出结果我们可以看出来通过clone()方法创建出来的对象,并非是通过构造器创建出来的对象并且是创建出了一个新的对象分配了新的地址。

使用场景

对象的创建非常复杂,可以使用原型模式快捷的创建对象。

性能和安全要求比较高。

浅克隆问题解析

先测试一个案例

创建具体原型类

public class Citation implements Cloneable {
    private String name;

    public String getName() {
        return name;
    }

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

    public void show(){
        System.out.println(name+"获得了奖章");
    }

    public Citation clone() throws CloneNotSupportedException {
        return (Citation) super.clone();
    }
}

测试

public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {
        Citation citation = new Citation();
        Citation clone = citation.clone();
        citation.setName("张三");
        clone.setName("李四");
        citation.show();
        clone.show();
    }
}

以上代码运行结果为:

张三获得了奖章

李四获得了奖章

对于基本类型String,即使是没有进行赋值就进行克隆,也是没有问题的。

那么修改一下代码,创建一个Student类来修改一下Citation类

public class Student {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
public class Citation implements Cloneable {
	private String sex;
    
    private Student student;

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }
    
    public Student getStudent() {
        return student;
    }

    public void setStudent(Student student) {
        this.student = student;
    }

    public void show(){
        System.out.println(student.getName()+sex+"获得了奖章");
    }

    public Citation clone() throws CloneNotSupportedException {
        return (Citation) super.clone();
    }
}

 再次测试

public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {
        //创建出原型对象
        Citation citation = new Citation();
        //向原型对象设置非基本类型的数据
        Student student = new Student();
        student.setName("张三");
        citation.setStudent(student);
        citation.setSex("男");
        
        //克隆出一个原型对象
        Citation clone = citation.clone();
        //拿到克隆出来的非基本类型数据设置克隆值
        clone.getStudent().setName("李四");
        clone.setStudent(student);
        clone.setSex("女");
        citation.show();
        clone.show();
    }
}

以上代码运行结果为: 

李四男获得了奖章

李四女获得了奖章

这就是对 新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址 的体现,对于基本类型,克隆出来的原型对象可以对其任意赋值而不会影响到原有的原型对象,但是对于非基本类型的Student,两个对象指向的内存地址是同一个,任意一个对象修改其属性都会影响到另一个对象。

使用深克隆解决

将Citation与Student实现序列化Serializable接口,这里就不放了,直接写测试代码

public class Client {
    public static void main(String[] args) throws Exception {
        //创建出原型对象
        Citation citation = new Citation();
        //向原型对象设置非基本类型的数据
        Student student = new Student();
        student.setName("张三");
        citation.setStudent(student);
        citation.setSex("男");

        //将要克隆的原型对象进行序列化写入文件
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("a.txt"));
        oos.writeObject(citation);
        oos.close();

        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("a.txt"));
        Citation clone = (Citation) ois.readObject();
        ois.close();

        clone.getStudent().setName("李四");
        clone.setSex("女");

        citation.show();
        clone.show();
    }
}

以上代码运行结果为: 

张三男获得了奖章

李四女获得了奖章

原理就是通过对象流将原型对象序列化写入文件,然后读取文件中的对象信息,创建出一个新的对象,这样做非基本类型的属性自然不会是指向同一个内存地址。

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zmbwcx2003

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值