Java设计模式学习笔记:单例模式(二)

上一篇文章介绍了单例模式的最常见的两种,饿汉式和懒汉式,以及两种单例模式的优缺点以及解决方案,传送门如下:

Java设计模式学习笔记:单例模式(一)

今天继续研究单例模式的几种高级模式。

首先解决一下上篇文章中的一个问题,解决懒汉式单例线程安全的问题,有两种方式:

1、内部类,代码如下:

package com.rq.pattern;

public class LazyStaticInnerSingten {

	private LazyStaticInnerSingten() {}
	
	public static LazyStaticInnerSingten getInstance() {
		return LazyHold.INSTANCE;
	}
	
	private static class LazyHold{
		private static final LazyStaticInnerSingten INSTANCE = new LazyStaticInnerSingten();
	}
}

内部类在调用之前就初始化了,所以不存在线程安全问题,由于其加载的特性(在调用时才加载)同时避免了他不会和饿汉式单例一样存在资源浪费的问题,所以完美地解决了线程安全和资源浪费两个问题。但是有另外一个问题,后面说。

2、双重检测机制,代码如下:

public class LazySingten {

	private LazySingten() {}
	
	private static LazySingten instance;
	
	public static LazySingten getInstance() {
		//检查是否要阻塞,解决线程安全问题
		if(instance == null) {
			synchronized (LazySingten.class) {
				//检查是否要重新创建实例
				if(instance == null) {
					instance = new LazySingten();
				}
			}
		}
		return instance;
	}
}

介绍完两种模式,再继续提出问题,内部类的方式虽然很完美,但是又出现一个新的问题,就是它会被反射破坏,代码如下:

public class LazyStaticInnerSingtenTest {

	public static void main(String[] args) {
		
		Class<?> clazz =  LazyStaticInnerSingten.class;
		try {
			//获得构造方法
			Constructor<?> c = clazz.getDeclaredConstructor(null);
			//设置强制访问
			c.setAccessible(true);
			//生成实例
			Object newInstance1 = c.newInstance();
			Object newInstance2 = c.newInstance();
			System.out.println(newInstance1);
			System.out.println(newInstance2);
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

执行结果:

com.rq.pattern.LazySingten@15db9742
com.rq.pattern.LazySingten@6d06d69c

可以看出,两个实例并不相同,也就是说,单例被破坏了,如何解决?方式如下:

1、在构造方法中,判断,如果单例被破坏,直接抛出异常,简单粗暴,代码如下:

public class LazyStaticInnerSingten {

	private LazyStaticInnerSingten() {
		if(LazyHold.INSTANCE != null) {
			throw new RuntimeException("不允许非法访问!");
		}
	}
	
	public static LazyStaticInnerSingten getInstance() {
		return LazyHold.INSTANCE;
	}
	
	private static class LazyHold{
		private static final LazyStaticInnerSingten INSTANCE = new LazyStaticInnerSingten();
	}
}

再测试,执行结果如下:

java.lang.reflect.InvocationTargetException
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	at com.rq.pattern.LazyStaticInnerSingtenTest.main(LazyStaticInnerSingtenTest.java:16)
Caused by: java.lang.RuntimeException: 不允许非法访问!
	at com.rq.pattern.LazyStaticInnerSingten.<init>(LazyStaticInnerSingten.java:7)
	... 5 more

阻止了通过反射的方式破坏单例。

下一篇文章会继续讲解单例模式的另外几种更高级的写法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值