highlight: a11y-dark
别怕,我会一直陪着你的。。。
github.com/YaYiXiBa/Ja… 欢迎去看我的代码哦。。。
| 分类 | 模式名称 | 定义 | 典型应用场景 |
|---|---|---|---|
| 创建型模式 | 单例 (Singleton) | 确保类只有一个实例,并提供全局访问点 | 数据库连接池、日志处理器 |
| 工厂方法 (Factory Method) | 定义创建对象的接口,让子类决定实例化哪个类 | 跨平台UI组件、多数据库驱动 | |
| 抽象工厂 (Abstract Factory) | 创建相关或依赖对象的家族,无需指定具体类 | 跨操作系统UI套件 | |
| 建造者 (Builder) | 分步骤构建复杂对象,分离构造与表示 | HTTP请求构造器、复杂配置对象 | |
| 原型 (Prototype) | 通过克隆现有对象创建新对象 | 游戏角色复制、高性能对象创建 | |
| 结构型模式 | 适配器 (Adapter) | 转换接口使不兼容的类能协同工作 | 旧系统接口改造、第三方库适配 |
| 桥接 (Bridge) | 将抽象与实现分离,使二者可独立变化 | 多维度扩展系统(如形状+渲染方式) | |
| 组合 (Composite) | 将对象组合成树形结构以表示"部分-整体"层次 | 文件系统、GUI组件树 | |
| 装饰器 (Decorator) | 动态地给对象添加额外职责 | 数据流加密/压缩、中间件栈 | |
| 外观 (Facade) | 为子系统提供统一的高层接口 | 简化复杂API调用 | |
| 享元 (Flyweight) | 通过共享技术有效支持大量细粒度对象 | 文本编辑器字符对象、棋牌游戏棋子 | |
| 代理 (Proxy) | 为其他对象提供代理以控制访问 | 图片懒加载、远程方法调用 | |
| 行为型模式 | 责任链 (Chain of Responsibility) | 将请求沿处理链传递,直到有对象处理它 | 审批流程、异常处理链 |
| 命令 (Command) | 将请求封装为对象,支持参数化、队列化等操作 | 撤销/重做、宏命令 | |
| 解释器 (Interpreter) | 定义语言的文法表示,并解释执行 | SQL解析、正则表达式引擎 | |
| 迭代器 (Iterator) | 提供顺序访问聚合对象元素的方法 | 自定义集合遍历 | |
| 中介者 (Mediator) | 通过中介对象封装一组对象交互 | 聊天室消息调度、GUI组件通信 | |
| 备忘录 (Memento) | 捕获对象内部状态并在需要时恢复 | 游戏存档、事务回滚 | |
| 观察者 (Observer) | 定义对象间一对多的依赖关系,状态变化时自动通知 | 事件通知系统、数据绑定 | |
| 状态 (State) | 允许对象在其内部状态改变时改变行为 | 订单状态机、游戏角色行为切换 | |
| 策略 (Strategy) | 定义算法家族并使其可互换 | 支付方式选择、排序算法切换 | |
| 模板方法 (Template Method) | 定义算法骨架,将某些步骤延迟到子类实现 | 框架钩子设计、标准化流程 | |
| 访问者 (Visitor) | 将算法与对象结构分离,在不修改结构的前提下添加新操作 | 抽象语法树处理、复杂对象结构统计 |
一:单例模式
单例模式其实十分常见,但是单独去学习的时候会发现有好多好多种,不要死记硬背,从最简单的开始,慢慢扩展。
饿汉式懒汉式老搞混?铁汁你记住,懒汉就是懒加载,另外一个就是饿汉。。。
饿汉式
- 优点:简单、线程安全
- 缺点:可能造成资源浪费(即使不用也会加载实例)
public class HungrySingleton {
/**
* private:私有的属性
* static:静态属性,作为类对象的成员,在加载式赋值。同时避免了线程安全,因为类只会被加载一次。
* final:类的确是只会被加载一次,但是会不会被修改呢?final就强制要求不可以再对其修改,保证了单例不变。
*/
private static final HungrySingleton singleton = new HungrySingleton();
/**
* 将构造方法定义为私有,使得类外无法再new出新的单例,只能在类加载的时候使用。
*/
private HungrySingleton(){
}
/**
* static:想一下我们现在提供的访问入口来自于谁?如果不加static是不是没有办法通过类名来访问?
*/
public static HungrySingleton getSingle(){
return singleton;
}
}
如此,使用的时候就可以HungrySingleton.getSingle()了。好好看我写的注释啊。
懒汉式
饿汉式即使不用也会加载?那怎么办?我用的时候再加载不就好了。
class LazySingleton {
/**
* static:这次初始化的时候直接将其定义为null
*/
private static LazySingleton instance; // 初始为null
public static LazySingleton getInstance() {
if (instance == null) { // 第一次调用时创建
instance = new LazySingleton();
}
return instance;
}
}
上述代码其实还有一个问题,如果同一时间段内getInstance被调用。两个线程一起到达了if (instance == null)。但是A线程先执行了instance = new LazySingleton();而B线程等A创建完之后也创建了一次,是不是会导致重复实例化?解决方式其实也很简单,限制一下只能有一个线程进入到 getInstance()方法不就可以了。
// 懒汉式(延迟初始化)
class LazySingleton {
/**
* static:这次初始化的时候直接将其定义为null
*/
private static LazySingleton instance; // 初始为null
public static synchronized LazySingleton getInstance() {
if (instance == null) { // 第一次调用时创建
instance = new LazySingleton();
}
return instance;
}
}
ps:有没有发现懒汉式不加final修饰了,因为是延迟赋值啊,初始化的时候是null,调用getInstance的时候才进行赋值。并且,final的作用不就是为了防止被修改吗,我们的同步方法的getInstance其实已经做了这一判断。
优点:解决了饿汉式的可能造成资源浪费(即使不用也会加载实例)的问题。 缺点:每次调用都同步,性能差。
双重校验锁
不想每次调用都走同步方法。行!
synchronized又不是只能写在方法前面。。。
public class DCLSingleton {
/**
* volatile:值改变对于其他线程立即可变,替代了final
*/
private static volatile DCLSingleton singleton;
private DCLSingleton(){
}
public static DCLSingleton getSingleton(){
if(singleton == null){//第一次判断
synchronized (DCLSingleton.class){//同步上锁,锁class
if(singleton == null){//第二次判断
singleton = new DCLSingleton();
}
}
}
return singleton;
}
}
这样是不是就避免了?只在【if(singleton == null){//第一次判断】的时候走到同步方法里,但是呢,又因为“第一次判断”我们是没有上锁的有可能存在A线程执行完同步代码,同时B线程认为singleton == null的情况,接着走到同步代码里面。所以加上第二次判断。
登记式/静态内部类
这篇关于内部类的文章说的很好,不知道什么是内部类看下www.cnblogs.com/GrimMjx/p/1… 这个其实理解起来有点难度,谁研究的呢。。。
public class InnerClassSingleton {
/**
* 静态内部类持有外部类的实例
*/
private static class Holder{
/**
*
* 切记:静态内部类只有在首次被主动引用时才会加载和初始化,也就是说在加载外部类的时候不会加载内部类,只有调用getSingle()的时候才触发
* private static final:饿汉式的代码,类加载的时候直接指定single只不过将new Single改成了new InnerClassSingleton,内部类持有外部类的实例;
*/
private static final InnerClassSingleton single = new InnerClassSingleton();
}
private InnerClassSingleton(){
}
/**
* 首次调用的时候会触发Holder类的加载
* @return
*/
public static InnerClassSingleton getSingle(){
return Holder.single;
}
}
这里实际上是利用了JVM的类加载机制,来保证Holder只会被加载一次,那么Holder的class对象也就只会存在一个只会有一个被持有的实例。既是线程安全的,又是懒加载,又不需要加额外的锁,利用的是JVM的机制。
枚举
public enum EnumSingle {
INSTANCE("app-config.properties");
private Properties config;
private EnumSingle(String configPath) {
// 加载配置
config = new Properties();
try (InputStream is = getClass().getClassLoader()
.getResourceAsStream(configPath)) {
config.load(is);
} catch (IOException e) {
throw new RuntimeException("加载配置失败", e);
}
}
public String getProperty(String key) {
return config.getProperty(key);
}
}
这个方式其实就是利用了枚举本身就是单例的特性,绝对的线程安全,如果涉及到反序列化的话可以尝试使用。 一般情况下饿汉式就足够,如果要求懒加载再考虑懒汉式,如果又要求线程安全就再加锁。