首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >单例设计模式(java与node实现)

单例设计模式(java与node实现)

作者头像
许喜朝
发布于 2020-11-26 09:18:14
发布于 2020-11-26 09:18:14
65200
代码可运行
举报
运行总次数:0
代码可运行

单例设计模式

什么是单例设计模式

单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中,应用该模式的类一个类只有一个实例。即一个类只有一个对象实例

具体实现

(1)将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。

(2)在该类内部产生一个唯一的实例化对象,并且将其封装为private static类型。

(3)定义一个静态方法返回这个唯一对象。

java语言实现
懒汉模式

延迟加载,当只有使用的时候才开始真正的实例化

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * 单例设计模式懒汉式
 */
public class SingleTonLazy {
    //定义一个实例化对象
    private static SingleTonLazy singleTonLazy = null;

    //构造方法私有化
    private SingleTonLazy(){}
    //静态工厂方法
    private static SingleTonLazy getInstance(){
        if (singleTonLazy == null){
            singleTonLazy = new SingleTonLazy();
        }
        return singleTonLazy;
    }
}

单例设计模式本身是线程安全的,因为从程序被创建就提供了一个静态类,除非关闭程序

但是在多线程模式下就会出现线程安全问题

加锁单例
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class SingleTonSync {
    private static SingleTonSync singleTonSync = null;
    //构造方法私有
    private SingleTonSync(){}

    public static SingleTonSync getSingleTonSync(){
        if (singleTonSync == null){
            //加锁操作
            synchronized (SingleTonSync.class){
                if (singleTonSync == null){
                    singleTonSync = new SingleTonSync();
                }
            }
        }

        return singleTonSync;
    }

}

当singleTonSync对象是空时进行加锁操作,来保证线程的安全

编译器,cpu进行编译时有可能对指令进行重排序,导致尚未初始化的示例

什么意思呢?

创建对象的正常顺序:1分配空间,2初始化,3引用赋值

被重排序以后:1分配空间,3引用赋值,2初始化

假入有两个线程 T1,T2

T1首次创建对象被重排序以后,T2有可能在对象引用赋值之后,初始化之前访问,此时singleTonSync不为空 T2线程就直接返回singleTonSync对象,但是由于没初始化就会发生空指针等异常 那么如何来处理呢?

可以通过volatile关键字修饰,对于volatile修饰的字段,可以防止指令重排序

防止重排序
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//加入volatile 对于volatile修饰的字段,可以防止指令重排序
private volatile static SingleTonSync singleTonSync = null;
//构造方法私有
private SingleTonSync(){}

public static SingleTonSync getSingleTonSync(){
    if (singleTonSync == null){
        //加锁操作
        synchronized (SingleTonSync.class){
            if (singleTonSync == null){
                singleTonSync = new SingleTonSync();
            }
        }
    }

    return singleTonSync;
}
饿汉模式
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class SingleTon{
    private static SingleTon singleTon = new SingleTon();

    private SingleTon(){}

    public static SingleTon getSingleTon(){
        return singleTon;
    }
}

通过jvm的类加载机制来保证单例

静态内部类
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class SingleTonStaticTest{
    //静态内部类
    private static class SingleTon{
        private static SingleTonStaticTest singleTonStaticTest = new SingleTonStaticTest();
    }
    private SingleTonStaticTest(){}

    public static SingleTonStaticTest getInstance(){
        return SingleTon.singleTonStaticTest;
    }
}

本质上是利用类的加载机制来保证线程的安全

只有在实际使用时才会触发类的初始化,所以也是懒加载的一种

反射创建单例对象的问题

通过反射来创建类会破坏单例

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
  //通过反射来创建单例对象
    Constructor<SingleTonStaticTest> sin = SingleTonStaticTest.class.getDeclaredConstructor();
    sin.setAccessible(true);
    SingleTonStaticTest singleTonStaticTest = sin.newInstance();
    SingleTonStaticTest instance = SingleTonStaticTest.getInstance();
    System.out.println(singleTonStaticTest == instance);
}

这里的两个对象就不是单例对象

那么怎么来解决呢?

懒汉模式是不能解决的,懒汉模式的单例对象应当避免使用反射的方式创建

饿汉模式和静态内部类可以通过异常处理解决

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class SingleTonStaticTest{
    //静态内部类
    private static class SingleTon{
        private static SingleTonStaticTest singleTonStaticTest = new SingleTonStaticTest();
    }
    private SingleTonStaticTest(){
        //解决反射创建单例对象的问题
        if (SingleTon.singleTonStaticTest!=null){
            throw new RuntimeException("单例模式不允许创建多个对象");
        }
    }
    public static SingleTonStaticTest getInstance(){
        return SingleTon.singleTonStaticTest;
    }
}

那么是不是所有的类都能通过反射创建呢?

查看newInstance的源码发现

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
    throw new IllegalArgumentException("Cannot reflectively create enum objects");

当类型为枚举时是会抛出一个异常的

枚举类型
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * 枚举类型单例
 */
public enum SingleTonEnmu {
    INSTANCE;
}
反序列化创建对象的问题

话不多说看代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class SingleTonStatic {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException, ClassNotFoundException {
      	//获取到单例对象
        SingleTonStaticTest instance = SingleTonStaticTest.getInstance();
     		//序列化对象
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test"));
        oos.writeObject(instance);
        oos.close();
      	//反序列化对象
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("test"));
        SingleTonStaticTest singleTonStaticTest = ((SingleTonStaticTest) ois.readObject());
        
      //输出false
      System.out.println(instance == singleTonStaticTest);

    }
}
class SingleTonStaticTest implements Serializable {
    //静态内部类
    private static class SingleTon{
        private static SingleTonStaticTest singleTonStaticTest = new SingleTonStaticTest();
    }
    private SingleTonStaticTest(){
        //解决反射创建单例对象的问题
        if (SingleTon.singleTonStaticTest!=null){
            throw new RuntimeException("单例模式不允许创建多个对象");
        }
    }
    public static SingleTonStaticTest getInstance(){
        return SingleTon.singleTonStaticTest;
    }
}

反序列化创建对象字节流中获取数据不会通过类的构造函数

那么如何解决?

查看Serializable接口的源码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
* Classes that need to designate a replacement when an instance of it
* is read from the stream should implement this special method with the
* exact signature.
*
* <PRE>
* ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;
* </PRE><p>

提供一个readResolve方法

改造反序列化创建对象
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class SingleTonStatic {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException, ClassNotFoundException {
				//获取到单例对象
        SingleTonStaticTest instance = SingleTonStaticTest.getInstance();
     		//序列化对象
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test"));
        oos.writeObject(instance);
        oos.close();
      	//反序列化对象
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("test"));
        SingleTonStaticTest singleTonStaticTest = ((SingleTonStaticTest) ois.readObject());
        
      //输出true
      System.out.println(instance == singleTonStaticTest);

    }
    }
}
class SingleTonStaticTest implements Serializable {
  	//序列化版本号
    static final long serialVersionUID = 42L;
    //静态内部类
    private static class SingleTon{
        private static SingleTonStaticTest singleTonStaticTest = new SingleTonStaticTest();
    }
    private SingleTonStaticTest(){
        //解决反射创建单例对象的问题
        if (SingleTon.singleTonStaticTest!=null){
            throw new RuntimeException("单例模式不允许创建多个对象");
        }
    }
    public static SingleTonStaticTest getInstance(){
        return SingleTon.singleTonStaticTest;
    }
  //返回自己的类
    Object readResolve() throws ObjectStreamException{
        return SingleTon.singleTonStaticTest;
    }
}
反序列化枚举类型

刚刚提到了单例对象可以通过反序列化被破坏,那么枚举类型会怎样呢?

查看反序列枚举类型的实现

查看readObject方法源码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//拿到对应的枚举类型
String name = readString(false);
Enum<?> result = null;
Class<?> cl = desc.forClass();
if (cl != null) {
    try {
        @SuppressWarnings("unchecked")
      //获取对应的实例
        Enum<?> en = Enum.valueOf((Class)cl, name);
        result = en;
    } catch (IllegalArgumentException ex) {
        throw (IOException) new InvalidObjectException(
            "enum constant " + name + " does not exist in " +
            cl).initCause(ex);
    }
    if (!unshared) {
        handles.setObject(enumHandle, result);
    }
}

通过源码我们可以看到valueof方法是一个静态方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static <T extends Enum<T>> T valueOf(Class<T> enumType,
                                            String name) {
    T result = enumType.enumConstantDirectory().get(name);
    if (result != null)
        return result;
    if (name == null)
        throw new NullPointerException("Name is null");
    throw new IllegalArgumentException(
        "No enum constant " + enumType.getCanonicalName() + "." + name);
}

综上枚举类型的单例对象对反序列有着天然的优势,换句话讲 通过反序列化的方式不会破坏枚举类型的单例对象

单例对象在jdk中的应用

java.lang.Runtime

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Runtime {
    private static Runtime currentRuntime = new Runtime();

    /**
     * Returns the runtime object associated with the current Java application.
     * Most of the methods of class <code>Runtime</code> are instance
     * methods and must be invoked with respect to the current runtime object.
     *
     * @return  the <code>Runtime</code> object associated with the current
     *          Java application.
     */
    public static Runtime getRuntime() {
        return currentRuntime;
    }

    /** Don't let anyone else instantiate this class */
    private Runtime() {}
    }

饿汉模式在jdk中的实现

node实现
单例核心代码
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 构造函数

function Person(){
    this.name = '张三'
}
// 单例核心代码
let instance = null
function singleTon(){
    if(!instance) instance = new Person()

    return instance
}
使用闭包对单例模式进行改造
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//使用闭包对单例核心代码进行改造

const singleTon = ( function(){
    // 构造函数
    function Person(){
        this.name = '张三'
    }
    let instance = null
    return function singleTon(){
        if(!instance) instance = new Person()

        return instance
    }
})()
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/11/24 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Java 单例模式通俗说
定义:单例模式就是将类的构造函数进行private化,然后只留出一个静态的Instance函数供外部调用者调用。
sowhat1412
2020/11/05
5210
设计模式 | 单例模式及典型应用
单例是最常见的设计模式之一,实现的方式非常多,同时需要注意的问题也非常多。要内容:
小旋锋
2019/01/21
1K0
设计模式——单例模式
关于单例模式,这是面试时最容易遇到的问题。当时以为很简单的内容,深挖一下,也可以关联出类加载、序列化等知识。
健程之道
2020/03/11
4260
Java 设计模式 | 单例模式
单例模式,是设计模式中最常见的模式之一,它是一种创建对象模式,用于产生一个对象的具体实例,可以确保系统中一个类只会产生一个实例。
utopia
2023/03/20
4580
Java 设计模式 | 单例模式
单例设计模式(Singleton)附带案例代码仓库
使上面定义的单例类(Singleton)可以创建多个对象,枚举方式(因为枚举方式是基于JVM底层的一个实现,它已经把所有的问题解决掉了)除外。有两种方式,分别是序列化和反射。
叫我阿杰好了
2022/11/07
2710
单例设计模式(Singleton)附带案例代码仓库
设计模式(一):创建型之单例模式
使上面定义的单例类(Singleton)可以创建多个对象,枚举方式除外。有两种方式,分别是序列化和反射
Java微观世界
2025/01/21
960
我向面试官讲解了单例模式,他对我竖起了大拇指
单例模式相信大家都有所听闻,甚至也写过不少了,在面试中也是考得最多的其中一个设计模式,面试官常常会要求写出两种类型的单例模式并且解释其原理,废话不多说,我们开始学习如何很好地回答这一道面试题吧。
cxuan
2020/07/22
6050
我向面试官讲解了单例模式,他对我竖起了大拇指
今天再来聊聊单例设计模式
今天我们再来探讨一下单例设计模式,可以说,单例设计模式在面试考察中是最常出现的,单例模式看似简单,每个人可能都可以写出来,但是能不能写好就是一个问题,往深了考察,又能考察出面试者对于并发、类加载、序列化的掌握程度。
故里
2020/11/25
2820
今天再来聊聊单例设计模式
java设计模式之单例模式|单例模式之饿汉模式、懒汉模式、枚举方式|最详细的6种懒汉模式详解
概括一下ObjectOutputStream().readObject()的整个大致流程
小小鱼儿小小林
2022/10/06
1.7K0
java设计模式之单例模式|单例模式之饿汉模式、懒汉模式、枚举方式|最详细的6种懒汉模式详解
设计模式【1.2】-- 枚举式单例有那么好用么?
单例模式:是一种创建型设计模式,目的是保证全局一个类只有一个实例对象,分为懒汉式和饿汉式。所谓懒汉式,类似于懒加载,需要的时候才会触发初始化实例对象。而饿汉式正好相反,项目启动,类加载的时候,就会创建初始化单例对象。
秦怀杂货店
2020/12/26
5400
【设计模式】单例设计模式
单例模式是一种设计模式,它确保一个类只能创建一个实例,并提供一种全局访问这个实例的方式。在Java中,单例模式可以通过多种方式来实现,其中最常见的是使用私有构造函数和静态方法实现
陶然同学
2023/10/14
3630
java | 深入理解Java枚举类型(二)
blog.csdn.net/javazejian/article/details/71333103
JavaFish
2019/10/17
1.3K0
Java单例模式
Java中单例(Singleton)模式是一种广泛使用的设计模式。单例模式的主要作用是保证在Java程序中,某个类只有一个实例存在,一些管理器和控制器常被设计成单例模式。
用户10358987
2024/04/23
1560
「源码分析」— 为什么枚举是单例模式的最佳方法
枚举类型(enum type)是在 Java 1.5 中引入的一种新的引用类型,是由 Java 提供的一种语法糖,其本质是 int 值。关于其用法之一,便是单例模式,并且在《Effective Java》中有被提到:
草捏子
2020/08/10
1.3K0
深入理解《单例模式》之源码分析
执行这段代码会发现o1<>o2,这就破坏了单例。 为什么呢?罪魁祸首就是如下代码,它是反射的newInstance()的底层实现。
本人秃顶程序员
2019/05/15
5470
深入理解《单例模式》之源码分析
02.创建型:单例设计模式2
创建型:单例设计模式2目录介绍01.如何实现一个单例02.饿汉式实现方式03.懒汉式实现方式04.双重DCL校验模式05.静态内部类方式06.枚举方式单例07.容器实现单例模式01.如何实现一个单例介绍如何实现一个单例模式的文章已经有很多了,但为了保证内容的完整性,这里还是简单介绍一下几种经典实现方式。概括起来,要实现一个单例,我们需要关注的点无外乎下面几个:构造函数需要是 private 访问权限的,这样才能避免外部通过 new 创建实例;考虑对象创建时的线程安全问题;考虑是否支持延迟加载;考虑 getI
杨充
2022/09/08
3050
摸鱼设计模式——单例模式
饿汉式单例,无论是否使用,都直接初始化。其缺点则是会浪费内存空间。因为假如整个实例都没有被使用,那么这个类依然会创建,这就白创建了。
摸鱼-Sitr
2021/01/04
6890
摸鱼设计模式——单例模式
【设计模式-单例模式】
今天来说一下同样属于创建型模式的单例模式,相信这个模式普遍都清楚,因为平时在编码的时候都会进行相应的使用,我这边就当做日志记录一下。免得以后忘了还得去搜,我发现我记忆里非常差,很多东西很快就忘记了,年纪大了没办法。
Liusy
2020/09/01
5850
【设计模式-单例模式】
一个单例模式,被问7个问题,难!
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。单例模式属于创建型模式,它提供了一种创建对象的最佳方式。
田维常
2022/04/19
8720
一个单例模式,被问7个问题,难!
当Kotlin邂逅设计模式之单例模式(一)
简述: 从这篇文章开始,我将带领大家一起来探讨一下Kotlin眼中的设计模式。说下为什么想着要开始这么一个系列文章。主要基于下面几点原因:
bennyhuo
2020/02/20
9960
推荐阅读
相关推荐
Java 单例模式通俗说
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档