1.单例模式

本文深入解析单例模式的定义、特点及其六种实现方式:懒汉模式、饿汉模式、双检锁模式、内部类模式、枚举实现。对比各模式的优缺点,如线程安全性、资源消耗等,推荐使用内部类模式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

单例模式

1.定义

  • 所谓单例,就是整个程序有且仅有一个实例。该类负责创建自己的对象,同时要确保只有一个对象被创建。

  • 好处:就是一个类你只能创建一个实例对象可以节约内存开销,一般常用于在工具类的实现(之前的静态方法也可以用于工具类的实现)。

2.特点

  1. 构造器私有
  2. 持有自己类的属性
  3. 对外提供获取实例的静态方法

一般单例模式分为两大类:懒汉模式,饿汉模式.详细分其实有懒汉,饿汉,双检锁,内部类,枚举。

3.懒汉模式

/**
 * 	懒汉模式,延迟初始化,在调用方法获取的时候才会实例对象
 * 	线程不安全的,严格意义上不是单例模式
 * @author Administrator
 *
 */
public class Singleton {
	//持有自己类型的属性
	private static Singleton instance;
	
	//构造方法私有化
	private Singleton() {}
    
	//对外提供获取实例的静态方法
	public static Singleton getInstance() {
		if(instance==null) {
			instance=new Singleton();
		}
		return instance;
	}	
}

测试

public class Test {
	public static void main(String[] args) {
		Singleton instance1 = Singleton.getInstance();
		Singleton instance2 = Singleton.getInstance();
		System.out.println(instance1==instance2);
	}
}

结果

true

4.饿汉模式

线程安全,比较常用,但是容易产生垃圾,因为饿汉模式一开始加载类的时候就初始化了实例。

/**
 * 	饿汉模式
 * @author Administrator
 *
 */
public class Singleton {
	//持有自己类型的属性
	//由于static修饰,只在加载类的时候执行一次
	private static Singleton instance = new Singleton();
	
	//构造方法私有化
	private Singleton() {}

	//对外提供获取实例的静态方法
	public static Singleton getInstance() {
		return instance;
	}
}

测试

public class Test {
	public static void main(String[] args) {
		Singleton instance1 = Singleton.getInstance();
		Singleton instance2 = Singleton.getInstance();
		System.out.println(instance1==instance2);
	}
}

结果

true

懒汉模式和饿汉模式的区别

  1. 懒汉是非线程安全,饿汉是线程安全
  2. 懒汉模式是一开始不实例化对象,调用方法时才实例化对象,饿汉模式一开始实例化对象,容易造成资源浪费.

综合两者的优缺点,可以使用双检锁模式。

5.双检锁模式

双检锁也称双重校验锁,综合了懒汉模式和饿汉模式的优缺点

/**
 * 	双检锁模式结合懒汉模式和饿汉模式的优点:延迟加载并且线程安全
 * @author Administrator
 *
 */
public class Singleton {
	
	//持有自己类型的属性
	private volatile static Singleton instance;
	
	//构造方法私有化
	private Singleton() {}
	
	//对外提供获取实例的静态方法
	public static Singleton getInstance() {
		if(instance==null) {
			synchronized(Singleton.class) {
				if(instance==null) {
					instance=new Singleton();
				}
			}	
		}
		return instance;
	}
}

小结:

  • 双检锁模式,进行两次判断,第一次判断是为了避免不要的实例,第二次是为了进行线程同步,避免多线程问题,由于newsingleton()创建instance对象的时候jvm中可能会重新排序,在多线程存在风险,使用volatile关键字可以当线程改变其值后通知其他线程已改变并且不会被java重新排序,解决该问题

6.内部类模式

/**
 * 	内部类模式实现单例模式
 * @author Administrator
 *
 */
public class Singleton {
	private Singleton() {}
	
	public static Singleton gatInstance() {
		return Inner.instance;
	}
	
	public static class Inner{
		//final修饰的常量,不能再被改变
		private static final Singleton instance= new Singleton();
	}
}

小结

  • 只有第一次调用gatInstance()方法时,虚拟机才加载Inner内部类并初始化instance,只有一个线程可以获取instance对象的初始化,其他线程无法再进行初始化,线程安全的,保证对象的唯一性。【推荐使用此方式实现单例模式】

7.枚举实现

/**
 * 	默认枚举的实例是线程安全的,并且在任何情下都是单例
 * 	枚举类默认隐藏了所有的构造方法
 * 
 * @author Administrator
 *
 */
public enum Singleton {
	INSTANCE;
	
	public static Singleton getInstance() {
		return Singleton.INSTANCE;
	}
}
<think>我们正在讨论单例模式如何防止外部直接创建对象。根据引用内容,单例模式的核心是确保一个类只有一个实例,并提供一个全局访问点。防止外部直接创建对象的关键在于控制构造函数的访问权限。 引用[1]提到将构造函数私有化(private),这样外部类就无法通过new操作符来实例化对象。 引用[3]和[4]也展示了同样的做法:私有化构造方法(private Singleton(){})。 因此,实现单例模式防止外部实例化的关键步骤是: 1. 将类的构造方法声明为private,这样外部类就无法直接调用new来创建对象。 2. 在类内部创建唯一的实例,并通过一个public static方法提供给外部使用。 另外,根据不同的实现方式(饿汉式、懒汉式、静态内部类等),实例的创建时机和线程安全性有所不同,但防止外部实例化的手段都是私有化构造方法。 下面我将分别展示几种单例模式的实现,并解释如何防止外部实例化。</think>### Java 单例模式防止外部实例化的实现方式 在 Java 中实现单例模式并防止外部直接创建对象,**核心是通过私有化构造函数并结合静态实例控制**。以下是三种主流实现方式及其防实例化原理: #### 1. 饿汉式(Eager Initialization)[^3][^4] ```java public class Singleton { // 私有静态实例(类加载时立即创建) private static final Singleton INSTANCE = new Singleton(); // 私有化构造函数(关键!) private Singleton() { // 防止通过反射创建实例 if (INSTANCE != null) { throw new IllegalStateException("单例已存在"); } } // 全局访问点 public static Singleton getInstance() { return INSTANCE; } } ``` **防实例化原理**: - 私有构造函数 (`private Singleton()`) 使外部无法通过 `new Singleton()` 创建对象 - `final` 修饰符确保实例唯一性 - 构造函数内校验防止反射攻击 #### 2. 静态内部类(Lazy Initialization - 推荐)[^1] ```java public class Singleton { // 私有构造函数 private Singleton() {} // 静态内部类持有实例 private static class Holder { private static final Singleton INSTANCE = new Singleton(); } public static Singleton getInstance() { return Holder.INSTANCE; // 首次调用时加载内部类 } } ``` **防实例化原理**: - 构造函数私有化阻断外部访问 - 实例由静态内部类持有,首次调用 `getInstance()` 时才加载(延迟加载) - JVM 保证类加载过程的线程安全 #### 3. 双重校验锁(Double-Checked Locking) ```java public class Singleton { private static volatile Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance == null) { // 第一次检查 synchronized (Singleton.class) { if (instance == null) { // 第二次检查 instance = new Singleton(); } } } return instance; } } ``` **防实例化原理**: - `volatile` 关键字禁止指令重排序 - 同步块确保多线程环境下的单例性 - 双重检查减少同步开销 --- ### 防止外部实例化的通用原则 1. **构造函数私有化** 通过 `private` 修饰构造函数,使外部代码无法调用 `new` 创建对象。 2. **禁止克隆** 重写 `clone()` 方法并抛出异常: ```java @Override protected Object clone() throws CloneNotSupportedException { throw new CloneNotSupportedException("禁止克隆单例对象"); } ``` 3. **防止序列化破坏** 实现 `readResolve()` 方法返回已有实例: ```java private Object readResolve() { return getInstance(); } ``` 4. **防御反射攻击** 在私有构造函数中添加实例存在性校验(如饿汉式示例)。 > **关键点**:所有单例模式的核心都是**私有化构造函数**,这是阻止外部实例化的基石。其他机制(如静态内部类、双重校验)主要解决线程安全和性能问题[^1][^2]。 --- ### 各实现方式对比 | 实现方式 | 线程安全 | 延迟加载 | 防反射 | 性能 | |-----------------|----------|----------|--------|------------| | 饿汉式 | ✅ | ❌ | ✅ | 最佳 | | 静态内部类 | ✅ | ✅ | ❌ | 优 | | 双重校验锁 | ✅ | ✅ | ❌ | 同步有开销 | > 实际开发中推荐**静态内部类**实现,兼顾线程安全、延迟加载和简洁性[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值