单例模式
核心作用:
- 保证一个类只有一个实例,并且提供一个访问该实例的全局访问点
常见场景:
- Windows的任务管理器
- Windows的回收站
- 项目中,读取配置文件的类,一般也只有一个对象,没必要每次都去new对象读取
- 网站的计数器一般也会采用单例模式,可以保证同步
- 数据库连接池的设计一般也是单例模式
- 在Servlet编程中,每个Servlet也是单例的
- 在Spring中,每个Bean默认就是单例的
- ……
优点:
- 由于单例模式只生成一个实例,减少了系统性能开销
- 单例模式可以在系统设置全局的访问点,优化共享资源访问
- 单例模式可以说只要是一个合格的开发都会写,但是如果要讲究,小小的单例模式可以牵扯到很多东西
常见的五种单例模式实现方式:
- 饿汉式(线程安全,调用效率高,不能延时加载)
- 懒汉式(线程安全,调用效率不高,可以延时加载)
- DCL懒汉式(由于JVM底层内部模型原因,偶尔会出现问题,不建议使用)
- 饿汉式改进:静态内部类式(线程安全,调用效率高,可以延时加载)
- 枚举单例(线程安全,调用效率高,不能延时加载)
// 饿汉式单例
public class SingletonDemo01 {
// 1. 私有化构造器
private SingletonDemo01() {
}
// 2. 类初始化的时候,立即加载该对象
private static SingletonDemo01 instance = new SingletonDemo01();
// 3. 提供获取该对象的方法,没有synchronized,效率高
public static SingletonDemo01 getInstance() {
return instance;
}
}
class SingleDemo01Test{
public static void main(String[] args) {
SingletonDemo01 instance = SingletonDemo01.getInstance();
SingletonDemo01 instance2 = SingletonDemo01.getInstance();
System.out.println(instance == instance2); // true
}
}
// 懒汉式单例
public class SingletonDemo02 {
// 1. 私有化构造器
private SingletonDemo02() {
}
// 2. 类初始化的时候,不立即加载该对象
private static SingletonDemo02 instance;
// 3. 提供获取该对象的方法,有synchronized,效率较低
public static synchronized SingletonDemo02 getInstance() {
if (instance == null) {
instance = new SingletonDemo02();
}
return instance;
}
}
class SingleDemo02Test{
public static void main(String[] args) {
SingletonDemo02 instance = SingletonDemo02.getInstance();
SingletonDemo02 instance2 = SingletonDemo02.getInstance();
System.out.println(instance == instance2); // true
}
}
// DCL懒汉式
public class SingletonDemo03 {
// 1. 私有化构造器
private SingletonDemo03() {
}
// 2. 类初始化的时候,不立即加载该对象
private volatile static SingletonDemo03 instance;
// 3. 提供获取该对象的方法,有synchronized,效率较低
public static SingletonDemo03 getInstance() {
if (instance == null) {
synchronized (SingletonDemo03.class) {
if (instance == null) {
instance = new SingletonDemo03();
}
}
}
return instance;
}
// 1.分配内存
// 2.执行构造方法
// 3.指向地址
}
class SingleDemo03Test{
public static void main(String[] args) {
SingletonDemo03 instance = SingletonDemo03.getInstance();
SingletonDemo03 instance2 = SingletonDemo03.getInstance();
System.out.println(instance == instance2); // true
}
}
// 静态内部类实现
public class SingletonDemo04 {
private SingletonDemo04(){}
private static class InnerClass {
private static final SingletonDemo04 instance = new SingletonDemo04();
}
public static SingletonDemo04 getInstance() {
return InnerClass.instance;
}
}
class SingleDemo04Test{
public static void main(String[] args) throws Exception {
SingletonDemo04 instance = SingletonDemo04.getInstance();
SingletonDemo04 instance2 = SingletonDemo04.getInstance();
System.out.println(instance == instance2); // true
// 反射机制:可以破坏以上的单例
Constructor<SingletonDemo04> declaredConstructor = SingletonDemo04.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
SingletonDemo04 instance3 = declaredConstructor.newInstance();
System.out.println(instance == instance3); // false
System.out.println(instance.hashCode()); // 1735600054
System.out.println(instance3.hashCode()); // 21685669
}
}
在构造时判断,防止反射的破坏
// DCL懒汉式
public class SingletonDemo03 {
// 1. 私有化构造器
private SingletonDemo03() {
synchronized (SingletonDemo03.class) {
if (instance != null) {
throw new RuntimeException("不要戏图用缓射来破fuai我们的单例");
}
}
}
// 2. 类初始化的时候,不立即加载该对象
private volatile static SingletonDemo03 instance;
// 3. 提供获取该对象的方法,有synchronized,效率较低
public static SingletonDemo03 getInstance() {
if (instance == null) {
synchronized (SingletonDemo03.class) {
if (instance == null) {
instance = new SingletonDemo03();
}
}
}
return instance;
}
// 1.分配内存
// 2.执行构造方法
// 3.指向地址
}
class SingleDemo03Test{
public static void main(String[] args) throws Exception {
SingletonDemo03 instance = SingletonDemo03.getInstance();
SingletonDemo03 instance2 = SingletonDemo03.getInstance();
System.out.println(instance == instance2); // true
Constructor<SingletonDemo03> declaredConstructor = SingletonDemo03.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
SingletonDemo03 instance3 = declaredConstructor.newInstance();
System.out.println(instance == instance3); // false
System.out.println(instance.hashCode()); // 1735600054
System.out.println(instance3.hashCode()); // 21685669
}
}
直接利用反射创建对象,单例模式依旧被破坏
class SingleDemo03Test{
public static void main(String[] args) throws Exception {
Constructor<SingletonDemo03> declaredConstructor = SingletonDemo03.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
SingletonDemo03 instance3 = declaredConstructor.newInstance();
SingletonDemo03 instance4 = declaredConstructor.newInstance();
System.out.println(instance3 == instance4); // false
System.out.println(instance3.hashCode()); // 1735600054
System.out.println(instance4.hashCode()); // 21685669
}
}
在私有化构造器中添加标志位进行判断,可以解决问题
// DCL懒汉式
public class SingletonDemo03 {
private static boolean flag = false;
// 1. 私有化构造器
private SingletonDemo03() {
synchronized (SingletonDemo03.class) {
if (flag == false) {
flag = true;
} else {
throw new RuntimeException("不要戏图用缓射来破fuai我们的单例");
}
}
}
// 2. 类初始化的时候,不立即加载该对象
private volatile static SingletonDemo03 instance;
// 3. 提供获取该对象的方法,有synchronized,效率较低
public static SingletonDemo03 getInstance() {
if (instance == null) {
synchronized (SingletonDemo03.class) {
if (instance == null) {
instance = new SingletonDemo03();
}
}
}
return instance;
}
}
class SingleDemo03Test{
public static void main(String[] args) throws Exception {
Constructor<SingletonDemo03> declaredConstructor = SingletonDemo03.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
SingletonDemo03 instance3 = declaredConstructor.newInstance();
SingletonDemo03 instance4 = declaredConstructor.newInstance();
System.out.println(instance3 == instance4);
System.out.println(instance3.hashCode());
System.out.println(instance4.hashCode());
}
}
但是仍然存在问题,反射可以通过将flag置为false再进行创建。
由于通过反射获得构造函数,在实例化调用newInstance()方法时,会进行判断,当类型是枚举时,将抛出异常,“不能反射地创建一个枚举对象”。
@CallerSensitive
public T newInstance(Object ... initargs)
throws InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, null, modifiers);
}
}
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
throw new IllegalArgumentException("Cannot reflectively create enum objects");
ConstructorAccessor ca = constructorAccessor; // read volatile
if (ca == null) {
ca = acquireConstructorAccessor();
}
@SuppressWarnings("unchecked")
T inst = (T) ca.newInstance(initargs);
return inst;
}
因此,枚举单例是安全的,可以天然地屏蔽单例被反射带来的破坏。
// 枚举单例
public enum SingletonDemo05 {
INSTANCE;
public SingletonDemo05 getInstance() {
return INSTANCE;
}
}
class SingletonDemo05Test {
public static void main(String[] args) {
SingletonDemo05 instance = SingletonDemo05.INSTANCE;
SingletonDemo05 instance2 = SingletonDemo05.INSTANCE;
System.out.println(instance == instance2);
}
}