单例设计模式示例

本文是我们名为“ Java设计模式 ”的学院课程的一部分。

在本课程中,您将深入研究大量的设计模式,并了解如何在Java中实现和利用它们。 您将了解模式如此重要的原因,并了解何时以及如何应用模式中的每一个。 在这里查看 !

目录

1.单例模式 2.如何使用单例模式创建类 3.何时使用Singleton 4.下载源代码

1.单例模式

有时对于某些类来说,只有一个实例很重要。 有许多对象,我们只需要它们的一个实例,如果实例化多个对象,我们将遇到各种问题,例如程序行为不正确,资源过度使用或结果不一致。

您可能只需要一个类的对象,例如,当您创建应用程序的上下文时,或者线程可管理的池,注册表设置,连接到输入或输出控制台的驱动程序等。该类型显然会导致程序不一致。

Singleton模式可确保一个类只有一个实例,并提供对其的全局访问点。 但是,尽管就类图而言,单例是最简单的,因为只有一个类,但其实现却有些棘手。

图1

图1

在本课程中,我们将尝试不同的方法来仅创建类的单个对象,还将了解一种方法比另一种方法更好。

2.如何使用单例模式创建类

创建这种类型的类的方法有很多种,但仍然可以看到一种方法比另一种方法更好。

让我们从一个简单的方法开始。

如果提供一个使对象可访问的全局变量该怎么办? 例如:

package com.javacodegeeks.patterns.singletonpattern;public class SingletonEager {public static SingletonEager sc = new SingletonEager();}

众所周知,一个类的静态变量只有一个副本,我们可以应用它。 就目前而言,客户端代码使用此sc静态变量就可以了。 但是,如果客户端使用新的运算符,则将有此类的新实例。

要停止在类之外实例化该类,让我们将该类的构造函数设为私有。

package com.javacodegeeks.patterns.singletonpattern;public class SingletonEager {public static SingletonEager sc = new SingletonEager();private SingletonEager(){}}

这行得通吗? 我想是的。 通过使构造函数保持私有状态,其他任何类都不能实例化该类。 获取此类的唯一方法是使用sc静态变量,该变量确保只有一个对象存在。

但是,众所周知,直接访问类成员不是一个好主意。 我们将提供一种方法,通过该方法,sc变量将获得访问权,而不是直接访问。

package com.javacodegeeks.patterns.singletonpattern;public class SingletonEager {private static SingletonEager sc = new SingletonEager();private SingletonEager(){}public static SingletonEager getInstance(){return sc;}
}

因此,这是我们的单例类,可确保仅创建该类的一个对象,即使存在多个请求,也将仅返回相同的实例化对象。

这种方法的一个问题是,一旦将类加载到JVM中,就会立即创建对象。 如果从未请求过该对象,则内存中将有一个无用的对象。

在需要时创建对象始终是一种好方法。 因此,我们将在第一个调用上创建一个对象,然后在其他后续调用上返回相同的对象。

package com.javacodegeeks.patterns.singletonpattern;public class SingletonLazy {private static SingletonLazy sc = null;private SingletonLazy(){}public static SingletonLazy getInstance(){if(sc==null){sc = new SingletonLazy();}return sc;}
}

getInstance()方法中,我们检查静态变量sc是否为null,然后实例化该对象并将其返回。 因此,在第一次调用时,如果sc为null,则将创建对象,而在接下来的后续调用中,它将返回相同的对象。

这看起来确实不错,不是吗? 但是,此代码将在多线程环境中失败。 想象两个线程同时访问该类,线程t1首次调用getInstance()方法,它检查静态变量sc是否为null,然后由于某种原因而被中断。 另一个线程t2调用getInstance()方法成功通过了if检查并实例化了该对象。 然后,线程t1醒来,它还创建了对象。 此时,将有两个此类。

为了避免这种情况,我们将使用synchronized关键字到getInstance()方法。 通过这种方式,我们强制每个线程等待其轮流才能进入该方法。 因此,没有两个线程会同时进入该方法。 同步带有价格,它将降低性能,但是如果对getInstance()方法的调用不会给您的应用程序造成实质性开销,则请忽略它。 另一个解决方法是转向渴望的实例化方法,如前面的示例所示。

package com.javacodegeeks.patterns.singletonpattern;public class SingletonLazyMultithreaded {private static SingletonLazyMultithreaded sc = null;private SingletonLazyMultithreaded(){}public static synchronized SingletonLazyMultithreaded getInstance(){if(sc==null){sc = new SingletonLazyMultithreaded();}return sc;}
}

但是,如果要使用同步,则有另一种称为“双重检查锁定”的技术可以减少同步的使用。 使用双重检查锁定,我们首先检查是否创建了实例,如果没有创建,则进行同步。 这样,我们只同步第一次。

package com.javacodegeeks.patterns.singletonpattern;public class SingletonLazyDoubleCheck {private volatile static SingletonLazyDoubleCheck sc = null;private SingletonLazyDoubleCheck(){}public static SingletonLazyDoubleCheck getInstance(){if(sc==null){synchronized(SingletonLazyDoubleCheck.class){if(sc==null){sc = new SingletonLazyDoubleCheck();}	}}return sc;}
}

除此之外,还有其他打破单例模式的方法。

  1. 如果该类是Serializable。
  2. 如果是可克隆的。
  3. 可以通过反思来打破。
  4. 同样,如果该类由多个类加载器加载。

下面的示例说明如何保护您的类免于被多次实例化。

package com.javacodegeeks.patterns.singletonpattern;import java.io.ObjectStreamException;
import java.io.Serializable;public class Singleton implements Serializable{private static final long serialVersionUID = -1093810940935189395L;private static Singleton sc = new Singleton();private Singleton(){if(sc!=null){throw new IllegalStateException("Already created.");}}public static Singleton getInstance(){return sc;}private Object readResolve() throws ObjectStreamException{return sc;}private Object writeReplace() throws ObjectStreamException{return sc;}public Object clone() throws CloneNotSupportedException{throw new CloneNotSupportedException("Singleton, cannot be clonned");}private static Class getClass(String classname) throws ClassNotFoundException {ClassLoader classLoader = Thread.currentThread().getContextClassLoader();if(classLoader == null) classLoader = Singleton.class.getClassLoader();return (classLoader.loadClass(classname));}}
  1. 在您的单例类中实现readResolve()writeReplace()方法,并通过它们返回相同的对象。
  2. 您还应该实现clone()方法并引发异常,以使单例无法被克隆。
  3. 构造函数中的“ if条件”可以防止使用反射多次实例化单例。
  4. 为了防止从不同的类加载器实例化单例,可以实现getClass()方法。 上面的getClass()方法将类加载器与当前线程关联; 如果该类加载器为null,则该方法使用与加载单例类相同的类加载器。

尽管我们可以使用所有这些技术,但是有一种简单的方法可以创建单例类。 从JDK 1.5开始,您可以使用枚举创建单例类。 枚举常量是隐式静态的和最终的,创建后就无法更改其值。

package com.javacodegeeks.patterns.singletonpattern;public class SingletoneEnum {public enum SingleEnum{SINGLETON_ENUM;}
}

当您尝试显式实例化Enum对象时,将出现编译时错误。 当Enum静态加载时,它是线程安全的。 Enum中的clone方法是最终的,可确保枚举常量永远不会被克隆。 枚举本质上是可序列化的,序列化机制可确保不会因反序列化而创建重复的实例。 也禁止使用反射实例化。 这些确保了枚举实例不存在超出枚举常量定义的实例。

3.何时使用Singleton

  1. 一个类必须完全有一个实例,并且必须可以从众所周知的访问点访问它。
  2. 当唯一的实例可以通过子类扩展,并且客户端应该能够使用扩展的实例而无需修改其代码。

4.下载源代码

这是有关Singleton Pattern的课程。 您可以在此处下载源代码: SingletonPattern-Project

翻译自: https://www.javacodegeeks.com/2015/09/singleton-design-pattern.html

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

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

相关文章

ansi编码转换_8b/1b编码是个什么东东

使用串行比并行总线可以节省更多的布线空间,芯片、电缆等的尺寸可以做得更小,同时传输速率更高。但是,在很多数字系统如CPU、DSP、FPGA等内部,进行数据处理的最小单位都是Byte,即8个bit,如何把一个或多个By…

使用CSDN云服务搭建一个WordPress 个人站点(详细教程)

目录 前言 站点效果图 搭建服务的一些准备工作 第一步。进入到云服务我的账号模块,创建一个项目, 第二步,先免费领取一台云主机, 第三步,进入云主机控制台 搭建站点操作步骤 第一步,登录云主机 第二…

使用CSDN云服务搭建一个WordPress 个人站点(视频教程)

目录 前言 视频教程 文字版教程 云主机体验地址 官方文档 前言 六一儿童节体验了一下CSDN云主机相关的服务,感觉最近官方最近变化很大,业务范围很广,这里简单体验了一下云主机,给大家分享一点心得,希望对大家有所启…

石板切割问题c语言_岩知识 | 岩板切板有讲究,工艺问题要注意

岩板的火继续烧着,岩板的深加工厂在不断地增加着,岩板的深加工产品正在走向各个建筑项目,逐渐在受到很多项目的宠爱。岩板作为与天然石材完全不同的产品,其优越的理化性能在许多方面胜过天然石材,但也有其严重的不足。…

JavaScript测验题回顾-刷题笔记001

目录 1.我们可以在下列哪个 HTML 元素中放置 Javascript 代码?​编辑 2.写 "Hello World" 的正确 Javascript 语法是? 3.插入 Javacript 的正确位置是? 4.引用名为 "xxx.js" 的外部脚本的正确语法是? 5.如…

java 方法委托托管_Java Web托管选项流程图

java 方法委托托管我经常被问到的一个问题是在何处以及如何托管Java Web应用程序。 在带有嵌入式服务器的Eclipse中创建它很好,但是如何将它带给人们呢? 长期以来,对于发烧友的程序员一直没有答案。 只有昂贵和超大型的选择。 事情最近发生了…

react重新渲染菜单_React实现递归组件

前言今天来实现一个 React 的递归组件。具体的效果图如下:图片说明假设后端返回的数据如下:[{ id: 1, parent_id: 0, name: 广东省, children: [{ id: 2, parent_id: 1, name: 广州市, …

JavaScript测验——给代码添加注释---第一关

校验规则 创建一个//样式的注释, 被注释的文本至少要包含 5 个字符。 创建一个/* */样式的注释, 被注释的文本至少要包含 5 个字符。

举重设计模式示例

本文是我们名为“ Java设计模式 ”的学院课程的一部分。 在本课程中,您将深入研究大量的设计模式,并了解如何在Java中实现和利用它们。 您将了解模式如此重要的原因,并了解何时以及如何应用模式中的每一个。 在这里查看 ! 目录 …

JavaScript测验——声明变量---第2关

校验规则 注意: 变量名称可以由数字、字母、美元符号$ 或者 下划线_组成,但是不能包含空格或者以数字为开头。 闯关: 使用var 关键字来创建一个名为salePrice的变量。

palapaweb怎样开启服务_为什么说微服务,要从前后端分离开始?一文带你揭秘深入微服务...

前言既要低头赶路,又要抬头望天,科技是为人服务的,任何技术背后都有更深层次的考量。之前的文章中咱们聊了很多微服务的相关内容,简而言之,微服务的本质,就是一种可以加速分工、促进合作的新协作机制。知其…

list集合

List集合 List集合的概述 有序集合(也称之为序列),用户可以精确的控制列表中的每个元素的插入位置。用户可以通过整数索引访问元素,并搜索列表中的元素 与 Set 集合不同,列表通常允许重复的元素 List 集合的特点 有…

JavaScript测验——使用赋值运算符---第3关

校验规则 以上代码数值8被赋给变量myVar中,然后再次将变量myVar解析为8并将其赋给myNum变量。 闯关: 把数值6赋给变量 x。 然后把变量x中的内容赋给变量y。

antd table排序 vue_商品品牌业务之Vue编写前端页面

今天是刘小爱自学Java的第145天。感谢你的观看,谢谢你。学习计划安排如下:打算从前端页面到后台服务器代码完整地写一遍,但显然我高估了自己的实力,几个小时的时间根本不够用。并且因为教程和vue现在最新的组件用法不一样&#xf…

JavaScript测验——使用赋值运算符初始化变量---第4关

通常在声明变量的时候会给变量初始化一个初始值。 例如: var myVar 0; 以上代码创建一个名为myVar的变量并指定一个初始值0。

Zabbix-3.0.0 安装Graphtree

导读Zabbix中,想要集中展示图像,唯一的选择是screen,后来zatree解决了screen的问题,但性能不够好。Graphtree 由OneOaaS开发并开源出来,用来解决Zabbix的图形展示问题,性能较好。一、Graphtree功能概述集中…

装饰器设计模式示例

本文是我们名为“ Java设计模式 ”的学院课程的一部分。 在本课程中,您将深入研究大量的设计模式,并了解如何在Java中实现和利用它们。 您将了解模式如此重要的原因,并了解何时以及如何应用模式中的每一个。 在这里查看 ! 目录 …

JavaScript测验——未初始化的变量---第5关

校验规则 当你用一个值是undefined的变量来做字符串拼接操作的时候,它会输出字符串"undefined"。 闯关: 定义 3 个变量a、b、c,并且分别给他们赋值:3、9、"I am pretty",这样它们值就不会是undefined了。

修改telnet提示并非_俊翔:修改ECU数据解除奔驰GL350尿素限制

为达到排放要求,很多乘用柴油车都要加尿素(Adblue),以降低废气排放。在轿车维修当中,最常见的有奔驰GL350、路虎揽胜出现尿素锁定。仪表上提示加尿素,并且限制启动次数。每当把发动机熄火再启动一次,次数就会减少&…

JavaScript测验——变量名大小写---第6关

校验规则 例如: var userName; var userLoginFlag;闯关: 修改已声明的变量,让它们的命名符合驼峰命名法的规范。 这个需要注意的是在声明和赋值时都应该使用驼峰命名法。