文章目录
- 一、什么是单例模式
- 二、饿汉式单例模式
-
- [2.1 饿汉式(静态常量)](#2.1 饿汉式(静态常量))
- [2.2 饿汉式(静态代码块)](#2.2 饿汉式(静态代码块))
- 三、懒汉式
-
- [3.1 懒汉式(线程不安全)](#3.1 懒汉式(线程不安全))
- [3.2 懒汉式(同步方法)](#3.2 懒汉式(同步方法))
- 四、静态内部类写法
- 五、枚举单例
一、什么是单例模式
正常类:你 new 一次,我 new 一次,每人一个新对象。都会在堆中创建一份空间。
java
User u1 = new User();
User u2 = new User();
//== 比较的是:对象的内存地址
// 判断是不是同一个对象
if(u1 == u2){
System.out.println("u1 和 u2 是同一个对象");
}else{
System.out.println("u1 和 u2 是两个完全不同的对象");
}
// 输出:u1 和 u2 是两个完全不同的对象
单例模式:不管程序里写多少次获取对象,永远只能有唯一一个对象,全程序共用这一个。
单例模式定义:
一个类只能有一个实例对象,并且提供一个全局访问入口,整个应用全程共用这一个对象。
核心特点:
- 构造方法私有,外部不能 new
- 类内部自己创建唯一实例
- 对外提供静态方法获取这个唯一实例
实现单例模式核心三步
- 私有化构造器:private 类名(){} 禁止外部 new
- 私有静态实例:private static 类名 instance;
- 公共静态获取方法:public static 类名 getInstance(){ return 类}
二、饿汉式单例模式
2.1 饿汉式(静态常量)
java
public class Singleton {
//1.类加载时直接创建实例
private static final Singleton instance = new Singleton();
//2.私有构造,防止外部new
private Singleton() {
}
//3.对外获取实例
public static Singleton getInstance() {
return instance; //返回唯一的实例对象
}
}
通俗理解
饿汉:很饿,一上来就先把对象造好等着
- 程序启动、类一加载,立马创建唯一对象
- 以后别人来拿,直接返回已经造好的
优点:写法简单、多线程绝对安全,不会造出多个对象、避免多线程问题
缺点:类加载就初始化,不用也会占用内存,如果这个对象很大、又一辈子不用,浪费资源
适用:单例类占用内存小、一定会用到
2.2 饿汉式(静态代码块)
java
public class Singleton {
private static Singleton instance;
static {
instance = new Singleton();
}
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
三、懒汉式
3.1 懒汉式(线程不安全)
java
public class Singleton {
private static Singleton instance;
private Singleton(){}
public static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
通俗理解
懒汉:很懒,你不来找我,我就不创建对象
- 程序启动不创建
- 第一次调用 getInstance() 才创建
- 优点:懒加载,用的时候才创建,省内存
- 缺点:多线程下不安全,可能创建多个对象。两个线程同时走到 if(instance == null),都判断为空,各自 new 了一个对象,单例被破坏。
只能用在单线程环境,多线程直接废弃。
3.2 懒汉式(同步方法)
java
public class Singleton {
private static Singleton instance;
private Singleton(){}
// 加 synchronized 锁
public static synchronized Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
优点:线程安全、懒加载
缺点:以后每次拿对象都要排队等锁,并发量大的时候性能很差,没必要。
四、静态内部类写法
java
public class Singleton {
// 私有构造
private Singleton(){}
// 静态内部类
private static class Inner{
private static final Singleton INSTANCE = new Singleton();
}
// 对外获取
public static Singleton getInstance(){
return Inner.INSTANCE;
}
}
原理:
- 外部类加载不会初始化内部类
- 调用 getInstance() 才加载内部类,才创建对象,实现懒加载
- JVM 加载静态内部类天然线程安全,不用自己加锁
五、枚举单例
java
public enum SingletonEnum {
// 唯一实例
INSTANCE;
// 可以写业务方法
public void test(){
System.out.println("单例方法");
}
}
使用:
java
SingletonEnum.INSTANCE.test();
- JVM 保证绝对线程安全
- 反射拿不到私有构造,无法破坏单例
- 序列化、反序列化也不会产生新对象
✅ 面试问:最好的单例写法?答:枚举