【设计模式】结构型设计模式总结之代理模式、装饰模式、外观模式、享元模式

文章目录

  • 代理模式
    • 示例
    • 结构
    • 分类
    • 动态代理
  • 装饰模式
    • 示例
    • 结构
    • 使用场景
    • 与代理模式区别
    • Context
  • 外观模式
    • 结构
    • 示例
    • 使用场景
    • Context
  • 享元模式
    • 结构
    • 示例
    • 使用场景
    • Message

代理模式

代理模式(Proxy Pattern) 是一种结构型设计模式,它提供了一个代理对象,控制对目标对象的访问。代理对象通常在客户端与目标对象之间起到中介的作用,用于扩展目标对象的功能。

定义:为其他对象提供一种代理以控制对这个对象的访问

示例

定义接口

// 公共接口
public interface IShop {
    void buy();
}

实现真实类

// 真实对象
public class Buyer implements IShop{
    @Override
    public void buy() {
        System.out.println("购买");
    }
}

定义代理类

// 代理类
public class Buying implements IShop{
    private IShop iShop;

    public Buying(IShop iShop) {
        this.iShop = iShop;
    }

    @Override
    public void buy() {
        iShop.buy();
    }
}

测试代码

public class Client {
    public static void main(String[] args) {
        // 创建真实对象
        IShop person1 = new Buyer();
        // 创建代理对象
        IShop proxy = new Buying(person1);
        // 使用代理对象
        proxy.buy();
    }
}

结构

Subject(抽象主题):定义目标对象和代理对象的公共接口。

RealSubject(真实主题):实现了Subject接口,定义了具体的业务逻辑。

Proxy(代理):代理对象,包含对真实主题的引用,并且可以在对真实主题的调用前后添加额外的功能。

1733493914683

分类

  1. 静态代理

静态代理是指在编译时就已经确定代理类的实现,通常是手动编写代理类。静态代理的关键特点是代理类与目标类之间有一一对应的关系。

  1. 动态代理

动态代理是在运行时动态生成代理对象,而不是在编译时就明确写好的。Java提供了通过反射机制动态的生成代理对象的机制

动态代理

java提供了java.lang.reflect.InvocationHandler,一个便捷的动态代理接口,实现它要重写其调用方法invoke

// 目标接口
public interface Subject {
    void request();
}

// 目标类
public class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("真实主题:处理请求。");
    }
}

// 动态代理的处理器
public class DynamicProxyHandler implements InvocationHandler {
    private Object target;

    public DynamicProxyHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理:在真实主题处理请求之前。");
        Object result = method.invoke(target, args);  // 调用目标方法
        System.out.println("代理:在真实主题处理请求之后。");
        return result;
    }
}

// 测试
public class DynamicProxyDemo {
    public static void main(String[] args) {
        // 创建一个真实的目标对象
        RealSubject realSubject = new RealSubject();

        // 使用Proxy创建代理对象
        Subject proxy = (Subject) Proxy.newProxyInstance(
            // 目标类的类加载器
            realSubject.getClass().getClassLoader(),
            
            // 目标类实现的接口
            new Class[] {Subject.class},  
            
            // 代理的处理器(即动态代理的逻辑)
            new DynamicProxyHandler(realSubject)
        );
        
        // 调用代理对象的方法
        proxy.request();
    }
}

装饰模式

定义

动态地向对象添加额外的职责,而不改变其结构。

使用场景

需要透明且动态地扩展类的功能时

示例

// 抽象组件类
public abstract class Component {
    // 抽象操作方法,由子类实现
    public abstract void operate();
}
// 具体组件类
public class ConcreteComponent extends Component {
    @Override
    public void operate() {
        System.out.println("执行基本操作");
    }
}
// 装饰者基类
public abstract class Decorator extends Component {
    protected Component component; // 持有组件对象的引用

    public Decorator(Component component) {
        this.component = component;
    }

    @Override
    public void operate() {
        component.operate(); // 调用组件的操作
    }
}
// 具体装饰者实现类
public class ConcreteDecoratorA extends Decorator {
    public LoggingDecorator(Component component) {
        super(component);
    }

    @Override
    public void operate() {
        System.out.println("开始");
        super.operate(); // 调用原始操作
        System.out.println("结束");
    }
}
public class DecoratorPatternDemo {
    public static void main(String[] args) {
        // 创建具体组件
        Component component = new ConcreteComponent();

        // 根据组件对象构造装饰者componentA并调用
        Component componentA = new ConcreteDecoratorA(component);
        componentA.operate();
    }
}

1733658580628

结构

  1. 抽象组件(Component): 一个接口或抽象类,被装饰的原始对象
  2. 具体组件(ConcreteComponent): 抽象组件的具体实现,是被装饰的核心对象
  3. 抽象装饰者(Decorator): 抽象类或接口,持有一个组件对象的引用,并定义与组件一致的接口
  4. 具体装饰者(ConcreteDecorator): 抽象装饰者实现类,对抽象装饰者做出具体的实现

使用场景

  1. 动态扩展一个类的功能。
  2. 替代多层次的继承结构。
  3. 当不能直接修改类或不希望影响其他对象时。

与代理模式区别

装饰模式

  • 目的:装饰模式用于动态地扩展一个对象的功能,且对客户端透明。它是继承关系的替代方案,可以通过包装原对象并为其添加新的功能,而不改变原对象的结构。
  • 使用场景:当你需要扩展对象的功能时,不希望直接修改原有的类时

代理模式

  • 目的:代理模式用于为其他对象提供一个替代品或代理对象,以便通过代理对象来控制对原对象的访问。代理对象本身不增强原对象的功能,而是控制访问、延迟加载、安全检查等。
  • 使用场景:当你希望控制对某个对象的访问(例如,延迟加载、访问权限控制、日志记录等)时,可以使用代理模式。

装饰模式强调对对象功能的增强和扩展。

代理模式关注对对象的访问控制、管理、替代。

核心区别:装饰模式是为了增强原对象的功能,而代理模式是为了控制原对象的访问。

Context

59696fbdc60c2005397adbbcf2f24f7b

// 抽象组件
public abstract class Context {
    public abstract void startActivity(@RequiresPermission Intent intent);
    public void startActivity(Intent intent, Bundle options);
}
// 具体组件实现类
class ContextImpl extends Context{
    @Override
    public void startActivity(Intent intent) {
        warnIfCallingFromSystemProcess();
        startActivity(intent, null);
    }
    
    @Override
    public void startActivity(Intent intent, Bundle options) {
        warnIfCallingFromSystemProcess();

        final int targetSdkVersion = getApplicationInfo().targetSdkVersion;

        if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) == 0
            && (targetSdkVersion < Build.VERSION_CODES.N
                || targetSdkVersion >= Build.VERSION_CODES.P)
            && (options == null
                || ActivityOptions.fromBundle(options).getLaunchTaskId() == -1)) {
            throw new AndroidRuntimeException(
                "Calling startActivity() from outside of an Activity"
                + " context requires the FLAG_ACTIVITY_NEW_TASK flag."
                + " Is this really what you want?");
        }
        mMainThread.getInstrumentation().execStartActivity(
            getOuterContext(), mMainThread.getApplicationThread(), null,
            (Activity) null, intent, -1, options);
    }
}
// 装饰者,持有ContextImpl的对象
public class ContextWrapper extends Context {
    Context mBase;

    @Override
    public void startActivity(Intent intent) {
        mBase.startActivity(intent);
    }

    @Override
    public void startActivity(Intent intent, @Nullable Bundle options) {
        mBase.startActivity(intent, options);
    }
}
Activity extends ContextThemeWrapper
ContextThemeWrapper extends ContextWrapper

Activity就是具体装饰者

ContextImpl的创建和ContextWrapperContextImpl引用在ActivityThreadmain函数中进行

外观模式

外观模式(Facade Pattern)是一种结构型设计模式,它为复杂的子系统提供一个简单的接口。

目的是简化系统的使用方式,使得调用者可以通过一个统一的入口来访问系统中的多个子系统,而不需要关心子系统的内部实现细节。

结构

外观模式通常包含以下几个角色:

  1. Facade(外观类):提供一个简化的接口,委托请求给子系统。

  2. Subsystem(子系统类):各个独立的子系统,完成具体的业务逻辑。

1733665652587

示例

// 外观类:简化多个功能操作的接口
class SmartphoneFacade {
    private Camera camera = new Camera();
    private MusicPlayer musicPlayer = new MusicPlayer();

    public void takePhotoAndPlayMusic() {
        camera.open();
        camera.takePhoto();
        musicPlayer.play();
    }

    public void stopMusicAndCloseCamera() {
        musicPlayer.stop();
        camera.open(); 
    }
}

// 子系统相机
class Camera {
    public void open() {
        System.out.println("打开相机");
    }

    public void takePhoto() {
        System.out.println("拍照");
    }
}
// 子系统音乐播放器类似

// 客户端
public class FacadePatternExample {
    public static void main(String[] args) {
        SmartphoneFacade smartphone = new SmartphoneFacade();

        smartphone.takePhotoAndPlayMusic();
        smartphone.stopMusicAndCloseCamera();
    }
}

使用手机只需要调用相关方法,而不用去管CameraMusicPlayer的具体实现

使用场景

  • 当系统较为复杂时,使用外观模式可以简化与子系统的交互,提供一个更易于使用的接口。
  • 为多个子系统提供一个统一的接口。
  • 需要解耦子系统与外部代码的依赖关系。

优点

  • 简化接口:隐藏了系统的复杂性,提供了更简单的接口。
  • 降低耦合:客户端与子系统之间的耦合度降低,修改子系统的实现不会影响到客户端。

Context

Context封装了很多重要的操作,如 startActivitysendBroadcastbindService 等。因此,Context对开发者来说是最重要的高层接口。Context只是一个定义了很多接口的抽象类,这些接口的功能实现并不是在Context及其子类中,而是通过其他子系统来完成。

Context只是一个抽象类,它的真正实现在Contextlmpl类中,Contextlmpl就是外观类。

  1. startActivity()startActivity() 方法启动一个新的 Activity,但实际的启动过程是通过 ActivityManagerService 来完成的。
  2. sendBroadcast():底层的实现则是通过 BroadcastManager 来处理。
  3. bindService()bindService() 方法允许应用与服务建立连接,实际操作是通过 ServiceManager 来管理服务的绑定。

享元模式

享元模式用来尽可能减少内存使用量,它适合用于可能存在大量重复对象的场景,来缓存可共享的对象,达到对象共享、避免创建过多对象的效果,可以提升性能、避免内存移除等。

核心思想是复用已经存在的对象,而不是每次都创建新对象。

享元对象中的部分状态是可以共享,可以共享的状态成为内部状态,内部状态不会随着环境变化;不可共享的状态则称为外部状态,它们会随着环境的改变而改变。

在享元模式中会建立一个对象容器,在经典的享元模式中该容器为一个 Map,它的键是享元对象的内部状态,它的值就是享元对象本身。客户端程序通过这个内部状态从享元工厂中获取享元对象,如果有缓存则使用缓存对象,否则创建一个享元对象并且存入容器中,这样一来就避免了创建过多对象的问题。

结构

  1. 抽象享元(Flyweight):定义享元对象的基类或接口
  2. 具体享元(ConcreteFlyweight):实现抽象享元对象
  3. 享元工厂(FlyweightFactory):用于管理享元对象的创建和共享,确保享元对象的复用。

示例

在火车票预订系统中,有很多用户会购买相同类型、相同时间、相同座位的火车票。每次有用户购买相同的火车票时,我们不需要为每个用户创建一个新的火车票对象,而是可以共享相同的火车票对象。享元模式可以帮助我们避免为每个用户创建重复的对象,从而节省内存。

享元对象(Flyweight)

TrainTicket 类是享元对象,表示火车票的固定信息,例如车次、出发时间、座位类型等。

// 享元类:火车票
public class TrainTicket {
    private String trainNumber; // 车次
    private String departureTime; // 出发时间

    // 构造方法,初始化火车票的固定信息
    public TrainTicket(String trainNumber, String departureTime) {
        this.trainNumber = trainNumber;
        this.departureTime = departureTime;
    }

    // 购票
    public void serve(String passengerName) {
        System.out.println("乘车人:" + passengerName + " ,车次 " + trainNumber +
                           ",发车时间: " + departureTime);
    }
}

享元工厂(FlyweightFactory)

TrainTicketFactory 类是享元工厂,用于管理和共享火车票对象。

public class TrainTicketFactory {
    private Map<String, TrainTicket> ticketMap = new HashMap<>();

    public TrainTicket getTrainTicket(String trainNumber, String departureTime) {
        String key = trainNumber + departureTime;
        if (!ticketMap.containsKey(key)) {
            ticketMap.put(key, new TrainTicket(trainNumber, departureTime));
            System.out.println("购票成功,乘车人:" + trainNumber + ", 车次: " + departureTime);
        }
        return ticketMap.get(key); // 返回共享的火车票对象
    }
}

客户端

public class TrainStation {
    public static void main(String[] args) {
        TrainTicketFactory ticketFactory = new TrainTicketFactory();

        TrainTicket ticket1 = ticketFactory.getTrainTicket("G101", "10:00");
        TrainTicket ticket2 = ticketFactory.getTrainTicket("G101", "10:00");
        TrainTicket ticket3 = ticketFactory.getTrainTicket("D202", "14:00");

        ticket1.serve("Alice");
        ticket2.serve("Bob");
        ticket3.serve("Charlie");
    }
}
  • TrainTicket 类是享元对象,火车票的固定信息,相同的,可以被多个乘客共享。
  • TrainTicketFactory 类是享元工厂,维护了一个火车票对象池,确保每种车次、出发时间和座位类型的火车票只创建一次。如果有相同的请求,返回已有的火车票对象。
  • TrainStation 类模拟了多个乘客购买相同车次、相同时间的火车票。由于使用了享元模式,虽然有多个乘客,但共享了相同的火车票对象。

使用场景

  1. 对象创建代价高,且每个对象的内部状态差别不大。
  2. 需要优化程序性能,减少内存消耗。

IMG_20241208_205403

Message

在Handler中,使用对象池来管理 Message 对象,能够有效避免频繁的对象创建,减少内存占用和GC频率。

public final class Message implements Parcelable {
    Message next;
    // 同步锁的对象
    public static final Object sPoolSync = new Object();
    // 对象池的头部,具体实现是链表
    private static Message sPool;
    // 当前池中存储的 Message 对象数量
    private static int sPoolSize = 0;
    // 对象池的最大容量
    private static final int MAX_POOL_SIZE = 50;

    public static Message obtain() {
        // 线程安全
        synchronized (sPoolSync) {
            if (sPool != null) {
                // 从池中取出一个 Message 对象
                Message m = sPool;
                // 更新池的头部为下一个对象
                sPool = m.next;
                // 清空当前 Message 对象的链表连接,避免不必要的引用
                m.next = null;
                // 清除消息的标志位,表示该对象已被重用
                m.flags = 0;
                // 更新池中存储的对象数量
                sPoolSize--;
                return m;
            }
        }
        // 如果池中没有可复用的对象,创建一个新的 Message 对象并返回
        return new Message();
    }
    
   /**
     * 回收该 Message 对象,准备将其放入对象池中。
     * 该方法会检查 Message 是否还在使用中,若仍在使用则抛出异常。
     */
    public void recycle() {
        if (isInUse()) {  
            if (gCheckRecycle) {  
                throw new IllegalStateException("This message cannot be recycled because it "
                        + "is still in use.");
            }
            return;  
        }
        recycleUnchecked();
    }

    /**
     * 实际进行回收操作,将该对象状态清空,并加入对象池中以供复用。
     */
    void recycleUnchecked() {
        // 清除对象的各个字段,确保该对象回收后不再持有任何引用
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = UID_NONE;
        workSourceUid = UID_NONE;
        when = 0;
        target = null;
        callback = null;
        data = null;

        synchronized (sPoolSync) {  
            if (sPoolSize < MAX_POOL_SIZE) {  
                next = sPool;  
                sPool = this;  
                sPoolSize++;  
            }
        }
    }
}

Message 类承担了三个职责:

  1. Flyweight 抽象角色:提供了统一的接口来处理对象的共享和复用。
  2. ConcreteFlyweight 具体享元角色:实际存储对象的状态并处理业务逻辑。
  3. FlyweightFactory 工厂角色:管理对象池,复用和回收 Message 对象。


参考:

  1. 《设计模式之禅》
  2. 《Android进阶之光》
  3. 《Android源码设计模式解析与实战》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/932221.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

CSS一些小点 —— 12.7

1. box-sizing: border-box box-sizing 属性&#xff0c;默认值为 content-box box-sizing: border-box 使padding和border的值不会再影响元素的宽高&#xff1b;padding和border的值算在指定宽高的内部&#xff08;但是外边距依然算做外部&#xff09; 2. overflow: hidden …

【毕业设计选题】深度学习类毕业设计选题参考 开题指导

目录 前言 毕设选题 开题指导建议 更多精选选题 选题帮助 最后 前言 大家好,这里是海浪学长毕设专题! 大四是整个大学期间最忙碌的时光&#xff0c;一边要忙着准备考研、考公、考教资或者实习为毕业后面临的升学就业做准备,一边要为毕业设计耗费大量精力。学长给大家整…

算法基础学习Day4(双指针)

文章目录 1.题目2.题目解答1.查找总价格为目标值的两个商品1.1题目及题目解析1.2算法学习暴力解法优化算法 1.3代码提交 2.三数之和2.1题目及题目解析2.2算法学习2.3代码提交 1.题目 LCR 179. 查找总价格为目标值的两个商品 - 力扣&#xff08;LeetCode&#xff09;15. 三数之…

哪里可以找到高质量的街道夜景短视频素材?夜景素材网站推荐

在短视频创作的浪潮中&#xff0c;街道夜景作为一种视觉效果独特、氛围浓郁的题材&#xff0c;深受创作者的青睐。不论是商业广告、创意短片还是个人Vlog&#xff0c;街道夜景的视频素材都能为你的作品增添不小的分量。那么&#xff0c;在哪里可以找到这些高质量的街道夜景短视…

实习冲刺第四十三天

25.K个一组翻转链表 给你链表的头节点 head &#xff0c;每 k 个节点一组进行翻转&#xff0c;请你返回修改后的链表。 k 是一个正整数&#xff0c;它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍&#xff0c;那么请将最后剩余的节点保持原有顺序。 你不能只是单…

[计算机网络] HTTP/HTTPS

一. HTTP/HTTPS简介 1.1 HTTP HTTP&#xff08;超文本传输协议&#xff0c;Hypertext Transfer Protocol&#xff09;是一种用于从网络传输超文本到本地浏览器的传输协议。它定义了客户端与服务器之间请求和响应的格式。HTTP 工作在 TCP/IP 模型之上&#xff0c;通常使用端口 …

微信创建小程序码 - 数量不受限制

获取小程序码&#xff1a;小程序码为圆图&#xff0c;且不受数量限制。 目录 文档 接口地址 请求方式 功能描述 注意事项 获取 scene 值 请求参数 返回参数 对接 请求方法 获取小程序码 调用获取小程序码 总结 文档 接口地址 https://api.weixin.qq.com/wxa/get…

在做题中学习(79):最小K个数

解法&#xff1a;快速选择算法 说明&#xff1a;堆排序也是经典解决问题的算法&#xff0c;但时间复杂度为&#xff1a;O(NlogK)&#xff0c;K为k个元素 而将要介绍的快速选择算法的时间复杂度为: O(N) 先看我的前两篇文章&#xff0c;分别学习&#xff1a;数组分三块&#…

第427场周赛: 转换数组、用点构造面积最大的矩形 Ⅰ、长度可被 K 整除的子数组的最大元素和、用点构造面积最大的矩形 Ⅱ

Q1、转换数组 1、题目描述 给你一个整数数组 nums&#xff0c;它表示一个循环数组。请你遵循以下规则创建一个大小 相同 的新数组 result &#xff1a; 对于每个下标 i&#xff08;其中 0 < i < nums.length&#xff09;&#xff0c;独立执行以下操作&#xff1a; 如…

【中间件开发】Redis基础命令详解及概念介绍

文章目录 前言一、Redis相关命令详解及原理1.1 string、set、zset、list、hash1.1.1 string1.1.2 list1.1.3 hash1.1.4 set1.1.5 zset 1.2 分布式锁的实现1.3 lua脚本解决ACID原子性1.4 Redis事务的ACID性质分析 二、Redis协议与异步方式2.1 Redis协议解析2.1.1 redis pipeline…

运输层4——TCP格式(重点!)

目录 一、TCP报文段格式 二、最大报文长度 MSS 一、TCP报文段格式 长度&#xff1a;前20个字节固定 后4n个字节&#xff08;报文段格式不固定&#xff09; 1、源端和目的端&#xff1a;各2个字节 作用&#xff1a;指明TCP链接的发送 2、序号 4字节 作用&#xff1…

「Mac玩转仓颉内测版46」小学奥数篇9 - 基础概率计算

本篇将通过 Python 和 Cangjie 双语实现基础概率的计算&#xff0c;帮助学生学习如何解决简单的概率问题&#xff0c;并培养逻辑推理和编程思维。 关键词 小学奥数Python Cangjie概率计算 一、题目描述 假设有一个袋子中有 5 个红球和 3 个蓝球&#xff0c;每次从袋子中随机…

从变更到通知:使用Python和MongoDB Change Streams实现即时事件监听

MongoDB提供了一种强大的功能&#xff0c;称为Change Streams&#xff0c;它允许应用程序监听数据库中的变更事件&#xff0c;并在数据发生变化时立即做出响应。这在mysql数据库是不具备没有这个功能的。又如&#xff1a;我们在支付环节想一直监听支付回调的状态&#xff0c;就…

决策树:ID3、C4.5和CART特征选择方式

1 前言 该文章主要目的是记录ID3、C4.5和CART特征选择方式&#xff0c;这里只对决策树进行简单介绍。 决策树&#xff08;Decision Tree&#xff09;算法是一种有监督学习算法&#xff0c;它利用分类的思想&#xff0c;根据数据的特征构建数学模型&#xff0c;从而达到数据的筛…

2023 年“泰迪杯”数据分析技能赛B 题企业财务数据分析与造假识别

2023 年“泰迪杯”数据分析技能赛B 题企业财务数据分析与造假识别 一、背景 财务数据是指企业经营活动和财务结果的数据记录&#xff0c;反映了企业的财务状况 与经营成果。对行业、企业的财务数据进行分析&#xff0c;就是要评价其过去的经营业绩、 衡量现在的财务状况、预测…

UE5.5 Geometry库平面切割原理分析

平面切割--FMeshPlaneCut 平面定义: 面上一个点 法线 算法流程如下 求几何体所有顶点和面的有向距离(Signs) Sign计算&#xff1a; float Sign (VertexPos - PlaneOrigin).Dot(PlaneNormal); 遍历所有几何体所有交叉边, 进行SplitEdge 对于位于切割面两侧的交叉边(Sign…

【计算机学习笔记】GB2312、GBK、Unicode等字符编码的理解

之前编写win32程序时没怎么关注过宽字符到底是个啥东西&#xff0c;最近在编写网络框架又遇到字符相关的问题&#xff0c;所以写一篇文章记录一下&#xff08;有些部分属于个人理解&#xff0c;如果有错误欢迎指出&#xff09; 目录 几个常见的编码方式Unicode和UTF-8、UTF-16、…

CSS 快速上手

目录 一. CSS概念 二. CSS语法 1. 基本语法规范 2. CSS的三种引入方式 (1) 行内样式 (2) 内部样式表 (3) 外部样式表 3. CSS选择器 (1) 标签选择器 (2) 类选择器 (3) id选择器 (4) 通配符选择器 (5) 复合选择器 <1> 空格 <2> 没有空格 <3> &q…

【时间之外】IT人求职和创业应知【60】-卡脖子

目录 新闻一&#xff1a;达成合作&#xff0c;将在中国推出生成式人工智能服务 新闻二&#xff1a;机器人新赛道 新闻三&#xff1a;简化用户信息获取流程&#xff0c;提升小程序体验 去年人口出生下降&#xff0c;3年以后&#xff0c;幼儿园要关闭很多&#xff0c;6年以后小…

centos9升级OpenSSH

需求 Centos9系统升级OpenSSH和OpenSSL OpenSSH升级为openssh-9.8p1 OpenSSL默认为OpenSSL-3.2.2&#xff08;根据需求进行升级&#xff09; 将源码包编译为rpm包 查看OpenSSH和OpenSSL版本 ssh -V下载源码包并上传到服务器 openssh最新版本下载地址 wget https://cdn.openb…