Java面试之单例模式的六种实现方式

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、为什么要用单例模式
  • 二、单例模式的六种实现
    • [2.1 饿汉式](#2.1 饿汉式)
      • [2.1.1 饿汉式代码实现](#2.1.1 饿汉式代码实现)
      • [2.1.2 饿汉式代码实现要点解析](#2.1.2 饿汉式代码实现要点解析)
      • [2.1.3 饿汉式代码实现优点](#2.1.3 饿汉式代码实现优点)
      • [2.1.4 饿汉式代码实现缺点](#2.1.4 饿汉式代码实现缺点)
    • [2.2 懒汉式1.0](#2.2 懒汉式1.0)
      • [2.2.1 懒汉式1.0代码实现](#2.2.1 懒汉式1.0代码实现)
      • [2.2.2 懒汉式1.0代码实现要点解析](#2.2.2 懒汉式1.0代码实现要点解析)
      • [2.2.3 懒汉式1.0代码实现优点](#2.2.3 懒汉式1.0代码实现优点)
      • [2.2.4 懒汉式1.0代码实现缺点](#2.2.4 懒汉式1.0代码实现缺点)
    • [2.3 懒汉式2.0](#2.3 懒汉式2.0)
      • [2.3.1 懒汉式2.0代码实现](#2.3.1 懒汉式2.0代码实现)
      • [2.3.2 懒汉式2.0代码实现要点解析](#2.3.2 懒汉式2.0代码实现要点解析)
      • [2.3.3 懒汉式2.0代码实现优点](#2.3.3 懒汉式2.0代码实现优点)
      • [2.3.4 懒汉式2.0代码实现缺点](#2.3.4 懒汉式2.0代码实现缺点)
    • [2.4 懒汉式3.0------双重检查Double Check](#2.4 懒汉式3.0——双重检查Double Check)
      • [2.4.1 双重检查代码实现](#2.4.1 双重检查代码实现)
      • [2.4.2 双重检查代码实现要点解析](#2.4.2 双重检查代码实现要点解析)
      • [2.4.3 双重检查代码实现优点](#2.4.3 双重检查代码实现优点)
      • [2.4.4 双重检查代码实现缺点](#2.4.4 双重检查代码实现缺点)
    • [2.5 静态内部类(推荐)](#2.5 静态内部类(推荐))
      • [2.5.1 静态内部类代码实现](#2.5.1 静态内部类代码实现)
      • [2.5.2 静态内部类代码实现要点解析](#2.5.2 静态内部类代码实现要点解析)
      • [2.5.3 静态内部类代码实现优点](#2.5.3 静态内部类代码实现优点)
      • [2.5.4 静态内部类代码实现缺点](#2.5.4 静态内部类代码实现缺点)
      • [2.5.5 静态内部类与饿汉式对比](#2.5.5 静态内部类与饿汉式对比)
    • [2.6 枚举(推荐)](#2.6 枚举(推荐))
      • [2.6.1 枚举代码实现](#2.6.1 枚举代码实现)
      • [2.6.2 枚举代码实现要点解析](#2.6.2 枚举代码实现要点解析)
      • [2.6.3 枚举实现优点](#2.6.3 枚举实现优点)

前言

由于设计模式在面向对象中起着举足轻重的作用,在面试中很多公司都喜欢问一下有关设计模式的问题。在常用的设计模式中,Singleton单例模式是唯一一个能用短短几十行代码完整实现的模式,因此,写一个Singletion类型是一个很常见的面试题。


一、为什么要用单例模式

节省内存,单例对象可避免频繁的创建与销毁,带来性能的提升。

在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。

二、单例模式的六种实现

2.1 饿汉式

2.1.1 饿汉式代码实现

java 复制代码
public class Singleton{
	private Singleton(){}
	private static Singleton instance = new Singleton();
	public static Singleton getInstance(){
		return instance;
	}
}

2.1.2 饿汉式代码实现要点解析

1、私有构造

2、创建私有静态实例

3、提供公有静态get方法返回该静态实例

2.1.3 饿汉式代码实现优点

1、实现简单

2、无线程安全问题

2.1.4 饿汉式代码实现缺点

1、初始化耗时,导致系统启动缓慢

2、在类装载的时候就完成了实例化,没有做到Lazy Loading(延迟加载)、按需加载

3、极端情况下,还是可以拿到多实例,如通过反射。

2.2 懒汉式1.0

2.2.1 懒汉式1.0代码实现

java 复制代码
public class Singleton{
	private Singleton(){}
	private static Singleton instance = null;
	public static Singleton getInstance(){
		if(instance == null){
			instance = new Singleton();
		}
		return instance;
	}
}

2.2.2 懒汉式1.0代码实现要点解析

1、私有构造

2、初始化一个为null的静态实例instance

3、提供公有静态get方法,判断instance为null时,则创建该对象并赋给instance,然后返回该静态实例

2.2.3 懒汉式1.0代码实现优点

1、延迟加载,在使用时才会开辟空间

2.2.4 懒汉式1.0代码实现缺点

1、线程不安全,只能在单线程情况下使用,在多线程情况下,一个线程进if(instance == null)判断中,还未来得及创建对象,另外一个线程也来到判断语句,这时也会通过判断,这样就会创建多个实例。

总的来说在多线程环境中不能使用这种方式。

2.3 懒汉式2.0

2.3.1 懒汉式2.0代码实现

java 复制代码
public class Singleton{
	private Singleton(){}
	private static Singleton instance = null;
	public static synchronized Singleton getInstance(){
		if(instance == null){
			instance = new Singleton();
		}
		return instance;
	}
}

2.3.2 懒汉式2.0代码实现要点解析

1、私有构造

2、创建私有静态实例

3、提供加synchronized的公有静态get方法返回该静态实例

2.3.3 懒汉式2.0代码实现优点

1、用的时候开辟内存

2、解决线程安全问题

2.3.4 懒汉式2.0代码实现缺点

1、效率太低。下一个线程想要获取对象,必须等待上一个线程释放锁之后才能获取。每次调用都需要获取锁与释放锁,在大量并发请求时将产生性能问题。

2、并发度低。由于加入了synchronized,并行度为1,导致并发度低。

3、极端情况下,还是可以拿到多实例,如通过反射。

2.4 懒汉式3.0------双重检查Double Check

2.4.1 双重检查代码实现

java 复制代码
public class Singleton{
	private Singleton(){}
	private static Singleton instance = null;
	public static synchronized Singleton getInstance(){
		if(instance == null){//这个检查是提高效率的
			synchronized (Singleton.class){
				if(instance == null){//这个检查是保证线程安全的
					instance = new Singleton();
				}
			}
		}
		return instance;
	}
}

2.4.2 双重检查代码实现要点解析

1、私有构造

2、创建私有静态实例

3、提供加synchronized的公有静态get方法返回该静态实例,该get方法中首先判断instance是否为null,然后加个synchronized块,里面再对instance进行判断,为空才创建对象

2.4.3 双重检查代码实现优点

1、实现简单

2、立即加载,无线程安全问题

2.4.4 双重检查代码实现缺点

极端情况下,还是可以拿到多实例,如通过反射。

2.5 静态内部类(推荐)

2.5.1 静态内部类代码实现

java 复制代码
public class Singleton{
	private Singleton(){}
	private static Singleton SingletonInstance {
		private static final Singleton INSTANCE = new Singleton();
	}
	
	public static Singleton getInstance(){
		return SingletonInstance.INSTANCE;
	}
}

2.5.2 静态内部类代码实现要点解析

1、私有构造

2、创建私有静态内部类,该静态内部类中含有static 和final修饰的实例对象

3、提供公有静态get方法返回该静态实例

2.5.3 静态内部类代码实现优点

1、延迟加载,效率高

2、无线程安全问题。类的静态属性只会在第一次加载类时初始化,这样JVM帮助我们保证了线程安全性,因为在类初始化时别的线程无法进入。

2.5.4 静态内部类代码实现缺点

静态内部类虽然保证了单例在多线程并发下的线程安全性,但是在遇到序列化对象时,默认的方式运行得到的结果就是多例的。

极端情况下,还是可以拿到多实例,如通过反射。

2.5.5 静态内部类与饿汉式对比

此方法可以看作饿汉式的改进版,两者都采用类装载机制来保证初始化实例时只有一个线程。

不同之处在于饿汉式只要Singleton类被装载就会实例化,没有延迟加载;

而静态内部类方式再Singleton类被装载时并不会立即实例化,而是在需要实例化时才会调用getInstance方法装载SingletonInstance 类,从而完成Singleton实例化。

总的来说推荐使用。

2.6 枚举(推荐)

2.6.1 枚举代码实现

java 复制代码
public class Singleton{
	//内部类使用枚举类
	private enum SingletonEnum {
		INSTANCE;
		private Singleton singleton;
		//在枚举类的构造器里初始化singleton
		SingletonEnum() {
			singleton = new Singleton ();
		}
		private Singleton getSingleton() {
            return singleton;
        }
	}
	//对外提供获取单例的方法
	public static Singleton getInstance(){
		return SingletonEnum.INSTANCE.getSingleton();
	}
}

2.6.2 枚举代码实现要点解析

1、内部类使用枚举类

2、在枚举类的构造器里初始化singleton,提供私有方法供本类获取singleton

3、对外提供获取单例的方法

2.6.3 枚举实现优点

1、支持延迟加载,可做到按需加载

2、支持高并发

3、防止反射和反序列化攻击

总的来说推荐使用。


相关推荐
Hello-Brand10 分钟前
Java核心知识体系10-线程管理
java·高并发·多线程·并发·多线程模型·线程管理
乐悠小码16 分钟前
数据结构------队列(Java语言描述)
java·开发语言·数据结构·链表·队列
史努比.18 分钟前
Pod控制器
java·开发语言
2的n次方_20 分钟前
二维费用背包问题
java·算法·动态规划
皮皮林55121 分钟前
警惕!List.of() vs Arrays.asList():这些隐藏差异可能让你的代码崩溃!
java
莳光.21 分钟前
122、java的LambdaQueryWapper的条件拼接实现数据sql中and (column1 =1 or column1 is null)
java·mybatis
程序猿麦小七26 分钟前
基于springboot的景区网页设计与实现
java·spring boot·后端·旅游·景区
weisian15132 分钟前
认证鉴权框架SpringSecurity-2--重点组件和过滤器链篇
java·安全
蓝田~34 分钟前
SpringBoot-自定义注解,拦截器
java·spring boot·后端
.生产的驴36 分钟前
SpringCloud Gateway网关路由配置 接口统一 登录验证 权限校验 路由属性
java·spring boot·后端·spring·spring cloud·gateway·rabbitmq