600字范文,内容丰富有趣,生活中的好帮手!
600字范文 > Java单例模式:为什么我强烈推荐你用枚举来实现单例模式

Java单例模式:为什么我强烈推荐你用枚举来实现单例模式

时间:2021-09-06 06:27:45

相关推荐

Java单例模式:为什么我强烈推荐你用枚举来实现单例模式

写在前面——原作的这篇文章真的写的非常的简洁,逻辑清晰,将Java单例模式的各种写法写的非常清楚,并介绍了用枚举实现单例的最佳实践。

单例模式简介

单例模式是 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>mons</groupId><artifactId>commons-lang3</artifactId><version>3.8.1</version></dependency>

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

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

如下:

import mons.lang3.SerializationUtils;import 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()的方式调用即可。方便、简洁又安全。

同样使用反序列化的方式测试一下是否存在反序列化攻击,代码和结果如下:

import mons.lang3.SerializationUtils;public class Main {//序列化反序列化public static void main(String[] args) {Singleton instance = Singleton.INSTANCE;byte[] serialize = SerializationUtils.serialize(instance);Singleton newInstance = SerializationUtils.deserialize(serialize);System.out.println(instance == newInstance);}}

结果

​​​​​​​

总结

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

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。