浅谈使用单元素的枚举类型实现单例模式

简介

通常情况下,我们写单例模式的时候无非就是三个步骤:构造器私有化,声明私有静态变量,提供静态获取实例的方法。简单说就是以下这种方式:

class SingletonA {
	private static SingletonA instence = new SingletonA();
	private SingletonA() {
	}
	public static SingletonA getInstance() {
		return instence;
	}
}

这是最基本的单例模式的写法,考虑到线程安全的问题,会用synchronized 关键字修饰getInstance()方法,另外还有饿汉式、懒汉式、静态内部类、双重校验锁的写法。
但是这种写法存在缺陷,可以利用反射的方式来实例化多个不同的实例,如下所示:

private static void testReflex() {

		try {

			SingletonA s1 = SingletonA.getInstance();
			Class<SingletonA> cls = SingletonA.class;
			Constructor<SingletonA> constructor = cls
					.getDeclaredConstructor(new Class[] {});
			constructor.setAccessible(true);
			SingletonA s2 = constructor.newInstance(new Object[] {});

			System.out.println(s1 == s2);//false
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

这种情况下,就会出现多个不同的实例,从而导致一些乱七八糟的结果。
还有一种情况就是在序列化和反序列换的时候也会出现多个不同的实例,如下:

class SingletonB implements Serializable {

	private static SingletonB instence = new SingletonB();

	private SingletonB() {
	}

	public static SingletonB getInstance() {
		return instence;
	}
}
private static void testSingletonB() {
		File file = new File("singleton");
		ObjectOutputStream oos = null;
		ObjectInputStream ois = null;
		try {
			oos = new ObjectOutputStream(new FileOutputStream(file));
			SingletonB SingletonB1 = SingletonB.getInstance();

			oos.writeObject(SingletonB1);
			oos.close();
			ois = new ObjectInputStream(new FileInputStream(file));
			SingletonB SingletonB2 = (SingletonB) ois.readObject();
			System.out.println(SingletonB1 == SingletonB2);//false

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} finally {
			if (oos != null) {
				try {
					oos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (ois != null) {
				try {
					ois.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

这种情况也会出现有多个实例的问题,这个问题可以在类中添加readResolve()方法来避免,即:

class SingletonB implements Serializable {

	private static SingletonB instence = new SingletonB();

	private SingletonB() {
	}

	public static SingletonB getInstance() {
		return instence;
	}

	// 不添加该方法则会出现 反序列化时出现多个实例的问题
	public Object readResolve() {
		return instence;
	}
}

这样在反序列化的时候就不会出现多个实例。

使用单元素的枚举实现单例模式

一个最简单的POJO类,如下:

enum SingletonC {
	INSTANCE;
	private String field;

	public String getField() {
		return field;
	}

	public void setField(String field) {
		this.field = field;
	}

}

测试方法:

	private static void testEnum() {
		File file = new File("singletonEnum");
		ObjectOutputStream oos = null;
		ObjectInputStream ois = null;
		try {

			oos = new ObjectOutputStream(new FileOutputStream(file));
			SingletonC singleton = SingletonC.INSTANCE;
			oos.writeObject(SingletonC.INSTANCE);
			oos.close();
			ois = new ObjectInputStream(new FileInputStream(file));
			SingletonC singleton2 = (SingletonC) ois.readObject();
			System.out.println(singleton == singleton2);//true

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} finally {
			if (oos != null) {
				try {
					oos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (ois != null) {
				try {
					ois.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

这种实现单例模式的方式是在 1.5之后才出现的,

这种方法在功能上与公有域方法相近,但是它更加简洁,无偿提供了序列化机制,绝对防止多次实例化,即使是在面对复杂序列化或者反射攻击的时候。虽然这种方法还没有广泛采用,但是单元素的枚举类型已经成为实现Singleton的最佳方法。 ----《Effective Java 中文版 第二版》

  • 10
    点赞
  • 6
    收藏
  • 打赏
    打赏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页
评论 6

打赏作者

Huang兄

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值