原型模式详解

原型模式

1.1原型模式概述

1.1.1原型模式定义

原型模式(Prototype Pattern)指原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象,属于创建型设计模式。
原型模式的核心在于复制原型对象。以系统中已存在的一个对象为原型,基于二进制流进行复制,不需要经历对象的初始化过程,提高了性能。

1.1.2原型模式的适用场景

  • 适用于需要创建大对象,或者对象初始化非常耗时,占用太多资源,可以考虑使用原型模式
  • 系统中大量使用该类对象,且调用者需要重新赋值的重复性操作过多。

1.1.4原型模式的通用写法

public interface IPrototype {
    T clone();
}
public class ConcreatePrototypeA implements IPrototype{

    private String desc;

    public ConcreatePrototypeA(String desc) {
        this.desc = desc;
    }

    @Override
    public ConcreatePrototypeA clone() {
        return new ConcreatePrototypeA(this.desc);
    }

    @Override
    public String toString() {
        return "ConcreatePrototypeA{" +
                "desc='" + desc + '\'' +
                '}';
    }
}

1.2深克隆与浅克隆

1.2.1浅克隆分析

在JAVA里,我们不需要创建原型接口,JAVA内置的原型接口—Cloneable接口,自定义的类只需要实现该接口并重写Object.clone()方法即可完成本类的复制。
这块需要注意,点开Cloneable接口查看源码:

/**
 * A class implements the Cloneable interface to
 * indicate to the {@link java.lang.Object#clone()} method that it
 * is legal for that method to make a
 * field-for-field copy of instances of that class.

 *
 * Invoking Object's clone method on an instance that does not implement the
 * Cloneable interface results in the exception
 * CloneNotSupportedException being thrown.

 *
 * By convention, classes that implement this interface should override
 * Object.clone (which is protected) with a public method.

 * See {@link java.lang.Object#clone()} for details on overriding this
 * method.

 *
 * Note that this interface does not contain the clone method.

 * Therefore, it is not possible to clone an object merely by virtue of the
 * fact that it implements this interface.  Even if the clone method is invoked
 * reflectively, there is no guarantee that it will succeed.

 *
 * @author  unascribed
 * @see     java.lang.CloneNotSupportedException
 * @see     java.lang.Object#clone()
 * @since   JDK1.0
 */
public interface Cloneable {
}

可以看到Cloneable其实是空接口,而clone方法其实属于Object的方法。
翻译Cloneable接口的注释我们就明白了,Cloneable其实就是一个标记接口,只有实现这个接口后,然后在类中重写Object中的clone方法,然后通过类调用clone方法才能克隆成功,
如果不实现这个接口,则会抛出CloneNotSupportedException(克隆不被支持)异常。
接下来,做个小测试:

package org.example.prototype;

import lombok.Data;

import java.util.List;

@Data
public class Person implements Cloneable{
    private int age;
    private String name;
    private List hobbies;

    public Person clone(){
        try {
            return (Person) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public String toString() {
        return "Person{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", hobbies=" + hobbies +
                '}';
    }
}
package org.example.prototype;

import java.util.ArrayList;
import java.util.List;

public class PrototypeTest {
    public static void main(String[] args) {
        Person person = new Person();
        person.setAge(18);
        person.setName("yml");
        List hobbies = new ArrayList<>();
        hobbies.add("篮球");
        hobbies.add("唱跳rap");
        person.setHobbies(hobbies);
        System.out.println(person);
        Person clone = person.clone();
        clone.getHobbies().add("游泳");
        System.out.println(person);
    }
}

上边代码使用super.clone给Person对象进行克隆,当我们改变克隆对象的属性时,得到的结果:

原型模式详解
以上结果说明,原型对象和克隆对象的引用对象属性指向同一对象的引用,意味着这种方式克隆的不是值而是引用的地址。这个显然不符合我们的预期,因为我们希望克隆对象和原型对象是两个独立的对象,互不影响。
Java自带的克隆方式为浅克隆。而如果我们想进行深克隆,可以浅克隆之后,手动给克隆对象的相关属性分配内存,不过这种方式太重复且繁琐,在Java中,一般使用序列化和反序列化实现深克隆。

1.2.2使用序列化实现深克隆

package org.example.prototype;

import lombok.Data;

import java.io.*;
import java.util.List;

@Data
public class Person implements Cloneable,Serializable{
    private int age;
    private String name;
    private List hobbies;

    public Person clone(){
        try {
            return (Person) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }

    public Person deepClone() throws IOException, ClassNotFoundException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream stream = new ObjectOutputStream(bos);
        stream.writeObject(this);
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
        return (Person) objectInputStream.readObject();
    }

    @Override
    public String toString() {
        return "Person{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", hobbies=" + hobbies +
                '}';
    }
}
public class PrototypeTest {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Person person = new Person();
        person.setAge(18);
        person.setName("yml");
        List hobbies = new ArrayList<>();
        hobbies.add("篮球");
        hobbies.add("唱跳rap");
        person.setHobbies(hobbies);
        System.out.println(person);
        Person deepClone = person.deepClone();
        deepClone.getHobbies().add("游泳");
        System.out.println(person);
    }
}

原型模式详解
从运行结果看来,我们的确实现了深克隆。

1.2.3原型模式在源码中的应用

  • JDK中ArrayList源码
 /**
     * Returns a shallow copy of this ArrayList instance.  (The
     * elements themselves are not copied.)
     *
     * @return a clone of this ArrayList instance
     */
    public Object clone() {
        try {
            ArrayList v = (ArrayList) super.clone();
            v.elementData = Arrays.copyOf(elementData, size);
            v.modCount = 0;
            return v;
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError(e);
        }
    }

其实ArrayList是对所有元素遍历一遍,这种也是实现深克隆的一种方式,但是这种相当于硬编码,更推荐序列化方式实现深克隆。

Original: https://www.cnblogs.com/amazing-yml/p/15961606.html
Author: amazing_yml
Title: 原型模式详解

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/579004/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • springcloud 整合 druid 阿里的数据库连接池

    配置 pom 配置 application.properties 3.Config 配置类 ​——————————————-…

    Java 2023年6月8日
    069
  • SpringBoot快速入门

    SpringBoot笔记 1.开端介绍 1.两种核心配置文件同时存在(properties的优先级高于yml) 2.多环境下核心配置文件 3.获取自定义配置 4.将自定义配置映射到…

    Java 2023年6月9日
    081
  • Nginx增加网页认证功能

    Nginx增加网页认证功能 增加认证功能模块 ngx_http_auth_basic_module 模块实现让访问者,只有输入正确的用户密码才允许访问web内容。web上的一些内容…

    Java 2023年6月8日
    099
  • 不落人后!简单好用的低代码开发,快速搭建智慧管理信息系统

    引言 伴随着社会的发展与时代的进步,管理与我们的生活密不可分,管理信息系统应用的范围十分广泛,体现在生活里的点点滴滴。管理信息系统不仅可以快速处理大量信息,还可以提高人们的工作效率…

    Java 2023年6月5日
    0132
  • 【LEETCODE】73、根据身高重建队列 第406题

    说实话,这道题我没想出来,但是看解题报告题解比较让人觉得眼前一亮,这里记录下来 package y2019.Algorithm.greedy.medium; import java…

    Java 2023年6月5日
    078
  • layui 合并指定单元格

    一:函数调用时机: 二:合并单元格函数 //合并日期单元格 function merge (res ){ var data =res .data ; console .log (d…

    Java 2023年6月5日
    078
  • java_序列化和反序列化

    1.序列化的作用: 序列化的原本意图是希望对一个Java对象作一下”变换”,变成字节序列,这样一来方便持久化存储到磁盘,避免程序运行结束后对象就从内存里消失…

    Java 2023年6月5日
    066
  • 操作线程的方法

    操作线程的方法操作线程有很多方法,这些方法可以使线程从某一种状态过渡到另一种状态。 线程的休眠能控制线程行为的方法之一是调用sleep()方法,sleep()方法可以指定线程休眠的…

    Java 2023年6月9日
    078
  • 设计模式之建造者模式–工厂模式、生成器模式、单例模式、原型模式

    一 、学习原型模式,从模式动机、模式定义与分析、模式实例及代码要点、模式优缺点及应用场景四个方面分析该模式。 1模式动机: √复制一个对象,从而克隆出多个与原型对象一模一样的对象—…

    Java 2023年6月7日
    081
  • spring 工具类大集合

    org.springframework.util.AntPathMatcher 它可以帮助我们做一些路径的匹配,可以用于路径映射规则匹配 。? (任何单字符) * (任意数量字符)…

    Java 2023年6月5日
    0117
  • SpringBoot启动流程原理解析(二)

    在上一章我们分析了SpingBoot启动流程中实例化SpingApplication的过程。 return new SpringApplication(primarySources…

    Java 2023年6月5日
    077
  • 制作Elasticsearch索引初始化docker镜像

    背景 由于公司使用自己的编排系统(基于docker),所以在业务服务启动时,需要对某些服务对象进行初始化,本文就对es索引的初始做一个小的分享; 大致流程 初始化步骤主要是基于do…

    Java 2023年6月8日
    074
  • Mysql学习

    显示字符集编码 mysql架构 逻辑架构 Client : 提供连接MySQL服务器功能的常用工具集 Server : MySQL实例,真正提供数据存储和数据处理功能的MySQL服…

    Java 2023年6月8日
    092
  • Java-仿华为商城项目-SpringBoot+MyBatis+MySQL

    用户注册 1.用户的注册功能。相当于在做数据的插入操作 insert into t_user (username,password) values (值列表) 2.在用户注册时要首…

    Java 2023年6月9日
    085
  • springCloud中增加gateway(超详细)

    地址:https://blog.csdn.net/qq_42815754/article/details/94622244 此博客只是为了记忆相关知识点,大部分为网络上的文章,在此…

    Java 2023年5月30日
    083
  • Java高并发教程:Reactor反应器模式

    Java高并发教程:Reactor反应器模式 Reactor反应器模式 到目前为止, 高性能网络编程都绕不开反应器模式。很多著名的服务器软件或者中间件都是基于反应器模式实现的,如N…

    Java 2023年5月29日
    058
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球