首页 > 其他分享 >趣解设计模式之《新娘到底叫啥名啊?》

趣解设计模式之《新娘到底叫啥名啊?》

时间:2023-09-17 16:03:02浏览次数:36  
标签:getInstance 趣解 新娘 private instance static SingletonDemo4 设计模式 public

〇、小故事

前一段时间,在网上流传了这么一段视频,视频是一对新人的婚礼现场,主持人让新郎当着众多亲戚朋友的面,大声对新娘表达自己的爱意,小伙子自信满满大声的对众人说:“我爱你,周秀楠!”。

但是台下的新娘却一脸茫然,从表情中根本没有看出一丝丝因为新郎的表白而开心的喜悦

主持人发现了新娘表情的尴尬,赶快看了一下新娘的名字,**这新娘也不叫“周秀楠”啊!**是题词本弄错了?还是新郎弄错了?主持人又问了一下新郎,“**新娘到底叫啥名啊?**”

新郎也懵了,赶快思索了一下,大声的喊道,“**我爱你,王家瑞!!**”

这个傻小子啊,竟然在婚礼现场叫错了新娘的名字,估计换做大多女生,回直接撇下婚礼现场怒气的离开了。那么,我们思考一下,为啥新娘会这么生气呢?这个问题确实蛮白痴的,因为叫错人了呗?别说是婚礼现场了,就算是在公司,一个同事叫错了你的名字,估计你也会很生气的。因为,我们每个人都是独立的个体,都有独立的个性和思考能力,我们当然希望自己是这个世界独一无二的。那么,在设计模式中,也有这么一种模式,是用来返回某个类的实例对象,而这种实例对象无论获取多少次都是唯一的,且不会再次创建的,那么这个模式就叫做——单例模式

一、模式定义

单例模式Singleton Pattern

确保一个类只有一个实例,并提供一个全局访问点

通用类图

二、单例模式的五种实现方式

2.1> 饿汉式(线程安全,调用效率高,但是不能延时加载)

这是实现一个安全的单例模式的最简单粗暴的写法,这种实现方式我们称之为饿汉式。之所以称之为饿汉式,是因为肚子很饿了,想马上吃到东西,不想等待对象构造“浪费”时间。这种写法,在类被加载的时候就把Singleton实例给创建出来了。

饿汉模式的缺点

在还不需要此实例的时候就已经把实例创建出来了,没起到lazy loading的效果。

饿汉模式的优点

实现简单,而且安全可靠。

饿汉模式的实现方式

public class SingletonDemo1{ 
     private static SingletonDemo1 instance = new SingletonDemo1(); 
     private SingletonDemo1(){} 
     public static SingletonDemo1 getInstance(){  
          return instance;  
      } 
}

2.2> 懒汉式(线程安全,调用效率不高,但是能延时加载)

相比饿汉式,懒汉式显得没那么“饿”,在真正需要的时候再去创建实例。在getInstance()方法中,先判断实例是否为空再决定是否去创建实例。实现方式如下所示:

public class SingletonDemo2 {
     
    // 类初始化时,不初始化这个对象(延时加载,真正用的时候再创建)
    private static SingletonDemo2 instance;
     
    // 构造器私有化
    private SingletonDemo2(){}
     
    // 方法同步,调用效率低
    public static SingletonDemo2 getInstance(){
        if(instance==null){
            instance=new SingletonDemo2();
        }
        return instance;
    }
}

看起来似乎很完美,但是存在线程安全问题。在并发获取实例的时候,可能会存在构建了多个实例的情况。所以,需要对getInstance()方法加上synchronized锁。实现方式如下所示:

public class SingletonDemo2 {
     
    //类初始化时,不初始化这个对象(延时加载,真正用的时候再创建)
    private static SingletonDemo2 instance;
     
    //构造器私有化
    private SingletonDemo2(){}
     
    //方法同步,调用效率低
    public static synchronized SingletonDemo2 getInstance(){
        if(instance==null){
            instance=new SingletonDemo2();
        }
        return instance;
    }
}

2.3> 双重校验

但是由于getInstance()方法加锁,会导致每次线程调用获取实例的时候,都需要排队等锁,效率很低。所以,产生了Double Check的方式。

通过加锁,可以保证同时只有一个线程走到第二个判空代码中去,这样保证了只创建一个实例。这里还用到了volatile关键字来修饰singletonDemo3变量,其最关键的作用是防止指令重排。实现方式如下所示:

public class SingletonDemo3 {
    
    private volatile static SingletonDemo3 singletonDemo3;

    private SingletonDemo3() {
    }

    public static SingletonDemo3 newInstance() {
        if (singletonDemo3 == null) {
            synchronized (SingletonDemo3.class) {
                if (singletonDemo3 == null) {
                    singletonDemo3 = new SingletonDemo3();
                }
            }
        }
        return singletonDemo3;
    }
}

2.4> 静态内部类(线程安全,调用效率高,可以延时加载)

通过静态内部类的方式实现单例模式是线程安全的,同时静态内部类不会在Singleton类加载时就加载,而是在调用getInstance()方法时才进行加载,达到了懒加载的效果。实现方式如下所示:

public class SingletonDemo4 {
    /**
 	 * 静态内部类
	 **/
    private static class SingletonClassInstance{
        private static final SingletonDemo4 instance = new SingletonDemo4();
    }

    private SingletonDemo4(){}

    public static SingletonDemo4 getInstance(){
        return SingletonClassInstance.instance;
    }
}

2.4.1> 反射攻击

利用反射机制,打破单例限制,代码实现如下所示:

public static void main(String[] args) throws Exception {
    SingletonDemo4 singletonDemo4 = SingletonDemo4.getInstance();
    Constructor<Singleton> constructor = SingletonDemo4.class.getDeclaredConstructor();
    constructor.setAccessible(true);
    SingletonDemo4 newSingletonDemo4 = constructor.newInstance();
    System.out.println(singletonDemo4 == newSingletonDemo4); // false
}

2.4.2> 反序列化攻击

引入pom依赖,这个依赖提供了序列化反序列化工具类。

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.8.1</version>
</dependency>

Singleton类实现java.io.Serializable接口。

public class SingletonDemo4 implements Serializable {

    private static class SingletonClassInstance {
        private static SingletonDemo4 instance = new SingletonDemo4();
    }

    private SingletonDemo4() {
    }

    public static SingletonDemo4 getInstance() {
        return SingletonClassInstance.instance;
    }

    public static void main(String[] args) {
        SingletonDemo4 singletonDemo4 = SingletonDemo4.getInstance();
        byte[] serialize = SerializationUtils.serialize(singletonDemo4);
        SingletonDemo4 newSingletonDemo4 = SerializationUtils.deserialize(serialize);
        System.out.println(singletonDemo4 == newSingletonDemo4); // false
    }

}

2.5> 枚举类(线程安全,调用效率高,不能延时加载,可以天然的防止反射和反序列化调用)

在effective java(这本书真的很棒)中说道,最佳的单例实现模式就是枚举模式。利用枚举的特性,让JVM来帮我们保证线程安全和单一实例的问题。除此之外,写法还特别简单。实现方式如下所示:

public enum SingletonDemo5 {

    // 枚举元素本身就是单例
    INSTANCE;

    // 添加自己需要的操作,直接通SingletonDemo5.INSTANCE.doSomething()的方式调用即可。方便、简洁又安全。
    public void doSomething() {
        System.out.println("doSomething");
    }
}

调用方法如下所示:

public class Main {
    public static void main(String[] args) {
        Singleton.INSTANCE.doSomething();
    }
}

今天的文章内容就这些了:

写作不易,笔者几个小时甚至数天完成的一篇文章,只愿换来您几秒钟的 点赞 & 分享

更多技术干货,欢迎大家关注公众号“爪哇缪斯” ~ \(^o^)/ ~ 「干货分享,每天更新」

标签:getInstance,趣解,新娘,private,instance,static,SingletonDemo4,设计模式,public
From: https://blog.51cto.com/u_15003301/7502363

相关文章

  • 软件设计模式系列之七——原型模式
    1模式的定义原型模式(PrototypePattern)是一种创建型设计模式,其主要目的是通过复制现有对象来创建新对象,而不是使用构造函数。原型模式将对象的创建委托给原型对象,通过克隆(复制)来生成新对象,这种方式可以避免对象的重复初始化,提高性能,并使对象的创建更加灵活和动态。原型模式的关......
  • 设计模式7大原则
    开闭原则对扩展开放,对修改关闭。依赖倒置原则面向接口编程。单一职责原则一个类、接口、方法只负责一项职责。接口隔离原则接口中方法尽量少。迪米特法则尽量降低类与类之间的耦合。里氏替换原则引用父类的地方能使用其子类。合成复用原则尽量使用合成/聚合的方式,不是使用继......
  • 【设计模式】模板方法模式Template Method:实现同一模板框架下的扩展
    (目录)模板方法模式的原理和代码实现都比较简单,也被广泛应用,但是因为使用继承机制,副作用往往盖过了主要作用,所以在使用时尤其要小心谨慎。原理模板方法模式原始定义是:在操作中定义算法的框架,将一些步骤推迟到子类中。模板方法让子类在不改变算法结构的情况下重新定义算法的某......
  • 【设计模式】访问者模式Visitor:实现对象级别的矩阵结构
    (目录)访问者模式:一个原理看似很简单,但是理解起来有一定难度,使用场景相对较少的行为型模式:它能将算法与其所作⽤的对象隔离开来假如有这样⼀位⾮常希望赢得新客户的资深保险代理⼈。他可以拜访街区中的每栋楼,尝试向每个路⼈推销保险。所以,根据⼤楼内组织类型的不同,他可......
  • 【设计模式】解释器模式Interpreter Pattern:实现自定义配置规则功能
    解释器模式使用频率不算高,**通常用来描述如何构建一个简单“语言”的语法解释器。**它只在一些非常特定的领域被用到,比如:编译器;规则引擎;正则表达式;SQL解析等。不过,了解它的实现原理,可以帮助思考如何通过更简洁的规则来表示复杂的逻辑。模式原理分析解释器模式的原始......
  • 使用设计模式改写if/else或switch/case语句
    在写代码的时候,经常会用到if/else语句或者switch/case语句。虽然很省事,但是没有体现到java的封装、继承、多态等特性。没有用到java的面向对象编程的精髓。比如这种if/else语句:Stringstr="菠萝";if("苹果".equals(str)){ System.out.println("又大又红的苹果");}else......
  • 8.前端设计模式之混合模式
    MaxinPattern: Addfunctionalitytoobjectsorclasseswithoutinheritance在不使用的继承的情况下为对象或者类添加功能在JavaScript中混合也是通过原型链实现的。比如有个Dog类:classDog{constructor(name){this.name=name;}}现在我们希望为Dog类添加bark、......
  • 在工作流引擎设计领域,是否自动计算未来的处理人的设计模式有哪些?
    概述流程的第一个节点发送下去的时候,就要把以后所有节点的处理人计算出来,能清楚的知道每个节点都是那些人处理.计算未来处理人包括抄送节点、与待办节点.默认的模式为:每个节点发送的时候即使计算,就是不计算未来处理人.流程设计特征.流程的所有节点的接受人不能是主管选择的,只能......
  • 设计模式 C++
    (设计模式)(李建忠C++)23种设计模式组件协作模板方法父类中定义组件(函数)的调用流程,每个组件使用虚函数进行实现,然后子类中可以重写父类中虚函数的实现。如果我们发现一个算法的组件(函数)的调用流程都是一样的,但是步骤中的各个组件的实现可能有所差异,此时会使用模板方法。【......
  • 软件设计模式系列之六——单例模式
    1模式的定义单例模式(SingletonPattern)是一种常见的创建型设计模式,其主要目的是确保一个类只有一个实例,并提供一个全局访问点来获取该实例。这意味着无论何时何地,只要需要该类的实例,都会返回同一个实例,而不是创建多个相同的实例。单例模式通常用于管理全局状态、资源共享或限制......