java设计模式(2)单例模式、工厂模式、原型模式、建造者模式

news2024/11/6 3:08:50

用pr设计的图片,当封面不错

单例模式 

单例对象的类必须保证只有一个实例存在

饿汉式单例

饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的

//饿汉式单例类.
public class Singleton {
    //构造器私有化
    private Singleton() {}
    //static修饰 在类初始化时,已经自行实例化
    private static final Singleton single = new Singleton();
    //静态工厂方法 
    public static Singleton getInstance() {
        return single;
    }
}

  • 优点:保证线程绝对安全
  • 缺点:所有对象类加载的时候就实例化,系统初始化就会导致大量的内存浪费

懒汉式单例

//懒汉式单例类
public class Singleton {
    //构造器私有化
    private Singleton() {}
    private static Singleton single = null;
    //静态工厂方法 
    public static Singleton getInstance() {
        if(single==null){
            //在第一次调用的时候实例化自己 
            single=new Singleton();
        }
        return single;
    }
}

优点:解决了饿汉式单例可能带来的内存浪费问题,节省内存
缺点:线程不安全的问题

在多线程环境下,我们就要考虑到线程的安全问题:即是否有不同的线程分别new一个对象实例,导致不同线程创建的class类的实例不是同一个。


线程安全懒汉式

可以直接在方法加synchronized,但是不推荐。会导致资源浪费

//懒汉式-线程安全
public class Singleton {
    //构造器私有化
    private Singleton() {
    }

    private static volatile Singleton single = null;

    //静态工厂方法
    public static synchronized Singleton getInstance() {
        if (single != null) {
            return single;
        }
        single = new Singleton()
        return single;
    }
}

懒汉式双重锁

双重锁检查:方法检查判定两次,并使用锁,所以形象称为双重检查锁定模式。

1.首先判断变量是否被初始化,没有被初始化,再去获取锁。

2.获取锁之后,再次判断变量是否被初始化。第二次判断目的在于有可能其他线程获取过锁,已经初始化改变量。第二次检查还未通过,才会真正初始化变量。

用 volatile定义的变量,将会保证对所有线程的可见性。禁止指令重排序优化。

//懒汉式-线程安全
public class Singleton {
    //构造器私有化
    private Singleton() {
    }

    private static volatile Singleton single = null;

    //静态工厂方法
    public static Singleton getInstance() {
        if (single != null) {
            return single;
        }
        synchronized (Singleton.class) {
            if (single == null) {
                //在第一次调用的时候实例化自己
                single = new Singleton();
            }
        }
        return single;
    }
}

工厂模式

准备工作

public interface Phone {
    //规范:每个手机都只能玩游戏
    void play();
}

oppo手机

public class OPPO implements Phone{
    @Override
    public void play() {
        System.out.println("使用oppo手机 play games");
    }
}

vivo手机

public class VIVO implements Phone{
    @Override
    public void play() {
        System.out.println("使用vivo手机 play games");
    }
}

小米手机

public class XiaoMi implements Phone {
    @Override
    public void play() {
        System.out.println("使用xiaomi手机 play games");
    }
}

简单工厂模式

简单工厂模式:创建一个工厂类根据传入的参数决定创建出哪一种产品的实例

//简单工厂模式
public class PhoneFactory {
    public static Phone getPhone(String phone) {
        //通过给工厂传入不同的参数,拿到不一样的手机
        if (phone.equals("oppo")) {
            return new OPPO();
        } else if (phone.equals("vivo")) {
            return new VIVO();
        } else if (phone.equals("xiaomi")) {
            return new XiaoMi();
        } else {
            return null;
        }
    }
}

测试:

 public static void main(String[] args) {
     Phone phone = PhoneFactory.getPhone("xiaomi");
     phone.play();
 }


工厂方法模式

此时每一种品牌都有自己的工厂制作自己品牌的手机。定义一个工厂接口基类,

public interface PhoneFactory {
   //定义创建产品的抽象方法
   Phone makePhone();
}

oppo工厂

//oppo工厂
public class OPPOFactory implements PhoneFactory {
    @Override
    public Phone makePhone() {
        return new OPPO();
    }
}

vivo工厂

//vivo工厂
public class VIVOFactory implements PhoneFactory{
    @Override
    public Phone makePhone() {
        return new VIVO();
    }
}

小米工厂

//小米工厂
public class XiaoMiFactory implements PhoneFactory {
    @Override
    public Phone makePhone() {
        return null;
    }
}

测试

public static void main(String[] args) {
    OPPOFactory oppoFactory = new OPPOFactory();
    Phone phone = oppoFactory.makePhone();
    phone.play();
}

看的出来,我们新增了很多个工厂类,那么工厂方法模式好在哪里呢?

符合 开闭原则,在新增产品的时候不需要改动已经存在的代码,利于程序的扩展。而简单工厂模式在添加新的产品时,不得不修改工厂方法,扩展性不好。

工厂方法模式缺点:就是在新增产品的时候需要新增产品类和工厂类(成对增加)。


抽象工厂模式

与工厂方法模式相比,抽象工厂模式中的工厂不再只是创建一种具体的产品(比如上面,我们的小米工厂就只是去创建小米手机)。

抽象工厂模式的工厂创建一组产品。这一组产品式一系列相关相互依赖对象。比如小米工厂创建的小米手机、小米手环、小米电视、小米充电宝、小米电脑....

简单整一个统一的电视接口

public interface TV {
    void watchTV();
}

小米电视

public class XiaoMITV implements TV {
    @Override
    public void watchTV() {
        System.out.println("使用小米电视 watch TV");
    }
}

抽象工厂Factory基类.。为了方便我就只写了俩个创建手机和创建电视。

//抽象工厂Factory基类
public interface Factory {
    Phone createPhone();
    TV createTV();
}

具体各个品牌的工厂类如下:为了方便我就只写了小米工厂手机和创建电视。

//小米工厂
public class XiaoMiFactory implements Factory {
    @Override
    public Phone createPhone() {
        return new XiaoMi();
    }

    @Override
    public TV createTV() {
        return new XiaoMITV();
    }
}

测试

public static void main(String[] args) {
    Factory factory = new XiaoMiFactory();
    Phone phone = factory.createPhone();
    phone.play();
    TV tv = factory.createTV();
    tv.watchTV();
}

抽象工厂模式优势:将具有一定共性的产品集合封装在一起。。更符合业务场景


原型模式

原型模式:用⼀个已经创建的实例作为原型,通过复制该原型对象来创建⼀个和原型相同的新对象。避免了重新执行构造过程步骤,当直接创建对象的代价比较大时,则采用这种模式

因为Java对原型模式的支持,所以原型模式在Java是很容易实现的一件事情。


浅拷贝

浅拷贝的整个过程就是,创建一个新的对象,然后新对象的每个值都是由原对象的值,通过 = 进行赋值;   

  • 基本数据类型是值赋值
  • 非基本的就是引用赋值

Java 中的 Object 类提供了浅克隆的 clone() ⽅法,具体原型类只要实现 Cloneable 接⼝就可实现对象的浅克隆,这⾥的 Cloneable 接⼝就是抽象原型类。

//对于实现了Cloneable接口的对象,可以调用Object#clone()来进行属性的拷贝
public interface Cloneable {
}

地址类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Address {
    private String province;//省
    private String city;//市
    private String area;//区
}

用户类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements Cloneable {
    private Integer userId;
    private String name;
    private String password;
    private Address address;//引用类型
    
    //浅拷贝
    @Override
    protected Object clone() throws CloneNotSupportedException {
        //浅拷贝是使用默认的 clone()方法来实现
        User user = (User) super.clone();
        return user;
    }
}

测试类-测试浅拷贝

public static void main(String[] args) throws CloneNotSupportedException {
    Address address = new Address("陕西省", "西安市", "雁塔区");
    User user = new User(1, "小羽毛", "123456", address);
    //克隆user对象
    User clone = (User)user.clone();
    //修改克隆后的对象,会影响到原来的对象的属性
    Address addressClone = clone.getAddress();
    addressClone.setCity("榆林市");
    addressClone.setCity("榆阳区");
    //输出结果
    System.out.println(user);
    System.out.println(clone);
}


深拷贝

通过序列化克隆对象(推荐使用)

深拷贝就是要创建一个全新的对象,新的对象内部所有的成员也都是全新的,只是初始化的值已经由被拷贝的对象确定了而已

浅拷贝实现Cloneable,重写,深拷贝是通过实现Serializable读取二进制流。

地址类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Address implements Serializable, Cloneable {
    private String province;//省
    private String city;//市
    private String area;//区

    //因为该类的属性,都是 String ,  因此这里使用默认的 clone 完成即可
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

用户类

22-04-23 西安 javaSE(14)文件流、缓冲流、转换流、对象流、标准流、关闭IO资源的封装类IOUtils(纳命来)_ioutils关闭_£小羽毛的博客-CSDN博客

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable, Cloneable {
    private Integer userId;
    private String name;
    private String password;
    private Address address;//引用类型

    //深拷贝 - 通过对象的序列化实现 (推荐)
    @Override
    protected Object clone() throws CloneNotSupportedException {
        try{
            //创建一个 32 字节 ( 默认大小 ) 的缓冲区
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(this);

            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);

            User clone = (User)ois.readObject();
            return clone;

        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }
}

Java 字节数组输出流 ( ByteArrayOutputStream ) 在内存中创建一个字节数组缓冲区,所有发送到输出流的数据保存在该字节数组缓冲区中

//创建一个新分配的字节数组。数组的大小和当前输出流的大小,内容是当前输出流的拷贝
public byte[] toByteArray()

ByteArrayInputStream和ByteArrayOutputStream两个流对象都操作的数组,并没有使用系统资源,所以不用进行 close 关闭

测试类-测试深拷贝

public static void main(String[] args) throws CloneNotSupportedException {
    Address address = new Address("陕西省", "西安市", "雁塔区");
    User user = new User(1, "小羽毛", "123456", address);
    //克隆user对象
    User clone = (User)user.clone();
    //修改克隆后的对象,会影响到原来的对象的属性
    Address addressClone = clone.getAddress();
    addressClone.setCity("榆林市");
    addressClone.setCity("榆阳区");
    //输出结果
    System.out.println(user);
    System.out.println(clone);
}


序列化拷贝

通过序列化然后将对象写入流再写出流的方式来对对象进行克隆。注意俩点

1.原型对象也不需要实现Cloneable接口,而是需要实现Serializable接口,使得对象支持序列化。

2.不需要把每一个级联对象的clone()方法都实现

User和Address都实现Serializable接口

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Address implements Serializable {
    private String province;//省
    private String city;//市
    private String area;//区
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {
    private Integer userId;
    private String name;
    private String password;
    private Address address;//引用类型

    //深拷贝 - 通过对象的序列化实现
    protected Object cloneUser() {
        try {
            //创建一个 32 字节 ( 默认大小 ) 的缓冲区
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(this);

            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);

            User clone = (User) ois.readObject();
            return clone;

        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

测试:只改动了 User clone = (User)user.cloneUser();

public static void main(String[] args) throws CloneNotSupportedException 
    Address address = new Address("陕西省", "西安市", "雁塔区");
    User user = new User(1, "小羽毛", "123456", address);
    //克隆user对象
    User clone = (User)user.cloneUser();
    //修改克隆后的对象,会影响到原来的对象的属性
    Address addressClone = clone.getAddress();
    addressClone.setCity("榆林市");
    addressClone.setCity("榆阳区");
    //输出结果
    System.out.println(user);
    System.out.println(clone);
}


建造者模式

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/428885.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

tmall.item.sizemapping.template.create( 新增天猫商品尺码表模板 )

¥开放平台免费API必须用户授权 新增天猫商品尺码表模板 男鞋、女鞋、运动鞋、户外鞋类目,尺码表维度为: 脚长(cm) 必选 内衣-文胸类目,尺码表维度为: 上胸围(cm) 必选 …

机器学习入门实例-MNIST手写数据集-简单探索二分分类

MNIST数据集介绍 MNIST数据集包含7w张带标签的手写数字图片。每次有新的分类算法出现时,常常会在改数据集测试效果。 from sklearn.datasets import fetch_openml# 获取的mnist是一个字典 mnist fetch_openml(mnist_784, version1) print(mnist.keys()) # dict_k…

Hutool-crypto 加密、解密详解!

1. 介绍 在Java开发的过程中,很多场景下都需要加密解密。 比如对敏感数据的加密,对配置文件信息的加密,通信数据的加密等等。 今天介绍的是Hutool工具包中的加密模块 crypto。 2. 加密分类 加密分为三类: 对称加密&#xff0…

Embarcadero RAD Studio Crack

Embarcadero RAD Studio Crack RAD Studio(r)是一款终极IDE,用于使用Delphi(r)、现代C和适用于Windows 11的高级Windows桌面UI库为多个平台创建单源原生应用程序,它为IDE添加了高DPI支持。这使得开发人员可以在更大的屏幕上以更高的分辨率工作。IDE现在支…

在VS和g++下的string结构的区别

文章目录1. 在VS下的结构2.在gcc下的结构3.写时拷贝/共享内存在之前的时间里&#xff0c;我们学习了string类的使用和模拟实现&#xff0c;但是在VS和g下使用string&#xff0c;发现了一点问题&#xff0c;下面我们通过一段代码来重现一下这个问题#include <iostream> #i…

融合DE 端和FE端数据,利用小波变换生成时频图,再分别利用DCNN、KNN和DNN进行对比实验(python代码)

1.数据集介绍&#xff1a; 试验台如图所示&#xff0c;试验台左侧有电动机&#xff0c;中间有扭矩收集器&#xff0c;右侧有动力测试仪&#xff0c;控制电子设备在图中没有显示。SKF6203轴承使用16通道数据采集卡采集轴承的振动数据&#xff0c;并在驱动端部分&#xff08;DE&…

AI模型训练、实施工程师的职业前景怎么样?

本篇文章主要讲解ai模型训练、模型实施工程师的职业前景和趋势分析 作者&#xff1a;任聪聪 日期&#xff1a;2023年4月18日 ai训练师、模型实施工程师&#xff0c;一般是指opencv、pytorh、python、java、机械学习、深度学习、图像识别、视频检测等领域的模型数据训练工作。 …

07 - 深度学习处理器架构⭐⭐⭐⭐

架构设计需要解决的两个主要问题:(1)如何提高处理器的能效比(性能/功耗)- 硬化算法(2)如何提高处理器的可编程性(通用性) - CPU 一、单核深度学习处理器(DLP-S) 1. 总体架构 (1)架构图 DMA是一种硬件机制,允许外围组件将其I/O数据直接传输到主存储器中,而无需…

CentOS 8 手动安装MongoDB

文章目录1. MongoDB概述2. 安装MongoDB2.1 在MongoDB官网选择对应版本2.2 去到MongoDB安装目录&#xff0c;并下载MongoDB安装包2.3 解压MongoDB安装包2.4 重命名解压后的MongoDB文件夹名2.5 创建MongoDB数据库数据存放路径2.6 创建MongoDB日志文件存放路径2.7 进入MongoDB文件…

Pixhawk基础—认识Pixhawk

Pixhawk简介 pixhawk是由3DR联合APM小组与PX4小组于2014年推出的飞控PX4的升级版&#xff0c;它同时拥有PX4和APM两套固件和相应的地面站软件。该飞控是目前全世界飞控产品中硬件规格最高的产品。 Pixhawk基础 端口介绍 1、Spektrum DSM receiver(Spektrum DSM信号转换为PWM…

Java基础总结(一)

文章目录前言封装继承多态抽象方法接口内部类static权限修饰符this superprivate关键字final关键字就近原则构造方法号StringBuilderStringJoiner字符串原理总结&#xff1a;1、字符串存储的内存原理2、号比较的是什么&#xff1f;3、字符串拼接的底层原理4、StringBuilder提高…

ASIC-WORLD Verilog(1)一日Verilog

写在前面 在自己准备写一些简单的verilog教程之前&#xff0c;参考了许多资料----asic-world网站的这套verilog教程即是其一。这套教程写得极好&#xff0c;奈何没有中文&#xff0c;在下只好斗胆翻译过来&#xff08;加了自己的理解&#xff09;分享给大家。 这是网站原文&…

Java反射面试总结(二)

为什么引入反射概念&#xff1f;反射机制的应用有哪些&#xff1f; 我们来看一下 Oracle 官方文档中对反射的描述&#xff1a; 从 Oracle 官方文档中可以看出&#xff0c;反射主要应用在以下几方面&#xff1a; 反射让开发人员可以通过外部类的全路径名创建对象&#xff0c;…

详解C语言结构体内存对齐:你知道如何快速计算结构体大小吗?

本篇博客会讲解C语言结构体的内存对齐&#xff0c;并且给出一种快速计算结构体大小的方式。主要讲解下面几点&#xff1a; 结构体的内存对齐是什么&#xff1f;如何快速计算结构体的大小&#xff1f;如何利用内存对齐节省结构体占用的内存空间&#xff1f;为什么结构体要内存对…

分布式数据库架构路线大揭秘

文章目录分布式数据库是如何演进的&#xff1f;数据库与分布式中间件有什么区别&#xff1f;如何处理分布式事务&#xff0c;提供外部一致性&#xff1f;如何处理分布式SQL&#xff1f;如何实现分布式一致性&#xff1f;数据库更适合金融政企的未来这些年大家都在谈分布式数据库…

MySQL-中间件mycat(一)

目录 &#x1f341;mycat基础概念 &#x1f341;Mycat安装部署 &#x1f343;初始环境 &#x1f343;测试环境 &#x1f343;下载安装 &#x1f343;修改配置文件 &#x1f343;启动mycat &#x1f343;测试连接 &#x1f990;博客主页&#xff1a;大虾好吃吗的博客 &#x1f9…

边缘网关thingsboard-gateway DTU902

thingsboard-gateway是一个采用python语言编写的开放源代码网关程序&#xff0c;用于将传统或第三方系统的设备与thingsboard平台连接。 支持 采集Modbus slaves、CAN、MQTT 、OPC-UA servers, Sigfox Backend。 除了具备普通 网关外&#xff0c;还具备可配置的边缘能力&…

rabbitmq深入实践

生产者&#xff0c;交换机&#xff0c;队列&#xff0c;消费者 交换机和队列通过 rounting key 绑定者&#xff0c;rounting key 可以是#.,*.这类topic模式&#xff0c; 生产者发送消息内容 rountingkey&#xff0c; 到达交换机后交换机检查与之绑定的队列&#xff0c; 如果能匹…

Yolov5之common.py文件解读

深度学习训练营原文链接前言0.导入需要的包以及基本配置1.基本组件1.1 autopad1.2 ConvDWConv模块1.3TransformerLayer模块1.4 Bottleneck和BottleneckCSPBottleneck模型结构1.5 CrossConv模块1.6 C3模块基于C3的改进1.7SPP1.8Focus模块1.9 Concat模块1.10 Contract和Expand1.1…

好东西!!!多亏几位大牛整理的面试题,让我成功上岸!!

凡事预则立&#xff0c;不预则废。相信很多程序员朋友在跳槽前都会临阵磨枪&#xff0c;在网络上搜集一些面试题进行准备。 然而&#xff0c;当机会来临时&#xff0c;却发现这些面试题往往“不快也不光”.... 由于Java面试涉及的范围很广&#xff0c;很杂&#xff0c;而且技…