文章目录
1. 什么是单例模式?
单例模式(Singleton Pattern)是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来访问这个实例。简单来说,单例模式就是保证一个类仅有一个实例,并提供一个访问它的全局访问点。
1.1 单例模式的核心要素
- 私有构造函数:防止通过
new
关键字直接实例化对象 - 私有静态变量:存储类的唯一实例
- 公有静态方法:提供获取唯一实例的方法(全局访问点)
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创建对象的过程包括三个步骤:
- 分配内存空间
- 初始化对象
- 将引用指向内存空间
由于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 优点
- 节省资源:只创建一个实例,节省系统资源
- 方便访问:提供全局访问点,方便获取实例
- 保证一致性:确保对象的唯一性,保证数据一致性
- 避免冲突:防止多个实例导致的冲突和错误
4.2 缺点
- 职责过重:单例承担了过多的职责,违反单一职责原则
- 扩展困难:单例模式难以扩展和继承
- 测试困难:单例的全局状态难以模拟和测试
- 高耦合性:客户端与单例类耦合,不利于解耦和维护
5. 单例模式的注意事项
5.1 线程安全问题
在多线程环境下,需要特别注意单例的线程安全性。前面我们已经介绍了几种线程安全的实现方式:
- 饿汉式:类加载时就创建实例,天然线程安全
- 同步方法:使用synchronized关键字保证线程安全
- 双重检查锁:兼顾性能和线程安全
- 静态内部类:利用类加载机制保证线程安全
- 枚举:由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));
}
}
解决方案:
- 在构造函数中添加防御代码
- 使用枚举实现单例,枚举天然防御反射攻击
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 如何选择实现方式
- 需要延迟加载:使用双重检查锁或静态内部类
- 不需要延迟加载:使用饿汉式
- 需要防止反射和序列化攻击:使用枚举
- 性能敏感场景:避免使用同步方法实现
8.3 单例的替代方案
如果单例模式不适合你的需求,可以考虑以下替代方案:
- 依赖注入:通过IoC容器管理对象生命周期(如Spring框架)
- 静态工具类:只包含静态方法的类,不需要实例化
- 对象池:管理多个可重用对象的技术
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. 总结
单例模式是一种简单但功能强大的设计模式,它确保一个类只有一个实例,并提供全局访问点。
主要实现方式包括:
- 饿汉式(静态常量/静态代码块)
- 懒汉式(普通/同步方法)
- 双重检查锁
- 静态内部类
- 枚举
每种实现方式都有其优缺点,应根据具体需求选择合适的实现。在使用单例模式时,需要特别注意线程安全、序列化问题和反射攻击等。
单例模式虽然简单,但应用广泛,如配置管理、连接池、窗口管理等。正确理解和应用单例模式,能够帮助我们设计出更高效、更健壮的系统。
11. 练习
- 实现一个线程安全的单例模式,要求延迟加载
- 修改实现,使之能够防御反射攻击
- 实现一个序列化安全的单例模式
- 设计一个使用单例模式的简单应用程序(如日志系统)
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] 从其他类记录的日志信息
这个简单的日志系统实现了以下功能:
- 使用静态内部类实现线程安全的延迟加载单例
- 提供不同级别的日志记录功能
- 支持配置日志级别
- 将日志写入文件
- 提供统一的全局访问点
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. 提供统一的全局访问点
通过单例模式,确保了在应用程序的不同部分使用的是同一个日志实例,保证了日志的一致性。