Java设计模式-单例模式(java单例模式原理)

createh54周前 (02-01)技术教程16

单例模式定义:它确保一个类只有一个实例,并提供一个全局访问点来访问该实例。

单例模式主要分为两大类:饿汉式和懒汉式,懒汉式又分为 单线程下的普通懒汉式,多线程下的双重校验锁、静态内部类、枚举。

单例模式的应用场景举例:

配置管理器当应用程序需要读取和维护配置信息时,通常会使用单例模式来创建一个配置管理器。这保证了整个应用中只有一份配置数据副本,所有组件都引用同一个配置对象,从而避免了数据不一致的问题。

日志记录器:单例模式确保在整个应用程序中只有一个日志记录器实例。这样,所有的日志消息都可以统一地被这个实例处理。

单例模式确保在整个应用程序中只有一个日志记录器实例。这样,所有的日志消息都可以统一地被这个实例处理。例如,在 Java 中,java.util.logging.Logger类可以通过单例模式来实现。开发人员可以通过Logger.getLogger("name")方法获取一个唯一的日志记录器实例,所有对该实例的调用都可以保证日志的一致性,比如记录不同模块的信息到同一个日志文件中。

缓存:缓存数据通常需要全局唯一的实例来管理,以便在不同的地方访问和修改缓存时保持一致性。

单例模式可以用于实现缓存。一个单例的缓存实例可以存储经常访问的数据,在整个应用程序中共享。例如,一个 Web 应用中的用户权限缓存。当用户登录后,系统会查询数据库获取用户的权限信息并存储在单例缓存中。在用户后续的操作中,如访问某个需要权限验证的页面时,系统可以直接从这个单例缓存中获取用户权限信息,而不是每次都去查询数据库,大大提高了系统的响应速度。

数据库连接池:

数据库连接是一个昂贵的操作,频繁地创建和销毁连接会消耗大量的系统资源。因此,通常会使用连接池来管理数据库连接,而连接池本身往往是一个单例,以确保在整个应用中只有一个连接池实例,优化数据库访问性能。

线程池:

当处理大量并发任务时,为每个任务创建一个新线程会导致系统资源耗尽。线程池可以预先创建一组线程,任务到来时,将任务分配给线程池中的线程来执行,任务执行完毕后线程可以被复用。

单例模式适用于线程池的实现。整个应用程序通常只需要一个线程池来管理线程资源。例如,在 Java 的ExecutorService框架中,通过Executors.newFixedThreadPool(n)创建一个固定大小的线程池,这个线程池可以作为一个单例存在于应用程序中。所有需要异步执行的任务都可以提交给这个单例线程池,它可以有效地管理线程的创建、分配和回收,提高系统的并发处理能力。

单例模式的优点:

  1. 控制实例数量:确保一个类只有一个实例,避免了实例的重复创建,节省系统资源。
  2. 全局访问点:提供一个全局访问点,使得访问该实例变得简单。
  3. 延迟加载:某些实现方式(如懒汉式单例)可以实现延迟加载,即在需要时才创建实例,从而提高系统性能。
  4. 资源共享和节省:单例模式确保在整个应用程序中只有一个实例存在。对于一些资源密集型的对象,如数据库连接池、线程池等,这可以避免创建多个相同的对象,从而节省系统资源。

(一)单例模式-饿汉式java样例

public class Singleton {
    // 1. 私有静态成员变量,直接创建实例对象
    private static Singleton instance = new Singleton();

    // 2. 私有构造方法,防止外部通过构造函数创建新的实例
    private Singleton() {
    }

    // 3. 公共静态方法,用于获取单例实例
    public static Singleton getInstance() {
        return instance;
    }

    // 可以在这里添加单例对象的其他业务方法,比如下面这个简单示例方法
    public void showMessage() {
        System.out.println("这是饿汉式单例模式的示例消息");
    }
}

(二)单例模式-单线程下-懒汉式java样例

public class LazySingleton {
    // 声明一个静态变量来保存单例实例,初始值为null
    private static LazySingleton instance;

    // 将构造方法设为私有,防止外部通过构造方法创建新的实例
    private LazySingleton() {
    }

    // 提供一个静态的公共方法来获取单例实例
    public static LazySingleton getInstance() {
        // 判断实例是否已经创建,如果没有则创建一个新的实例
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
  /* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */
    private Object readResolve() {
        return instance;
    }

   // 可以在这里添加其他需要的方法等,例如示例的一个简单方法
    public void showMessage() {
        System.out.println("这是懒汉式单例模式的示例方法。");
    }
}

(三)单例模式-多线程下-双重校验锁java样例

public class Singleton {
    // 使用volatile关键字修饰实例变量,保证多线程环境下的可见性以及防止指令重排序
    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;
    }
   /* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */
    private Object readResolve() {
        return instance;
    }
    // 可以在这里添加其他业务方法,比如示例的一个简单方法
    public void doSomething() {
        System.out.println("执行单例实例中的业务操作");
    }
}

(四)单例模式-静态内部类

public class Singleton {

    // 私有构造方法,防止外部通过构造方法创建实例
    private Singleton() {
    }

    // 定义静态内部类,在内部类中创建单例实例
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    // 提供公共的静态方法获取单例实例
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
  
   /* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */
    private Object readResolve() {
        return getInstance();
    }
    // 可以在这里添加其他业务方法,例如下面这个简单示例方法
    public void showMessage() {
        System.out.println("这是单例模式中执行的操作。");
    }
}

(五)单例模式-枚举

public enum SingletonEnum {
    INSTANCE;

    // 可以在这里定义单例对象需要的属性和方法
    private String message = "这是枚举单例模式中的信息";

    public String getMessage() {
        return message;
    }

    public void setMessage(String newMessage) {
        message = newMessage;
    }
}

相关文章

Java:在Java中使用私有接口方法(java私有的)

  接口是定义一组方法及其签名的契约, 它可以由任何类及其在该类中实现的方法进行扩展。从Java9 开始,你可以在接口中使用私有方法。  由于私有方法只能在定义它的接口内访问,因此你可以利用这些方法编...

Python之面向对象:私有属性是掩耳盗铃还是恰到好处

引言声明,今天的文章中没有一行Python代码,更多的是对编程语言设计理念的思考。上一篇文章中介绍了关于Python面向对象封装特性的私有属性的相关内容,提到了Python中关于私有属性的实现是通过“...

Java基础——面试官:你来说说反射如何获取私有对象的属性和方法

最近,@Python大星 的朋友小鹿参加了一场#Java#面试。有一道题是这样的 >>>【面试官问:你来说说反射如何获取私有对象的属性和方法?】问题的答案我们文章中揭晓,先看下反射的...

java匿名内部类的定义以及使用场景

匿名内部类定义 匿名内部类是Java中一种没有显式声明名称的内部类。它们在创建时被同时声明和实例化,通常用于创建一次性使用的类。它们的特点是:无名称: 无法像普通类一样被其他代码引用。一次性: 通常只...

内部类Java(内部类定义)

概念将一个类A定义在另一个类B里面,里面的那个类A就称为内部类,B则称为外部类。什么时候使用内部类一个事物内部还有一个独立的事物,内部的事物脱离外部的事物无法独立使用比如汽车只有一个发动机,电脑只有一...

java 核心技术-12版 卷Ⅰ- 6.1.4 静态和私有方法

原文6.1.4 静态和私有方法在 Java 8 中,允许在接口中增加静态方法。理论上讲,没有任何理由认为这是不合法的。只是这似乎有违于将接口作为抽象规范的初衷。目前为止,通常的做法都是将静态方法放在伴...