什么是单例模式?
保证一个类只有一个实例,并且提供一个访问该实例的全局访问点
如何保证一个类只有一个实例?
1)将构造方法设置为私有的,无法实例化
2)将方法设置为static
如何提供一个全局访问点?
设置一个get方法获取实例
常见的五种单例模式实现方式:
1.饿汉式(线程安全,调用效率高,不能延迟加载)
缺点:类一初始化,内存空间就已开辟,当没调用该类时,造成空间的浪费
/**
* 饿汉式单例
*/
public class SingletonDemo01 {
//私有构造方法
private SingletonDemo01() {
}
//设置为static,类初始化立即加载该对象
private static SingletonDemo01 instance = new SingletonDemo01();
//获取实例,没有synchronized,效率高
public static SingletonDemo01 getInstance() {
return instance;
}
}
class SingleTonTest01{
public static void main(String[] args) {
SingletonDemo01 instance01 = SingletonDemo01.getInstance();
SingletonDemo01 instance02 = SingletonDemo01.getInstance();
System.out.println(instance01 == instance02); //返回true,代表同一个对象
}
}
2.懒汉式(线程安全,调用效率不高,可以延迟加载)
为了解决第一种单例模式的的缺点,将上面的代码加以改进形成了懒汉式模型
但是这种实现方式还是有缺点的,获取该对象的方法增加了synchronized方法,所以效率较低,为了解决这一问题,继续改进
/**
* 懒汉式单例
*/
public class SingletonDemo02 {
//私有构造方法
private SingletonDemo02() {
}
//定义一个对象,类初始化时,不立即加载该对象
private static SingletonDemo02 instance;
//获取实例,增加synchronized,效率较低!
public static synchronized SingletonDemo02 getInstance() {
if (instance == null) {
instance = new SingletonDemo02();
}
return instance;
}
}
class SingleTonTest02 {
public static void main(String[] args) {
SingletonDemo02 instance01 = SingletonDemo02.getInstance();
SingletonDemo02 instance02 = SingletonDemo02.getInstance();
System.out.println(instance01 == instance02); //返回true,代表同一个对象
}
}
3.DCL懒汉式(双重检测)
优点:效率提高
缺点:非原子性,极端情况下会存在破坏对象创建的问题
/**
* DCL懒汉式单例(双重检测)
*/
public class SingletonDemo03 {
//私有构造方法
private SingletonDemo03() {
}
//定义一个对象,类初始化时,不立即加载该对象
private static volatile SingletonDemo03 instance;
//获取对象
public static SingletonDemo03 getInstance() {
if (instance == null) {
//第一次实例化时 进行synchronized,下次再使用时直接获取已创建好的对象
synchronized (SingletonDemo03.class) {
if (instance == null) {
instance = new SingletonDemo03();
}
}
}
return instance;
}
}
class SingleTonTest03 {
public static void main(String[] args) {
SingletonDemo03 instance01 = SingletonDemo03.getInstance();
SingletonDemo03 instance02 = SingletonDemo03.getInstance();
System.out.println(instance01 == instance02); //返回true,代表同一个对象
}
}
4.饿汉式改进(静态内部类实现)
为了改进上一种实现的缺点,将实例化对象添加到静态内部类中
缺点:如果利用反射机制可以破坏私有构造方法,将私有权限忽略,导致可以直接实例化。
/**
* 静态内部类实现
*/
public class SingletonDemo04 {
//私有构造方法
private SingletonDemo04() {
}
//静态内部类实例化对象
private static class InnerClass {
private static final SingletonDemo04 instance = new SingletonDemo04();
}
//get方法获取内部类中的对象
public static SingletonDemo04 getInstance() {
return InnerClass.instance;
}
}
class SingleTonTest04 {
public static void main(String[] args) {
SingletonDemo04 instance01 = SingletonDemo04.getInstance();
SingletonDemo04 instance02 = SingletonDemo04.getInstance();
System.out.println(instance01 == instance02); //返回true,代表同一个对象
}
}
5.枚举单例
为了解决反射机制破坏私有构造方法这个问题,继续改进,使用枚举单例
优点:可以有效规避反射方式破坏类的结构
/**
* 枚举类型单例模式
*/
public enum SingletonDemo05 {
INSTANCE;
public SingletonDemo05 getInstance() {
return INSTANCE;
}
}
class SingleTonTest05 {
public static void main(String[] args) {
SingletonDemo05 instance01 = SingletonDemo05.INSTANCE;
SingletonDemo05 instance02 = SingletonDemo05.INSTANCE;
System.out.println(instance01 == instance02); //返回true,代表同一个对象
}
}