【设计模式】六、【创建性模式】揭秘单例模式:从生活例子到Java代码

这篇具有很好参考价值的文章主要介绍了【设计模式】六、【创建性模式】揭秘单例模式:从生活例子到Java代码。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

转自:提升工作效率-单例模式详解

六、单例模式(Singleton)

1、介绍

单例模式的产生,它主要是为了解决在软件系统中,对于一些类,我们只需要创建一个全局唯一的实例。例如,系统的配置信息、数据库连接池等。如果有多个实例可能会导致状态不一致,或者额外的资源消耗。这个需求在很多软件系统中都会出现,因此单例模式成为了一种常见的解决方案。

2、生活实例

首先,让我们想象这样一个场景:你的城市只有一个邮局,每个人都要通过这个邮局去邮寄或取信件。这个邮局就是这个城市的唯一实例,你无法创建另一个邮局,这就是单例模式在现实生活中的应用。

3、java代码实例

现在,让我们用 Java 来创建一个单例类。假设我们要创建一个 “邮局” 的单例类:

public class PostOffice {
    // 创建一个 PostOffice 类的对象
    private static PostOffice instance = new PostOffice();

    // 让构造函数为 private,这样该类就不会被实例化
    private PostOffice() {}

    // 获取唯一可用的对象
    public static PostOffice getInstance() {
        return instance;
    }

    public void showMessage() {
        System.out.println("Hello, this is the only post office in town!");
    }
}

在这段代码中,我们首先创建了一个 static 类型的 PostOffice 对象,并且将构造函数设置为 private,以防止其他人直接创建新的 PostOffice 对象。我们提供了一个 public 的方法,叫做 getInstance,它返回了我们初始化时创建的那个唯一的 PostOffice 对象。

现在,你可以这样使用这个 PostOffice 单例:

public class SingletonDemo {
    public static void main(String[] args) {
        // 不合法的构造函数,会报错:The constructor PostOffice() is not visible
        // PostOffice po = new PostOffice();

        // 获取唯一可用的对象
        PostOffice po = PostOffice.getInstance();

        // 显示消息
        po.showMessage();
    }
}

在这个例子中,我们试图通过 PostOffice 的构造函数来创建新的对象,但是会报错,因为构造函数是 private 的,不能直接访问。但是我们可以通过 getInstance 方法来获取到唯一的 PostOffice 对象,然后调用 showMessage 方法。

希望这个例子可以帮助你理解单例模式!这个模式很有趣,因为它把我们在现实生活中常见的一个概念(唯一性)应用到了编程中。

4、其它例子

让我们首先通过Java的Runtime类来看看单例模式是如何在实际中应用的。Runtime类管理着Java程序的运行时环境。每个Java应用都有一个Runtime类实例,使应用能够与其运行时环境相互作用。但是你无法直接创建一个新的Runtime实例,因为Runtime类的构造方法是私有的。相反,你必须通过Runtime类的静态方法getRuntime()来获取Runtime实例。这就是单例模式的一个实现。

public class SingletonExample {
    public static void main(String[] args) {
        Runtime runtime = Runtime.getRuntime();  // 获取Runtime实例
        // ...
    }
}

Spring框架的Bean也是单例模式的一个典型实现。在Spring框架中,一个bean默认是单例的,也就是说,Spring容器中的所有bean默认都是单例模式创建的。这样做的好处是,可以重复使用Bean,而不是每次需要时都创建一个新的Bean。这样可以大大提高效率和性能。

以下是一个Spring的例子。假设我们有一个名为"myService"的bean,你可以多次从Spring容器中获取它,但每次获取的都是同一个实例。

public class SingletonExample {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");

        MyService serviceA = (MyService) context.getBean("myService");
        MyService serviceB = (MyService) context.getBean("myService");

        System.out.println(serviceA == serviceB);  // 打印结果是 "true"
    }
}

以上的代码示例表明,serviceA和serviceB是同一个实例,它们是Spring容器中的单例bean。

这就是单例模式在Java Runtime类和Spring框架中的应用,它们都通过特定的方式实现了全局唯一实例的创建和管理,从而提高了程序的性能和效率。

5、几个你可能经常见到的也使用了单例模式的例子

Java中有几个常见的单例模式的例子,包括:

  1. java.lang.Runtime: 这个类用于管理Java应用程序的运行时环境。你不能直接创建Runtime类的新实例,而是必须通过调用其静态方法getRuntime()来获取Runtime实例。

  2. java.lang.System: System类包含一些有用的类字段和方法。它不能被实例化,所有的字段和方法都是静态的。

  3. java.awt.Desktop: Desktop类允许Java应用程序启动已在本机桌面上注册的关联应用程序,以处理URI或文件。Desktop实例是通过静态方法getDesktop()获取的。

  4. java.lang.management.ManagementFactory: 这个类是工厂方法用于获取管理接口的对象,如:操作系统、线程、内存等。

以上都是Java中常见的单例模式的应用例子。请注意,每个类都有其独特的方法来控制实例的创建和访问,这正是单例模式的核心。

6、常见的单例模式的写法

在Java中,常见的单例模式的写法主要有以下几种:

  1. 懒汉式(线程不安全):懒汉式是指在真正需要使用实例的时候再去创建。这种方式的好处是如果最后这个实例没有被使用,那么就不会创建,从而避免了资源的浪费。但是,缺点是需要处理多线程同步问题,否则可能会出现多个线程同时创建实例的情况。"懒"字,可以理解为这个实例"懒"得不愿意一开始就创建,它想等到真正需要使用的时候再去创建。

    public class Singleton {
        private static Singleton instance;
        private Singleton() {}
    
        public static Singleton getInstance() {
            if (instance == null) {
                instance = new Singleton();
            }
            return instance;
        }
    }
    

    这种方式在多线程环境下是不安全的,如果多个线程能够同时进入 if (instance == null),并且此时 instancenull,那么会有多个线程执行 instance = new Singleton(),这样会导致实例化多次 instance

  2. 懒汉式(线程安全)

    public class Singleton {
        private static Singleton instance;
        private Singleton() {}
    
        public static synchronized Singleton getInstance() {
            if (instance == null) {
                instance = new Singleton();
            }
            return instance;
        }
    }
    

    这种方式能够在多线程中很好的工作,但是每次调用 getInstance 方法时都需要进行同步,造成不必要的同步开销。

  3. 饿汉式:饿汉式是指在类加载时就创建实例。这种方式的好处是可以确保线程安全,因为实例是在类加载时就创建好的,所以不会存在多个线程同时创建实例的情况。但是,缺点是如果这个实例最后没有被使用,那么就会造成资源的浪费。因为"饿"字,可以想象成这个实例"饿"得不能等到真正需要使用的时候再创建,所以一开始就创建好了。

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

    这种方式基于类加载机制,避免了多线程同步问题,但是如果 Singleton 类没有被装载,那么 instance 不会被实例化,这时候类被装载其实是不需要创建实例的,会造成不必要的资源浪费。

  4. 双重检查锁定(Double Checked Locking)

    双重检查锁定(Double Checked Locking)的名称确实看起来有点晦涩,不过它的实现方式其实还是很直观的。

    我们可以通过一个生活实例来帮助理解。比如你的室友需要使用洗手间,但是他不确定洗手间是否有人。他可以直接尝试打开门,但是如果洗手间里有人,那将是一个非常尴尬的情况。所以他可能先敲一下门,这就是第一次“检查”,如果没有人回应,他就可以认为洗手间是空的。但是在他打开门的时候,他可能还会再次听听里面是否有水声等提示有人的声音,这就是第二次“检查”。这个过程就像我们在创建单例对象时,先检查对象是否已经创建,如果没有创建,就获取锁并再次检查,然后才开始创建对象。

    public class Singleton {
        private volatile static Singleton instance;
    
        private Singleton() {}
    
        public static Singleton getInstance() {
            if (instance == null) {
                synchronized (Singleton.class) {
                    if (instance == null) {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }
    
    

    这段代码做了两次检查,第一次是在同步块外,如果instance不为null,则直接返回,这样就避免了每次都需要进入同步块,可以提高效率。如果instance为null,才进行同步,然后在同步块内再进行一次检查。如果这时instance仍为null,则创建新的实例。

    这样的双重检查方式可以确保即使有多个线程同时调用getInstance()方法,也能保证只创建一个Singleton实例。同时由于使用了关键字volatile,保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新。

    双重检查锁定可以在多线程环境下保持高性能。

  5. 静态内部类方式

    public class Singleton {
        private static class SingletonHolder {
            private static final Singleton INSTANCE = new Singleton();
        }
        private Singleton (){}
    
        public static Singleton getInstance() {
            return SingletonHolder.INSTANCE;
        }
    }
    

    这种方式同样利用了类加载机制来保证只创建一个instance实例。它与饿汉式的区别在于:饿汉式只要Singleton类被装载了,那么instance就会被实例化(没有达到lazy loading效果),而这种方式是Singleton类被装载了,SingletonHolder类没有被主动使用,只有通过显式调用getInstance方法时,才会显式装载SingletonHolder类,从而实例化instance。

  6. 枚举方式

    public enum Singleton {
        INSTANCE;
    
        public void whateverMethod() {
        }
    }
    

    这是最安全的方法,它能防止多次实例化,防止反序列化重新创建新的对象,绝对防止多次实例化。

  7. 静态内部类方式的改进版(Initialization on Demand Holder,IODH)

    假设你在一家公司工作,这家公司的大楼里有一个自动贩卖机。这个自动贩卖机只在第一个需要用它的员工使用卡片激活后才会开启。一旦被激活,所有的员工都可以使用这个自动贩卖机。此外,这个大楼中不会有第二台自动贩卖机出现,所以无论哪个员工想要用自动贩卖机,他们都只能使用这一台。

    这个例子就是对应到IODH模式的实现:

    • 自动贩卖机对应到代码中的Singleton实例
    • 激活自动贩卖机对应到在第一次需要Singleton实例时创建它
    • 所有的员工都使用同一台自动贩卖机对应到所有对Singleton.getInstance()的调用都返回同一个实例

    以下是在Java中使用IODH模式的例子:

    public class Singleton {
        private static class Holder {
            private static final Singleton INSTANCE = new Singleton();
        }
    
        private Singleton() {
        }
    
        public static Singleton getInstance() {
            return Holder.INSTANCE;
        }
    }
    

    在这个例子中,Singleton实例是在Holder类中创建的,只有当第一次调用Singleton.getInstance()时,Holder类才会被加载,Singleton实例才会被创建。之后的所有Singleton.getInstance()调用都将返回同一个实例。

    所以,如果你觉得IODH难以理解或记忆,你可以想象它就像是一个只有在第一个需要用它的人激活后才会开启的自动贩卖机,并且无论有多少人需要使用自动贩卖机,他们都只能使用这一台。

7、一些常见的示例:

  1. 饿汉式(线程安全,调用效率高,但不能延时加载):
    Java中的java.lang.Runtime就是使用了饿汉式单例模式。Runtime类没有公开的构造方法,但提供了一个静态方法Runtime.getRuntime()来获取Runtime类的唯一实例。

  2. 懒汉式(线程安全,调用效率不高,但可以延时加载):
    Spring框架中,在单例作用域的Bean默认为懒加载,第一次调用getBean()时才会初始化Bean,这就是懒汉式的应用。

  3. 双重检测锁式(由于JVM底层内部模型原因,偶尔会出问题,不建议使用):
    在Android开发中,如果要实现一个单例且这个单例在多线程环境下使用且性能要求较高,双重检测锁式是一种常见的实现方式。

  4. 静态内部类式(线程安全,调用效率高,可以延时加载):
    Android源码中,对于系统服务(SystemService)的管理,往往采用静态内部类式来实现单例。

  5. 枚举式(线程安全,调用效率高,不能延时加载,可以天然的防止反射和反序列化调用):
    Java的枚举类型实际上就是一个类,因此可以在枚举中添加自己的方法。当我们需要单例时,可以使用枚举方式,这是最简单的方式。在Effective Java中,Joshua Bloch提倡使用枚举方式来实现单例。

  6. 双重检测锁式的优化版(完全解决DCL失效问题):
    实际应用中,这种方式还是相对较少的。因为在大多数情况下,对于单例模式的要求并不会严苛到需要在DCL基础上进行优化。

  7. 静态内部类方式的改进版(Initialization on Demand Holder,IODH):
    IODH方式同样也是很常见的一种方式,这种方式在很多开源框架中都有应用,如Spring框架、Apache commons工具类库等。

这些模式在不同的场合有不同的应用,可以根据具体的需求选择使用。文章来源地址https://www.toymoban.com/news/detail-462793.html

到了这里,关于【设计模式】六、【创建性模式】揭秘单例模式:从生活例子到Java代码的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 创建型设计模式06-单例模式

    🧑‍💻作者:猫十二懿 ❤️‍🔥账号:CSDN 、掘金 、个人博客 、Github 🎉公众号:猫十二懿 单例模式是一种创建型设计模式,它的目的是 确保一个类只有一个实例,并提供一个全局访问点来访问该实例 。在单例模式中,类自身负责创建自己的唯一实例,并确保在系统中只

    2024年02月08日
    浏览(41)
  • 学习笔记-设计模式-创建型模式-单例模式

    一个类只有一个实例,并提供一个全局访问此实例的点,哪怕多线程同时访问。 单例模式主要解决了 一个全局使用的类被频繁的创建和消费 的问题。 单例模式的案例场景 数据库的连接池不会反复创建 spring中一个单例模式bean的生成和使用 在我们平常的代码中需要设置全局

    2024年02月08日
    浏览(49)
  • 【地铁上的设计模式】--创建型模式:单例模式(五)--枚举单例

    什么是枚举单例 枚举单例是指使用枚举类型来实现单例模式,它是单例模式中最简单、最安全的一种实现方式。在枚举类型中定义的枚举值只会被实例化一次,即保证了全局唯一的实例,而且实现简单、线程安全、防止反射攻击、支持序列化等。 如何实现枚举单例 实现枚举

    2023年04月25日
    浏览(79)
  • 设计模式-创建型模式(单例、工厂、建造、原型)

    设计模式:软件设计中普遍存在(反复出现)的各种问题,所提出的解决方案。 面向对象三大特性:封装、继承、多态。 面向对象设计的SOLID原则: (1)开放封闭原则:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情

    2024年02月08日
    浏览(52)
  • c#设计模式-创建型模式 之 单例模式

    目录 前言: 优点: 缺点: 饿汉式(静态变量方式) 懒汉式(线程不安全) 懒汉式(双重检查锁定) 推荐方式Lazy 总结: 这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供 了一种访问其唯一的对象的方式,可以直接访问,

    2024年02月13日
    浏览(37)
  • GO设计模式——4、单例模式(创建型)

    目录 单例模式(Singleton Pattern)   优缺点 使用场景 饿汉式和懒汉式单例模式         单例模式(Singleton Pattern)是一个类 只允许创建一个 对象(或者实例),那这个类就是一个单例类,这种设计模式就叫作单例设计模式,简称单例模式。单例模式的要点有三个:一是

    2024年02月05日
    浏览(47)
  • C++设计模式创建型之单例模式

    一、概述         单例模式也称单态模式,是一种创建型模式,用于创建只能产生一个对象实例的类。例如,项目中只存在一个声音管理系统、一个配置系统、一个文件管理系统、一个日志系统等,甚至如果吧整个Windows操作系统看成一个项目,那么其中只存在一个任务管理

    2024年02月14日
    浏览(47)
  • 深入理解设计模式-创建型之单例模式

    如果有些数据在系统中应该且只能保存一份,那就应该设计为单例类。 如:配置类:在系统中,我们只有一个配置文件,当配置文件被加载到内存之后,应该被映射为一个唯一的【配置实例】,此时就可以使用单例,当然也可以不用。 全局计数器:我们使用一个全局的计数

    2024年02月12日
    浏览(57)
  • 【Java 设计模式】创建型之单例模式

    在软件开发中,单例模式是一种常见的设计模式, 它确保一个类只有一个实例,并提供一个全局访问点 。单例模式在需要控制某些资源,如数据库连接池、线程池等共享资源的情况下非常有用。在本文中,我们将介绍 Java 设计模式中的单例模式,了解其实现方式、使用场景

    2024年01月18日
    浏览(51)
  • Java23种设计模式-创建型模式之单例模式

    单例模式 (Singleton Pattern):通过单例模式的方法创建的 类在当前进程中只有一个实例 (根据需要,也有可能一个线程中属于单例,如:仅线程上下文内使用同一个实例),该类负责 创建自己的对象 ,同时 确保只有单个对象 被创建。 注 : 1、单例类 只能 有 一个实例 。

    2024年04月26日
    浏览(53)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包