Java Singleton设计模式

它是Java中最简单的设计模式之一。

如果有人问我哪种设计模式好,那么我会很自豪地说Singleton。

但是,当他们深入询问单身人士的概念时,我感到很困惑。

真的单身是那么困难吗?

确实不是,但是它有许多我们需要了解的场景(尤其是初学者)。

定义:

在所有情况下,该类只应允许一个实例,并且我们应提供对该实例的全局访问点。

定义就像1,2,3和A,B,C,D一样简单。

让我们看看如何实现Singleton类。

我们如何确保对象始终都是一个?

提示:将对象创建逻辑仅放在一个位置,并且不允许用户每次尝试执行该逻辑时都只能执行一次。

对象创建逻辑->它是什么
我们如何用Java创建对象?

是的,使用构造函数,我们不应该允许用户每次尝试访问构造函数并执行它。
但是我们应该这样做一次,至少要得到一个对象。

那么,如何确保构造函数只能访问和执行一次?

  1. 防止在类外部访问构造函数,以使任何外部人员都无法创建实例。
    如何使它->如何防止类外部的方法访问?
    简单,将make方法作为私有权限,类似地将构造函数作为私有权限。
  2. 防止构造函数在类中多次执行。
    如何制作->这有多种实现方式,下面以示例来看。

如果满足以上两个条件,则我们班级将始终有一个对象。 该类称为Singleton,因为它在我们请求的所有时间都产生单个对象。

没有太多理论,我们现在将开始执行它。

创建单例对象的方法有很多:

方法1

  • 急于初始化或使用前初始化
package com.kb.singleton;public class EagerSingletonClass {private static volatile EagerSingletonClass singletonInstance = new EagerSingletonClass();//making constructor as private to prevent access to outsidersprivate EagerSingletonClass() {}public static EagerSingletonClass getInstance(){return singletonInstance;}}

EagerSingletonClass的实例在类启动时创建。 由于它是静态的,因此会在加载EagerSingletonClass的过程中加载并创建它。

  • Junit测试类为上述类的单例测试。
package com.kb.singleton;import static org.junit.Assert.*;import org.junit.Test;public class EagerSingletonClassTest {@Testpublic void testSingleton() {EagerSingletonClass instance1 = EagerSingletonClass.getInstance();EagerSingletonClass instance2 = EagerSingletonClass.getInstance();System.out.println("checking singleton objects equality");assertEquals(true, instance1==instance2);}}

优势:
此策略在加载类期间创建对象,因此从多线程方案中可以更快更安全。 我们只需要使实例具有可变性即可处理多线程方案。

坏处 :

这种策略会在类加载本身时创建实例,因此,如果我们不使用它,那么这将浪费整个时间和内存来创建实例。 因此,最好在需要时选择一种策略来创建实例。

什么时候使用以上策略?
只要我们100%确保在我们的应用程序中肯定使用了该对象。
要么 当物体不重时,我们可以管理速度和内存。

方法2

  • 延迟初始化或在需要时初始化

与其在启动时创建对象,不如在需要时创建对象,这是很好的。 因此,让我们看看如何做到这一点:

package com.kb.singleton;public class LazySingleton {private static volatile LazySingleton singletonInstance = null;//making constructor as private to prevent access to outsidersprivate LazySingleton() {}public static LazySingleton getInstance(){if(singletonInstance==null){synchronized (LazySingleton.class) {singletonInstance = new LazySingleton();}}return singletonInstance;}}

在以上程序中,仅当通过getInstance()方法发出请求时,我们才创建了一个对象。

在此,在首次调用getInstance()的过程中,对象“ singletonInstance”将为null,并在条件变为true时执行if条件块并创建一个对象。

然后,对getInstance()方法的后续调用将返回相同的object。

但是,如果我们看一下多线程方案,问题就出在以下上下文中:2个线程t1和t2调用getInstance()方法,线程t1执行if(singletonInstance == null)并发现singletonInstance为null,因此它进入同步块以创建一个宾语。

但是在执行对象创建逻辑之前,如果线程t2执行if(singletonInstance == null),那么它还将发现singletonInstance为null,因此它还将尝试输入同步块,但是由于已经输入的第一个线程t1,它没有锁。

因此,线程t2等待线程t1完成同步块的执行。

因此线程t1执行并创建对象。 现在线程t2也正在等待同步块时进入同步块,并再次创建对象。

因此,两个线程创建了两个对象。 因此无法实现单例。

解决上述问题的方法是“ 双重检查锁定”。

它说在我们在同步块内执行对象创建的逻辑之前,请重新检查同步块内的实例变量。

这样,我们可以避免多个线程多次创建对象。

怎么样 ?

线程t1检查条件if(singletonInstance == null),第一次为true,因此它进入同步块,然后再次检查条件if(singletonInstance == null),这也为true,因此创建了对象。

现在线程t2进入方法getInstance()并假定它已在线程t1执行对象创建逻辑之前执行了if(singletonInstance == null)条件,然后t2也等待进入同步块。

在线程t1从同步块中出来之后,线程t2进入了同一个块,但是我们再次在其中有if条件if(singletonInstance == null)但线程t1已经创建了一个对象,它使条件变为false并进一步停止执行并返回相同的实例。

让我们看看如何在代码中完成它:

package com.kb.singleton;public class LazySingletonDoubleLockCheck {private static volatile LazySingletonDoubleLockCheck singletonInstance = null;//making constructor as private to prevent access to outsidersprivate LazySingletonDoubleLockCheck() {}public static LazySingletonDoubleLockCheck getInstance(){if(singletonInstance==null){synchronized (LazySingleton.class) {if(singletonInstance ==null){singletonInstance = new LazySingletonDoubleLockCheck();}}}return singletonInstance;}}

让我们做单元测试

package com.kb.singleton;import static org.junit.Assert.*;import org.junit.Test;public class LazySingletonDoubleLockCheckTest {@Testpublic void testSingleton() {LazySingletonDoubleLockCheck instance1 = LazySingletonDoubleLockCheck.getInstance();LazySingletonDoubleLockCheck instance2 = LazySingletonDoubleLockCheck.getInstance();System.out.println("checking singleton objects equality");assertEquals(true, instance1==instance2);//fail("Not yet implemented");}}

上面的实现是针对单例模式的最佳建议解决方案,它最适合于单线程,多线程等所有情况。

方法3

  • 使用内部类的单例

让我们看一下下面的使用内部类创建对象的代码:

package com.kb.singleton;public class SingletonUsingInnerClass {private SingletonUsingInnerClass() {}private static class LazySingleton{private static final SingletonUsingInnerClass  SINGLETONINSTANCE = new SingletonUsingInnerClass();}public static SingletonUsingInnerClass getInstance(){return LazySingleton.SINGLETONINSTANCE;}}

单元测试代码

package com.kb.singleton;import static org.junit.Assert.*;import org.junit.Test;public class SingletonUsingInnerClassTest {@Testpublic void testSingleton() {SingletonUsingInnerClass instance1 = SingletonUsingInnerClass.getInstance();SingletonUsingInnerClass instance2 = SingletonUsingInnerClass.getInstance();System.out.println("checking singleton objects equality");assertEquals(true, instance1==instance2);}}

以上使用内部类创建对象的方法是创建单例对象的最佳方法之一。

在这里,除非并且直到有人尝试访问LazySingleton静态内部类的静态引用变量,否则将不会创建该对象。

因此,这还将确保在需要时以及在需要时创建对象。 而且实现起来非常简单。 从多线程进行也是安全的。

方法4

  • 具有序列化和反序列化的Singleton

现在假设我们的应用程序是分布式的,我们序列化我们的单例对象并将其写入文件。 后来我们通过反序列化单例对象来阅读它。 取消序列化对象始终会创建一个文件内具有可用状态的新对象。 如果在写入文件后进行任何状态更改,然后尝试取消序列化对象,则将获得原始对象,而不是新的状态对象。 因此,在此过程中我们得到了2个对象。

让我们尝试通过程序来了解这个问题:

首先->使Singleton类可序列化以序列化和反序列化此类的对象。
第二件事->将对象写入文件(序列化)
第三件事->更改对象状态 第四件事-> de序列化对象

我们的单例课程如下:

package com.kb.singleton;import java.io.Serializable;public class SingletonSerializeAndDesrialize implements Serializable {private int x=100;private static volatile SingletonSerializeAndDesrialize singletonInstance = new SingletonSerializeAndDesrialize();private SingletonSerializeAndDesrialize() {}public static SingletonSerializeAndDesrialize getInstance() {return singletonInstance;}public int getX() {return x;}public void setX(int x) {this.x = x;}}

序列化我们的对象,然后对状态进行一些更改,然后取消序列化。

package com.kb.singleton;import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;public class SerializeAndDeserializeTest {static SingletonSerializeAndDesrialize instanceOne = SingletonSerializeAndDesrialize.getInstance();public static void main(String[] args) {try {// Serialize to a fileObjectOutput out = new ObjectOutputStream(new FileOutputStream("filename.ser"));out.writeObject(instanceOne);out.close();instanceOne.setX(200);// Serialize to a fileObjectInput in = new ObjectInputStream(new FileInputStream("filename.ser"));SingletonSerializeAndDesrialize instanceTwo = (SingletonSerializeAndDesrialize) in.readObject();in.close();System.out.println(instanceOne.getX());System.out.println(instanceTwo.getX());} catch (IOException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}}}

输出:

200

100

它清楚地表明,即使单身,我们也有2个不同的对象。 之所以发生这种情况,是因为反序列化会创建具有文件中可用状态的新实例。

如何克服这个问题? 意味着如何防止在反序列化期间创建新实例?

解决方案非常简单–在您的singleton类中实现以下方法:

Access_modifier  Object readResolve() throws ObjectStreamException{
}

例:

Public Object readResolve() throws ObjectStreamException{
return modifiedInstance;
}

将其应用于上面的单例课程,则完整的单例课程如下:

package com.kb.singleton;import java.io.ObjectStreamException;
import java.io.Serializable;public class SingletonSerializeAndDesrialize implements Serializable {private int x=100;private static volatile SingletonSerializeAndDesrialize singletonInstance = new SingletonSerializeAndDesrialize();private SingletonSerializeAndDesrialize() {System.out.println("inside constructor");}public static SingletonSerializeAndDesrialize getInstance() {return singletonInstance;}public int getX() {return x;}public void setX(int x) {this.x = x;}public Object readResolve() throws ObjectStreamException{return singletonInstance;}}

现在运行上面的序列化和反序列化类,以检查两个实例的输出。

输出:

200

200

这是因为,在反序列化期间,它将调用readResolve()方法,并且在此返回现有实例,这将阻止创建新实例并确保单例对象。

  • 小心序列号

在序列化之后和取消序列化之前,只要类结构发生更改。 然后,在反序列化过程中,它将找到一个不兼容的类,并因此引发异常:java.io.InvalidClassException:SingletonClass; 本地类不兼容:流classdesc serialVersionUID = 5026910492258526905,本地类serialVersionUID = 3597984220566440782

因此,为避免发生此异常,我们必须始终对可序列化的类使用序列号ID。 其语法如下:

private static final long serialVersionUID = 1L;

所以最后通过涵盖以上所有情况,单例类的最佳解决方案如下,我建议始终使用此解决方案:

package com.kb.singleton;import java.io.Serializable;public class FinalSingleton implements Serializable{private static final long serialVersionUID = 1L;private FinalSingleton() {}private static class LazyLoadFinalSingleton{private static final FinalSingleton  SINGLETONINSTANCE = new FinalSingleton();}public static FinalSingleton getInstance(){return LazyLoadFinalSingleton.SINGLETONINSTANCE;}private Object readResolve() {return getInstance();}}

翻译自: https://www.javacodegeeks.com/2014/05/java-singleton-design-pattern.html

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

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

相关文章

elasticsearch配置文件解析

1.Cluster(集群)# 集群名称标识了你的集群,自动探查会用到它。默认值为elasticsearch# 如果你在同一个网络中运行多个集群,那就要确保你的集群名称是独一无二的。## cluster.name: my-application 2.Node(节点&#xf…

刚刚出炉的Asp.net网站部署视频教程

刚刚出炉的Asp.net网站部署视频教程,希望对新手朋友有所帮助主要包括内容: 1、IIS的安装与配置 2、Asp.net环境的安装与常见问题解决 3、Asp.net网站的配置和使用 51aspx会陆续推出基础教程与大家见面,敬请期待! 中间不妥之处还希望大家多多包…

添加jQuery方法解析url查询部分

Web前端不同页面间传值可以使用 cookies、localStorage 和 sessionStorage 等本地存储。 但是,今天我们尝试使用 url 查询,假设我们要传递字符串 str 到 modify.html 页面: var str "nameBob Chen&gender男&date1998/04/26&am…

计算机网络 实验教案,《计算机网络》实验教案.pdf

《计算机网络》实验教案《计算机网络》实验教案临沂师范学院信息学院1实验一 网线制作与以太网组网一、实验目的和要求使学生掌握RJ -45 头的制作。学会以太网组网。二、实验课时:2 课时。三、实验环境与工具RJ-45 头若干、双绞线若干米、RJ&…

Java 8可选:如何使用它

Java 8带有新的Optional类型,类似于其他语言中提供的类型。 这篇文章将介绍这种新类型的使用方式,即主要用途。 什么是可选类型? 可选的是新容器类型,如果有可用值,则该容器类型将包装单个值。 因此,其含义…

strip用法

Python strip() 方法用于移除字符串头尾指定的字符(默认为空格或换行符)或字符序列。 注意:该方法只能删除开头或是结尾的字符,不能删除中间部分的字符。 str "00000003210Runoob01230000000"; print str.strip( 0 ); …

为JAVA性能而设计(一)

为JAVA性能而设计(一) 发布时间:2007-1-9 15:51:42 来源:JavaWorld 作者:Brian Go…为JAVA性能而设计(二) 发布时间:2007-1-9 15:52:57 来源:JavaWorld 作…

ubuntu16 升级pip3后报错File /usr/bin/pip3, line 9, in module from pip import main ImportError: cannot...

问题&#xff1a;ubuntu16 执行pip3 install --upgrade pip之后&#xff0c;pip3执行出错。 Traceback (most recent call last): File "/usr/bin/pip3", line 9, in <module> from pip import mainImportError: cannot import name main 截图如下&#xff1a;…

HTTP 简介

1、HTTP 简介 HTTP协议&#xff08;HyperText Transfer Protocol&#xff0c;超文本传输协议&#xff09;&#xff0c;是用于从WWW万维网服务器传输超文本到本地浏览器的传送协议。 HTTP基于TCP/IP通信协议来传递数据&#xff08;HTML 文件, 图片文件, 查询结果等&#xff09…

计算机博士英语复试题目,博士复试自我介绍中英文双语解读

博士复试自我介绍中英文双语解读关于博士复试自我介绍中英文篇一(中文篇)尊敬的老师,晚上好!我很高兴能来这里参加面试。现在让我给一个简短的自我介绍。我是* * *,出生在* *。我是一个老师的信息科学与工程学院,山东科技大学。我在1997年进入这所大学,主修计算机科学与技术。2…

DataFrame.to_dict(orient='dict')英文文档翻译

本文转载自 https://blog.csdn.net/llx1026/article/details/77929287 DataFrame.to_dict(orientdict)将DataFrame格式的数据转化成字典形式参数&#xff1a;当然参数orient可以是字符串{dict, list, series, split, records, index}中的任意一种来决定字典中值的类型字典dict&…

如何在J2ME中创建MIDlet

总览 Java移动应用程序称为J2ME。 通常&#xff0c;当我们在移动技术领域工作时&#xff0c;我们必须考虑J2ME应用程序。 通过这种方式&#xff0c;我们可以开发我们的移动应用程序&#xff0c;也可以通过jad或jar文件将其安装在我们的设备中。 近年来&#xff0c;手机开发中最…

感悟测试驱动开发

软件开发方法学的泰斗Kent Beck先生最为推崇"模式、极限编程和测试驱动开发"。在他所创造的极限编程&#xff08;XP&#xff09;方法论中&#xff0c;就向大家推荐"测试先行"这一最佳实践&#xff0c;并且还专门撰写了《测试驱动开发》一书&#xff0c;详细…

如何用python写html的插件,使用python开发vim插件及心得分享

如何使vim下开发python调试更方便如何用 Python 给 Vim 写插件如何使 Vim 下开发 Python 调试更方便怎么用python调用matlab&#xff1f;打算用vim写Python 各位指点下:w 之后 文件被保存到哪了? 桌面上有个文件夹 怎么才能保存进去? 如何让:w保存在当前目录下 :q退出vim :wq…

创建一个学生信息表,与页面分离

一、需求分析 做一个jsp页面&#xff0c;动态显示信息表的内容。 1、 做一个实体类&#xff1a;StudentInfo &#xff08;包含4个字段&#xff09; 2、 如图模拟生成3条数据&#xff0c;本质上就是new StudentInfo 3个实例&#xff0c;每个实例代表一行记录&#xff08;后面…

【Unity】材质基础

【Unity】材质基础 a.基本概念 b.Albedo Maps反射率贴图 c.Alpha Maps着色器shader下四大渲染模式 d.Metallic and Smoothness Maps e.Normal Maps法线贴图 f.Height Maps g.Occlusion Maps h.Emission Maps i.Detail Mask & Secondary Maps j.Standard 金属/Standard&…

DictVectorizer中的fit_transform

导入特征提取化中的字典向量化 from sklearn.feature_extraction import DictVectorizer dv DictVectorizer () x_train dv.fit_transform(x_train) x_test dv.fit_transform(x_test) 此处的fit是找到当前字典中的键 举个例子&#xff1a; 如果x_test不fit,那x_test只能转化…

Java中的三态布尔值

我不时地想念Java中SQL的三值BOOLEAN语义。 在SQL中&#xff0c;我们有&#xff1a; TRUE FALSE UNKNOWN &#xff08;也称为NULL &#xff09; 时不时地&#xff0c;我希望自己也能用Java表达这种UNKNOWN或UNINITIALISED语义&#xff0c;而普通的true和false还不够。 实现…

阻塞分析

--阻塞 /*********************************************************************************************************************** 阻塞&#xff1a;其中一个事务阻塞&#xff0c;其它事务等待对方释放它们的锁,同时会导致死锁问题。 整理人&#xff1a;中国风(R…

科学计算机二进制算法,计算机是怎么理解二进制的?

计算机是怎么理解二进制的?计算机的发明最初纯粹是为了计算数字, 让一个机器能够通过输入不同的数字, 进行加减乘除等. 首先要约定好机器能处理的数是什么样的, 即输入是什么样的, 才能去制造计算机. 二进制只是一种尝试, 十进制也有科学家尝试过, 但由于复杂程度较二进制要高…