引言
在 Java 编程中,对象的创建和实例化是通过使用 new 关键字以及调用类中定义的构造函数来完成的。下面是一个简单的示例:
Clazz instance = new Clazz();
我们可以阅读如下代码片段:
在这个例子中,new Clazz() 调用了 Clazz 类的构造函数,创建了一个新的对象实例,并将这个新对象的引用赋值给变量 instance。
当我们需要创建一个单例(Singleton)模式的类时,我们的目标是确保整个应用程序中只有一个类的实例存在。为了实现这一点,我们需要遵循一些关键的步骤和约定:
- 私有化构造函数:所有构造函数都应该是私有的,这样可以防止外部通过 new 关键字创建类的实例。
- 持有单例对象的变量:类内部需要一个私有的静态变量来保存单例对象。这个变量通常是私有的、静态的,并且可以是最终的(final),以确保其值不会被改变。
- 单例对象的命名:这个变量通常被命名为 instance 或者 INSTANCE,以表明它代表了类的唯一实例。
- 提供全局访问点:需要提供一个公开的静态方法,允许其他对象访问这个单例实例。这个方法通常被称为 getInstance(),并且它负责控制类的实例化过程。
- 静态工厂方法:getInstance() 方法也被称为静态工厂方法,因为它不直接返回一个新创建的对象,而是通过某种逻辑来提供对单例对象的引用。
有了这些理解,让我们更深入地理解 singleton 。以下是为类创建单例对象的7种方法。
方法1:懒汉式(线程不安全)
这是最基本的实现方式,只在getInstance()方法第一次被调用时才创建实例。
private static Clazz instance;
public static Clazz getInstance() {
if (instance == null) {
instance = new Clazz();
}
return instance;
}
方法2:懒汉式(线程安全)
与第一种方法类似,但是通过使用 synchronized 关键字来确保线程安全。
private static volatile Clazz instance;
public static synchronized Clazz getInstance() {
if (instance == null) {
instance = new Clazz();
}
return instance;
}
方法3:饿汉式(线程安全)
在类加载时就创建单例对象,这种方式简单且线程安全。
private static final Clazz instance = new Clazz();
public static Clazz getInstance() {
return instance;
}
方法4:双重检查锁定(Double-Checked Locking)
这种方法结合了懒汉式和饿汉式的优点,同时使用了volatile关键字和synchronized块来保证线程安全。
private static volatile Clazz instance;
public static Clazz getInstance() {
if (instance == null) {
synchronized (Clazz.class) {
if (instance == null) {
instance = new Clazz();
}
}
}
return instance;
}
方法5:静态内部类
通过Java的类加载机制来实现单例,这种方式是线程安全的,并且防止了通过反射和序列化的方式破坏单例。
private static volatile Clazz instance;
public static Clazz getInstance() {
if (instance == null) {
synchronized (Clazz.class) {
if (instance == null) {
instance = new Clazz();
}
}
}
return instance;
}
方法6:枚举单例
使用Java枚举类型的特性来实现单例,这是最简单且最安全的方式。
public enum Clazz {
INSTANCE;
public void doSomething() {
// ...
}
}
方法7:利用 Spring 框架或 Guava 库
这种方法相对繁琐,但如果你原本就基于 Spring 框架在开发项目,又或者你的项目中之前就依赖引入了 Guava 库,那采用这种方法也很合适。
基于 Guava 库
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
public class SupplierSingleton {
private SupplierSingleton() {}
private static final Supplier<SupplierSingleton> singletonSupplier = Suppliers.memoize(()-> new SupplierSingleton());
public static SupplierSingleton getInstance() {
return singletonSupplier.get();
}
public static void main(String[] args) {
SupplierSingleton supplierSingleton = SupplierSingleton.getInstance();
}
}
基于 Spring 框架
这种方法在基于 Spring Boot 框架的企业开发中被大量使用。
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
class SingletonBean { }
@Configuration
public class SingletonBeanConfig {
@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
public SingletonBean singletonBean() {
return new SingletonBean();
}
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(SingletonBean.class);
SingletonBean singletonBean = applicationContext.getBean(SingletonBean.class);
}
}
Spring是一个非常流行的框架,上下文和依赖注入是Spring的核心。
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
interface ISingletonBean {}
class SingletonBean implements ISingletonBean { }
public class SingletonBeanConfig extends AbstractModule {
@Override
protected void configure() {
bind(ISingletonBean.class).to(SingletonBean.class);
}
public static void main(String[] args) {
Injector injector = Guice.createInjector(new SingletonBeanConfig());
SingletonBean singletonBean = injector.getInstance(SingletonBean.class);
}
}
结论
在软件开发过程中,我们经常会遇到各种业务问题和非功能性需求的挑战,例如性能优化、系统安全性、以及对CPU和内存资源的限制等。在这些约束条件下,设计模式应运而生,为我们提供了一套成熟的解决方案,帮助我们有效地解决问题和优化系统设计。
Singleton模式正是这样一种设计模式,它确保了一个类在应用程序中只有一个实例存在,并且提供了一个全局访问点。这种模式在需要全局或上下文范围内可访问的对象时显得尤为重要,例如在创建动态异构容器、管理上下文环境、实现对象池、或者设计具有战略意义的功能对象时。
在Java编程领域,Singleton模式的应用非常广泛。它不仅有助于减少资源消耗和避免不必要的对象创建,还可以保证应用程序中某些关键对象的唯一性,从而确保数据的一致性和系统的稳定性。通过使用Singleton模式,我们可以确保在任何给定时间点,对于共享资源的访问都是同步的,避免了多线程环境下的并发问题。
总的来说,Singleton模式是一种简单而强大的设计模式,它通过限制实例化来解决特定的设计问题。在面对复杂的业务逻辑和非功能性需求时,合理运用Singleton模式可以提高代码的可维护性、可扩展性和性能。因此,对于Java开发者来说,深入理解和掌握这种模式是非常有价值的。