【设计模式】单例模式 在java中的应用
文章目录
- 引言
- 什么是单例模式
- 单例模式的应用场景
- 单例模式的优缺点
- 优点
- 缺点
- 单例模式的基本实现
- 饿汉式单例模式
- 懒汉式单例模式
- 双重检查锁定
- 静态内部类
- 枚举单例
- 单例模式的线程安全问题
- 多线程环境下的单例模式
- 线程安全的实现方式
- 1. **懒汉式单例模式(线程不安全)**
- 2. **懒汉式单例模式(线程安全,使用同步方法)**
- 3. **双重检查锁定**
- 4. **静态内部类**
- 5. **枚举单例**
- 单例模式的序列化与反序列化
- 序列化导致的问题
- 如何防止反序列化破坏单例
- 单例模式的反射攻击
- 反射攻击导致的问题
- 如何防止反射破坏单例
- 使用枚举类型防止反射和序列化攻击
- 单例模式的实际应用案例
- 数据库连接池管理
- 日志管理
- 配置文件管理
- 总结
- 单例模式的最佳实践
- 单例模式的使用建议
引言
什么是单例模式
单例模式(Singleton Pattern)是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来访问该实例。单例模式通过控制实例化过程来避免创建多个实例,从而节省资源并确保全局状态的一致性。
在 Java 中,实现单例模式的核心思想是私有化构造方法,并通过一个公共的静态方法来返回类的唯一实例。
单例模式的应用场景
单例模式适用于以下场景:
- 资源管理器:如线程池、数据库连接池等,这些资源通常是重量级的,频繁创建和销毁会带来性能开销,因此需要一个全局唯一的实例来管理这些资源。
- 配置管理:应用程序的配置文件通常是全局唯一的,使用单例模式可以确保配置的唯一性和一致性。
- 日志记录器:日志记录器在整个应用程序中通常是唯一的,使用单例模式可以确保日志记录的统一性和线程安全性。
- 缓存:缓存数据通常需要全局唯一的实例来管理,以便在不同的地方访问和修改缓存时保持一致性。
单例模式的优缺点
优点
- 控制实例数量:确保一个类只有一个实例,避免了实例的重复创建,节省系统资源。
- 全局访问点:提供一个全局访问点,使得访问该实例变得简单。
- 延迟加载:某些实现方式(如懒汉式单例)可以实现延迟加载,即在需要时才创建实例,从而提高系统性能。
缺点
- 不易扩展:由于单例类的构造方法是私有的,继承和扩展变得困难。
- 隐藏依赖关系:单例模式隐藏了类之间的依赖关系,增加了代码的复杂性和维护难度。
- 多线程问题:在多线程环境下实现单例模式时,需要考虑线程安全问题,否则可能会创建多个实例。
单例模式的基本实现
饿汉式单例模式
饿汉式单例模式在类加载时就创建实例,确保类在第一次引用时就已经实例化。
public class HungrySingleton {// 类加载时就创建实例private static final HungrySingleton instance = new `();// 私有构造方法,防止外部实例化private HungrySingleton() {}// 提供公共的静态方法获取实例public static HungrySingleton getInstance() {return instance;}
}
优点:
- 实现简单。
- 线程安全,因为实例在类加载时就创建了。
缺点:
- 如果实例占用资源较多且未被使用,会造成资源浪费。
懒汉式单例模式
懒汉式单例模式在第一次需要使用实例时才创建,避免了资源浪费。
public class LazySingleton {// 静态变量保存单例实例private static LazySingleton instance;// 私有构造方法,防止外部实例化private LazySingleton() {}// 提供公共的静态方法获取实例public static LazySingleton getInstance() {if (instance == null) {instance = new LazySingleton();}return instance;}
}
优点:
- 延迟加载,只有在需要时才创建实例。
缺点:
- 线程不安全,多线程环境下可能会创建多个实例。
双重检查锁定
双重检查锁定在懒汉式单例模式的基础上,使用同步块和双重检查机制来保证线程安全。
public class DoubleCheckedLockingSingleton {// volatile 确保 instance 的可见性和有序性private static volatile DoubleCheckedLockingSingleton instance;// 私有构造方法,防止外部实例化private DoubleCheckedLockingSingleton() {}// 提供公共的静态方法获取实例public static DoubleCheckedLockingSingleton getInstance() {if (instance == null) {synchronized (DoubleCheckedLockingSingleton.class) {if (instance == null) {instance = new DoubleCheckedLockingSingleton();}}}return instance;}
}
优点:
- 线程安全。
- 延迟加载,性能较好。
缺点:
- 实现较为复杂,需要理解 volatile 关键字和双重检查机制。
静态内部类
静态内部类方式利用 JVM 类加载机制来保证线程安全,同时实现延迟加载。
public class StaticInnerClassSingleton {// 私有构造方法,防止外部实例化private StaticInnerClassSingleton() {}// 静态内部类private static class SingletonHolder {private static final StaticInnerClassSingleton INSTANCE = new StaticInnerClassSingleton();}// 提供公共的静态方法获取实例public static StaticInnerClassSingleton getInstance() {return SingletonHolder.INSTANCE;}
}
优点:
- 线程安全。
- 延迟加载。
- 实现简单,利用 JVM 的类加载机制,避免了同步开销。
缺点:
- 无法提前创建实例。
枚举单例
枚举单例利用枚举类型本身的特性来保证单例模式的实现,是最简单且最安全的实现方式。
public enum EnumSingleton {INSTANCE;// 可以添加其他方法public void someMethod() {// 方法实现}
}
优点:
- 线程安全。
- 防止反序列化破坏单例。
- 防止反射攻击。
缺点:
- 无法实现延迟加载(但通常不需要,因为 JVM 保证枚举类的唯一性)。
单例模式的线程安全问题
多线程环境下的单例模式
在多线程环境中,单例模式的实现需要特别注意线程安全问题。多个线程同时访问单例类的实例获取方法时,可能会导致创建多个实例,违背单例模式的初衷。为了确保单例模式在多线程环境下的正确性,需要采取一些措施来保证线程安全。
线程安全的实现方式
1. 懒汉式单例模式(线程不安全)
最简单的懒汉式单例实现并没有考虑线程安全,在多线程环境下可能会创建多个实例。
public class LazySingleton {private static LazySingleton instance;private LazySingleton() {}public static LazySingleton getInstance() {if (instance == null) {instance = new LazySingleton();}return instance;}
}
2. 懒汉式单例模式(线程安全,使用同步方法)
通过在 getInstance 方法上加 synchronized 关键字,确保每次只有一个线程能够执行该方法,从而保证线程安全。
public class SynchronizedLazySingleton {private static SynchronizedLazySingleton instance;private SynchronizedLazySingleton() {}public static synchronized SynchronizedLazySingleton getInstance() {if (instance == null) {instance = new SynchronizedLazySingleton();}return instance;}
}
优点:
- 简单易实现。
- 线程安全。
缺点:
- 每次调用
getInstance方法时都需要进行同步,性能开销较大。
3. 双重检查锁定
双重检查锁定在第一次检查实例是否为 null 时不加锁,只有在实例为 null 时才进行同步操作,从而减少了同步开销。
public class DoubleCheckedLockingSingleton {private static volatile DoubleCheckedLockingSingleton instance;private DoubleCheckedLockingSingleton() {}public static DoubleCheckedLockingSingleton getInstance() {if (instance == null) {synchronized (DoubleCheckedLockingSingleton.class) {if (instance == null) {instance = new DoubleCheckedLockingSingleton();}}}return instance;}
}
优点:
- 线程安全。
- 相比同步方法,性能更高。
缺点:
- 实现复杂,需要注意
volatile关键字的使用。
4. 静态内部类
利用 Java 的类加载机制,静态内部类在被调用时才会被加载,从而实现延迟加载和线程安全。
public class StaticInnerClassSingleton {private StaticInnerClassSingleton() {}private static class SingletonHolder {private static final StaticInnerClassSingleton INSTANCE = new StaticInnerClassSingleton();}public static StaticInnerClassSingleton getInstance() {return SingletonHolder.INSTANCE;}
}
优点:
- 线程安全。
- 实现简单,延迟加载。
缺点:
- 无法防止反射攻击。
5. 枚举单例
枚举类型本身是线程安全的,并且枚举的实例是全局唯一的,可以防止反射和序列化导致的重新创建实例问题。
public enum EnumSingleton {INSTANCE;public void someMethod() {// some method}
}
优点:
- 线程安全。
- 防止反射和序列化导致的重新创建实例问题。
缺点:
- 枚举类型在某些情况下可能不适用(例如需要继承其他类)。
单例模式的序列化与反序列化
序列化导致的问题
在 Java 中,单例模式可能会由于序列化和反序列化而被破坏。具体来说,当一个单例对象被序列化到文件中,然后再从文件中反序列化回来时,会创建一个新的实例,这样就违反了单例模式的原则。
例如,考虑以下单例类:
import java.io.Serializable;public class Singleton implements Serializable {private static final long serialVersionUID = 1L;private static final Singleton instance = new Singleton();private Singleton() {}public static Singleton getInstance() {return instance;}
}
如果我们进行序列化和反序列化操作:
import java.io.*;public class SingletonSerializationTest {public static void main(String[] args) throws Exception {Singleton instanceOne = Singleton.getInstance();// 序列化对象到文件ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("singleton.ser"));out.writeObject(instanceOne);out.close();// 从文件中反序列化对象ObjectInputStream in = new ObjectInputStream(new FileInputStream("singleton.ser"));Singleton instanceTwo = (Singleton) in.readObject();in.close();System.out.println("instanceOne hashCode: " + instanceOne.hashCode());System.out.println("instanceTwo hashCode: " + instanceTwo.hashCode());}
}
输出可能会是两个不同的哈希码,表明反序列化创建了一个新的实例。
如何防止反序列化破坏单例
为了防止反序列化破坏单例,可以在单例类中实现 readResolve 方法。这个方法在反序列化时会被自动调用,返回当前的单例实例,从而确保反序列化不会创建新的实例。
import java.io.Serializable;public class Singleton implements Serializable {private static final long serialVersionUID = 1L;private static final Singleton instance = new Singleton();private Singleton() {}public static Singleton getInstance() {return instance;}// 添加 readResolve 方法protected Object readResolve() {return getInstance();}
}
现在,如果我们再次运行序列化和反序列化操作,输出将会是相同的哈希码,表明反序列化没有创建新的实例:
import java.io.*;public class SingletonSerializationTest {public static void main(String[] args) throws Exception {Singleton instanceOne = Singleton.getInstance();// 序列化对象到文件ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("singleton.ser"));out.writeObject(instanceOne);out.close();// 从文件中反序列化对象ObjectInputStream in = new ObjectInputStream(new FileInputStream("singleton.ser"));Singleton instanceTwo = (Singleton) in.readObject();in.close();System.out.println("instanceOne hashCode: " + instanceOne.hashCode());System.out.println("instanceTwo hashCode: " + instanceTwo.hashCode());}
}
通过实现 readResolve 方法,可以确保序列化和反序列化过程中不会破坏单例模式,从而维护单例的唯一性。
单例模式的反射攻击
反射攻击导致的问题
Java 的反射机制允许在运行时动态地创建对象、调用方法和访问字段。通过反射,可以绕过私有构造函数,直接创建单例类的新实例,从而破坏单例模式。
例如,考虑以下单例类:
public class Singleton {private static final Singleton instance = new Singleton();private Singleton() {}public static Singleton getInstance() {return instance;}
}
使用反射可以创建新的实例:
Singleton instance1 = Singleton.getInstance();Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton instance2 = constructor.newInstance();System.out.println(instance1 == instance2); // 输出 false
如何防止反射破坏单例
为了防止反射攻击,可以在单例类的构造函数中加入防御措施,确保在构造函数被多次调用时抛出异常。
public class Singleton {private static final Singleton instance = new Singleton();private static boolean instanceCreated = false;private Singleton() {if (instanceCreated) {throw new RuntimeException("单例实例已经存在,不能创建多个实例");}instanceCreated = true;}public static Singleton getInstance() {return instance;}
}
在上述代码中,instanceCreated 标志用于检测是否已经创建过实例。如果构造函数被再次调用,则抛出异常,防止通过反射创建新的实例。
使用枚举类型防止反射和序列化攻击
使用枚举类型实现单例模式是防止反射和序列化攻击的最简单和最有效的方法。枚举类型在 Java 中是天然的单例,并且防止反射攻击和序列化攻击。
public enum EnumSingleton {INSTANCE;public void someMethod() {// some method}
}
枚举类型的单例不仅简单,而且可以防止反射和序列化导致的单例破坏问题。
单例模式的实际应用案例
数据库连接池管理
数据库连接池是一个典型的单例模式应用场景。通过单例模式,确保整个应用程序只存在一个数据库连接池实例,从而有效管理数据库连接资源。
public class DatabaseConnectionPool {private static DatabaseConnectionPool instance;private ConnectionPool pool;private DatabaseConnectionPool() {// 初始化连接池}public static synchronized DatabaseConnectionPool getInstance() {if (instance == null) {instance = new DatabaseConnectionPool();}return instance;}public Connection getConnection() {return pool.getConnection();}
}
日志管理
日志管理也是单例模式的一个常见应用。通过单例模式,确保整个应用程序使用同一个日志记录器实例,从而统一管理日志输出。
public class Logger {private static Logger instance;private Logger() {// 初始化日志配置}public static synchronized Logger getInstance() {if (instance == null) {instance = new Logger();}return instance;}public void log(String message) {// 记录日志}
}
配置文件管理
配置文件管理通常也使用单例模式,确保整个应用程序只加载一次配置文件,并提供统一的接口访问配置数据。
public class ConfigurationManager {private static ConfigurationManager instance;private Properties properties;private ConfigurationManager() {// 加载配置文件properties = new Properties();try (InputStream input = new FileInputStream("config.properties")) {properties.load(input);} catch (IOException ex) {ex.printStackTrace();}}public static synchronized ConfigurationManager getInstance() {if (instance == null) {instance = new ConfigurationManager();}return instance;}public String getProperty(String key) {return properties.getProperty(key);}
}
总结
单例模式的最佳实践
- 私有构造函数:确保构造函数是私有的,防止外部直接实例化。
- 静态方法获取实例:通过静态方法获取唯一的实例。
- 防止反射攻击:在构造函数中添加检查,防止反射攻击。
- 防止序列化破坏:实现
readResolve方法,防止反序列化创建新的实例。 - 线程安全:在多线程环境中,使用合适的方式(如双重检查锁定、静态内部类、枚举单例)确保线程安全。
单例模式的使用建议
- 资源管理:适用于需要全局管理的资源,如数据库连接池、日志记录器、配置文件等。
- 性能考虑:在性能敏感的应用中,确保单例的创建和访问是高效的。
- 避免过度使用:虽然单例模式有其优势,但过度使用可能导致代码难以测试和维护。在设计时应慎重考虑是否真的需要单例模式。
- 测试友好:在单元测试中,可以使用依赖注入或其他设计模式来替代单例,以便于测试。
通过遵循这些最佳实践和使用建议,可以在项目中有效地应用单例模式,确保其稳定性和可维护性。
相关文章:
【设计模式】单例模式 在java中的应用
文章目录 引言什么是单例模式单例模式的应用场景单例模式的优缺点优点缺点 单例模式的基本实现饿汉式单例模式懒汉式单例模式双重检查锁定静态内部类枚举单例 单例模式的线程安全问题多线程环境下的单例模式线程安全的实现方式1. **懒汉式单例模式(线程不安全&#…...
burp suite 8
声明! 学习视频来自B站up主 **泷羽sec** 有兴趣的师傅可以关注一下,如涉及侵权马上删除文章,笔记只是方便各位师傅的学习和探讨,文章所提到的网站以及内容,只做学习交流,其他均与本人以及泷羽sec团队无关&a…...
为什么在Java中super与this不能共存于子类构造器中,其中this起什么作用
在 Java 中,super 和 this 是两个关键字,它们在子类的构造器中有特定的用途和限制。 super 关键字: super 用于从父类(超类)访问成员(属性和方法)或者调用父类的构造方法。 在子类的构造器中&…...
Hypothesis:高效的 Python 测试工具
简介:Hypothesis 是一个强大的 Python 测试库,旨在自动生成各种测试案例,以帮助开发者发现潜在的边界问题和隐藏的错误。通过对输入数据进行智能化的探索,Hypothesis 能够为测试提供更全面的覆盖,避免遗漏一些极端或不…...
Terminus Calculator 计算原理分享
在《使命召唤:黑色行动 6》僵尸模式中,Terminus 关卡的研究办公室里有一个复杂的数学谜题需要解决。为了获得多相共振器,玩家需要计算出三个数字并输入电脑。虽然可以花费 5000 精华来获得答案,但使用 Terminus Calculator 可以更…...
Wwise 使用MIDI文件、采样音频
第一种:当采样音频只有一个文件的时候 1.拖入MIDI文件到Interactive Music Hierarchy层级 2.拖入采样音频到Actor-Mixer Hierarchy层级 3.勾选MIDI显示出面板,设置Root Note与采样音频音高相同,这里是C#5 4.播放测试,成功&…...
在CentOS上无Parallel时并发上传.wav文件的Shell脚本解决方案
在CentOS上无Parallel时并发上传.wav文件的Shell脚本解决方案 背景概述解决方案脚本实现脚本说明使用指南注意事项在CentOS操作系统环境中,若需并发上传特定目录下的.wav文件至HTTP服务器,而系统未安装GNU parallel工具,我们可通过其他方法实现此需求。本文将介绍一种利用Sh…...
【RocketMQ 源码分析(一)】设计理念与源码阅读技巧
RocketMQ 的设计理念与源码阅读技巧 一、设计理念二、源码设计三、源码阅读技巧 一直想仔细仔细看看这个 RocketMQ 的源码,学学它的设计思想和编码风格,没准在以后自己在设计和编码的时候有思考的方向。这是专栏的第一篇 —— 介绍下 RocketMQ 的一些设计…...
独立ip服务器有什么优点?
网站的性能和安全性直接影响到用户体验和业务发,独立IP服务器作为一种主流的托管方式,因其独特的优势而受到许多企业和个人站长的青睐。与共享IP相比,独立IP服务器到底有哪些优点呢? 使用独立IP的用户不必担心与其他网站共享同一…...
如何使用Python库连接Redis
1、redis-py 库封装一个 Redis 工具类可以帮助我们简化 Redis 的操作并提高代码的复用性和可维护性。 安装redis pip install redisimport redis import logginglogging.basicConfig(levellogging.INFO) logger logging.getLogger(__name__)class RedisUtils:def __init__(s…...
Vant UI +Golang(gin) 上传文件
前端基本用法:点击查看 实现代码: const afterRead (file) > {console.log(file);//set content-type to multipart/form-dataconst formData new FormData();formData.append("file", file.file);request.POST("/api/v1/users/up…...
【Unity高级】如何实现粒子系统的间歇式喷射
先看下要最终实现的效果: 代码如下: using UnityEngine; using System.Collections;public class ParticleBurstController : MonoBehaviour {private ParticleSystem _particleSystem; // 获取粒子系统public float burstDuration 2f; // 每次…...
通过linux命令获取自选股票价格及大盘涨跌幅
技术发展与数据获取需求 互联网与金融数据融合:随着互联网的普及和金融市场的数字化发展,金融数据的获取和分析变得更加便捷和重要。投资者希望能够及时、准确地获取股票价格和市场指数等信息,以便做出合理的投资决策。Linux 作为一种强大的操作系统,为数据获取和处理提供…...
透彻理解并解决Mockito模拟框架的单元测试无法运行的问题
本篇的实例基于Maven IDE (VS Code) 运行 在VS Code 运行的时候, 不需要在pom.xml 中添加任何插件就可以在测试类中看到如下的绿色按钮,单击就可以运行使用Mockito 注解 ExtendWith(MockitoExtension.class) 或是 Mockito 代码方式的测试。 不使用注…...
vue3字典数据的显示问题(使用hooks解决)
我们在使用 element-plus的时候,经常会使用一些字典数据, 在搜索框的时候,字典数数要使用 el-select el-option 来显示,当在table表格的时候,我们通常记录的是 字典数据的id , 又要把它改变成 字典数据的 name 属性 因…...
Elasticsearch 单节点安全配置与用户认证
Elasticsearch 单节点安全配置与用户认证 安全扫描时发现了一个高危漏洞:Elasticsearch 未授权访问 。在使用 Elasticsearch 构建搜索引擎或处理大规模数据时,需要启用基本的安全功能来防止未经授权的访问。本文将通过简单的配置步骤,为单节…...
二分查找(带图详解)
优选算法系列 文章目录 优选算法系列前言一、二分查找的思想二、算法使用小总结 三、代码实现四、二分查找拓展4.1、查找第一次出现的target小总结 4.2、target最后出现的位置小总结 五、代码总结 前言 在这篇博客中,我会给大家分享二分查找及其扩展。 这是链接-&…...
【Git】:标签管理
目录 理解标签 创建标签 操作标签 理解标签 标签的作用 标记版本:标签 tag ,可以简单的理解为是对某次 commit 的⼀个标识,相当于起了⼀个别名。例如,在项目发布某个版本的时候,针对最后⼀次 commit 起⼀个 v1.0 这样…...
物品识别 树莓派 5 YOLO v5 v8 v10 11 计算机视觉
0. 要实现的效果 让树莓派可以识别身边的一些物品,比如电脑,鼠标,键盘,杯子,行李箱,双肩包,床,椅子等 1. 硬件设备 树莓派 5 raspberrypi.com/products/raspberry-pi-5/树莓派官方摄…...
单片机软件工程师前景分析
单片机软件工程师的前景在2024年看起来是积极的。随着物联网(IoT)、自动化、智能设备等领域的快速发展,对于能够开发基于单片机(MCU)如STM32、ARM、51等嵌入式系统的软件工程师需求持续增长。这些工程师负责设计和实现…...
Flask RESTful 示例
目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题: 下面创建一个简单的Flask RESTful API示例。首先,我们需要创建环境,安装必要的依赖,然后…...
《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...
【Java_EE】Spring MVC
目录 Spring Web MVC 编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 编辑参数重命名 RequestParam 编辑编辑传递集合 RequestParam 传递JSON数据 编辑RequestBody …...
项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)
Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败,具体原因是客户端发送了密码认证请求,但Redis服务器未设置密码 1.为Redis设置密码(匹配客户端配置) 步骤: 1).修…...
Java多线程实现之Thread类深度解析
Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...
【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...
sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!
简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求,并检查收到的响应。它以以下模式之一…...
【7色560页】职场可视化逻辑图高级数据分析PPT模版
7种色调职场工作汇报PPT,橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版:职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...
虚拟电厂发展三大趋势:市场化、技术主导、车网互联
市场化:从政策驱动到多元盈利 政策全面赋能 2025年4月,国家发改委、能源局发布《关于加快推进虚拟电厂发展的指导意见》,首次明确虚拟电厂为“独立市场主体”,提出硬性目标:2027年全国调节能力≥2000万千瓦࿰…...
