GOF设计模式之单例模式
单例模式即保证类只有一个实例,并且提供一个访问该实例的全局访问点。定义一个类,将其属性私有化、构造器也私有化,只留下一个方法给外部调用。比较适用调用和销毁频繁的类、一些工具类对象、创建时耗时或者占用资源又不得不用的类、一些访问频繁的数据库和文件对象。
其五种写法
一、懒汉式
public class Singleton {
//类初始化时,立即加载这个对象(没有延时加载的优势),类加载是天然的线程安全
private static Singleton instance = new Singleton();
private Singleton(){
}
//方法没有同步,调用效率高
public static Singleton getInstance() {
return instance;
}
}
二、饿汉式
public class Singleton {
//类初始化时,不初始化(延时加载,到要用的时候在加载)
private static Singleton instance;
private Singleton(){
}
//方法同步,调用效率低
public static synchronized Singleton getInstance() {
if(instance == null ) {
instance = new Singleton();
}
return instance ;
}
}
三、双重检测锁式
public class Singleton {
private static Singleton instance = null;
public static Singleton getInstance() {
/*虽然也添加了同步块,但是同步块再if之下,只有第一次才
* 进行同步,不必每次获取对象都进行同步,提高了执行效率。*/
if(instance ==null) {
Singleton sc;
synchronized (Singleton.class) {
sc = instance ;
synchronized (Singleton.class) {
if(sc == null) {
sc = new Singleton();
}
}
instance = sc;
}
}
return instance ;
}
private Singleton() {
}
}
//由于编译器优化原因和JVM底层内部模型原因,偶尔回出现问题,慎用!
四、静态内部类式
public class Singleton {
//调用此方法时才创建对象且线程安全,并且实现了延时加载,调用效率高。
private static class SingletonClassInstance{
private static final Singleton instance = new Singleton();
}
private Singleton() {
}
public static Singleton getInstance() {
return SingletonClassInstance.instance;
}
}
五、枚举式
public enum Singleton {
//这个枚举本身就是一个单例对象,且天然线程安全,但是无法延时加载
INSTANCE;
//可以添加自己需要的操作
public void SingletonOperation() {
}
}
如何避免反射漏洞
首先需要了解时怎么通过反射漏洞的,就是调用了**.setAccessible(true);**方法使其跳过权限检查,从而可以直接访问其私有的成员,从二直接创建对象。如何避免呢?可以直接在类内部私有无参构造器中添加一个判断即可
private Singleton(){
//判断instance是否创建,如果不为空直接抛出异常即可。
if(instance != null) {
throw new RuntimeException();
}
}
如何避免反序列化漏洞
反序列化漏洞是将已经创建的类对象通过序列化存储到文件中,再通过反序列化将文件读取,从而创建一个新的类对象。避免方法即在类中添加下面这个私有方法
//反序列化时,如果创建了readResolve()则直接返回此方法指定的对象,不需要再创建新对象
private Object readResolve() {
return instance;
}
如何选用
1、单例对象如果占用资源少不需要延时加载:枚举比饿汉式好;
2、单例对象占用资源大需要延时加载:静态内部类好于懒汉式。
学习笔记,如有不对的地方,烦请大神帮忙指点指点!