java设计模式之单例模式
来源:互联网 发布:淘宝怎么设置主营类目 编辑:程序博客网 时间:2024/06/10 23:37
单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中一个类只有一个实例。即一个类只有一个对象实例。--取自百度百科 当然,这不是我们今天要讨论的重点,重点是如何来实现保证系统中一个类只有一个实例,即单例模式是如何实现的呢?
实现方式一:饿汉式
package cn.zzit.singleton;/** * 饿汉式实现单例模式: * 1.构造器私有化 * 2.定义私有的静态类变量,并初始化 * 3.提供一个公开的供外部访问静态的返回类的的方法 * 优点:线程安全的,访问效率高 * 缺点:不支持延迟加载,增加性能开销 * @author yufu * */public class SingletonDemo01 { //类初始化时,立即加载对象,加载类时,天然是线程安全的 private static SingletonDemo01 instance=new SingletonDemo01(); //构造器私有化 private SingletonDemo01(){ } //公开的访问方法 public static SingletonDemo01 getInstance(){ return instance; }}
饿汉式虽然实现了单例模式,但是如果创建出来的对象一直没有使用,就额外的增加了开销,所以我们就需要合理的规避这一点,因此我们接下来要使用懒汉式来实现单例模式。
实现方式二:懒汉式
package cn.zzit.singleton;/** * 懒汉式实现单例模式: 1.构造器私有化 2.定义私有的静态类变量 3.提供一个公开的供外部访问静态的返回类的的方法 优点:支持延迟加载,减少性能开销 * 缺点:线程不安全(要保证线程安全需要加synchronized进行修饰),由于要保证线程安全加synchronized修饰,访问效率相对低下 * * @author yufu * */public class SingletonDemo02 { //类初始化时,不初始化对象(延迟加载,真正使用的时候再创建) private static SingletonDemo02 instance = null; //构造器私有化 private SingletonDemo02() { } //公开的访问方法 public static synchronized SingletonDemo02 getInstance() { if (instance == null) { instance = new SingletonDemo02(); } return instance; }}
懒汉式解决了饿汉式的性能上的开销问题,但是,它是线程不安全的,如果要保证线程的安全,需要在方法上加synchronized锁,这样的话是保证了线程的安全,但是同时访问效率也降低了,那么我们就想了能不能把锁不加在方法上,加在方法的内部?那就要用到了双重检测锁机制。
实现方式三:双重检测锁
package cn.zzit.singleton;/** * 使用双重检测锁模式实现单例模式 * 双重检测锁模式看似综合了饿汉式和懒汉式的优点,并解决了他们的缺点,其实不然,由于编译器优化和jvm底层内部原因,偶尔会出问题,实际开发中不建议使用 * * @author yufu * */public class SingletonDemo03 { private static SingletonDemo03 instance = null; private SingletonDemo03() { } public static SingletonDemo03 getInstance() { // 此处判断对象是否为空,如果不为空,直接返回,提高访问效率 if (instance == null) { // 此处如果有两条线程同时访问到此,其中一个获得锁,另一个在外等待,保证访问的安全性 synchronized (SingletonDemo03.class) { // 如果当前持锁对象为空,把该对象初始化,之后该对象释放锁,并返回,当下一个对象访问时由于对象已经被赋值初始化,所以直接返回 if (instance == null) { instance = new SingletonDemo03(); } } } return instance; }}
可能有读者就会想,这就综合了懒汉与饿汉优点,完美解决了他们的缺点,其实由于编译器优化和jvm底层内部原因,双重检测锁机制偶尔会出问题,实际开发中不建议使用,那么有没有一种更好的方式呢?那就要用到静态内部类来实现。
实现方式四:静态内部类
package cn.zzit.singleton;/** * 使用静态内部类实现单例模式 * 1.外部类没有static属性,不会像饿汉式那样立即加载对象 * 2.只有真正调用getInstance,才会加载静态内部类,加载时是线程安全的, * instance是static final 类型,保证内存中只有这样一个实例,而且只能被赋值一次,从而保证了 * 线程安全性 * 3.兼备了并发高效调用和 和延迟加载的优势 * @author yufu * */public class SingletonDemo04 { private static class SingleClassInstance{ private static final SingletonDemo04 instance=new SingletonDemo04(); } public static SingletonDemo04 getInstance(){ return SingleClassInstance.instance; } private SingletonDemo04(){ }}
测试代码:
package cn.zzit.test;import cn.zzit.singleton.SingletonDemo01;import cn.zzit.singleton.SingletonDemo02;import cn.zzit.singleton.SingletonDemo03;import cn.zzit.singleton.SingletonDemo04;/** * 单例对象 占用资源少,不需要延迟加载 * 枚举式好于饿汉式 * 单例对象占用资源大,需要延迟加载 * 静态内部类好于懒汉式 * @author yufu * */public class Test { public static void main(String[] args) throws Exception { SingletonDemo01 s1=SingletonDemo01.getInstance(); SingletonDemo01 s2=SingletonDemo01.getInstance(); System.out.println(s1==s2); SingletonDemo02 s3=SingletonDemo02.getInstance(); SingletonDemo02 s4=SingletonDemo02.getInstance(); System.out.println(s3==s4); SingletonDemo03 s5=SingletonDemo03.getInstance(); SingletonDemo03 s6=SingletonDemo03.getInstance(); System.out.println(s5==s6); SingletonDemo04 s7=SingletonDemo04.getInstance(); SingletonDemo04 s8=SingletonDemo04.getInstance(); System.out.println(s7==s8); }}
看似上面的方式都实现了单例模式,但是其实他们都是可以通过反射和反序列化来破解的,反射破解以上单例模式的代码:
Class<SingletonDemo02> clazz=(Class<SingletonDemo02>) Class.forName("cn.zzit.singleton.SingletonDemo02"); Constructor<SingletonDemo02> c=clazz.getDeclaredConstructor(null); c.setAccessible(true);//去除安全监测机制 SingletonDemo02 ss2=c.newInstance(null);
为了防止反射破解上述方式实现的单例模式,需要对私有的构造器进行处理:
private SingletonDemo02() { if (instance != null) { throw new RuntimeException(); } }
反序列化破解上面实现的单例模式代码:
//使用反序列化破解单例模式 //使用序列化将对象写入磁盘 FileOutputStream fos=new FileOutputStream("e:/a.txt"); ObjectOutputStream oos=new ObjectOutputStream(fos); oos.writeObject(s1); oos.close(); fos.close(); //使用反序列化将对象从磁盘中读出 FileInputStream fis=new FileInputStream("e:/a.txt"); ObjectInputStream ois=new ObjectInputStream(fis); SingletonDemo01 ss1=(SingletonDemo01) ois.readObject(); System.out.println(s1==ss1);
那么如何防止反序列化破解单例模式呢?代码如下:
private Object readResolve() throws ObjectStreamException{ return instance; }
实现方式五:枚举类
package cn.zzit.singleton;/** * 通过枚举类来实现单例模式 实现方式简单,避免了其他方式使用反射和反序列化创建对象的漏洞 * 唯一的缺点就是没有懒加载,不支持延迟加载 * @author yufu * */public enum SingletonDemo05 { //这个枚举元素,本事就是单例对象 INSTANCE;}
枚举类型实现起来比较简单,同时天然的就避免了通过使用反射和反序列化来破解单例模式,但是枚举的缺点就是没有懒加载,不支持延迟加载。
所以在实际开发中用到单例模式的,要做好合理的选择,一般情况下:如果单例对象,占用资源少,不需要延迟加载 ,枚举式好于饿汉式;如果单例对象占用资源大,需要延迟加载,静态内部类好于懒汉式。
阅读全文
0 0
- java设计模式之单例模式
- Java模式设计之单例模式
- Java模式设计之单例模式
- Java模式设计之单例模式
- java设计模式之单例模式
- Java模式设计之单例模式
- Java模式设计之单例模式
- Java设计模式之单例模式
- Java设计模式之单例模式
- Java设计模式之单例模式
- Java设计模式之单例模式
- Java模式设计之单例模式
- java设计模式之单例模式
- java设计模式之单例模式
- java设计模式之单例模式
- java设计模式之单例模式
- java设计模式之单例模式
- java设计模式之单例模式
- 技术债务-坏味道、Bug、漏洞
- MFC窗体程序中添加调试控制台
- Java基础(七)面向对象高级特性
- 【POJ】3104
- python_day2
- java设计模式之单例模式
- HTML5_js实现小游戏别踩白方块
- 暑假总结
- codeforces843D Dynamic Shortest Path -- 最短路
- 190. Reverse Bits
- tomcat websocket FutureToSendHandler TimeoutException
- 【网络流24题】圆桌聚餐(二分图)
- Common Sense of 信息安全
- 【2017年】阿里巴巴算法笔试第二题