定义:

Singleton是“ 四人帮”设计模式的一部分,它属于创新设计模式。 在本文中,我们将更深入地研究Singleton模式的用法。 就建模而言,它是最简单的设计模式之一,但另一方面,就使用的复杂性而言,这也是最有争议的模式之一。
在Java中,Singleton模式将确保在Java虚拟机中仅创建一个类的实例。 它用于提供对对象的全局访问点。 在实际使用中,Singleton模式用于日志记录,缓存,线程池,配置设置,设备驱动程序对象。

设计模式通常与工厂设计模式结合使用。 此模式也用于Service Locator JEE模式。

结构体:

单例类图

  • 静态成员:包含单例类的实例。
  • 私有构造函数:这将阻止其他人实例化Singleton类。
  • 静态公共方法:此方法提供对Singleton对象的全局访问点,并将实例返回给客户端调用类。

实现示例:延迟初始化

让我们看一下Java中的单例实现示例。 下面的代码使用惰性初始化过程。

public class SingletonExample {// Static member holds only one instance of the// SingletonExample classprivate static SingletonExample singletonInstance;// SingletonExample prevents any other class from instantiatingprivate SingletonExample() {}// Providing Global point of accesspublic static SingletonExample getSingletonInstance() {if (null == singletonInstance) {singletonInstance = new SingletonExample();}return singletonInstance;}public void printSingleton(){System.out.println('Inside print Singleton');}
}

单例模式代码说明

当使用SingletonExample.getSingletonInstance()。printSingleton()从客户端调用此类时。 那么在第一次时只会创建一个实例。 在第二次以后的所有后续调用中,我们将引用相同的对象,并且getSingletonInstance()方法返回在第一次创建时所用的SingletonExample类的相同实例。 您可以通过添加以下代码作为打印语句来测试:

public static SingletonExample getSingletonInstance() {if (null == singletonInstance) {singletonInstance = new SingletonExample();System.out.println('Creating new instance');}return singletonInstance;}

如果现在我们从客户端类中将Singleton类称为:

SingletonExample.getSingletonInstance().printSingleton();
SingletonExample.getSingletonInstance().printSingleton();
SingletonExample.getSingletonInstance().printSingleton();
SingletonExample.getSingletonInstance().printSingleton();

上面的调用的输出是:

Creating new instance
Inside print Singleton
Inside print Singleton
Inside print Singleton
Inside print Singleton

实施示例:双重检查锁定

上面的代码在单线程环境中绝对可以正常工作,并且由于延迟初始化而可以更快地处理结果。 但是,以上代码可能会在多线程环境中的结果中产生一些突然的行为,因为在这种情况下,如果多个线程尝试同时访问getSingletonInstance()方法,则它们可能会创建同一SingletonExample类的多个实例。 想象一个实际的情况,我们必须创建一个日志文件并对其进行更新,或者在使用诸如打印机之类的共享资源时进行更新。 为避免这种情况,我们必须使用某种锁定机制,以便第二个线程在第一个线程完成该过程之前不能使用此getInstance()方法。

堆中的单例

在图3中,我们显示了多个线程如何访问单例实例。 由于单例实例是存储在堆的PermGen空间中的静态类变量。 这也适用于getSingletonInstance()实例方法,因为它也是静态的。 在多线程环境中,为了防止每个线程创建单例对象的另一个实例并因此导致并发问题,我们将需要使用锁定机制。 这可以通过synced关键字实现。 通过使用这个synced关键字,我们可以防止Thread2或Thread3在getSingletonInstance()方法内部的Thread1时访问单例实例。

从代码角度来看,这意味着:

public static synchronized SingletonExample getSingletonInstance() {if (null == singletonInstance) {singletonInstance = new SingletonExample();}return singletonInstance;}

因此,这意味着每次调用getSingletonInstance()都会给我们带来额外的开销。 为了避免这种昂贵的操作,我们将使用双重检查锁定,以便仅在第一次调用期间进行同步,并且将这种昂贵的操作限制为仅发生一次。 仅在以下情况才需要:

singletonInstance = new SingletonExample();

代码示例:

public static volatile SingletonExample getSingletonInstance() {if (null == singletonInstance) {synchronized (SingletonExample.class){if (null == singletonInstance) {singletonInstance = new SingletonExample();}}}return singletonInstance;}

在上面的代码片段中,假设有多个线程并发并尝试创建新实例。 在这种情况下,可能有三个或更多线程在同步块上等待访问。 由于我们使用了同步,因此只有一个线程可以访问。 当第一个线程退出该块时,将等待所有在同步块上等待的其余线程。 但是,当剩余的并发线程进入同步块时,由于双重检查:空检查,它们被阻止进一步进入。 由于第一个线程已经创建了一个实例,因此没有其他线程会进入此循环。

其余所有不幸运地与第一个线程一起进入同步块的线程将在第一个null检查时被阻塞。 这种机制称为双重检查锁定 ,它提供了显着的性能优势,并且是具有成本效益的解决方案。

实施示例:易失性关键字

我们还可以在实例变量声明中使用volatile关键字。

private volatile static SingletonExample singletonInstance;

volatile关键字在多线程环境中用作并发控制工具,并以最准确的方式提供最新更新。但是请注意,双重检查锁定可能在Java 5之前不起作用。在这种情况下,我们可以使用早期加载机制。 如果我们还记得原始的示例代码,则使用了延迟加载。 如果提早加载,我们将在开始时实例化SingletonExample类,并将其引用到私有静态实例字段。

public class SingletonExample {private static final SingletonExample singletonInstance = new SingletonExample;// SingletonExample prevents any other class from instantiatingprivate SingletonExample() {}// Providing Global point of accesspublic static SingletonExample getSingletonInstance() {return singletonInstance;}public void printSingleton(){System.out.println('Inside print Singleton');}
}

在这种方法中,单例对象是在需要之前创建的。 JVM负责静态变量的初始化,并确保进程是线程安全的,并确保在线程尝试访问它之前创建实例。 在延迟加载的情况下,当客户端类调用getSingleInstance()而在早期加载的情况下,是singletonInstance创建当类在存储器中加载的singletonInstance被创建。

实现示例:使用枚举

使用Enum在Java 5或更高版本中实现Singleton:
Enum是线程安全的,并且通过Enum实现Singleton可以确保即使在多线程环境中,Singleton也只有一个实例。 让我们看一个简单的实现:

public enum SingletonEnum {INSTANCE;public void doStuff(){System.out.println('Singleton using Enum');}
}
And this can be called from clients :
public static void main(String[] args) {SingletonEnum.INSTANCE.doStuff();}

常见问题解答:

问题:为什么我们不能使用静态类而不是单例呢?
回答:

  • 与静态类相比,单例的主要优点之一是它可以实现接口并扩展类,而静态类则不能(它可以扩展类,但不继承其实例成员)。 如果我们考虑静态类,那么它只能是嵌套的静态类,因为顶级类不能是静态类。 静态意味着它属于它所在的类,而不属于任何实例。 因此,它不能是顶级课程。
  • 另一个区别是,静态类仅与Singleton不同,其所有成员都将是静态的。
  • Singleton的另一个优点是它可以延迟加载,而static每次首次加载时都会被初始化。
  • 单例对象存储在堆中,而静态对象存储在堆栈中。
  • 我们可以克隆Singleton的对象,但是不能克隆静态类对象。
  • Singleton可以使用多态的面向对象功能,但是静态类不能。

问题:单例类可以被子类化吗?
答:坦率地说,单例只是一种设计模式,可以将其子类化。 但是,值得理解子类继承子类的逻辑或要求,因为子类可能不会通过扩展Singleton类来继承单例模式目标。 但是,可以通过在类声明中使用final关键字来防止子类化。

问题:使用克隆是否可以有多个单例实例?
答:很好! 我们现在干什么? 为了防止创建单例实例的另一个实例,我们可以从clone()方法内部引发异常。

问题:如果我们使用序列化和反序列化创建另一个单例实例,会产生什么影响?
答:当我们序列化一个类并反序列化它时,它将创建单例类的另一个实例。 基本上,您对单例实例进行反序列化的次数将创建多个实例。 在这种情况下,最好的方法是将单例设为枚举。 这样,底层的Java实现即可处理所有细节。 如果这不可能,那么我们将需要重写readobject()方法以返回相同的单例实例。

问题:Singleton可以使用哪种其他模式?
答:还有其他一些模式,例如Factory方法,构建器和原型模式,它们在实现过程中使用Singleton模式。

问题:JDK中的哪些类使用单例模式?
答案:java.lang.Runtime:在每个Java应用程序中,只有一个Runtime实例,允许该应用程序与其运行的环境进行交互。 getRuntime等效于单例类的getInstance()方法。

Singleton设计模式的用途:

单例模式的各种用法:

  • 硬件接口访问:单例的使用取决于要求。 但是,在需要外部硬件资源使用限制的情况下,实际上可以使用单例,例如,可以将打印后台处理程序做成单例的硬件打印机,以避免多次并发访问并产生死锁。
  • 记录器:同样,单例是在日志文件生成中使用的一个很好的潜在候选者。 想象一个应用程序,其中日志记录实用程序必须根据从用户收到的消息来生成一个日志文件。 如果存在多个使用此日志记录实用程序类的客户端应用程序,则它们可能会创建此类的多个实例,并且在并发访问同一记录器文件期间可能会引起问题。 我们可以将logger实用程序类用作单例,并提供全局参考。
  • 配置文件:这是单例模式的另一个潜在候选者,因为它具有性能优势,因为它可以防止多个用户重复访问和读取配置文件或属性文件。 它创建配置文件的单个实例,该实例可被多个调用同时访问,因为它将提供加载到内存对象中的静态配置数据。 该应用程序仅在第一次读取配置文件,然后从第二次调用开始,客户端应用程序从内存对象中读取数据。
  • 缓存:我们可以将缓存用作单例对象,因为它可以具有全局参考点,并且对于将来对缓存对象的所有调用,客户端应用程序都将使用内存中对象。

动手:

让我们以一个小的实际示例来更详细地了解Singleton设计模式。
问题陈述:
设计一个小型的ATM打印应用程序,它可以生成多种类型的交易对帐单,包括迷你对帐单,明细对帐单等。但是,客户应注意这些对帐单的创建。 确保将内存消耗降至最低。 设计方案: 可以使用四个设计模式的两个核心Gang来满足以上要求-工厂设计模式和Singleton设计模式。 为了在ATM机中为ATM事务生成多种类型的语句,我们可以创建一个Statement Factory对象,该对象具有工厂方法createStatements() 。 createStatement将创建DetailedStatement或MiniStatement对象。 客户端对象将完全不知道对象的创建,因为它仅与Factory接口交互。 我们还将创建一个名为StatementType的接口。 这将允许添加其他对帐单类型对象,例如信用卡对帐单等。 因此,该解决方案遵循面向对象的“打开/关闭”设计原则,具有可扩展性和可扩展性。 减少内存消耗的第二个要求可以通过使用Singleton设计模式来实现。 不需要多次启动Statement Factory类,并且一个工厂可以创建多个语句对象。 单例模式将创建StatementFactory类的单个实例,从而节省内存。

ATM范例

  • 工厂:工厂是一个抽象类,是客户的单点联系。 所有具体的工厂类都需要实现抽象工厂方法。
  • StatementFactory:这是Factory创建类,由creator方法组成。 该类从Factory抽象类扩展。这是所有产品(例如Statements)的主要创建者类。
  • StatementType:这是一个产品接口,它提供对需要由Factory类创建的各种类型的产品的抽象。
  • DetailedStatement:这是StatementType接口的具体实现,它将打印详细的语句。
  • MiniStatement:这是StatementType接口的具体实现,它将打印迷你语句。
  • 客户端:这是客户端类,它将调用StatementFactory和StatementType并请求创建对象。

假设:
该设计解决方案仅适用于一台ATM机。

样例代码:

工厂.java

public abstract class Factory {protected abstract StatementType createStatements(String selection);}

StatementFactory.java

public class StatementFactory extends Factory {private static StatementFactory uniqueInstance;private StatementFactory() {}public static StatementFactory getUniqueInstance() {if (uniqueInstance == null) {uniqueInstance = new StatementFactory();System.out.println('Creating a new StatementFactory instance');}return uniqueInstance;}public StatementType createStatements(String selection) {if (selection.equalsIgnoreCase('detailedStmt')) {return new DetailedStatement();} else if (selection.equalsIgnoreCase('miniStmt')) {return new MiniStatement();}throw new IllegalArgumentException('Selection doesnot exist');}
}

StatementType.java

public interface StatementType {String print();
}

DetailedStatement.java

public class DetailedStatement implements StatementType {@Overridepublic String print() {System.out.println('Detailed Statement Created');return 'detailedStmt';}
}

MiniStatement.java

public class MiniStatement implements StatementType {@Overridepublic String print() {System.out.println('Mini Statement Created');return 'miniStmt';}
}

客户端程序

public class Client {public static void main(String[] args) {System.out.println('Enter your selection:');BufferedReader br = new BufferedReader(new InputStreamReader(System.in));String selection = null;try {selection = br.readLine();} catch (IOException e) {e.printStackTrace();}Factory factory = StatementFactory.getUniqueInstance();StatementType objStmtType = factory.createStatements(selection);System.out.println(objStmtType.print());}}

结论:

在以上文章中,我们详细介绍了Singleton模式,如何在实际应用中实现Singleton模式。 尽管单例模式看起来很简单,但除非有强烈要求,否则我们应该阻止自己使用它。 您可以将结果归咎于多线程环境中结果的不可预测性。 尽管我们可以在Java 5及更高版本中使用枚举 ,但有时很难始终在枚举中实现您的逻辑,否则Java 5之前可能会有遗留代码。希望我们的读者喜欢本文。

参考: Singleton设计模式–来自ICG博客上我们JCG合作伙伴 Mainak Goswami 的内省和最佳实践 。

翻译自: https://www.javacodegeeks.com/2013/02/singleton-design-pattern-an-introspection-and-best-practices.html

单例设计模式–内省和最佳实践相关推荐

  1. 反射和内省_单例设计模式–内省和最佳实践

    反射和内省 定义: Singleton是" 四人帮"设计模式的一部分,它属于创新设计模式. 在本文中,我们将更深入地研究Singleton模式的用法. 就建模而言,它是最简单的设计 ...

  2. 类变量类方法,main方法,代码块,单例设计模式,final,抽象类

    一.类变量和类方法 1.类变量 也叫静态变量/静态属性,是该类的所有对象共享的变量,任何一个该类的对象去访问它时,取到的都是相同的值,同样任何一个该类的对象去修改它时,修改的也是同一个变量. 1).定 ...

  3. 设计模式之单例设计模式

    1 设计模式(Design pattern) 代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用.设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案.这些解决方案是众多软件开发人 ...

  4. export function函数传参_04 js高阶函数(惰性函数、柯里化函数、compose函数)和单例设计模式...

    高阶函数的定义 在<javascript设计模式和开发实践>中是这样定义的. 函数可以作为参数被传递: 函数可以作为返回值输出. 结合这两个特点,首先想到的肯定是回调函数,回调函数也是高阶 ...

  5. 【秒懂设计模式】单例设计模式

     秒懂设计模式--单例设计模式 (三)单例设计模式 1.先解释一下,什么是单例模式呢? 在Java中是这样定义的:"一个类有且仅有一个实例,并且自行实例化向整个系统提供." 显然从 ...

  6. 彻底理解单例设计模式

    单例设计模式 1. 单例模式概念 单例模式(Singleton Pattern)是Java中最简单的设计模式之一.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. 这种模式涉及到一个 ...

  7. 23种设计模式:单例设计模式(饿汉式 VS 懒汉式)

    23种设计模式:单例设计模式(饿汉式 VS 懒汉式) 每博一文案 世事浮沉,有太多的责任需要我们担当,生活中总有些挫折和磨难,让我们觉得快要杠不住了. 但当我们咬牙坚持过那段难熬的时光后,发现并没有想 ...

  8. 单例设计模式与final关键字

    目录 final关键字 什么是设计模式: 1.静态方法和属性的经典使用 2.设计模式是在大量的实践中总结和理论化之后优选的代码结构.编程风格.以及解决问题的思考方式.设计模式就像是经典的棋谱,不同的棋 ...

  9. JavaSE面向对象:继承、多态、Super、Object类、重写、static、final、静态成员、懒汉式、饿汉式、单例设计模式、初始化块、抽象类、抽象方法、接口

    感谢尚硅谷免费提供的视频 继承 ** 理解 ** 生活中的继承:子女继承了父母的财产或相貌或才华 java中的继承:一个类不用自己定义属性或方法,就已经具备了另一个类的属性和方法 把这种现象称为jav ...

最新文章

  1. linux 常用查看网络连接方法及脚本
  2. tombstone问题分析
  3. unity和python通讯_Python 与 Unity mlagents 交互 API
  4. 【ES】es 冻结的索引如何查询
  5. superoneclick 2.2_一季度食品监督抽检2.2%不合格:农兽药残留超标等系主因
  6. 如何检查python模块的版本?
  7. 框架源码深入需要准备的知识之解析XML
  8. python装饰器 廖雪峰_python装饰器的一个妙用
  9. 游戏开发之函数的增强(相比于C语言)(C++基础)
  10. android最新图表框架,Android中绘制图表的开源框架AChartEngine初识
  11. java实现批量更新数据_Java数据库编程中的几个常用技巧(批量更新)
  12. Java代理模式详解
  13. 计算机死机按什么恢复出厂设置,电脑怎么恢复出厂设置?win7恢复出厂设置教程...
  14. 【2022】软著申请流程及注意事项分享一下
  15. 黑马程序员————第三天
  16. mysql为什么不用b树_MySQL用B+树(而不是B树)做索引的原因
  17. table表格tr、td、合并、间距、边框合并、设置边距 代码
  18. Mac版本的夜神模拟器
  19. 练习4-7 求e的近似值 (15 分)
  20. 【CS224n】(lecture9)Transformer的变体

热门文章

  1. discuz 版块导航function_forumlist.php,Discuz! X2“扩建”左侧版块导航 让社区层次一目了然...
  2. 用python进行自然语言处理_Python自然语言处理示例:SVM和贝叶斯分类
  3. 【3】flink sink
  4. 本地方法(JNI)——从java 程序中调用C函数
  5. 一文理清Http2.0
  6. 如何用for循环出数据库的数据
  7. 会话技术Cookie
  8. java oca_OCA第2部分中的Java难题
  9. 策略模式示例代码_策略设计模式示例
  10. java lambda函数_Java SE 8新功能介绍:使用Lambda Expression进行函数式编程