设计模式-单例模式(Singleton Pattern)
上一篇 <<<观察者模式(Observer Pattern)
下一篇 >>>创建对象的方式汇总
单例模式:一个类仅有一个实例,并提供一个访问它的全局访问点。
- 优点:减少代码冗余、提高代码复用性、安全性、隐藏真实角色、非入侵、节约内存、重复利用
-
缺点:线程安全问题,数量很多的话容易导致内存泄露
应用场景
- spring IOC容器
- 线程池(数据库、多线程)
- 枚举、常量类
- 配置文件常量
- 日志
- HttpApplication、servlet
- windows系统的任务管理器、回收站、网站计数器、显卡驱动、打印机等
单例优缺点
优点:
a、防止其他对象自身的实例化,保证所有的对象都访问一个实例
b、类在实例化进程上有一定的伸缩性
c、提供了对唯一实例的受控访问
d、节约系统创建和销毁对象的资源
e、允许对共享资源的多重占用
缺点:
a、不适用于变化的对象
b、没有抽象层,所以扩展难度很大
c、职责过重,违背了单一职责原则
d、滥用容易导出溢出或丢失情况
单例模式的种类
第一个if是判断实例对象是否存在,针对读写都有的操作。
第二个if是对同时进行初始化操作的多个线程进入锁状态的再次判断,如果前面已经有创建过的话,将不再实例化,彻底解决单例问题。
静态内部类方式与双重检验锁的区别
双重检验锁是采用了懒汉式并且只针对写操作加了锁,保证了线程的安全又加快了读的操作。但如果多线程进来的时候,写操作会存在阻塞的现象,效率不高。
静态内部类是在使用时才会被初始化,但内部类又使用了饿汉式的模式,所以既拥有饿汉式的线程安全,又支持懒汉式的资源不浪费,不存在线程阻塞的情况,比双重检验锁更加的高效。
单例模式创建方式如何选择
- 如果不需要延迟加载单例,可以使用枚举或者饿汉式,相对来说枚举性好于饿汉式。
- 如果需要延迟加载,可以使用静态内部类或者懒汉式,相对来说静态内部类好于懒汉式。
单例模式如何破坏
a、利用反射机制,通过declaredConstructors.setAccessible(true);方法即可破解构造函数私有化的缺陷
Class<?> classInfo = Class.forName("com.jgspx.singleton.crack.regex.RegixObject");
Constructor declaredConstructors = classInfo.getDeclaredConstructor();
declaredConstructors.setAccessible(true);
Object o = declaredConstructors.newInstance();
- 破解:在构造函数里判断如果已经实例化的话就抛出异常,防止多次被实例化
public class RegixObject {
/**
* 如果是饿汉式,则反射机制调用构造函数的时候就会报错
*/
private static RegixObject regixObject = new RegixObject();
public static RegixObject getInstance(){
/**
* 如果是懒汉式,先利用反射,然后代码调用,则构造函数里加上判断也没用
*/
if(regixObject==null){
regixObject = new RegixObject();
}
return regixObject;
}
private RegixObject(){
//加上这一句话,可破解多次初始化的情况
if(regixObject!=null){
throw new RuntimeException("初始化已执行过");
}
}
}
b、利用序列化,将序列化后的结果存入硬盘,然后再次反序列化,等到的结果就和原先的不一致
序列化:将存放在内存中的对象信息序列操作后变成可以存放在硬盘的数据。
反序列化:将硬盘上的数据解析后放入到内存中。
public static void main(String[] args) throws Exception{
User instance = User.getInstance();
FileOutputStream fos = new FileOutputStream("./user.obj");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(instance);
oos.flush();
oos.close();
FileInputStream fis = new FileInputStream("./user.obj");
ObjectInputStream ois = new ObjectInputStream(fis);
User singleton2 = (User) ois.readObject();
System.out.println(singleton2==instance);
}
- 破解:在原有类中增加方法readResolve()
public class User implements Serializable {
public static User user = new User();
public static User getInstance(){
return user;
}
//返回序列化获取对象 ,保证为单例
public Object readResolve() {
return user;
}
}
- ObjectInputStream
- case TC_OBJECT:return checkResolve(readOrdinaryObject(unshared)); *
- ObjectStreamClass desc = readClassDesc(false);---获取当前类的超类(没有实现Serializable的)。eg:User extend A,且A没有实现Serializable,则desc=A.class,否则desc=Object.class
- if (desc.hasReadResolveMethod()){Object rep = desc.invokeReadResolve(obj);}
最强单例模式--枚举
a. 枚举单例源码
public enum SingleV6 {
TT;
SingleV6(){
System.out.println("我是无参构造函数被执行到了");
}
public void add(){
System.out.println("添加操作被启动");
}
}
b. 枚举单例反编译后的代码
public final class SingleV6 extends Enum
{
public static SingleV6[] values()
{
return (SingleV6[])$VALUES.clone();
}
public static SingleV6 valueOf(String name)
{
return (SingleV6)Enum.valueOf(com/jarye/singleton/v6/SingleV6, name);
}
private SingleV6(String s, int i)
{
super(s, i);
System.out.println("\u6211\u662F\u65E0\u53C2\u6784\u9020\u51FD\u6570\u88AB\u6267\u884C\u5230\u4E86");
}
public void add()
{
System.out.println("\u6DFB\u52A0\u64CD\u4F5C\u88AB\u542F\u52A8");
}
public static final SingleV6 TT;
private static final SingleV6 $VALUES[];
static
{
TT = new SingleV6("TT", 0);
$VALUES = (new SingleV6[] {
TT
});
}
}
枚举类型其实就是class类,继承于Enum类,内置了name、ordinal和values方法,且没有默认的无参构造函数。
A、底层转换类继承Enum
B、使用静态代码快方式,当静态代码快执行的时候初始化该对象
c. 枚举单例无法被破解的原因
- 无参方式破解
Class<?> classInfo = Class.forName("com.jarye.singleton.v6.SingleV6");
Constructor<?> declaredConstructor = classInfo.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
declaredConstructor.newInstance();
- 参照源码的有参方式破解
Class<?> classInfo2 = Class.forName("com.jarye.singleton.v6.SingleV6");
Constructor<?> declaredConstructor2 = classInfo2.getDeclaredConstructor(String.class,Integer.class);
declaredConstructor2.setAccessible(true);
declaredConstructor2.newInstance("zhangsan",3);
相关文章链接:
<<<23种常用设计模式总览
<<<代理模式(Proxy Pattern)
<<<装饰模式(Decorator Pattern)
<<<观察者模式(Observer Pattern)
<<<责任链模式(Chain of Responsibility Pattern)
<<<策略模式(Strategy Pattern)
<<<模板方法模式(Template Pattern)
<<<外观/门面模式(Facade Pattern)
<<<建造者模式(Builder Pattern)
<<<适配器模式(Adapter Pattern)
<<<原型模式(Prototype Pattern)
<<<工厂相关模式(Factory Pattern)