0
点赞
收藏
分享

微信扫一扫

设计模式-单例模式(Singleton Pattern)

上一篇 <<<观察者模式(Observer Pattern)
下一篇 >>>创建对象的方式汇总


单例模式:一个类仅有一个实例,并提供一个访问它的全局访问点。

  • 优点:减少代码冗余、提高代码复用性、安全性、隐藏真实角色、非入侵、节约内存、重复利用
  • 缺点:线程安全问题,数量很多的话容易导致内存泄露

应用场景

  • spring IOC容器
  • 线程池(数据库、多线程)
  • 枚举、常量类
  • 配置文件常量
  • 日志
  • HttpApplication、servlet
  • windows系统的任务管理器、回收站、网站计数器、显卡驱动、打印机等

单例优缺点

单例模式的种类




静态内部类是在使用时才会被初始化,但内部类又使用了饿汉式的模式,所以既拥有饿汉式的线程安全,又支持懒汉式的资源不浪费,不存在线程阻塞的情况,比双重检验锁更加的高效。


单例模式创建方式如何选择

  • 如果不需要延迟加载单例,可以使用枚举或者饿汉式,相对来说枚举性好于饿汉式。
  • 如果需要延迟加载,可以使用静态内部类或者懒汉式,相对来说静态内部类好于懒汉式。

单例模式如何破坏

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("初始化已执行过");
}
}
}

序列化:将存放在内存中的对象信息序列操作后变成可以存放在硬盘的数据。
反序列化:将硬盘上的数据解析后放入到内存中。

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);}

最强单例模式--枚举

public enum  SingleV6 {
TT;

SingleV6(){
System.out.println("我是无参构造函数被执行到了");
}

public void add(){
System.out.println("添加操作被启动");
}
}
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、使用静态代码快方式,当静态代码快执行的时候初始化该对象

  • 无参方式破解
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)

举报

相关推荐

0 条评论