曾经面试的时候,在说到单例模式的时候,我还是含含糊糊的,我觉得自己懂,但是又在某个结点被卡死了,所以决定写篇文章来说说单例模式
什么是单例模式?
单例模式: 一个类有且仅有一个实例,并且自行实例化向整个系统提供
那我们为什么需要单例模式呢?
为了节约JVM的资源,每创建一个对象,都需要在堆中分配内存,内存是有限的,更何况在某些情况下我们并不是都需要那么多的对象,比如在一个Controller的某个方法中多次用SimpleDateFormat类格式化日期,你就不需要多次的创建SimpleDateFormat类的对象,SimpleDateFormat的applyPattern()方法就可以设置格式化的形式。
单例模式之饿汉式
public class SingleTon{
private static SingleTon singleton = new SingleTon();
private SingleTon(){
}
public static SingleTon getInstance(){
return singleton;
}}
为什么称这种为饿汉式呢? 因为是一上来就创建对象,而不是等到需要了再创建对象。那么这种形式的单例模式是如何保证是单例的呢? 这是因为静态变量的特性。静态变量是什么时候被初始化的呢? 这就需要谈到类的生命周期了,什么是类的生命周期呢? 当你在使用一个类的静态方法或者静态变量、创建对象时,相应类的字节码就会被加载进内存中。
类的生命周期
类的加载
查找并加载字节码
连接
确定类与类之间的关系:
验证
正确性校验
准备
static静态变量分配内存,赋初始化默认值
在准备阶段,会把num=0,之后再将0修改为10
在准备阶段,JVM中只有类,没有对象
解析
把类中的符号引用,转为直接引用
在解析阶段,JVM就可以将全类名映射成实际的内存地址,就用内存地址代替全类名
初始化
给静态变量赋予正确的值。
显式赋值
使用: 对象的初始化时、对象的垃圾回收,对象的销毁
卸载
JVM结束类生命周期的时机:
- 正常结束
- 异常结束/错误
- System.exit();
- 操作系统异常
其中,加载、验证、准备、初始化和卸载这五个阶段的顺序是确定的,类的加载过程必须按照这种顺序按部就班的"开始"(仅仅指的是开始,而非执行或者结束,因为这些阶段通常是互相交叉着混合进行。(这里是引自深入理解Java虚拟机(第2版),这里还是有点不大理解为什么要互相交叉着混合进行的)。
我们注意到在类的初始化阶段,对类的静态变量进行显式赋值。
什么时候能够触发类的初始化呢?
对于初始化阶段虚拟机是严格规定了如下几种情况,如果类未初始化会对类进行初始化。(言下之意就是说类只会初始化一次)
创建类的实例对应字节码中的new指令
访问类的静态变量(除常量【被final修饰的静态变量】,常量在编译期被确定),对应的字节码指令为 getstatic
访问类的静态方法invokestatic
反射
当初始化一个类时,发现其父类还未初始化,则先触发父类