单例模式详解

发布于:2025-04-19 ⋅ 阅读:(65) ⋅ 点赞:(0)

1. 什么是单例模式?

单例模式(Singleton Pattern)是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来访问这个实例。简单来说,单例模式就是保证一个类仅有一个实例,并提供一个访问它的全局访问点。

1.1 单例模式的核心要素

  1. 私有构造函数:防止通过new关键字直接实例化对象
  2. 私有静态变量:存储类的唯一实例
  3. 公有静态方法:提供获取唯一实例的方法(全局访问点)

1.2 为什么需要单例模式?

在实际开发中,有些对象我们只需要一个实例:

  • 资源共享:有些共享资源(如线程池、连接池)需要统一管理
  • 状态统一:需要统一维护某些全局状态
  • 节省资源:有些重量级对象创建耗费资源,只需创建一次
  • 防止重复:某些操作或资源不应该被重复使用(如读写配置文件)

2. 单例模式的实现方式

Java中实现单例模式的方式有多种,下面我们从最简单到较复杂依次介绍:

2.1 饿汉式(静态常量)

这是最简单的实现方式。在类加载时就创建实例,不存在线程安全问题。

public class Singleton {
    // 1. 私有构造函数
    private Singleton() {
    }
    
    // 2. 在类内部创建实例
    private static final Singleton INSTANCE = new Singleton();
    
    // 3. 提供公共的获取实例方法
    public static Singleton getInstance() {
        return INSTANCE;
    }
    
    // 测试方法
    public void showMessage() {
        System.out.println("Hello, I am a singleton instance!");
    }
}

使用示例

public class SingletonTest {
    public static void main(String[] args) {
        // 获取单例实例
        Singleton instance = Singleton.getInstance();
        
        // 调用实例方法
        instance.showMessage();
        
        // 验证是否是同一个实例
        Singleton instance2 = Singleton.getInstance();
        System.out.println("两个实例是否相同: " + (instance == instance2));
    }
}

输出

Hello, I am a singleton instance!
两个实例是否相同: true

优点

  • 实现简单
  • 线程安全
  • 避免了同步问题

缺点

  • 不管是否需要,实例在类加载时就创建,可能造成资源浪费

2.2 饿汉式(静态代码块)

与上面类似,只是将实例化过程放在静态代码块中:

public class Singleton {
    // 1. 私有构造函数
    private Singleton() {
    }
    
    // 2. 声明实例但不初始化
    private static Singleton instance;
    
    // 3. 在静态代码块中初始化
    static {
        instance = new Singleton();
    }
    
    // 4. 提供公共的获取实例方法
    public static Singleton getInstance() {
        return instance;
    }
}

优缺点:与第一种方式相同,只是写法不同。

2.3 懒汉式(线程不安全)

延迟加载,在第一次调用getInstance()方法时才创建实例:

public class Singleton {
    // 1. 私有构造函数
    private Singleton() {
    }
    
    // 2. 声明实例但不初始化
    private static Singleton instance;
    
    // 3. 提供获取实例的方法,在首次调用时创建实例
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

优点

  • 延迟加载,需要时才创建实例

缺点

  • 线程不安全,在多线程环境下可能创建多个实例

2.4 懒汉式(线程安全,同步方法)

在getInstance()方法上添加synchronized关键字,确保多线程环境下的安全性:

public class Singleton {
    // 1. 私有构造函数
    private Singleton() {
    }
    
    // 2. 声明实例但不初始化
    private static Singleton instance;
    
    // 3. 提供获取实例的方法,加锁保证线程安全
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

优点

  • 延迟加载
  • 线程安全

缺点

  • 每次调用getInstance()方法都需要同步,性能较差

2.5 双重检查锁(DCL)

结合了延迟加载和线程安全,同时提高了性能:

public class Singleton {
    // 1. 私有构造函数
    private Singleton() {
    }
    
    // 2. 声明实例但不初始化,使用volatile关键字防止指令重排序
    private static volatile Singleton instance;
    
    // 3. 提供获取实例的方法,使用双重检查锁
    public static Singleton getInstance() {
        // 第一次检查,如果实例已存在,直接返回,避免同步开销
        if (instance == null) {
            // 同步锁
            synchronized (Singleton.class) {
                // 第二次检查,防止多线程同时通过第一次检查
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

为什么需要volatile关键字?

Java创建对象的过程包括三个步骤:

  1. 分配内存空间
  2. 初始化对象
  3. 将引用指向内存空间

由于JVM的指令重排优化,步骤2和3可能会交换顺序。这可能导致一个线程看到的是一个未完全初始化的对象。使用volatile可以防止这种情况发生。

优点

  • 延迟加载
  • 线程安全
  • 大部分情况下无需同步,性能较好

缺点

  • 代码稍复杂
  • 需要volatile关键字(JDK 1.5及以上版本)

2.6 静态内部类

利用Java类加载机制保证线程安全和延迟加载:

public class Singleton {
    // 1. 私有构造函数
    private Singleton() {
    }
    
    // 2. 静态内部类,用于持有实例
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    
    // 3. 提供获取实例的方法
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

工作原理

  • 当Singleton类加载时,SingletonHolder类不会立即加载
  • 只有在首次调用getInstance()方法时,SingletonHolder类才会加载,并创建Singleton实例
  • 类加载过程是线程安全的,保证了实例的唯一性

优点

  • 延迟加载
  • 线程安全
  • 实现简洁

缺点

  • 无法传参

2.7 枚举实现

最简洁的实现方式,由Java语言规范保证单例和线程安全:

public enum Singleton {
    INSTANCE;
    
    // 可以定义实例方法
    public void showMessage() {
        System.out.println("Hello from singleton enum!");
    }
}

使用示例

public class SingletonTest {
    public static void main(String[] args) {
        Singleton instance = Singleton.INSTANCE;
        instance.showMessage();
    }
}

优点

  • 最简洁
  • 自动线程安全
  • 提供序列化机制
  • 防止反射攻击

缺点

  • 无延迟加载
  • 可能不适合某些场景(如需要继承其他类)

3. 单例模式的应用场景

单例模式在实际开发中有很多应用场景,包括:

3.1 配置管理器

管理应用程序配置,确保配置信息的一致性:

public class ConfigManager {
    private static class Holder {
        private static final ConfigManager INSTANCE = new ConfigManager();
    }
    
    private Properties properties;
    
    private ConfigManager() {
        properties = new Properties();
        try {
            properties.load(new FileInputStream("config.properties"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    public static ConfigManager getInstance() {
        return Holder.INSTANCE;
    }
    
    public String getProperty(String key) {
        return properties.getProperty(key);
    }
}

使用示例

public class ConfigTest {
    public static void main(String[] args) {
        ConfigManager config = ConfigManager.getInstance();
        String dbUrl = config.getProperty("database.url");
        System.out.println("Database URL: " + dbUrl);
    }
}

3.2 数据库连接池

管理数据库连接资源,避免重复创建连接:

public class ConnectionPool {
    private static volatile ConnectionPool instance;
    private List<Connection> connections;
    
    private ConnectionPool() {
        connections = new ArrayList<>();
        // 初始化连接池
        for (int i = 0; i < 10; i++) {
            try {
                Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "user", "password");
                connections.add(conn);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    
    public static ConnectionPool getInstance() {
        if (instance == null) {
            synchronized (ConnectionPool.class) {
                if (instance == null) {
                    instance = new ConnectionPool();
                }
            }
        }
        return instance;
    }
    
    public synchronized Connection getConnection() {
        if (connections.size() > 0) {
            Connection conn = connections.remove(0);
            return conn;
        }
        return null; // 实际应用中应该等待或创建新连接
    }
    
    public synchronized void releaseConnection(Connection conn) {
        connections.add(conn);
    }
}

3.3 窗口管理器

在GUI应用程序中管理窗口:

public class WindowManager {
    private static WindowManager instance;
    private List<Window> windows;
    
    private WindowManager() {
        windows = new ArrayList<>();
    }
    
    public static synchronized WindowManager getInstance() {
        if (instance == null) {
            instance = new WindowManager();
        }
        return instance;
    }
    
    public void addWindow(Window window) {
        windows.add(window);
    }
    
    public void removeWindow(Window window) {
        windows.remove(window);
    }
    
    public Window getActiveWindow() {
        // 返回当前活动窗口
        return windows.isEmpty() ? null : windows.get(windows.size() - 1);
    }
}

3.4 日志记录器

集中管理日志记录,确保日志一致性:

public class Logger {
    private static class LoggerHolder {
        private static final Logger INSTANCE = new Logger();
    }
    
    private FileWriter writer;
    
    private Logger() {
        try {
            writer = new FileWriter("application.log", true);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    public static Logger getInstance() {
        return LoggerHolder.INSTANCE;
    }
    
    public synchronized void log(String message) {
        try {
            writer.write(new Date() + ": " + message + "\n");
            writer.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    // 确保资源释放
    public void close() {
        try {
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

使用示例

public class LogTest {
    public static void main(String[] args) {
        Logger logger = Logger.getInstance();
        logger.log("应用程序启动");
        // 执行一些操作...
        logger.log("操作完成");
        logger.close();
    }
}

4. 单例模式的优缺点

4.1 优点

  1. 节省资源:只创建一个实例,节省系统资源
  2. 方便访问:提供全局访问点,方便获取实例
  3. 保证一致性:确保对象的唯一性,保证数据一致性
  4. 避免冲突:防止多个实例导致的冲突和错误

4.2 缺点

  1. 职责过重:单例承担了过多的职责,违反单一职责原则
  2. 扩展困难:单例模式难以扩展和继承
  3. 测试困难:单例的全局状态难以模拟和测试
  4. 高耦合性:客户端与单例类耦合,不利于解耦和维护

5. 单例模式的注意事项

5.1 线程安全问题

在多线程环境下,需要特别注意单例的线程安全性。前面我们已经介绍了几种线程安全的实现方式:

  1. 饿汉式:类加载时就创建实例,天然线程安全
  2. 同步方法:使用synchronized关键字保证线程安全
  3. 双重检查锁:兼顾性能和线程安全
  4. 静态内部类:利用类加载机制保证线程安全
  5. 枚举:由Java语言规范保证线程安全

5.2 序列化问题

当单例类实现Serializable接口时,可能会破坏单例。反序列化时会创建新的实例,即使构造函数是私有的。

解决方案:在单例类中添加readResolve()方法:

public class Singleton implements Serializable {
    private static final long serialVersionUID = 1L;
    
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    
    private Singleton() {
    }
    
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
    
    // 防止反序列化破坏单例
    private Object readResolve() {
        return SingletonHolder.INSTANCE;
    }
}

5.3 反射攻击

使用反射机制可以调用私有构造函数,从而破坏单例:

public class ReflectionAttack {
    public static void main(String[] args) throws Exception {
        Singleton instance1 = Singleton.getInstance();
        
        // 使用反射创建另一个实例
        Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
        constructor.setAccessible(true);
        Singleton instance2 = constructor.newInstance();
        
        System.out.println("instance1: " + instance1);
        System.out.println("instance2: " + instance2);
        System.out.println("instance1 == instance2: " + (instance1 == instance2));
    }
}

解决方案

  1. 在构造函数中添加防御代码
  2. 使用枚举实现单例,枚举天然防御反射攻击
public class Singleton {
    private static Singleton instance;
    
    // 私有构造函数中添加防御
    private Singleton() {
        if (instance != null) {
            throw new RuntimeException("单例类不允许多个实例!");
        }
    }
    
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

6. 单例模式与其他模式的关系

6.1 单例与工厂模式

单例模式经常与工厂模式一起使用。工厂类通常设计为单例,因为不需要多个工厂实例:

public class ProductFactory {
    private static ProductFactory instance;
    
    private ProductFactory() {
    }
    
    public static synchronized ProductFactory getInstance() {
        if (instance == null) {
            instance = new ProductFactory();
        }
        return instance;
    }
    
    public Product createProduct(String type) {
        if ("A".equals(type)) {
            return new ProductA();
        } else if ("B".equals(type)) {
            return new ProductB();
        }
        return null;
    }
}

6.2 单例与策略模式

单例可与策略模式结合,管理不同的策略实现:

public class StrategyManager {
    private static StrategyManager instance;
    private Map<String, Strategy> strategies;
    
    private StrategyManager() {
        strategies = new HashMap<>();
        // 注册策略
        strategies.put("A", new StrategyA());
        strategies.put("B", new StrategyB());
    }
    
    public static synchronized StrategyManager getInstance() {
        if (instance == null) {
            instance = new StrategyManager();
        }
        return instance;
    }
    
    public Strategy getStrategy(String type) {
        return strategies.get(type);
    }
}

7. 单例模式的测试

测试单例模式可能比较困难,因为单例维护着全局状态。下面是一些测试技巧:

7.1 验证唯一性

测试单例实例的唯一性:

import org.junit.Test;
import static org.junit.Assert.*;

public class SingletonTest {
    @Test
    public void testSingletonUniqueness() {
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        
        assertSame("getInstance() should always return the same instance", instance1, instance2);
    }
}

7.2 测试多线程环境

测试在多线程环境下的单例安全性:

import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import static org.junit.Assert.*;

public class SingletonMultiThreadTest {
    @Test
    public void testSingletonInMultiThreadedEnvironment() throws Exception {
        final int threadCount = 100;
        ExecutorService executor = Executors.newFixedThreadPool(threadCount);
        List<Callable<Singleton>> tasks = new ArrayList<>();
        
        // 创建多个任务,每个任务获取单例实例
        for (int i = 0; i < threadCount; i++) {
            tasks.add(() -> Singleton.getInstance());
        }
        
        // 执行所有任务
        List<Future<Singleton>> results = executor.invokeAll(tasks);
        
        // 验证所有任务获取的是同一个实例
        Singleton firstInstance = results.get(0).get();
        for (Future<Singleton> result : results) {
            assertSame("All threads should get the same instance", firstInstance, result.get());
        }
        
        executor.shutdown();
    }
}

8. 实用单例模式的最佳实践

8.1 何时使用单例

单例模式适用于以下场景:

  • 需要严格控制资源使用的场景
  • 需要统一管理全局状态的场景
  • 创建对象代价较大的场景
  • 需要一个协调多个对象的访问点的场景

8.2 如何选择实现方式

  1. 需要延迟加载:使用双重检查锁或静态内部类
  2. 不需要延迟加载:使用饿汉式
  3. 需要防止反射和序列化攻击:使用枚举
  4. 性能敏感场景:避免使用同步方法实现

8.3 单例的替代方案

如果单例模式不适合你的需求,可以考虑以下替代方案:

  1. 依赖注入:通过IoC容器管理对象生命周期(如Spring框架)
  2. 静态工具类:只包含静态方法的类,不需要实例化
  3. 对象池:管理多个可重用对象的技术

9. 实际案例分析

9.1 Spring中的单例

Spring框架默认将所有Bean配置为单例模式。每个Bean ID在IoC容器中只对应一个实例:

@Configuration
public class AppConfig {
    @Bean
    public UserService userService() {
        return new UserServiceImpl();
    }
}

在这个例子中,userService被创建为单例,Spring框架确保每次注入时获取到的都是同一个实例。

9.2 Java Runtime类

Java的Runtime类是单例模式的标准示例:

public class RuntimeExample {
    public static void main(String[] args) {
        // 获取Runtime实例
        Runtime runtime = Runtime.getRuntime();
        
        // 查看可用处理器数量
        System.out.println("可用处理器数量: " + runtime.availableProcessors());
        
        // 查看可用内存
        System.out.println("可用内存: " + runtime.freeMemory() / 1024 / 1024 + "MB");
        
        // 查看最大内存
        System.out.println("最大内存: " + runtime.maxMemory() / 1024 / 1024 + "MB");
        
        // 尝试重复获取实例,验证是否是同一个
        Runtime anotherRuntime = Runtime.getRuntime();
        System.out.println("是否是同一个实例: " + (runtime == anotherRuntime));
    }
}

10. 总结

单例模式是一种简单但功能强大的设计模式,它确保一个类只有一个实例,并提供全局访问点。

主要实现方式包括:

  1. 饿汉式(静态常量/静态代码块)
  2. 懒汉式(普通/同步方法)
  3. 双重检查锁
  4. 静态内部类
  5. 枚举

每种实现方式都有其优缺点,应根据具体需求选择合适的实现。在使用单例模式时,需要特别注意线程安全、序列化问题和反射攻击等。

单例模式虽然简单,但应用广泛,如配置管理、连接池、窗口管理等。正确理解和应用单例模式,能够帮助我们设计出更高效、更健壮的系统。

11. 练习

  1. 实现一个线程安全的单例模式,要求延迟加载
  2. 修改实现,使之能够防御反射攻击
  3. 实现一个序列化安全的单例模式
  4. 设计一个使用单例模式的简单应用程序(如日志系统)

11.1 练习答案

11.1.1 线程安全的延迟加载单例

下面是使用双重检查锁实现的线程安全延迟加载单例:

public class ThreadSafeSingleton {
    // 使用volatile关键字防止指令重排序
    private static volatile ThreadSafeSingleton instance;
    
    // 私有构造函数
    private ThreadSafeSingleton() {
    }
    
    // 双重检查锁实现线程安全的延迟加载
    public static ThreadSafeSingleton getInstance() {
        if (instance == null) {
            synchronized (ThreadSafeSingleton.class) {
                if (instance == null) {
                    instance = new ThreadSafeSingleton();
                }
            }
        }
        return instance;
    }
    
    // 业务方法
    public void businessMethod() {
        System.out.println("执行业务逻辑");
    }
}

测试代码:

public class ThreadSafeSingletonTest {
    public static void main(String[] args) {
        // 创建多个线程同时获取实例
        Runnable task = () -> {
            ThreadSafeSingleton singleton = ThreadSafeSingleton.getInstance();
            System.out.println(Thread.currentThread().getName() + ": " + singleton);
        };
        
        // 启动10个线程
        for (int i = 0; i < 10; i++) {
            new Thread(task).start();
        }
    }
}

输出示例:

Thread-0: ThreadSafeSingleton@6d03e736
Thread-1: ThreadSafeSingleton@6d03e736
Thread-2: ThreadSafeSingleton@6d03e736
...

可以看到所有线程获取的都是相同的实例。

11.1.2 防御反射攻击的单例

下面是能够防御反射攻击的单例实现:

public class ReflectionSafeSingleton {
    // 静态内部类持有单例实例
    private static class Holder {
        private static final ReflectionSafeSingleton INSTANCE = new ReflectionSafeSingleton();
    }
    
    // 标记是否已经创建过实例
    private static boolean initialized = false;
    
    // 私有构造函数,添加防御反射攻击的代码
    private ReflectionSafeSingleton() {
        synchronized (ReflectionSafeSingleton.class) {
            if (initialized) {
                throw new RuntimeException("单例已初始化,不允许通过反射创建新实例!");
            }
            initialized = true;
        }
    }
    
    // 获取实例的方法
    public static ReflectionSafeSingleton getInstance() {
        return Holder.INSTANCE;
    }
}

测试代码:

import java.lang.reflect.Constructor;

public class ReflectionSafeSingletonTest {
    public static void main(String[] args) {
        try {
            // 正常获取实例
            ReflectionSafeSingleton instance1 = ReflectionSafeSingleton.getInstance();
            System.out.println("正常获取实例: " + instance1);
            
            // 尝试通过反射创建新实例
            Constructor<ReflectionSafeSingleton> constructor = 
                ReflectionSafeSingleton.class.getDeclaredConstructor();
            constructor.setAccessible(true);
            ReflectionSafeSingleton instance2 = constructor.newInstance();
            System.out.println("反射创建实例: " + instance2);
        } catch (Exception e) {
            System.out.println("反射创建实例失败: " + e.getMessage());
        }
    }
}

输出示例:

正常获取实例: ReflectionSafeSingleton@6d03e736
反射创建实例失败: java.lang.reflect.InvocationTargetException: 单例已初始化,不允许通过反射创建新实例!

当尝试通过反射创建第二个实例时,会抛出异常。

11.1.3 序列化安全的单例

下面是能够防御序列化攻击的单例实现:

import java.io.*;

public class SerializableSingleton implements Serializable {
    private static final long serialVersionUID = 1L;
    
    // 静态内部类持有单例实例
    private static class Holder {
        private static final SerializableSingleton INSTANCE = new SerializableSingleton();
    }
    
    // 私有构造函数
    private SerializableSingleton() {
    }
    
    // 获取实例的方法
    public static SerializableSingleton getInstance() {
        return Holder.INSTANCE;
    }
    
    // 防止反序列化创建新实例
    private Object readResolve() {
        return Holder.INSTANCE;
    }
}

测试代码:

import java.io.*;

public class SerializableSingletonTest {
    public static void main(String[] args) throws Exception {
        // 获取单例实例
        SerializableSingleton instance1 = SerializableSingleton.getInstance();
        System.out.println("原始实例: " + instance1);
        
        // 将实例序列化到文件
        ObjectOutputStream oos = new ObjectOutputStream(
            new FileOutputStream("singleton.ser"));
        oos.writeObject(instance1);
        oos.close();
        
        // 从文件反序列化
        ObjectInputStream ois = new ObjectInputStream(
            new FileInputStream("singleton.ser"));
        SerializableSingleton instance2 = (SerializableSingleton) ois.readObject();
        ois.close();
        
        System.out.println("反序列化实例: " + instance2);
        System.out.println("是否相同实例: " + (instance1 == instance2));
    }
}

输出示例:

原始实例: SerializableSingleton@6d03e736
反序列化实例: SerializableSingleton@6d03e736
是否相同实例: true

通过实现readResolve()方法,确保反序列化时返回的是单例实例。

11.1.4 使用单例模式的简单日志系统

以下是一个简单的日志系统实现,它使用单例模式:

import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;

public class LoggerSystem {
    // 日志级别枚举
    public enum LogLevel {
        DEBUG, INFO, WARNING, ERROR
    }
    
    // 当前日志级别
    private LogLevel currentLevel = LogLevel.INFO;
    // 日期格式器
    private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    // 日志文件
    private PrintWriter writer;
    
    // 单例持有者
    private static class LoggerHolder {
        private static final LoggerSystem INSTANCE = new LoggerSystem();
    }
    
    // 私有构造函数,初始化日志文件
    private LoggerSystem() {
        try {
            writer = new PrintWriter(new FileWriter("application.log", true), true);
        } catch (IOException e) {
            System.err.println("无法打开日志文件: " + e.getMessage());
            // 如果无法打开文件,使用标准输出
            writer = new PrintWriter(System.out, true);
        }
    }
    
    // 获取实例方法
    public static LoggerSystem getInstance() {
        return LoggerHolder.INSTANCE;
    }
    
    // 设置日志级别
    public void setLogLevel(LogLevel level) {
        this.currentLevel = level;
    }
    
    // 记录日志
    private void log(LogLevel level, String message) {
        if (level.ordinal() >= currentLevel.ordinal()) {
            String logMessage = String.format("[%s] [%s] %s", 
                dateFormat.format(new Date()), level, message);
            writer.println(logMessage);
        }
    }
    
    // 公共日志方法
    public void debug(String message) {
        log(LogLevel.DEBUG, message);
    }
    
    public void info(String message) {
        log(LogLevel.INFO, message);
    }
    
    public void warning(String message) {
        log(LogLevel.WARNING, message);
    }
    
    public void error(String message) {
        log(LogLevel.ERROR, message);
    }
    
    // 关闭日志
    public void close() {
        if (writer != null) {
            writer.close();
        }
    }
}

测试代码:

public class LoggerSystemTest {
    public static void main(String[] args) {
        // 获取日志实例
        LoggerSystem logger = LoggerSystem.getInstance();
        
        // 记录不同级别的日志
        logger.debug("这是调试信息");  // 默认不会显示,因为默认级别是INFO
        logger.info("这是一般信息");
        logger.warning("这是警告信息");
        logger.error("这是错误信息");
        
        // 修改日志级别
        logger.setLogLevel(LoggerSystem.LogLevel.DEBUG);
        logger.debug("修改级别后的调试信息");  // 现在会显示
        
        // 在其他类中使用相同实例
        OtherClass.testLogger();
        
        // 关闭日志
        logger.close();
    }
    
    // 辅助测试类
    static class OtherClass {
        static void testLogger() {
            LoggerSystem logger = LoggerSystem.getInstance();
            logger.info("从其他类记录的日志信息");
        }
    }
}

输出示例 (application.log):

[2023-06-10 15:30:45] [INFO] 这是一般信息
[2023-06-10 15:30:45] [WARNING] 这是警告信息
[2023-06-10 15:30:45] [ERROR] 这是错误信息
[2023-06-10 15:30:45] [DEBUG] 修改级别后的调试信息
[2023-06-10 15:30:45] [INFO] 从其他类记录的日志信息

这个简单的日志系统实现了以下功能:

  1. 使用静态内部类实现线程安全的延迟加载单例
  2. 提供不同级别的日志记录功能
  3. 支持配置日志级别
  4. 将日志写入文件
  5. 提供统一的全局访问点

ic void main(String[] args) {
// 获取日志实例
LoggerSystem logger = LoggerSystem.getInstance();

    // 记录不同级别的日志
    logger.debug("这是调试信息");  // 默认不会显示,因为默认级别是INFO
    logger.info("这是一般信息");
    logger.warning("这是警告信息");
    logger.error("这是错误信息");
    
    // 修改日志级别
    logger.setLogLevel(LoggerSystem.LogLevel.DEBUG);
    logger.debug("修改级别后的调试信息");  // 现在会显示
    
    // 在其他类中使用相同实例
    OtherClass.testLogger();
    
    // 关闭日志
    logger.close();
}

// 辅助测试类
static class OtherClass {
    static void testLogger() {
        LoggerSystem logger = LoggerSystem.getInstance();
        logger.info("从其他类记录的日志信息");
    }
}

}


输出示例 (application.log):

[2023-06-10 15:30:45] [INFO] 这是一般信息
[2023-06-10 15:30:45] [WARNING] 这是警告信息
[2023-06-10 15:30:45] [ERROR] 这是错误信息
[2023-06-10 15:30:45] [DEBUG] 修改级别后的调试信息
[2023-06-10 15:30:45] [INFO] 从其他类记录的日志信息


这个简单的日志系统实现了以下功能:
1. 使用静态内部类实现线程安全的延迟加载单例
2. 提供不同级别的日志记录功能
3. 支持配置日志级别
4. 将日志写入文件
5. 提供统一的全局访问点

通过单例模式,确保了在应用程序的不同部分使用的是同一个日志实例,保证了日志的一致性。 

网站公告

今日签到

点亮在社区的每一天
去签到