广告
返回顶部
首页 > 资讯 > 后端开发 > Python >Java设计模式之原型模式详解
  • 511
分享到

Java设计模式之原型模式详解

2024-04-02 19:04:59 511人浏览 泡泡鱼

Python 官方文档:入门教程 => 点击学习

摘要

目录一、前言二、优点及适用场景三、原型模式的注意事项四、浅复制和深复制五、浅复制demo演示六、深复制demo演示一、前言 原型模式是一种比较简单的模式,也非常容易理解,实现一个接口

一、前言

原型模式是一种比较简单的模式,也非常容易理解,实现一个接口,重写一个方法即完成了原型模式。在实际应用中,原型模式很少单独出现。经常与其他模式混用,他的原型类Prototype也常用抽象类来替代。

该模式的思想就是将一个对象作为原型,对其进行复制、克隆,产生一个和原对象类似的新对象。在Java中,复制对象是通过clone()实现的,先创建一个原型类,通过实现Cloneable 接口


public class Prototype implements Cloneable {  
 
		public Object clone() throws CloneNotSupportedException {  
			Prototype proto = (Prototype) super.clone();  
			return proto;  
		}  
	}

只需要实现Cloneable接口,覆写clone方法,此处clone方法可以改成任意的名称,因为Cloneable接口是个空接口,你可以任意定义实现类的方法名,如cloneA或者cloneB,因为此处的重点是super.clone()这句话,super.clone()调用的是Object的clone()方法,而在Object类中,clone()是native的,说明这个方法实现并不是使用java语言,是底层C实现阿达

至于cloneA或者cloneB名字可以任意取,是因为要你主动去调用的,所以你名字取成什么,你调用的时候就调用该名字就可以了

二、优点及适用场景

使用原型模式创建对象比直接new一个对象在性能上要好的多,因为上面我也提到过,Object类的clone()是native的,它直接操作内存中的二进制流,特别是复制大对象时,性能的差别非常明显。

使用原型模式的另一个好处是简化对象的创建,使得创建对象就像我们在编辑文档时的复制粘贴一样简单。

因为以上优点,所以在需要重复地创建相似对象时可以考虑使用原型模式。比如需要在一个循环体内创建对象,假如对象创建过程比较复杂或者循环次数很多的话,使用原型模式不但可以简化创建过程,而且可以使系统的整体性能提高很多。

三、原型模式的注意事项

使用原型模式复制对象不会调用类的构造方法。因为对象的复制是通过调用Object类的clone()来完成的,它直接在内存中复制数据,因此不会调用到类的构造方法。不但构造方法中的代码不会执行,甚至连访问权限都对原型模式无效。

说到这里,就得顺便提一下单例模式,在单例模式中,只要将构造方法的访问权限设置为private型,就可以实现单例。但是clone方法直接无视构造方法的权限,所以,单例模式与原型模式是冲突的,在使用时要特别注意。

四、浅复制和深复制

另外还得知道两个特别重要的概念 : 浅复制   深复制

浅复制:将一个对象复制后,基本数据类型的变量都会重新创建,而数组容器对象、引用对象等都不会拷贝,指向的还是原对象所指向的地址。浅拷贝实现 Cloneable,重写clone方法

深复制:将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。简单来说,就是深复制进行了完全彻底的复制,而浅复制不彻底。深拷贝是通过实现 Serializable 读取二进制流

五、浅复制demo演示

首先我们创建一个抽象原型类 Animal.class,实现了Cloneable接口,并且重写了clone方法


package cn.zygxsq.design.module.prototypePattern;
 

public abstract class Animal implements Cloneable{
    private String id;
    public String name;
 
    abstract void shout();
 
    public String getId() {
        return id;
    }
 
    public void setId(String id) {
        this.id = id;
    }
 
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    
    public Object clone() throws CloneNotSupportedException {
        Animal clone = (Animal) super.clone();
        return clone;
    }
}

再创建两个实现类

Dog.class 和Cat.class


package cn.zygxsq.design.module.prototypePattern;
 

public class Dog extends Animal {
    public Dog(){
        name = "狗狗";
    }
 
    @Override
    public void shout() {
        System.out.println("我的叫声是:汪汪汪");
    }
}

package cn.zygxsq.design.module.prototypePattern;
 

public class Cat extends Animal {
    public Cat(){
        name = "猫猫";
    }
 
    @Override
    public void shout() {
        System.out.println("我的叫声是:喵喵喵");
    }
}

然后创建一个数据缓存类,用户存储从数据库中获取到的大对象数据或者曾经使用过的大对象数据

以后下一次想要再次对这个对象数据操作的时候,直接从缓存里获取并且clone一个


package cn.zygxsq.design.module.prototypePattern;
 
import com.Google.common.collect.Maps;
import org.springframework.beans.factory.InitializingBean;
 
import java.util.HashMap;
import java.util.Hashtable;
import java.util.concurrent.ConcurrentMap;
 

public class DataCache {
    //正常的情况是 实现 InitializingBean ,用于WEB服务启动的时候加载数据
    // 这里测试由于不是web服务,所以就模拟加载数据
 
    private static ConcurrentMap<String, Animal> animalCache = Maps.newConcurrentMap();
 
    public static Animal getAnimal(String id) throws Exception{
        Animal cache = animalCache.get(id);
        return (Animal) cache.clone();
    }
 
    public static void init(){
        Dog dog = new Dog();
        String dogid = "111";
        dog.setId(dogid);
        animalCache.put(dogid,dog);
 
        Dog dog2 = new Dog();
        String dogid2 = "222";
        dog2.setId(dogid2);
        animalCache.put(dogid2,dog2);
 
        Cat cat = new Cat();
        String catid = "333";
        cat.setId(catid);
        animalCache.put(catid,cat);
 
    }
}

最后咱们开始测试

先是从数据库里加载缓存,然后要从缓存里获取数据,并且的到的是一个个clone出来的对象


package cn.zygxsq.design.module.prototypePattern;
 
import com.alibaba.fastJSON.jsON;
 

public class TestPrototype {
    public static void main(String[] args) {
        DataCache.init(); // 模拟加载数据到缓存中
 
        try {
            Animal animal = DataCache.getAnimal("111");
            System.out.println(animal.getName()+"---"+JSON.toJSONString(animal));
 
            Animal animal222 = DataCache.getAnimal("222");
            System.out.println(animal222.getName()+"---"+JSON.toJSONString(animal222));
 
            Animal animal333 = DataCache.getAnimal("333");
            System.out.println(animal333.getName()+"---"+JSON.toJSONString(animal333));
 
            animal.shout();
            animal222.shout();
            animal333.shout();
        } catch (Exception e) {
            e.printStackTrace();
        }
 
 
    }
}

运行结果: 

小伙伴们看的时候,好像没什么问题,确实没什么问题,但是细细一看,还是有一定的问题的


package cn.zygxsq.design.module.prototypePattern;
 
import com.alibaba.fastjson.JSON;
 

public class TestCloneProblem {
 
    public static void main(String[] args) {
        //做完TestPrototype的main方法后,好像觉得浅复制没有什么问题
        //那么可以看一下下面的a1的name 和 克隆后的name指向的是同一个地址
        DataCache.init(); // 模拟加载数据到缓存中
 
        try {
            Animal a1 = DataCache.getAnimal("111");
            Animal a2 = (Animal)a1.clone();
            System.out.println(a1==a2);
            System.out.println(a1.name == a2.name);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
 
}

运行结果:

大家踩一下 a1的name 和 克隆后的name是什么样的关系呢

大家可以看到,a1的name和a2的name是一样的,由于他们的类型是String,所以他们指向的是同一个地址,名称为“狗狗”的引用地址,大概的样子可以看下图

那怎么样才能不让a1.name 和a2.name不相同呢,也就是完完全全的复制,这个就得用到深复制了

深复制其实用到的就是流复制

可以在clone()的方法定义一个深复制的方法,比如deepClone()

六、深复制demo演示

记住,深复制的时候,方法一定得实现可序列化,Serializable


package cn.zygxsq.design.module.prototypePattern;
 
import java.io.*;
 

public abstract class Animal implements Cloneable, Serializable{
    private String id;
    public String name;
 
    abstract void shout();
 
    public String getId() {
        return id;
    }
 
    public void setId(String id) {
        this.id = id;
    }
 
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    
    public Object clone() throws CloneNotSupportedException {
        Animal clone = (Animal) super.clone();
        return clone;
    }
 
 
    
    public Object deepClone() {
        ByteArrayOutputStream byteArrayOutputStream = null;
        ObjectOutputStream objectOutputStream = null;
        ByteArrayInputStream byteArrayInputStream = null;
        ObjectInputStream objectInputStream = null;
        try {
            // 序列化
            byteArrayOutputStream = new ByteArrayOutputStream();
            objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
            objectOutputStream.writeObject(this);
            //反序列化
            byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
            objectInputStream = new ObjectInputStream(byteArrayInputStream);
            Animal deepProtoType = (Animal) objectInputStream.readObject();
            return deepProtoType;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                byteArrayOutputStream.close();
                objectOutputStream.close();
                byteArrayInputStream.close();
                objectInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
 
        }
 
    }
}

测试一下结果


package cn.zygxsq.design.module.prototypePattern;
 
import com.alibaba.fastjson.JSON;
 

public class TestPrototypeDeepClone {
    public static void main(String[] args) {
        DataCache.init(); // 模拟加载数据到缓存中
 
        try {
            Animal a1 = DataCache.getAnimal("111");
            Animal a2 = (Animal)a1.deepClone();
            System.out.println(a1==a2);
            System.out.println(a1.name == a2.name);
            System.out.println(a1.name);
        } catch (Exception e) {
            e.printStackTrace();
        }
 
 
    }
}

 运行结果:

这就是深复制和浅复制以及原型模式的使用

到此这篇关于Java设计模式之原型模式详解的文章就介绍到这了,更多相关Java原型模式内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: Java设计模式之原型模式详解

本文链接: https://www.lsjlt.com/news/125209.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Java设计模式之原型模式详解
    目录一、前言二、优点及适用场景三、原型模式的注意事项四、浅复制和深复制五、浅复制demo演示六、深复制demo演示一、前言 原型模式是一种比较简单的模式,也非常容易理解,实现一个接口...
    99+
    2022-11-12
  • Java设计模式之java原型模式详解
    目录介绍角色Java语言提供的clone()方法代码演示—克隆羊结论深浅拷贝深浅拷贝探讨实现深克隆的方式一 : 手动对引用对象进行克隆实现深克隆的方式一 :序列化原型模式对单例模式的...
    99+
    2022-11-12
  • java设计模式--原型模式详解
    目录引例原型模式浅拷贝在原先Sheep类基础上实现Cloneable接口,重写clone方法。客户端调用Sheep类新添的Cow类客户端调用克隆深拷贝1.Cow类也实现Cloneab...
    99+
    2022-11-12
  • Java设计模式之原型模式的示例详解
    目录定义案例需求方案一方案二对比分析总结 定义 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象 即实现了一个原型接口,该接口用于创建当前对象的克隆,当直接创建对象的代...
    99+
    2022-11-13
  • Java设计模式之原型设计示例详解
    目录简单说一下(定义)稍微夸一下(优缺点)顺便提一下(适用场景)着重讲一下(深、浅克隆)多多用一下(结构、代码实现)简单说一下(定义) 什么是原型模式:原型模式是用于创建重复的对象,...
    99+
    2022-11-13
  • Java 超详细讲解设计模式之原型模式讲解
    目录传统方式原型模式基本介绍原型模式在spring框架中源码分析深入讨论-浅讨论和深拷贝原型模式的注意事项和细节传统方式 克隆羊问题 现在有一只羊 tom,姓名为: tom,年龄为:...
    99+
    2022-11-13
  • Golang设计模式之原型模式详细讲解
    目录原型模式概念示例原型模式 原型是一种创建型设计模式, 使你能够复制对象, 甚至是复杂对象, 而又无需使代码依赖它们所属的类。 所有的原型类都必须有一个通用的接口, 使得即使在对象...
    99+
    2023-01-11
    Go原型模式 Go设计模式
  • Android常用设计模式之原型模式详解
    目录前言一、基本使用二、对象与集合的使用三、浅拷贝与深拷贝四、Kotlin的应用总结前言 什么是原型模式? 它是指创建对象的种类,并通过拷贝这些原型创建新的对象。 它是用于创建重复的...
    99+
    2022-11-13
    Android 设计模式原型模式 Android 原型模式
  • Javascript设计模式之原型模式详细
    目录1、原型模式示例一示例二示例三2、观察者模式1、原型模式 原型模式用于在创建对象时,通过共享某个对象原型的属性和方法,从而达到提高性能、降低内存占用、代码复用的效果。 示例一 ...
    99+
    2022-11-12
  • JavaScript设计模式之原型模式详情
    目录前言案例回顾原型的拓展前言 设计模式呢最多的可能是用到类,我们去通过类来封装一些实用的方法,通过设计模式去实现各个方法之间的解耦等,由于JS中的继承是用原型链继承的,所以原型模式...
    99+
    2022-11-13
  • 深入理解Java设计模式之原型模式
    目录一、前言二、什么是原型模式三、原型模式的适用场景四、原型模式的实现1.浅拷贝实现2.深拷贝实现五、总结一、前言 单例模式可以避免重复创建消耗资源的对象,但是却不得不共用对象。若是...
    99+
    2022-11-12
  • Java 深入理解创建型设计模式之原型模式
    1.思考问题 现在有一只羊 tom,姓名为: tom,年龄为:1,颜色为:白色,请编写程序创建和 tom羊属性完全相同的10只羊。 按照传统的思路来,我们可能会按照下面的方式去写。 ...
    99+
    2022-11-13
  • Java结构型设计模式之装饰模式详解
    目录介绍实现优缺点介绍 意图:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更灵活。 主要解决:我们扩展一个类常使用继承方式实现,由于继承为类引入静态特征,...
    99+
    2023-05-14
    Java设计模式装饰模式 Java装饰模式
  • Java结构型设计模式之组合模式详解
    目录组合模式应用场景优缺点主要角色组合模式结构分类透明组合模式创建抽象根节点创建树枝节点创建叶子节点客户端调用安全组合模式创建抽象根节点创建树枝节点创建叶子节点客户端调用组合模式 组...
    99+
    2022-11-13
  • java设计模式:原始模型模式
    目录什么是原始模型模式原始模型模式中的角色抽象原型角色(Prototype)具体原型角色(ConcretePrototype)A...
    99+
    2022-11-12
  • 一文带你了解Java设计模式之原型模式
    目录定义解决的问题核心要点类图浅复制与深复制的区别代码实现未使用设计模式实现Cloneable接口深复制-重写clone深复制-通过对象序列化实现(推荐)拓展定义 用原型实例指定创建...
    99+
    2022-11-13
  • JavaScript设计模式之原型模式和适配器模式示例详解
    目录原型模式原型模式介绍代码实现适配器模式适配器模式介绍代码实现小结原型模式 原型模式介绍 原型模式是指原型实例指向创建对象的种类,并通过拷贝这些原型创建新的对象,是一种用来创建对象...
    99+
    2022-11-13
  • Java设计模式之java状态模式详解
    目录状态模式的结构状态模式的角色 示例代码适用场景投票案例认识状态模式状态和行为行为的平行性环境和状态处理对象状态模式优点状态模式的缺点状态模式和策略模式对比参考文章 总结...
    99+
    2022-11-12
  • Java设计模式之java策略模式详解
    目录为什么使用策略模式?策略模式包含角色策略模式的类图排序案例策略模式的优点策略模式的缺点适用场景源码分析策略模式的典型应用Java Comparator 中的策略模式参考文...
    99+
    2022-11-12
  • Java设计模式之java命令模式详解
    目录命令模式的介绍角色订单案例命令模式的优点适用场景示例代码应用宏命令----执行一组命令示例代码总结JDK源码解析 Runable是一个典型命令模式,Runnable担当命令的角色...
    99+
    2022-11-12
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作