23种设计模式之创建型模式 - 单例模式

文章目录

  • 一、单例模式
    • 1.1单例模式定义
    • 1.2 单例模式的特点
  • 二、实现单例模式的方式
    • 2.1 饿汉式
    • 2.2 懒汉式
    • 2.3 双重检查锁:
    • 2.4 静态内部类
    • 2.5 枚举实现(防止反射攻击):

一、单例模式

1.1单例模式定义

单例模式确保系统中某个类只有一个实例,并提供一个访问它的全局访问点。主要解决一个全局使用的类频繁地创建与销毁,控制实例数目,节省系统资源。

1.2 单例模式的特点

  • 单例类只能有一个实例
  • 单例类必须自己创建自己的唯一实例
  • 单例类必须给所有其他对象提供这一实例
  • 单例模式保证了全局对象的唯一性,比如系统启动读取配置文件就需要单例保证配置的一致性

单例的四大原则:

  • 构造私有
  • 以静态方法或者枚举返回实例
  • 确保实例只有一个,尤其是多线程环境
  • 确保反序列换时不会重新构建对象

二、实现单例模式的方式

2.1 饿汉式

饿汉式单例在类加载初始化时就创建好一个静态的对象供外部使用,除非系统重启,这个对象不会改变,所以本身就是线程安全的。
Singleton 通过将构造方法限定为 private 避免了类在外部被实例化,在同一个虚拟机范围内,Singleton 的唯一实例只能通过 getInstance()方法访问。(事实上,通过 Java 反射机制是能够实例化构造方法为 private 的类的,会使 Java单例实现失效)

/**
 * @Author huang.bX
 * @Date 2021/7/21
 */
public class SingletonTest01 {
    public static void main(String[] args) {
        Hungry instance = Hungry.getInstance();
        Hungry instance1 = Hungry.getInstance();
        Hungry instance2 = Hungry.getInstance();
        System.out.println(instance.getClass());
        System.out.println(instance1.getClass());
        System.out.println(instance2.getClass());
    }
}

//饿汉式
class Hungry {
    //1构造器私有化,外部不能直接new
    private Hungry() {
    }
    //2本类的内部创建实例
    private final static Hungry hungry = new Hungry();

    //提供一个全局访问点共有的静态方法 返回实例对象
    public static Hungry getInstance(){
        return hungry;
    }
}

在这里插入图片描述

2.2 懒汉式

该示例虽然用延迟加载方式实现了懒汉式单例,但在多线程环境下会产生多个 Singleton 对象;

/**
 * @Author huang.bX
 * @Date 2021/7/21
 */
public class SingletonTest03 implements Runnable {

    @Override
    public void run(){
        for (int i=1;i<1000;i++){
            LazyMan lazyMan=LazyMan.getInstance();
            System.out.println(lazyMan.hashCode());
        }
    }
    public static void main(String[] args) {
        /*
         *   LazyMan instance1 = LazyMan.getInstance();
         *   LazyMan instance2 = LazyMan.getInstance();
         *  System.out.println(instance1.hashCode()==instance2.hashCode());
        */
       new Thread(new SingletonTest03()).start();
       new Thread(new SingletonTest03()).start();
       new Thread(new SingletonTest03()).start();


    }
}

class LazyMan{
    private LazyMan(){

    }
    private static LazyMan lazyMan;

    //public static LazyMan getInstance()线程不安全
    public static synchronized LazyMan getInstance(){
        if (lazyMan==null){
           lazyMan = new LazyMan();
        }
        return lazyMan;
    }
}

在这里插入图片描述

2.3 双重检查锁:

使用双重检查锁进一步做了优化,可以避免整个方法被锁,只对需要锁的代码部分加锁,可以提高执行效率。

/**
 * @Author huang.bX
 * @Date 2021/7/21
 */
public class SingletonTest05 {
    public static void main(String[] args) {
        DoubleLock instance1 = DoubleLock.getInstance();
        DoubleLock instance2 = DoubleLock.getInstance();
        System.out.println(instance1.hashCode()==instance2.hashCode());
    }
}

class DoubleLock{

    private static volatile DoubleLock doubleLock;
    private DoubleLock(){}

    public static DoubleLock getInstance(){
        if (doubleLock==null){
            synchronized (DoubleLock.class){
                if (doubleLock==null){
                    doubleLock = new DoubleLock();
                }
            }
        }
        return doubleLock;
    }
}

在这里插入图片描述

2.4 静态内部类

这种方式引入了一个内部静态类(static class),静态内部类只有在调用时才会加载,它保证了 Singleton 实例的延迟初始化,又保证了实例的唯一性。它把 singleton 的实例化操作放到一个静态内部类中,在第一次调用 getInstance() 方法时,JVM 才会去加载 InnerObject 类,同时初始hsingleton 实例,所以能让 getInstance() 方法线程安全。特点是:即能延迟加载,也能保证线程安全。静态内部类虽然保证了单例在多线程并发下的线程安全性,但是在遇到序列化对象时,默认的方式运行得到的结果就是多例的。

/**
 * @Author huang.bX
 * @Date 2021/7/21
 */
public class SingletonTest06 {
    public static void main(String[] args) {
        StaticInClass instance1 = StaticInClass.getInstance();
        StaticInClass instance2= StaticInClass.getInstance();
        System.out.println(instance1.hashCode()==instance2.hashCode());
    }
}

class StaticInClass{

    private static volatile StaticInClass staticInClass;
    //构造器私有化
    private StaticInClass(){}

    //定义一个静态内部类,该类中有一个静态属性
    private static class Inner{
        private static final StaticInClass INSTANCE = new StaticInClass();
        
    }
    public static synchronized StaticInClass getInstance(){
        return Inner.INSTANCE;
    }

}

在这里插入图片描述

2.5 枚举实现(防止反射攻击):

事实上,通过 Java 反射机制是能够实例化构造方法为 private 的类的。这也就是我们现在需要引入的枚举单例模式。

/**
 * @Author huang.bX
 * @Date 2021/7/21
 */
public class SingletonTest07 {
    public static void main(String[] args) {
        Singleton instance1 = Singleton.INSTANCE;
        Singleton instance2 = Singleton.INSTANCE;
        System.out.println(instance1.hashCode());
        System.out.println(instance2.hashCode());
        System.out.println(instance2.hashCode()==instance1.hashCode());
        System.out.println(instance1.getClass());
        System.out.println(instance2.getClass());
        System.out.println(instance1.getDeclaringClass());
    }
}

enum Singleton{
    INSTANCE;//属性
    public void say(){
        System.out.println("ok!");
    }
}

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

docker学习笔记 四-----docker基本使用方法

基础命令奉上&#xff1a; 1、docker命令查询方法 docker --help 获取docker命令帮助 docker search --help 查询docker 子命令search的帮助 2、查询镜像 查询镜像 docker search 192.168.206.100:5000/mysql 查询指定服务器指定镜像 docker search mysql …

Redis入门到实战-第二十弹

Redis实战热身Time series篇 完整命令参考官网 官网地址 声明: 由于操作系统, 版本更新等原因, 文章所列内容不一定100%复现, 还要以官方信息为准 https://redis.io/Redis概述 Redis是一个开源的&#xff08;采用BSD许可证&#xff09;&#xff0c;用作数据库、缓存、消息代…

Redis入门到实战-第十六弹

Redis实战热身Cuckoo filter篇 完整命令参考官网 官网地址 声明: 由于操作系统, 版本更新等原因, 文章所列内容不一定100%复现, 还要以官方信息为准 https://redis.io/Redis概述 Redis是一个开源的&#xff08;采用BSD许可证&#xff09;&#xff0c;用作数据库、缓存、消息…

Transformer的前世今生 day10(Transformer编码器

前情提要 ResNet&#xff08;残差网络&#xff09; 由于我们加更多层&#xff0c;更复杂的模型并不总会改进精度&#xff0c;可能会让模型与真实值越来越远&#xff0c;如下&#xff1a; 我们想要实现&#xff0c;加上一个层把并不会让模型变复杂&#xff0c;即没有它也没关系…

【启发式算法】同核分子优化算法 Homonuclear Molecules Optimization HMO算法【Matlab代码#70】

文章目录 【获取资源请见文章第4节&#xff1a;资源获取】1. 算法简介2. 部分代码展示3. 仿真结果展示4. 资源获取 【获取资源请见文章第4节&#xff1a;资源获取】 1. 算法简介 同核分子优化算法&#xff08;Homonuclear Molecules Optimization&#xff0c;HMO&#xff09;是…

数据结构面试常见问题之串的模式匹配(KMP算法)系列-大师改进

&#x1f600;前言 KMP算法是一种改进的字符串匹配算法&#xff0c;由D.E.Knuth&#xff0c;J.H.Morris和V.R.Pratt提出&#xff0c;因此人们称它为克努特—莫里斯—普拉特操作&#xff08;简称KMP算法&#xff09; KMP算法的优势: 提高了匹配效率&#xff0c;时间复杂度为O(m…

力扣面试150 移除元素 双指针

Problem: 27. 移除元素 思路 &#x1f468;‍&#x1f3eb; 三叶题解 复杂度 时间复杂度: O ( n ) O(n) O(n) 空间复杂度: O ( 1 ) O(1) O(1) Code class Solution {public int removeElement(int[] nums, int val) {int j nums.length - 1;for (int i 0; i < j;…

Netty服务端基本启动流程源码刨析

前言: 希望看这篇文章之前对Java Nio编程比较熟悉&#xff0c;并有用过Netty开发简单代码 服务端代码 先大致说一下NioEventLoopGroup组件的作用&#xff0c;可以把它看是作内部维护了一个NioEventLoop数组的对象&#xff0c;它的构造方法的参数用来指定维护数组的大小。NioEve…

快速上手Spring Cloud 十:Spring Cloud与微前端

快速上手Spring Cloud 一&#xff1a;Spring Cloud 简介 快速上手Spring Cloud 二&#xff1a;核心组件解析 快速上手Spring Cloud 三&#xff1a;API网关深入探索与实战应用 快速上手Spring Cloud 四&#xff1a;微服务治理与安全 快速上手Spring Cloud 五&#xff1a;Spring …

强化基础-Java-泛型

什么是泛型&#xff1f; 泛型其实就参数化类型&#xff0c;也就是说这个类型类似一个变量是可变的。 为什么会有泛型&#xff1f; 在没有泛型之前&#xff0c;java中是通过Object来实现泛型的功能。但是这样做有下面两个缺陷&#xff1a; 1 获取值的时候必须进行强转 2 没有…

Learn OpenGL 26 视差贴图

什么是视差贴图 视差贴图(Parallax Mapping)技术和法线贴图差不多&#xff0c;但它有着不同的原则。和法线贴图一样视差贴图能够极大提升表面细节&#xff0c;使之具有深度感。它也是利用了视错觉&#xff0c;然而对深度有着更好的表达&#xff0c;与法线贴图一起用能够产生难…

商标跨类异议与跨类保护!

有个朋友对普推知产老杨说收到某邮件&#xff0c;名下商标让某公司抢注了现在公告期&#xff0c;让赶紧提出来异议去处理下&#xff0c;怎么会有这样的事&#xff0c;相同的名称基本上在同类别相关产品是无法公告和获得初审的。 经详细检索分析后&#xff0c;发现不是这样一回…

【Linux】理解父子进程(系统调用创建进程,fork函数,写时拷贝)

目录 fork函数 返回值 内存分配 父子进程是操作系统一个重要的概念&#xff0c;特别是在多任务处理和并发编程中&#xff0c;在Linux中&#xff0c;每个进程都有一个唯一的进程ID&#xff0c;并且每个进程都有可能创建其他进程。当一个进程创建了一个新的进程时&#xff0c;…

【Linux】进程>环境变量地址空间进程调度

主页&#xff1a;醋溜马桶圈-CSDN博客 专栏&#xff1a;Linux_醋溜马桶圈的博客-CSDN博客 gitee&#xff1a;mnxcc (mnxcc) - Gitee.com 目录 1.环境变量 1.1 基本概念 1.2 常见环境变量 1.3 查看环境变量方法 1.4 和环境变量相关的命令 1.5 环境变量的组织方式 1.6 通…

最“原始”的收音机长啥样?

同学们大家好&#xff0c;今天我们继续学习杨欣的《电子设计从零开始》&#xff0c;这本书从基本原理出发&#xff0c;知识点遍及无线电通讯、仪器设计、三极管电路、集成电路、传感器、数字电路基础、单片机及应用实例&#xff0c;可以说是全面系统地介绍了电子设计所需的知识…

vs code

vs code 下载安装 https://code.visualstudio.com/https://code.visualstudio.com/ 下载完后&#xff0c;下一步下一步就安装完了&#xff0c;安装好后可以下载各种好用的插件

【前端面试3+1】03深拷贝浅拷贝、let和var、css盒模型、【有效括号】

一、深拷贝浅拷贝 深拷贝和浅拷贝都是用于复制对象或数组的概念&#xff0c;但它们之间有着重要的区别&#xff1a; 1. 浅拷贝&#xff1a; 浅拷贝是指在拷贝对象或数组时&#xff0c;只会复制一层对象的属性或元素&#xff0c;而不会递归地复制嵌套的对象或数组。因此&#xf…

2024年第十二届计算机与通信管理国际会议(ICCCM 2024)即将召开!

2024年第十二届计算机与通信管理国际会议&#xff08;ICCCM 2024&#xff09;将2024年7月19-21日在日本鹿儿岛召开。会议由鹿儿岛大学主办。此次会议旨在为业界建立一个广泛、有效的交流合作平台&#xff0c;让我们及时了解行业发展动态、掌握最新技术&#xff0c;拓宽研究视野…

修改Jupyter Notebook的默认路径,以及在PowerShell中自定义其启动路径

修改Jupyter Notebook的默认路径&#xff0c;以及在PowerShell中自定义其启动路径 设置 Jupyter Notebook 配置文件&#xff0c;修改默认路径要在PowerShell中设置自定义的启动脚本&#xff0c;以确保Jupyter Notebook能够自动定位到当前路径设置后的效果 在使用Jupyter Notebo…