单例模式特点
保证一个类只有一个实例,并且提供一个访问该实例的全局访问点
windows中的任务管理器
操作系统的文件系统,一个操作系统只能有一个文件系统
servlet编程中,每个servlet也是单例
SpringMVC中,控制对象也是单例
Spring中的bean
单例模式分类
饿汉式
特点:减少系统开销
public class SingletonDemo {
//类初始化的时候立即加载(没有延时加载的优势),由于加载类的时候天热的线程安全
private static final SingletonDemo singleDemo = new SingletonDemo();
//私有化构造器
private SingletonDemo() {
}
//方法没有同步,调用效率高
public static SingletonDemo getInstance(){
return singleDemo;
}
}
懒汉式
懒汉式普通实现
特点:真正用的时候加载,资源的利用率高,但是每次调用的时候需要同步,并发下效率低
public class SingletonDemo1 {
//调用的时候加载
private static SingletonDemo1 instance = null;
//私有化构造器
private SingletonDemo1() {
}
//方法同步,调用效率低
//为什么加锁:多线程下,不加锁会产生多个实例
public static synchronized SingletonDemo1 getInstance() {
if (instance == null) {
instance = new SingletonDemo1();
}
return instance;
}
}
懒汉式静态内部类实现
特点:
外部没有static属性不会饿汉式那样立即加载对象
真正调用getInstance()时,才会加载静态内部类.类加载时是线程安全的,static final保证内存中实例唯一
兼具并发高效和延时加载的优势
public class SingletonDemo2 {
private static class singletonClass {
private static final SingletonDemo2 instance = new SingletonDemo2();
}
public static SingletonDemo2 getInstance() {
return singletonClass.instance;
}
private SingletonDemo2() {
}
}
懒汉式其他实现
双重检测锁式(由于jvm底层内部模型原因,偶尔会出问题,不建立使用)
枚举单例(线程安全,调用效率高,不能延时加载)
反射和反序列化漏洞
反射漏洞
反射可以破解单例(不包含枚举式)实现方式!(可以在构造方法中手动抛出异常控制)
//测试
public class Client {
public static void main(String[] args) throws Exception {
SingletonDemo2 demo = SingletonDemo2.getInstance();
SingletonDemo2 demo1 = SingletonDemo2.getInstance();
System.out.println(demo);
System.out.println(demo1);
Class<SingletonDemo2> singletonDemo2Class = (Class<SingletonDemo2>) Class.forName("singleton.SingletonDemo2");
Constructor<SingletonDemo2> constructor = singletonDemo2Class.getDeclaredConstructor();
constructor.setAccessible(true);
SingletonDemo2 demo2 = constructor.newInstance();
SingletonDemo2 demo3 = constructor.newInstance();
System.out.println(demo2);
System.out.println(demo3);
}
}
//SingletonDemo2类的构造方法修改为
//私有化构造器
private SingletonDemo2() {
if (singletonClass.instance != null) {
throw new RuntimeException();
}
}
反序列化
反序列化可以破解单例(不包含枚举式)实现方式!(可以添加readResolve()方法)
public class Client {
public static void main(String[] args) throws Exception {
SingletonDemo2 demo = SingletonDemo2.getInstance();
SingletonDemo2 demo1 = SingletonDemo2.getInstance();
System.out.println(demo);
System.out.println(demo1);
FileOutputStream fileOutputStream = new FileOutputStream("/Users/wjk/Desktop/a.txt");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(demo);
objectOutputStream.close();;
fileOutputStream.close();
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("/Users/wjk/Desktop/a.txt"));
SingletonDemo2 singletonDemo2 =(SingletonDemo2) objectInputStream.readObject();
System.out.println(singletonDemo2);
}
}
//SingletonDemo2类实现序列化添加readResolve()
private Object readResolve() throws ObjectStreamException{
return singletonClass.instance;
}
}