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

反射和内省

定义:

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范例

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

反射和内省

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

福利预告,跳一跳助手即将发布,您不知道还有这些...

福利预告,跳一跳助手即将来临 一周后,即2月9号正式发布 跳一跳助手,免费免费免费提供! 透露一下 跳一跳助手的界面是这样滴: 这工具有啥用? 举个栗子: 花城君用该工具,不小心就玩到…

Redis Client Lettuce 5 GA发布

经过13个月的开发阶段和208张已解决的故障单&#xff0c;我很高兴宣布Lettuce 5.0全面上市。 这是一个主要发行版&#xff0c;带有一些重大更改&#xff0c;新的有趣功能以及Java 9兼容性。 从Maven Central获取发布 <dependency><groupId>io.lettuce</groupI…

NOIP模拟测试14「旋转子段·走格子·柱状图」

旋转子段 连60分都没想&#xff0c;考试一直肝t3&#xff0c;t2&#xff0c;没想到t1最简单 我一直以为t1很难&#xff0c;看了题解发现也就那样 题解 性质1 一个包含a[i]旋转区间值域范围最多为min(a[i],i)----max(a[i],i) 感性理解 举个例子,例如3 7 1 4 5 6 2 这个子段包含a…

检测域名是否到期

如果你想做一个网站&#xff0c;首先需要购买一个域名&#xff0c;与其说是购买不如说是租用&#xff0c;因为你要想使用这个域名&#xff0c;每年都需要缴纳一笔费用。既然是租用&#xff0c;所以就有期限&#xff0c;在到期前没有续费&#xff0c;则该域名会被收回&#xff0…

jax-ws cxf_走向REST:在Spring和JAX-RS(Apache CXF)中嵌入Jetty

jax-ws cxf对于服务器核心Java开发人员来说&#xff0c;向世界“展示”的唯一方法是使用API​​。 今天的帖子都是关于JAX-RS的 &#xff1a;使用Java编写和公开RESTful服务。 但是&#xff0c;我们不会使用涉及应用程序服务器&#xff0c;WAR打包以及诸如此类的传统的重量级方…

问题 1046: [编程入门]自定义函数之数字后移

题目描述有n个整数&#xff0c;使前面各数顺序向后移m个位置&#xff0c;最后m个数变成前面m个数。写一函数&#xff1a;实现以上功能&#xff0c;在主函数中输入n个数和输出调整后的n个数。输入输入数据的个数n n个整数 移动的位置m输出移动后的n个数样例输入10 1 2 3 4 5 6 7…

Reliable Multicast Programming(PGM)协议

Reliable Multicast Programming (PGM)实际通用可靠多播协议&#xff0c;在某种程度上保证多播的可靠性。是IP上层协议&#xff0c;和TCP还有UDP同级&#xff0c;工作在传输层。 在组播传输视频项目中&#xff0c;发现在网络较差的时候&#xff0c;组播传输视频性能下降迅猛&am…

P1488 肥猫的游戏

P1488 肥猫的游戏https://www.luogu.org/problem/P1488 博弈论一个三角形&#xff0c;只有三种情况&#xff1a;1邻接&#xff0c;2邻接&#xff0c;3邻接主要是2邻接的情况比较麻烦边<0,n-1>需要特判一下 #include <iostream> #include <cstdio> #include &…

使用OAuth2令牌的安全REST服务

1.简介 在本教程中&#xff0c;我们将介绍如何将Spring Security与OAuth结合使用以保护REST服务。 在演示应用程序中&#xff0c;可以使用路径模式&#xff08; / api / ** &#xff09;访问服务器上受保护的REST资源&#xff0c;以便基于该路径的请求URL映射到不同的控制器方法…

java 内联调用深度_Java中内联虚拟方法调用的性能

java 内联调用深度总览 动态编译的好处之一是它能够支持在虚拟方法代码上的广泛方法内联。 内联代码可提高性能时&#xff0c;代码仍必须检查类型&#xff08;以防由于优化而更改了类型&#xff09;或在多个可能的实现之间进行选择。 这导致了问题。 通过接口调用的方法的多个…

组件切换方式(Vue.js)

这里&#xff0c;我用一个注册登录两组件的切换实例来演示&#xff1a; 切换方式一 <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8" /><title>独秀不爱秀</title> </head><body><div …

在GWT中序列化/反序列化Json

JSON和GWT 最近&#xff0c; GWT用户小组中进行了有趣的讨论 &#xff0c;涉及在客户端对JSON进行序列化/反序列化的最佳实践。 这篇文章旨在突出其重点。 到目前为止&#xff0c;在GWT中有三种将对象转换为JSON并从客户端转换回JSON的方法&#xff1a; gwt-jackson框架&#…

NOIP模拟测试15「建造城市city(插板法)·轰炸·石头剪刀布」

建造城市 题解 先思考一个简单问题 10个$toot$ 放进5间房屋&#xff0c;每个房屋至少有1个$toot$&#xff0c;方案数 思考&#xff1a;插板法&#xff0c;$10$个$toot$有$9$个缝隙&#xff0c;$5$间房屋转化为$4$个挡板&#xff0c;放在toot缝隙之间得到$C_{9}^{4}$ 10个$toot$…

弹簧和线程:TaskExecutor

在Web应用程序中使用线程并不常见&#xff0c;尤其是当您必须开发长期运行的任务时。 考虑到spring&#xff0c;我们必须格外注意并使用它已经提供的工具&#xff0c;而不是生成我们自己的线程。 我们希望线程由spring管理&#xff0c;因此能够在不产生任何影响的情况下使用应…

数位dp从会打模板到不会打模板

打了几个数位$dp$&#xff0c;发现自己除了会打模板之外没有任何长进&#xff0c;遇到非模板题依然什么都不会 那么接下来这篇文章将介绍如何打模板&#xff08;滑稽&#xff09; 假设我们要处理$l----r$ 采用记忆化搜索的方式&#xff0c;枚举$<r$每一种情况&#xff0c;枚…

javafx简单吗_JavaFX即将推出您附近的Android或iOS设备吗?

javafx简单吗已经有大新闻最近在世界上的JavaFX的关于JavaFX的是许多更多的组件开源&#xff0c;开源的广告在2012 JavaOne大会 。 在2月的开放源代码更新中 &#xff0c; Richard Bair编写了一份JavaFX项目表&#xff0c;该表在撰写本文时&#xff08;2013年2月11日&#xff0…

【webrtc】webrtc的rtp重传代码分析

pgm不太能用&#xff0c;没有想象中的可靠&#xff0c;重传机制貌似仍然使用组播重传&#xff0c;丢包率80%的网络感觉没啥改进&#xff0c;如果有所好转延迟估计也是个不小的问题。 后听说rtp也有nack机制&#xff0c;webrtc基于rtp实现了重传在一定程度上保证可靠性。 在各路…

了解java.nio.file.Path – 1

介绍 Java的最后几个发行版本&#xff0c;即Java 7&#xff0c;Java 8和即将到来的Java 9&#xff0c;具有许多功能&#xff0c;这些功能使Java开发人员的生活更加轻松。 &#xff08;我知道Java 9会使它变得更困难&#xff0c;但是只有在您采用新的范例时才可以。之后&#xf…

socket-01

对于所有的Web应用&#xff0c;本质上其实就是一个socket服务端&#xff0c;用户的浏览器其实就是一个socket客户端 转载于:https://www.cnblogs.com/yanhuaqiang/p/11329925.html

ActionScript3学习笔记2-包

在 ActionScript 3.0 中&#xff0c;包是用命名空间实现的&#xff0c;但包和命名空间并不同义。在声明包时&#xff0c; 可以隐式创建一个特殊类型的命名空间并保证它在编译时是已知的。显式创建的命名空间在 编译时不必是已知的。 下面的示例使用 package 指令来创建一个包含…