IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 设计模式之原型模式 -> 正文阅读

[Java知识库]设计模式之原型模式

1. 原型模式

原型模式用来解决对象的创建问题,它是指用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的对象。其类图结构如下:

在这里插入图片描述

  • 抽象原型类(Prototype):声明了clone()方法的接口或基类,其中clone()方法必须由派生类实现。
  • 具体原型类(ConcretePrototype):用于实现或扩展clone()方法的类,clone()方法必须要实现。

原型模式的优点:

  • JAVA自带的原型模式基于内存二进制流的复制,在性能上比直接new一个对象更加优良。
  • 原型模式保留了对象在某一时刻的状态,简化了对象创建过程,可辅助实现撤销操作恢复到历史某一个状态。

原型模式的缺点:

  • 需要为每一个具体的原型类配置一个clone()方法
  • clone()方法位于类的内部,当对已有类进行改造时需要修改代码,违背开闭原则。
  • 当对象之间存在多重嵌套引用时,为了实现深克隆,每层对象都必须支持深克隆,实现起来比较麻烦。

2. 原型实现

生物学上有著名的克隆羊案例,接下来从程序的角度利用原型模式体会克隆羊案例。

新建一个羊类,类名为:Sheep,实现Cloneable接口,内容如下:

public class Sheep implements Cloneable{

    /**
     * 体重
     */
    private Double weight;

    /**
     * 颜色
     */
    private String color;

    /**
     * 构造方法
     */
    public Sheep (){

    }

    /**
     * 构造方法
     */
    public Sheep (Double weight, String color){
        this.weight = weight;
        this.color = color;
    }

    @Override
    public String toString() {
        return String.format("对象地址:%d 体重:%.2fKg 颜色:%s", hashCode(), weight, color);
    }

    @Override
    protected Sheep clone() {
        try{
            return (Sheep) super.clone();
        } catch (Exception exception){
            exception.printStackTrace();
        }
        return new Sheep();
    }
}

新建一个客户端类,类名为:Client,内容如下:

public class Client {
    public static void main(String[] args) {
        // 创建原型羊
        Sheep sheep = new Sheep(30.0D, "白色");
        System.out.println("原型羊:" + sheep);

        // 根据原型羊进行克隆得到克隆羊
        Sheep cloneSheep = sheep.clone();
        System.out.println("克隆羊:" + cloneSheep);
    }
}

在这里插入图片描述

4. 深浅拷贝

  • 浅拷贝:对象的浅拷贝是指拷贝一个对象时,新对象和原来对象完全相同,但是对于可变的引用数据类型时,仍指向原型对象属性所指向的内存地址。
  • 深拷贝:对象的深拷贝是指拷贝一个对象时,属性中引用的其他对象也会被克隆,不再指向原型对象属性的内存地址。

4.1 浅拷贝案例

克隆羊案例中使用的super.clone()方法使用的是Object类的clone()方法,它是一个浅拷贝,我们给羊这个类新增一个属性friend表示羊的朋友,代码如下:

public class Sheep implements Cloneable{
    /**
     * 体重
     */
    private Double weight;

    /**
     * 颜色
     */
    private String color;

    /**
     * 朋友
     */
    private Sheep friend;

    /**
     * 构造方法
     */
    public Sheep (){
    }

    /**
     * 构造方法
     */
    public Sheep (Double weight, String color, Sheep friend){
        this.weight = weight;
        this.color = color;
        this.friend = friend;
    }

    public Sheep getFriend() {
        return friend;
    }

    public Double getWeight() {
        return weight;
    }

    public void setWeight(Double weight) {
        this.weight = weight;
    }

    @Override
    public String toString() {
        return String.format("对象地址:%d 体重:%.2fKg 颜色:%s 朋友:[%s]", hashCode(), weight, color, friend);
    }

    @Override
    protected Sheep clone() {
        try{
            return (Sheep) super.clone();
        } catch (Exception exception){
            exception.printStackTrace();
        }
        return new Sheep();
    }
}

新建客户端测试,内容如下:

public class Client {
    public static void main(String[] args) {
        // 创建一个朋友羊,白色的30Kg,它没有朋友
        Sheep friend = new Sheep(30.0D, "白色", null);

        // 创建一个原型羊,黑色的20Kg,朋友是朋友羊
        Sheep sheep = new Sheep(20.0D, "黑色", friend);

        // 根据原型羊克隆出克隆羊
        Sheep cloneSheep = sheep.clone();

        // 打印原型羊和克隆羊
        System.out.println(String.format("原型羊:%s", sheep));
        System.out.println(String.format("克隆羊:%s", cloneSheep));
        System.out.println("----------------------朋友羊长胖了-----------------------------");

        // 原型羊的朋友吃草长胖了10KG
        sheep.getFriend().setWeight(friend.getWeight() + 10);

        // 打印原型羊和克隆羊
        System.out.println(String.format("原型羊:%s", sheep));
        System.out.println(String.format("克隆羊:%s", cloneSheep));
    }
}

在这里插入图片描述

在这个案例中我们修改原型羊的朋友的体重时,克隆羊的朋友的体重也一起跟着修改了,克隆羊的朋友属性和原型羊的朋友属性指向的是同一个对象的地址,这是因为friend属性的类型是一个Sheep引用类型,clone()方法克隆原型羊的时候克隆的是引用。

在这里插入图片描述

4.2 深拷贝案例

有时我们希望克隆对象的可变引用类型也是一个新的对象,那么原型羊的朋友羊再长胖了和克隆羊也没有关系,我们的朋友不是同一个朋友,这就是对象的深拷贝,此时需要对原型对象中的可变引用类型的属性做特殊处理,特殊处理clone()方法如下:

@Override
protected Sheep clone() {
    try{
        Sheep cloneSheep = (Sheep) super.clone();
        // 对可变引用类型的属性特殊处理
        if (!Objects.isNull(cloneSheep.friend)){
            cloneSheep.friend = cloneSheep.friend.clone();
        }
        return cloneSheep;
    } catch (Exception exception){
        exception.printStackTrace();
    }
    return new Sheep();
}

新建客户端测试,内容如下:

public class Client {
    public static void main(String[] args) {
        // 创建一个朋友羊,白色的30Kg,它没有朋友
        Sheep friend = new Sheep(30.0D, "白色", null);

        // 创建一个原型羊,黑色的20Kg,朋友是朋友羊
        Sheep sheep = new Sheep(20.0D, "黑色", friend);

        // 根据原型羊克隆出克隆羊
        Sheep cloneSheep = sheep.clone();

        // 打印原型羊和克隆羊
        System.out.println(String.format("原型羊:%s", sheep));
        System.out.println(String.format("克隆羊:%s", cloneSheep));
        System.out.println("----------------------朋友羊长胖了-----------------------------");

        // 原型羊的朋友吃草长胖了10KG
        sheep.getFriend().setWeight(friend.getWeight() + 10);

        // 打印原型羊和克隆羊
        System.out.println(String.format("原型羊:%s", sheep));
        System.out.println(String.format("克隆羊:%s", cloneSheep));
    }
}

在这里插入图片描述

在对象的深克隆中,克隆羊和原型羊的朋友不再是同一个对象,原型羊的朋友长胖了,但是克隆羊的朋友并没有长胖。

在这里插入图片描述

4.3 序列化实现深拷贝

对于对象的深度克隆需要保证该对象中每个可变的引用类型属性都实现Cloneable接口重写clone()方法,在clone()方法中还得对这些属性特殊处理,这样clone()方法变得复杂。JAVA中还可以通过序列化接口Serializable将对象在内存中序列化和反序列化一下来对对象进行深克隆,此时需要对象中可变的引用属性类型都要实现Serializable接口

新建一个马类作为原型羊的朋友,实现Serializable接口,类名为Horse,内容如下:

public class Horse implements Serializable {
    /**
     * 体重
     */
    private Double weight;

    /**
     * 构造方法
     */
    public Horse(){

    }

    /**
     * 构造方法
     */
    public Horse(Double weight){
        this.weight = weight;
    }

    public Double getWeight() {
        return weight;
    }

    public void setWeight(Double weight) {
        this.weight = weight;
    }

    @Override
    public String toString() {
        return String.format("对象地址:%d 体重:%.2fKg ", hashCode(), weight);
    }
}

新建一个羊类,包含一个朋友属性类型为马类,实现Serializable接口,类名为Sheep,内容如下:

public class Sheep implements Serializable {
    /**
     * 体重
     */
    private Double weight;

    /**
     * 颜色
     */
    private String color;

    /**
     * 朋友
     */
    private Horse friend;

    /**
     * 构造方法
     */
    public Sheep(){
    }

    /**
     * 构造方法
     */
    public Sheep(Double weight, String color, Horse friend){
        this.weight = weight;
        this.color = color;
        this.friend = friend;
    }

    public Horse getFriend() {
        return friend;
    }

    @Override
    public String toString() {
        return String.format("对象地址:%d 体重:%.2fKg 颜色:%s 朋友:[%s]", hashCode(), weight, color, friend);
    }
}

新建一个工具类用来进行序列化和反序列化来克隆,类名为BeanUtil,内容如下:

public class BeanUtil {
    /**
     * 深克隆对象
     */
    public static <T> T clone(Serializable bean, Class<T> type){
        // 输出流
        ByteArrayOutputStream byteArrayOutputStream = null; new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = null;

        // 输入流
        ByteArrayInputStream byteArrayInputStream = null;
        ObjectInputStream objectInputStream = null;
        try {
            // 先序列化,即把对象写入内存流
            byteArrayOutputStream = new ByteArrayOutputStream();
            objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
            objectOutputStream.writeObject(bean);
            objectOutputStream.flush();

            // 反序列化,即把内存流读成对象
            byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
            objectInputStream = new ObjectInputStream(byteArrayInputStream);
            return  (T) objectInputStream.readObject();
        } catch (Exception exception) {
            exception.printStackTrace();
        } finally {
            try{
                // 关闭流,后开的先关
                if (!Objects.isNull(objectInputStream)) {
                    objectInputStream.close();
                }
                if (!Objects.isNull(byteArrayInputStream)) {
                    byteArrayInputStream.close();
                }
                if (!Objects.isNull(byteArrayOutputStream)) {
                    byteArrayOutputStream.close();
                }
                if (!Objects.isNull(objectOutputStream)) {
                    objectOutputStream.close();
                }
            } catch (Exception exception){
                exception.printStackTrace();
            }
        }
        return null;
    }
}

新建一个客户端测试,类名为Client,内容如下:

public class Client {
    public static void main(String[] args) {
        // 创建一个马,体重30Kg,作为原型羊的朋友
        Horse friend = new Horse(30.0D);

        // 创建原型羊,体重20Kg白色,朋友是马
        Sheep sheep = new Sheep(20.0D, "白色", friend);

        // 通过序列化方式进行深克隆
        Sheep cloneSheep = BeanUtil.clone(sheep, Sheep.class);

        // 打印原型羊和克隆羊
        System.out.println(String.format("原型羊:%s", sheep));
        System.out.println(String.format("克隆羊:%s", cloneSheep));
        System.out.println("----------------------朋友马长胖了-----------------------------");

        // 原型羊的朋友吃草长胖了10KG
        sheep.getFriend().setWeight(friend.getWeight() + 10);

        // 打印原型羊和克隆羊
        System.out.println(String.format("原型羊:%s", sheep));
        System.out.println(String.format("克隆羊:%s", cloneSheep));
    }
}

在这里插入图片描述

原型羊的朋友长胖了但是克隆羊的朋友并没有长胖,实现了对象的深克隆。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-03-04 15:22:29  更:2022-03-04 15:25:17 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 10:54:57-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码