单例模式
概念:是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类
作用:保证一个类只有一个实例,并且提供一个访问该实例的全局访问点。
优点:单列模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源,如读取配置,产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留在内存的方式来解决。
一>>单例模式实现方式
主要是以下五种:
主要:
1.懒汉式(线程安全,调用效率不高,但可以延时加载)
2.饿汉式(线程安全,调用效率高,但不可以延时加载)
其他:
3.双重检测锁式(由于jvm底层内部模型原因,偶尔会出现问题,不建议使用)
4.静态内部类式(线程安全,调用效率高,但是可以延时加载)
5.枚举单例(线程安全,调用效率高,不能延时加载,并且天然的防止反射和反序列漏洞)
①饿汉式
代码:
public class SingletonDemo {
//类初始化立即加载这个对象
private static SingletonDemo s = new SingletonDemo();
private SingletonDemo() {
};
public static SingletonDemo getInstance() {
return s;
}
}
②懒汉式
代码:
public class SingletonDemo2 {
// 私有静态对象,加载时候不做初始化
private static SingletonDemo2 s = null;
// 私有构造方法,避免外部创建实例
private SingletonDemo2() {
};
public static SingletonDemo2 getInstance() {
if (s == null) {
s = new SingletonDemo2();
}
return s;
}
}
③静态内部类
代码:
public class SingletonDemo3 {
private static class SingletonClassInstance {
// static final 保证了内存中只有这样一个实例存在,而且只能被赋值一次,从而保证了线程安全性
private static final SingletonDemo3 s = new SingletonDemo3();
}
// 只有真正调用getInstance()才会加载静态内部类,加载类时是线程安全的
public static SingletonDemo3 getInstance() {
return SingletonClassInstance.s;
}
private SingletonDemo3() {
}
}
④枚举单例
代码:
*优点:实现简单 枚举本身就是单例模式,由JVM从根本上提供保障,避免通过反射和反序列化的漏洞
*缺点:无延迟加载
*/
public enum SingletonDemo4 {
/*
* 定义一个枚举的元素,他就代表了singleton的一个实例
*/
INSTANCE;
/*
* 单例可以有自己的操作
*/
public void singletonOperation() {
// 功能处理
}
}
注:双重检测锁式偶尔会出现问题,不建议使用,代码就不放了。
二>>各实现方式对比
单例对象占用资源少,不需要延时加载
枚举式好于饿汉式
单例对象占用资源大,需要延时加载
静态内部类式好于懒汉式
三>>如何防止单例模式的反射漏洞(主要针对是懒汉和饿汉式)
只需要在私有化构造器中添加判断
private SingletonDemo() {
if(s != null){
throw new RuntimeException();
}
};
四>>如果防止单例模式的但序列话漏洞(主要针对是懒汉和饿汉式)
可以通过定义readResolve(防止获得不同对象),反序列化时,如果对象所在类定义了readResolve()那就定义返回哪个对象
具体就是在代码中加上如下代码
//反序列化时,如果定义了readResolve()则直接返回此方法指定的对象,而不需要单独在创建新对象。
private object readResolve() throws ObjectStreamException(){
return s;
}