设计模式篇(一):单例模式

  1. 概念
  2. 应用场景
  3. 优缺点
  4. 实现方式
  5. 面试

一、概念

一个类只有一个实例,并且提供一个访问该实例的全局访问点。

二、应用场景

1、最常见的比如说windows系统下的任务管理器
2、在项目中,读取配置文件的类
3、计数器
4、数据库连接池
5、Spring框架中,bean默认就是单例的
6、Spring MVC/struts框架中,控制器对象也是单例的

三、优缺点

优点:

1、因为单例模式只生成一个实例,减少了系统性能开销
2、当一个对象的产生需要较多资源时,可以通过在应用中启动时直接生产一个单例对象,然后永久驻留内存
3、单例模式可以在系统设置全局的访问点,优化环共享资源访问

缺点:

1、不适用于需要在不同用例场景下发生变化的对象
2、没有接口,不能继承,与单一职责冲突

四、实现方式

单例模式常见的实现方式主要有五种
1、饿汉式(线程安全,效率高,不能延迟加载)
2、懒汉式(线程安全,效率不高,可以延迟加载)
3、双重检测锁式(不建议使用)
4、静态内部类式(线程安全,调用效率高,可以延迟加载)
5、枚举单例(线程安全,调用效率高,不能延迟加载)

饿汉式(单例对象立即加载)

public class SingletonDemo{

	private static SingleTonDemo st = new SingletonDemo();
	
	private SingletonDemo(){
	}
	public static SingletonDemo getInstance(){
	
		return st;
		
	}
}

public class Client{

	public static void main(String[] args){
	
		SingletonDemo st1 = SingletonDemo.getInstance();
		SingletonDemo st2 = SingletonDemo.getInstance();
		System.out.println(st1 == st2);//结果为true
		
	}
	
}

在饿汉式单例模式中,static变量会在类加载时进行初始化,此时也不会出现多线程访问该对象的问题,虚拟机会保证只加载一次该类,所以不会发生并发访问的问题。
**缺点:**如果只是加载本类,而不调用getInstance()方法,则会造成资源浪费。

懒汉式(单例对象延迟加载)

public class SingletonDemo02{

	private static SingletonDemo02 st;
	
	private SingletonDemo02(){
	}
	
	public static synchronized SingletonDemo02 getInstance(){
	
		if(st == null){
			st = new SingletonDemo02();
		}
		return st;
	}
}

在懒汉单例模式中,对象会在需要用到的时候再加载,资源利用率增高了
**缺点:**每次调用getInstance()方法时都要同步,并发效率较低

双重检测锁实现

public class SingletonDemo03 {

    private static SingletonDemo03 st = null;
    
    public static SingletonDemo03 getInstance(){
        
        if (st == null){
            SingletonDemo03 st2;
            synchronized (SingletonDemo03.class){
                st2 = st;
                if (st2 == null){
                    synchronized (SingletonDemo03.class){
                        if (st2 == null){
                            st2 = new SingletonDemo03();
                        }
                    }
                    st = st2;
                }
            }
        }
        return st;
    }
    
    private SingletonDemo03(){
        
    }

}

此模式将同步内容写入if内部,提高执行效率,不必每次获取对象以后都进行同步,只有第一次才同步
**缺点:**由于编译器优化原因和JVM底层内部模型原因,偶尔会出问题,所以不建议使用

静态内部类实现(也是一种懒加载方式)

public class SingletonDemo04 {

    private static class SingletonClassInstance{
        private static final SingletonDemo04 instance = new SingletonDemo04();
    }
    
    public static SingletonDemo04 getInstance(){
        return SingletonClassInstance.instance;
    }
    
    private SingletonDemo04(){
    }

}

外部类没有static属性,所以不会像饿汉式一样立即加载对象
只有在调用getInstance()时才会加载静态内部类,加载时线程是安全的,因为instance被static final所修饰,所以可以保证内存中只有一个这样的实例存在,并且只会被赋值一次,从而保证了线程安全性。
兼备了并发高效调用和延迟加载的优势

枚举实现

public enum SingletonDemo05 {

    /** 定义一个枚举元素,代表Singleton的一个实例 */
    INSTANCE;
    /** 单例可以有自己的操作 */
    public void singletonOperation(){
        //需要进行的操作
    }
    
}

实现简单
枚举本身就是单例模式,由JVM从根本上提供保障,避免通过反射和反序列化的漏洞
**缺点:**不能延迟加载

五、面试

如何破解上述几种(不包含枚举实现)实现方式?怎么预防?
可以通过反射和序列化来破解
1、预防反射破解时,可以在构造方法中手动抛出异常控制
2、预防反序列化破解时,可以通过定义readResolve()防止获得不同对象

public class SingletonDemo implements Serializable {

    private static SingletonDemo st;

    private SingletonDemo() throws Exception {
        if (st != null){
            throw new Exception("只能创建一个对象");
        }
    }

    public static synchronized SingletonDemo getInstance() throws Exception {
        
        if (st == null){
            st = new SingletonDemo();
        }
        return st;
    }
    
    /** 反序列化时,如果对象所在类定义了readResolve(),实际上是一种回调,定义返回哪个对象 */
    private Object readResolve() throws ObjectStreamException {
        return st;
    }

}

下一篇博客将会讲解工厂模式,链接地址

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值