前言
单例模式,就是在整个应用程序中都只有一个实例,并且提供一个类方法来供全局调用,在编译期间会一直存储在内存中,直到程序退出,系统自动释放此内存。
下面这个图是不是很熟悉,这个是任务管理器。
你可以尝试一下,在一个电脑上,打开一个任务管理器之后,再打开一次任务管理器,这时候桌面上不会产生新的任务管理器!这是单例模式,也就是说,整个windows系统只存在一个任务管理器的实例!
电脑上的回收站也是同样的道理。
一、单例模式优点
一个类只能产生一个实例,这样做的好处有:
1.减少内存开支
当你想关闭QQ进程的时候,可以使用任务管理器;当你想关闭浏览器的时候,也可以使用任务管理器。当然,两次打开的任务管理器其实是同一个任务管理器。这样的话,电脑的CPU就会减少一部分开销!
2.避免状态切换时的不正确
假如打开了两个任务管理器A和B,关闭A里面的QQ的话,那么B中的QQ有那么一刻没有及时更新,微软肯定不能接受这样的效果!
二、单例模式的实现与选择
1.单例模式的实现
网上有很多,这里推荐实现方式:
2.怎么选择单例模式
(1)如果单例对象占用资源少,不需要延迟加载,那么枚举式优于饿汉式
(2)如果单例对象占用资源大,需要延时加载,那么静态内部类式优于懒汉式
三、单例模式破解
菜鸟教程给的只是案例,如果没有对代码优化的话,可以通过以下两种方式破解单例!
1.1通过反射破解
/** * 测试反射破解单例模式 */ public class Reflect { public static void main(String[] args) throws Exception { // 通过反射直接调用私有构造器破解单例模式 Class<RuleManager> clz = (Class<Singleton>) Class.forName("com.Singleton"); Constructor<Singleton> constructor = clz.getDeclaredConstructor(null); constructor.setAccessible(true); Singleton s3 = constructor.newInstance(); Singleton s4 = constructor.newInstance(); System.out.println(s3); System.out.println(s4); } }
打印出来的s3和s4结果不一样,说明已经破解了!
1.2解决方法
获取对象的时候手动抛出异常
if (instance!=null) { throw new RuntimeException(); }
2.1 通过序列化与反序列化破解
//获取单例 Singleton s1 = Singleton.getInstance(); //将序列化到本地文件 FileOutputStream fos = new FileOutputStream("d:/test.txt"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(s1); oos.close(); fos.close(); //反序列化 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/test.txt")); Singleton s2 = (Singleton) ois.readObject(); Singleton s3 = (Singleton) ois.readObject(); System.out.println(s2); System.out.println(s3);
2.2 解决方法
在生成单例的类里加入readResolve方法
private Object readResolve() throws ObjectStreamException { return instance; }
四、单例模式的应用
windows中任务管理器,回收站
常见的工具类
数据库连接类
项目中用于读取配置文件的类
Spring中,每个Bean
默认都是单例的,这样便于Spring容器进行管理
网站计数器
Servlet中Application