Java中Singleton的三种实现方式解析
一、什么是Singleton?
《设计模式》的作者、Eclipse和 Junit 的开发者 Erich Gamma 在它的理论体系中将 Singleton 定义为仅仅被实例化一次的类。在当今面向对象程序的实际开发中,Singleton 通常被用来代表一个无状态的对象,例如函数和那些本质上唯一的系统组件。
值得注意的是,使类成为 Singleton 会使得它的客户端测试变得非常困难,因为我们不可能给Singleton替换模拟实现,除非我们实现一个充当其类型的接口。
实现 Singleton 有三种常见方法,他们或是保持构造器私有并导出公有的静态成员,或是声明一个包含单个元素的枚举类型。
二、Singleton实现 —— 构造器私有
1、公有静态成员为一个final域
//Singleton with public final field
public class Elvis {
public static final Elvis INSTANCE = new Elvis();
pritvate Elvis() { ... }
public void leaveTheBuilding() { ... }
}
在这个类中,我们仅仅拥有一个私有的构造器,它也只在初始化final域时被调用一次。由于缺少可以使用的构造器,后续的程序无法再创建 Elvis 对象。这保证了在该Java程序的整个生命周期中, Elvis 对象有且只有一个存在。
但需要注意的是,一些高权限的客户端可以借助 AccessibleObject.setAccessible 方法通过反射机制调用私有的构造器。为了避免这样的可能的攻击,可以修改构造器,让它在被要求创建第二个实例的时候抛出异常。
公有域方法的主要优势在于,API很清楚地表明了这个类是一个 Singleton ,毕竟这是一个公有的静态属性。另外,这个方法要更加简单。
2、公有静态成员为一个静态工厂方法
//Singleton with static factory
public class Elvis {
private static final Elvis INSTANCE = new Elvis();
pritvate Elvis() { ... }
public static Elvis getInstance(){ return INSTANCE; }
public void leaveTheBuilding(){ ... }
}
显然,无论怎样调用 getInstance 方法,返回的都是同一个对象的引用。注意上面提示的反射攻击问题依然存在。
静态工厂方法有两大优势
- 第一,它提供了更多的灵活性,在不改变API的前提下,我们可以轻易地自由调整这个类是否是Singleton。工厂方法返回该类的唯一实例,但它很容易修改成别的样子,例如为每个调用该方法的线程提供唯一实例。
- 第二,如果程序需要,我们可以编写一个泛型 Singleton 工厂。
- 第三,我们可以通过方法引用作为提供者,比如 Elvis::instance 就是一个 Supplier< Elvis >
(注:方法引用是Java8的一个新特性)
除非我们需要上述的其中一种优势,我们还是应该选择更简单易懂的使用公有域的方法。
3、将利用上述方法实现的Singleton类变为可序列化的
使用上述两种方法实现的 Singleton ,要把他们变成可序列化的,不能仅仅在声明中加上 implements Serializable 。为了维护并保证 Singleton ,我们必须生命所有实例域都是瞬时的,并提供一个 readResolve 方法。否则在我们每次序列化时都会创建一个新的实例。为了防止这种情况,我们要在 Elvis 类中加入如下这样的 readResolve 方法。
//readResolve method to preserve singleton property
private Object readResolve(){
//Return the one true Elvis and let the garbage collector take care of the Elvis impersonator
return INSTANCE;
}
三、Singleton实现 —— 声明包含单个元素的枚举类型
//Enum singleton - the preferred approach
public enum Elvis{
INSTANCE;
public void leaveTheBuilding(){ ... }
}
这种方法在功能上与公有域方法相似,但更加简洁,无偿地提供了序列化机制,绝对防止多次实例化,即使是在面对复杂的序列化或者反射攻击的时候。 虽然这种方法还没有广 泛采用,但是单元素的枚举类型经常成为实现 Singleton 的最佳方法。 注意,如果 Singleton 必须扩展一个超类,而不是扩展 Enum 的时候,则不宜使用这个方法(虽然可以声明枚举去实现接口)。
Java中Singleton的三种实现方式解析的更多相关文章
- java中线程的三种实现方式
一下记录下线程的3中实现方式:Thread,Runnable,Callable 不需要返回值时,建议使用Runnable:有返回值时建议使用Callable 代码如下所示: package com.f ...
- java中 this 的三种用法
Java中this的三种用法 调用属性 (1)this可以调用本类中的任何成员变量 调用方法(可省略) (2)this调用本类中的成员方法(在main方法里面没有办法通过this调用) 调用构造方法 ...
- JAVA中单例模式的几种实现方式
1 线程不安全的实现方法 首先介绍java中最基本的单例模式实现方式,我们可以在一些初级的java书中看到.这种实现方法不是线程安全的,所以在项目实践中如果涉及到线程安全就不会使用这种方式.但是如果不 ...
- 基于Java的二叉树的三种遍历方式的递归与非递归实现
二叉树的遍历方式包括前序遍历.中序遍历和后序遍历,其实现方式包括递归实现和非递归实现. 前序遍历:根节点 | 左子树 | 右子树 中序遍历:左子树 | 根节点 | 右子树 后序遍历:左子树 | 右子树 ...
- c++中new的三种用法详细解析
转载至: http://www.jb51.net/article/41524.htm 以下的是对c++中new的三种使用方法进行了详细的分析介绍,需要的朋友可以过来参考下,希望对大家有所帮助. 一. ...
- Java中String对象两种赋值方式的区别
本文修改于:https://www.zhihu.com/question/29884421/answer/113785601 前言:在java中,String有两种赋值方式,第一种是通过“字面量”赋值 ...
- 细说java中Map的两种迭代方式
曾经对java中迭代方式总是迷迷糊糊的,今天总算弄懂了.特意的总结了一下.基本是算是理解透彻了. 1.再说Map之前先说下Iterator: Iterator主要用于遍历(即迭代訪问)Collecti ...
- Java创建线程的三种主要方式
Java创建线程的主要方式 一.继承Thread类创建 通过继承Thread并且重写其run(),run方法中即线程执行任务.创建后的子类通过调用 start() 方法即可执行线程方法. 通过继承Th ...
- Java中对象的三种状态
Java中的对象的三种状态是和垃圾回收紧密相关的,因此有必要深究. 状态一:可触及态:从根节点开始,可以搜索到这个对象,也就是可以访问到这个对象,也有人将其称为可达状态. 状态二:可复活态:从根节点开 ...
随机推荐
- (五)SpringBoot面试题
SpringBoot面试题 1.Spring Boot 的自动配置是如何实现的? 2.shiro和oauth还有cas他们之间的关系是什么?问下您公司权限是如何设计,还有就是这几个概念的区别. 2.1 ...
- Spring Boot配置,读取配置文件
Spring Boot配置,读取配置文件 一.配置Spring Boot 1.1 服务器配置 1.2 使用其他Web服务器 1.3 配置启动信息 1.4 配置浏览器显示ico 1.5 Yaml语法 1 ...
- 小米和MAC触摸板手势汇总
小米的触摸手势: 左键:单指单击 右键:双指单击 选取并打开:单指双击 滚动页面:双指 移动 拖拽项目:双击并拖拽 放大/缩小:双指张开,双指捏合 MAC触摸板手势: http://www.cr173 ...
- 给jekyll博客添加天气插件
layout: post title: 给博客添加天气插件 date: 2020-04-26 author: Dapenson header-img: img/post-bg-debug.png ca ...
- Flink-v1.12官方网站翻译-P027-State Schema Evolution
状态方案的演变 Apache Flink流媒体应用通常被设计为无限期或长时间运行.与所有长期运行的服务一样,应用程序需要更新以适应不断变化的需求.这对于应用程序所针对的数据模式也是一样的,它们会随着应 ...
- 设计模式(十四)——模板模式(SpringIOC源码分析)
1 豆浆制作问题 编写制作豆浆的程序,说明如下: 1) 制作豆浆的流程 选材--->添加配料--->浸泡--->放到豆浆机打碎 2) 通过添加不同的配料,可以制作出不同口味的豆浆 3 ...
- 让visual studio总是以管理员身份启动
环境 win10 visual studio 2019 步骤 找到visual studio的安装目录 怎么找? 右击win10开始菜单中visual studio的快捷方式, 依次选择 更多-> ...
- 纯js添加类
1.el.setAttribute('class','abc'); <!DOCTYPE HTML><HTML><HEAD><meta charset=&quo ...
- Codeforces Round #651 (Div. 2) B. GCD Compression(数论)
题目链接:https://codeforces.com/contest/1370/problem/B 题意 给出 $2n$ 个数,选出 $2n - 2$ 个数,使得它们的 $gcd > 1$ . ...
- 【noi 2.6_6252】带通配符的字符串匹配(DP)
题意:给出一个带有通配符("?"可以代替一个字符,"*"可以代替零个或多个字符)的a字符串和一个不带通配符的b字符串,判断他们是否能够匹配. 解法:f[i][j ...