单例模式简介

单例模式是 Java 中最简单,也是最基础,最常用的设计模式之一。在运行期间,保证某个类只创建一个实例,保证一个类仅有一个实例,并提供一个访问它的全局访问点。下面就来讲讲Java中的N种实现单例模式的写法。

饿汉式

public class Singleton {

    private static Singleton instance = new Singleton();

    private Singleton() {
} public static Singleton getInstance() {
return instance;
} }

这是实现一个安全的单例模式的最简单粗暴的写法,这种实现方式我们称之为饿汉式。之所以称之为饿汉式,是因为肚子很饿了,想马上吃到东西,不想等待生产时间。这种写法,在类被加载的时候就把Singleton实例给创建出来了。

饿汉式的缺点就是,可能在还不需要此实例的时候就已经把实例创建出来了,没起到lazy loading的效果。优点就是实现简单,而且安全可靠。

懒汉式

public class Singleton {

    private static Singleton instance;

    private Singleton() {
} public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
} }

相比饿汉式,懒汉式显得没那么“饿”,在真正需要的时候再去创建实例。在getInstance方法中,先判断实例是否为空再决定是否去创建实例,看起来似乎很完美,但是存在线程安全问题。在并发获取实例的时候,可能会存在构建了多个实例的情况。所以,需要对此代码进行下改进。

public class SingletonSafe {

    private static volatile SingletonSafe singleton;

    private SingletonSafe() {
} public static SingletonSafe getSingleton() {
if (singleton == null) {
synchronized (SingletonSafe.class) {
if (singleton == null) {
singleton = new SingletonSafe();
}
}
}
return singleton;
}
}

这里采用了双重校验的方式,对懒汉式单例模式做了线程安全处理。通过加锁,可以保证同时只有一个线程走到第二个判空代码中去,这样保证了只创建 一个实例。这里还用到了volatile关键字来修饰singleton,其最关键的作用是防止指令重排。

静态内部类

public class Singleton {

    private static class SingletonHolder {
private static Singleton instance = new Singleton();
} private Singleton() { } public static Singleton getInstance() {
return SingletonHolder.instance;
}
}

通过静态内部类的方式实现单例模式是线程安全的,同时静态内部类不会在Singleton类加载时就加载,而是在调用getInstance()方法时才进行加载,达到了懒加载的效果。

似乎静态内部类看起来已经是最完美的方法了,其实不是,可能还存在反射攻击或者反序列化攻击。且看如下代码:

public static void main(String[] args) throws Exception {
Singleton singleton = Singleton.getInstance();
Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton newSingleton = constructor.newInstance();
System.out.println(singleton == newSingleton);
}

运行结果:

通过结果看,这两个实例不是同一个,这就违背了单例模式的原则了。

除了反射攻击之外,还可能存在反序列化攻击的情况。如下:

引入依赖:

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.8.1</version>
</dependency>

这个依赖提供了序列化和反序列化工具类。

Singleton类实现java.io.Serializable接口。

如下:

public class Singleton implements Serializable {

    private static class SingletonHolder {
private static Singleton instance = new Singleton();
} private Singleton() { } public static Singleton getInstance() {
return SingletonHolder.instance;
} public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
byte[] serialize = SerializationUtils.serialize(instance);
Singleton newInstance = SerializationUtils.deserialize(serialize);
System.out.println(instance == newInstance);
} }

运行结果:

通过枚举实现单例模式

在effective java(这本书真的很棒)中说道,最佳的单例实现模式就是枚举模式。利用枚举的特性,让JVM来帮我们保证线程安全和单一实例的问题。除此之外,写法还特别简单。

public enum Singleton {

    INSTANCE;

    public void doSomething() {
System.out.println("doSomething");
} }

调用方法:

public class Main {

    public static void main(String[] args) {
Singleton.INSTANCE.doSomething();
} }

直接通过Singleton.INSTANCE.doSomething()的方式调用即可。方便、简洁又安全。

总结

以上列举了多种单例模式的写法,分析了其利弊之处。同时还介绍了目前最佳的单例写法——枚举模式,相信在未来,枚举模式的单例写法也会越来越流行。

Java单例模式:为什么我强烈推荐你用枚举来实现单例模式的更多相关文章

  1. 拔高你的Java代码质量吧:推荐使用枚举定义常量(转)

    提高你的Java代码质量吧:推荐使用枚举定义常量 一.分析 常量的声明是每一个项目中不可或缺的,在Java1.5之前,我们只有两种方式的声明:类常量和接口常量.不过,在1.5版之后有了改进,即新增了一 ...

  2. 《Java多线程编程核心技术》推荐

    写这篇博客主要是给猿友们推荐一本书<Java多线程编程核心技术>. 之所以要推荐它,主要因为这本书写得十分通俗易懂,以实例贯穿整本书,使得原本抽象的概念,理解起来不再抽象. 只要你有一点点 ...

  3. java虚拟机的学习书籍推荐

    javaEE开发已然是一个老生常谈的话题了,但经典之所以会成为经典,就是因为有可重复琢磨之处,每一次的反复推敲都会有不一样的收获.如果你不满足于做一个只会写if…else…的Java程序员,而是希望更 ...

  4. 《JAVA多线程编程核心技术》 笔记:第六章:单例模式与多线程

    一.立即加载/"饿汉模式"和延迟加载/"懒汉模式" 立即加载(又称饿汉模式):在使用类的时候已经将对象创建完毕,常见实现方法是直接new实例化 延迟加载(又称懒 ...

  5. 【Java】-NO.16.EBook.4.Java.1.005-【疯狂Java讲义第3版 李刚】- 枚举

    1.0.0 Summary Tittle:[Java]-NO.16.EBook.4.Java.1.005-[疯狂Java讲义第3版 李刚]- 枚举 Style:EBook Series:Java Si ...

  6. 9种Java单例模式详解(推荐)

    单例模式的特点 一个类只允许产生一个实例化对象. 单例类构造方法私有化,不允许外部创建对象. 单例类向外提供静态方法,调用方法返回内部创建的实例化对象.  懒汉式(线程不安全) 其主要表现在单例类在外 ...

  7. 最简单的设计模式——单例模式的演进和推荐写法(Java 版)

    前言 如下是之前总结的 C++ 版的:软件开发常用设计模式—单例模式总结(c++版),对比发现 Java 实现的单例模式和 C++ 的在线程安全上还是有些区别的. 概念不多说,没意思,我自己总结就是: ...

  8. 枚举类型的单例模式(java)

    Inspired by Effective Java. Singleton模式是在编程实践中应用最广泛的几种设计模式之一.以前知道的,实现单例的方法有两种(下面的A.B).刚刚在读<Effect ...

  9. [改善Java代码]非稳定排序推荐使用List

    我们知道Set与List的最大区别就是Set中的元素不可以重复(这个重复指的equals方法的返回值相等),其他方面则没有太大的区别了,在Set的实现类中有一个比较常用的类需要了解一下:TreeSet ...

随机推荐

  1. windows ,linux永久和临时修改pip源

    临时修改(建议)pypi镜像源方法:如果有untrust 报错,可使用https开头的网站,或加上--trusted 例如: pip install pywin32 -i http://mirrors ...

  2. Vue-阻止页面回退

    1.原生js方法 <script language="javascript"> //防止页面后退 使用在vue时 挂载到mounted中 history.pushSta ...

  3. Java后台技术IBATIS入门

    做过.net后台开发的同志一定用过Entity FrameWork,该框架实现了实体Entity到数据库行的映射,通过操作实体DataSet,就能够直接同步修改到数据库.但是Java暂时没有类似的技术 ...

  4. hive3.1.1 hive-site.xml

    <property> <name>hive.metastore.warehouse.dir</name> <value>/data/hive/wareh ...

  5. QLabel设置伙伴关系和快捷键(Alt+首字母)

    Qlabe中设置伙伴关系是使用Setbuddy函数: Qlabel.Setbuddy(QLineEdit) #将Qlabel和QLineEdit之间设置伙伴关系 另外,需要配合热键(快捷键)进行使用, ...

  6. 模块学习-json pickle

    json json序列化 import json def sayhi(name): print("hello",name) info = { 'name':'mogu', 'age ...

  7. 吴裕雄--天生自然Numpy库学习笔记:NumPy Ndarray 对象

    NumPy 最重要的一个特点是其 N 维数组对象 ndarray,它是一系列同类型数据的集合,以 0 下标为开始进行集合中元素的索引. ndarray 对象是用于存放同类型元素的多维数组. ndarr ...

  8. 【PAT甲级】1052 Linked List Sorting (25 分)

    题意: 输入一个正整数N(<=100000),和一个链表的头结点地址.接着输入N行,每行包括一个结点的地址,结点存放的值(-1e5~1e5),指向下一个结点的地址.地址由五位包含前导零的正整数组 ...

  9. iOS 开发之 23种设计模式

    整理了 iOS 开发中用到的设计模式: iOS 开发之 设计模式[一]原型模式 (Prototype pattern) iOS 开发之 设计模式[二]工厂方法模式 iOS 开发之 设计模式[三]抽象工 ...

  10. 嵌入式实时程序设计中C/C++代码的优化

    1 引言 计算机技术和信息技术的高速发展的今天,计算机和计算机技术大量应用在人们的日常生活中,嵌入式计算机也得到了广泛的应用.嵌入式计算机是指完成一种或多种特定功能的计算机系统,是软硬件的紧密结合体. ...