Menu
CoddyTech

单例模式

本课属于 Coddy Java 学习旅程的“面向对象编程”单元。第 65 课,共 87 课。

单例模式是一种创建型设计模式,可确保一个类在整个应用程序中只有一个实例,同时提供全局访问该实例的入口。这适用于需要共享的资源,例如配置管理器、连接池或日志服务。

这种模式通过将构造函数设为私有,阻止直接实例化。取而代之的是,通过静态方法控制对唯一实例的访问:

public class DatabaseConnection {
    private static DatabaseConnection instance;
    
    // 私有构造方法防止直接创建实例
    private DatabaseConnection() {
        System.out.println("Connection created");
    }
    
    // 全局访问入口
    public static DatabaseConnection getInstance() {
        if (instance == null) {
            instance = new DatabaseConnection();
        }
        return instance;
    }
    
    public void query(String sql) {
        System.out.println("Executing: " + sql);
    }
}

每次调用 getInstance() 都会返回同一个对象。只有在首次请求时才会创建该实例。这称为延迟初始化:

DatabaseConnection db1 = DatabaseConnection.getInstance();
DatabaseConnection db2 = DatabaseConnection.getInstance();

System.out.println(db1 == db2);  // true,指向同一个实例

单例的关键组成部分包括:一个保存实例的 private static 字段、一个 private 构造函数,以及一个返回该实例的 public static 方法。这种组合可以确保,无论你请求对象多少次,得到的始终都是同一个对象。

challenge icon

挑战

简单

让我们使用单例模式为应用构建一个配置管理器!你的配置管理器会确保应用的所有部分共享相同的设置,无论它们请求访问多少次。

你会把代码分到两个文件中:

  • AppConfig.java:创建一个管理应用配置设置的单例类。你的 AppConfig 应包含:

    一个用于保存唯一实例的私有静态字段。

    一个名为 appName 的私有 String 字段,以及一个名为 maxUsers 的私有 int 字段。

    一个私有构造函数,将 appName 初始化为 "MyApplication",将 maxUsers 初始化为 "100",然后输出 AppConfig initialized。

    一个公有静态 getInstance() 方法,仅当实例尚不存在时才创建它(延迟初始化),然后返回该实例。

    一个用于更新应用名称的 setAppName(String name) 方法。

    一个用于更新最大用户数的 setMaxUsers(int max) 方法。

    一个用于输出 App: [appName], Max Users: [maxUsers] 的 displaySettings() 方法。

  • Main.java:演示你的单例能否正常工作!你会收到两个输入:新的应用名称(String)和新的最大用户数(整数)。

    首先,获取一个 AppConfig 实例,并将它存入名为 config1 的变量。调用 displaySettings() 显示默认值。

    然后,通过在 config1 上调用 setAppName 和 setMaxUsers,使用输入值更新设置。

    再获取一个实例,并将它存入 config2。在 config2 上调用 displaySettings(),证明它反映了通过 config1 所做的更改。

    最后,通过比较 config1 == config2,输出两个引用是否指向同一个对象:Same instance: [true/false]。

你会按顺序收到两个输入:新的应用名称(String)和新的最大用户数(整数)。

这个挑战展示了单例模式的核心优势:无论你请求获取配置多少次,得到的始终是同一个具有共享状态的实例!

亲手试试

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // 读取输入
        String newAppName = scanner.nextLine();
        int newMaxUsers = scanner.nextInt();
        
        // TODO: 获取 AppConfig 的第一个实例,并存入 config1
        
        // TODO: 在 config1 上调用 displaySettings(),显示默认值
        
        // TODO: 在 config1 上使用 setAppName 和 setMaxUsers 更新设置
        
        // TODO: 获取另一个实例,并存入 config2
        
        // TODO: 在 config2 上调用 displaySettings(),证明更改是共享的
        
        // TODO: 输出两个引用是否指向同一个对象:
        // 使用 config1 == config2 输出 "Same instance: [true/false]"
    }
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: Java 在线编译器