面向对象设计之开闭原则

设计模式专栏: http://t.csdnimg.cn/4Mt4u

目录

1.引言

2.如何理解“对扩展开放、对修改关闭”

3.修改代码就意味着违反开闭原则吗

4.如何做到“对扩展开放、对修改关闭”

5.如何在项目中灵活应用开闭原则

6.总结


1.引言

        开闭原则(Open Closed Principle,OCP),又称为“对扩展开发、对修改关闭”原则。开闭原则既是 SOLID 原则中最难理解、最难掌握的,又是最有用的。之所以说开闭原则难理解,是因为“怎样的代码改动才被定义为'扩展’?怎样的代码改动才被定义为'修改’?怎么才算满足或违反'开闭原则’?修改代码就一定意味着违反'开闭原则’吗?”等问题都比较难理解。之所以说开闭原则难掌握,是因为“如何做到'对扩展开发、对修改关闭’?如何在项目中灵活应用'开闭原则’,避免在追求高扩展性的同时影响代码的可读性?”等问题都比较难掌握。

        之所以说开闭原则最有用,是因为扩展性是代码质量的重要衡量标准。在22种经典设计模式中,大部分设计模式都是为了解决代码的扩展性问题而产生的,它们主要遵守的设计原则就是开闭原则。

2.如何理解“对扩展开放、对修改关闭”

        开闭原则的英文描述是:software enities(modules,classes,fiuncions,etc.)should be open for extension but closed for modification。对应的中文为:软件实体(模块、类和方法等)应该“对扩展开放、对修改关闭”,详细表述为: 添加一个新功能时应该是在已有代码基础上扩展代码(新类和方法等),而非修改已有代码(修改模块、类和方法等)。

        为了让读者更好地理解开闭原则,我们举例说明。

        下面是一段AR(应用程序编程接口)监控告警的代码。其中,AlertRule 类存储告警规则:Notification类负责告警通知,支持电子邮件、短信和微信等多种通知渠道;NotificationEmergencyLevel类表示告警通知的紧急程度,包括SEVERE(严重)、URGENCY(紧急)、NORMAL(普通)和 TRIVIAL(无关紧要),不同的紧急程度对应不同的通知渠道。

public class Alert{private AlertRule rule;private Notifcation notification;public Alert(AlertRule rule, Notification notification){this.rule = rule;this.notifcation = notifcation;}public void check(String api,long requestCount, long errorCount, long duration){long tps=requestCount/duration;if (tps > rule.getMatchedRule(api).getMaxTps())       {            notification.notify(NotificationEmergencyLevel.URGENCY,"...");}if(errorCount>rule.getMatchedRule(api).getMaxErrorCount()){notification.notify(NotifcationEmergencyLevel.SEVERE,"...");}}
}

      上面这段代码的业务逻辑主要集中在check()函数中。当接口的TPS(Transactions PerSecond,每秒事务数)超过预先设置的最大值时,或者当接口请求出错数大于最大允许值时就会触发告警,通知接口的相关负责人或团队。

        如果我们需要添加更多的告警规则:“当每秒接口超时请求个数超过预先设置的最大值时也要触发告警并发送通知”,那么如何改动代码呢?代码的主要改动有两处:第一处是修改check()函数的入参,添加一个新的统计数据 timeoutCount,表示超时接口请求数; 第二处是有check()函数中添加新的告警逻辑。具体的代码改动如下所示。

public class Alert{//...省略AlertRule/Notifcation属性和构造函数...//改动一:添加参数timeoutCountpublic void  check(String api, long regueatcount, long erorcount, long timeoutCount, long duration){long tps=reqestCount /duration;if (tps > rule.getMatchedRule(api) .getMaxTps()){notification.notify(NotificationEmergencyLevel.URGENCY, "...");}if(errorCount> rule.getMatchedRule(api).getMaxErrorCount())){notification.notify(NotificationEmergencyLevel.SEVERE, "...");}//改动二:添加接口超时处理逻辑long timeoutTps=timeoutCount /duration;if(timeoutTps > rule.getMatchedRule(api).getMaxTimeoutTps())         {notification.notify(NotificationEmergencyLevel.URGENCY, "...");}}
}

        上述代码的改动带来下列两方面的问题。一方面,对接口进行了修改,调用这个接口的代码就要做相应的修改。另一方面,修改了check()函数,相应的单元测试需要修改。

        上述代码改动是基于“修改”方式增加新的告警。如果我们遵守开闭原则,也就是“对扩展开放、对修改关闭”,那么如何通过“扩展”方式增加新的告警呢?

        我们先重构添加新的告警之前的 Alert类的代码,让它的扩展性更好。重构的内容主要包含两部分:第一部分是将check()函数的多个入参封装成ApiStatInfo类;第二部分是引入handler(告警处理器),将if判断逻辑分散到各个handler 中。具体的代码实现如下。

public class Alert{private list<AlertHandler> alertHandlers = new ArrayList<>();public void addAlertHandler(AlertHandler alertHandler)    {            this.alertHandlers.add(alertHandler);}public void check(ApiStatInfo apistatInfo){for(AlertHandler handler:alertHandlers){handler.check(apistatInfo);}}public class ApistatInfo{//省略constructor、getter和setter方法private string api;private long requestCount;private long errorCount;private long duration;}public abstract class AlertHandler(protected AlertRule rule;protected Notification notification;public AlertHandler(AlertRule rule, Notifcation notification){this.rule rule;this.notification=notifcation;}public abstract void check(ApiStatInfo apistatInfo) ;}public class TpsAlertHandler extends AlertHandler {public TpsAlertHandler(AlertRule rule, Notification notification){super(rule, notifcation);}}@Overridepublic void check(ApiStatInfo apistatInfo){long tps = apiStatInfo.getReguestcount () / apiStatInfo.getpuration();if (tps > rule.getMatchedRule(apiStatInfo.getApi()).getMaxTps()){         notification.notify(NotifcationEmergencyLevel.URGENCY, "...");}}}public class ErrorAlertHandler extends AlertHandler{public ErrorAlertHandler (AlertRule rule, Notifcation notification){super(rule, notifcation);}@Overridepublic void check (ApiStatInfo apistatInfo) {if (apistatInfo.getErrorCount() > rule.getMatchedRule(apistatInfo.getApi()).getMaxErrorCount()){notifcation.notify(NotifcationEmergencyLevel.SEVERE,"...");}}
}

     接下来,我们看一下重构之后的Alent类的具体使用方式,如下列代码所示。其中ApplicationContext是一个单例类,负责Alert类的创建、组装(alertRule和notification 的依赖注入)和初始化(添加handler)。

public class ApplicationContext{private AlertRule alertRule;private Notifcation notification;private Alert alert;public void initializeBeans(){alertRule=new AlertRule(/*.省略参数,*/);//省略一些初始化代码notifcation=new Notification(/*.省略参数.*/);//省略一些初始化代码alert=new Alert();alert.addAlertHandler(new TpsAlertHandler(alertRule,notifcation));alert.addAlertHandler(new ErrorAlertHandler(alertRule, notifcatíon));}public Alert getAlert(){ return alert; )//“饿汉式”单例private static final Applicationcontext instance = new ApplicationContext();private ApplicationContext(){initializeBeans();}public static ApplicationContext getInstance(){return instance;}
}public class Demo{public static void main(string[] args){ApiStatInfo apistatInfo = new ApiStatInfo();//...省略设置apistatInfo数据值的代码Applicationcontext.getinstance().getAlert().check(apistatInfo)}
}

        对于重构之后的代码,如果添加新的告警:“如果每秒接口超时请求个数超过最大值,就告警”,那么如何改动代码呢?主要的改动有下面4处。

        改动一: 在 ApiStatInfo 类中添加新属性 timeoutCount。

        改动二:添加新的 TimeoutAlertHander 类。

        改动三:在ApplicationContecxt 类的 initializeBeans()方法中,向 alert对象中注册 

Timeout-AlertHandler。

        改动四:使用 Alert 类时,需要给check()函数的入参 apiStatInfo对象设置timeoutCount属性值。

        改动之后的代码如下所示:

public class Alert{//代码未改动}public class ApistatInfo{//省略constructorgetter和setter方法private String api;private long requegtCount;private long errorCount;private long duratlon;   private 1ong timeoutCount;//改动一:添加新属性timeoutcount
}public abstract class AlertHandler{//代码未改动
public class TpsAlertHandler extends AlertHandler{ //代码未改动}
public class ErrorAlertHandler extends AlertHandler { //代码未改动}
//改动二:添加新的TimeoutAlertHander类
public class TimeoutAlertHandler extends AlertHandler {//省略代码public class ApplicationContext{private AlertRule alertRule;private Notification notifcation;private Alert alert;public void initinlizeBeanst{alertRule = new AlertRule(/*.省路参数。*/);//省略一些初始化代码    notification = new Notification(/*.省略参数.*/);//省路一些初始化代码alert = now Alert();alert.addAlertHandler(new psAlertHandler(alertRule, notification ));alert.addAlertHandler(new ErrorAlertHandler(alertRule, notification));//改动三:向alert对象中注册TimeoutAlertHandleralert.addAlertHandler(new imeoutAlertHandler(alertRule, notification));}//...省略其他未改动代码
}public class Demo{public static void main(Stringl] args){ApiStatInfo apiStatInfo = new ApistatInfo();apistatInfo.setTimeoutCount(289);//改动四:设置timeoutCount值//...省路apistatInfo的set字段代码ApplicationContext.getInstance().getAlert().check(apiStatInfo);
}

        重构之后的代码更加灵活,更容易扩展。如果想要添加新的告警,那么只需要基于扩展的方式创建新的handler类,不需要改动check()函数。不仅如此,我们只需要为新的handler头添加新的单元测试,旧的单元测试都不会失败,也不用修改。

3.修改代码就意味着违反开闭原则吗

        读者可能对上面重构之后的代码产生疑间:在添加新的告警时,尽管改动二(添加新的TimeoutAlertHander类)是基于扩展而非修改的方式完成的,但改动一、改动三和改动四是基于修改而非扩展的方式完成的,改动一、改动三和改动四不违反开闭原则吗?

        我们先分析一下改动一: 在 ApiStatInfo类中添加新属性 timeoutCount。

        在改动一中,我们不仅在ApiStatInfo的类中添加了新的属性,还添加了对应的getter和setter方法。那么,上述问题就转化为:在类中添加新的属性和方法属于“修改”还是“扩展”?

        我们回忆一下开闭原则的定义: 软件实体(模块、类和方法等)应该“对扩展开放、对修改关闭”。从定义中可以看出,开闭原则作用的对象可以是不同粒度的代码,如模块、类和方法(及其属性)。对于同一代码改动,在粗代码粒度下,可以被认定为“修改”,在细代码粒度下,可以被认定为“扩展”。例如,“改动一”中添加属性和方法相当于修改类,在类这个层面,这个代码改动可以被认定为“修改”;但这个代码改动并没有修改已有的属性和方法,在方法(及其属性)这一层面,它又可以被认定为“扩展”。

        实际上,我们没有必要纠结某个代码改动是“修改”还是“扩展”,更没有必要纠结它是否违反“开闭原则”。回到开闭原则的设计初衷:只要代码改动没有破坏原有代码的正常运行和原有的单元测试,我们就可以认为这是一个合格的代码改动。

        我们再来分析一下改动三和改动四:在ApplicationContext类的initializeBeans()方法中向alert 对象中注册 TimeoutAlertHandler;使用Alert类时,给check()函数的入参 apiStatInfo对象设置 timeoutCount 属性值。

        这两处改动是在方法内部进行的,无论从哪个层面(模块、类、方法)来看,都不能算是“扩展”,而是“修改”。不过,有些修改是在所难免的,是可以接受的。

        在重构之后的 Alent 类代码中,核心逻辑集中在Alent类及其各个handler类中。当添加的告警时,Alen类完全不需要修改,而只需要扩展(新增)一个handler类。如果把 Alent类及其各个handler 类看作一个“模块”,那么,从模块这个层面来说,向模块添加新功能时只需要扩展,不需要修改,完全满足开闭原则。

        我们也要认识到,添加一个新功能时,不可能做到任何模块、类和方法的代码都不“修改”。类需要创建、组装,并且会进行一些初始化操作,这样才能构建可运行的程序,这部分代码的修改在所难免。我们努力的方向是尽量让修改操作集中在上层代码中,尽量让核心、复杂、通用、底层的那部分代码满足开闭原则。

4.如何做到“对扩展开放、对修改关闭”

        在上面的 Alert类的例子中,我们通过引入一组 handler 类的方式满足了开闭原则。如果读者没有太多复杂代码的设计和开发经验,就可能有这样的疑问:这样的代码设计思路我怎么想不到呢?你是怎么想到的呢?

        实际上,之所以作者能够想到,依靠的是扎实的理论知识和丰富的实战经验,这需要读者慢慢学习和积累。对于如何做到“对扩展开放、对修改关闭”,作者有一些指导思想和具体方法分享给读者。

        实际上,开闭原则涉及的就是代码的扩展性问题,该原则是判断一段代码是否易扩展的“金标准”。如果某段代码在应对未来需求变化时,能够到“对扩展开放、对修改关闭”,就说明这段代码的扩展性很好。

        为了写出扩展性好的代码,我们需要具备扩展意识、抽象意识和封装意识。这些意识可能比任何开发技巧都重要。

        在编写代码时,我们需要多花点时间思考: 对于当前这段代码,未来可能有哪些需求变更。如何设计代码结构,事先预留了扩展点,在未来进行需求变更时,不需要改动代码的整体结构,新的代码能够灵活地插入到扩展点上,完成需求变更,从而实现代码的最小化改动。

        我们还要善于识别代码中的可变部分和不可变部分。我们将可变部分封装,达到隔离变化的效果,并提供抽象化的不可变接口给上层系统使用。当具体的实现发生变化时,只需要基于相同的抽象接口扩展一个新的实现,替换旧的实现,上层系统的代码几乎不需要修改。

        为了实现开闭原则,除在写代设时,我们需要时间具备扩展意识、抽象意识、封装意识以外,我们还有一些具体的方法可以使用。

        代码的扩展性是评判代码质量的重要标准。实际上,本文涉及的大部分知识点都是围绕如何提高高代码的扩展性来展开讲解的,本文提到的大部分设计原则和设计模式都是以提高代码的扩展性为最终目的。22种经典设计模式中的大部分都是为了解决代码的扩展性问题而总结出来的,都是以开闭原则为指导原则而设计的。

        有众多的设计原则和设计模式中,常用来提高代码扩展性的方法包括多态、依赖注入、基于接口而非实现编程,以及大部分的设计模式(如策略模式、模板方法模式和职责链模式等)设计模式这一部分的内容较多,后面也会详细讲解。本节通过一个简单例子来介绍如何利用多态、依赖注入、基于接口而非实现编程实现开闭原则。

        例如,我们希望实现通过Kafka发送异步消息。对于这样一个功能的开发,我们抽象定义一组与具体消息队列(Kafka)无关的异步消息发送接口。所有上层系统都依赖这组抽象的接口编程,并且通过依赖注入的方式来调用。当需要替换消息队列或消息格式时,如将Kafka替换成RocketMQ或将消息的格式从JSON替换为XML,因为代码设计满足开闭原则,所以替换起来非常轻松。具体的代码实现如下所示。

//这一部分代码体现了抽象意识
public interface MessageQueue(...)
public class KafkaMessageQueue implements MessageQueue {...)
public class RocketMQMessageQueue implements MessageQueue (...)public interface MessageFromatter{...}
public class JsonMessagerromatter implements MessageFromatter {..)
public class ProtoBufMessageFromatter implements MessageFromatter { ... }public class Demo(private MessageQueue msgQueue;//基于接口而非实现编程public Demo(MessageQueue msgQueue){ //依赖注入this.msqQueue = msgQueue;}public void send (Notification notification, Messageformatter msgforatter) {...}
}

5.如何在项目中灵活应用开闭原则

        上文提到,写出支持开闭原则(扩展性好)的代码的关键是预留扩展点。如何才能识别出有可能的扩展点呢?

        如果我们开发的是业务系统,如金融系统、电商系统和物流系统等,要想识别出尽可能多的扩展点,就要对业务有足够的了解。只有这样,才能预见未来可能要支持的业务需求。如要我们开发的是与业务无关的、通用的、偏底层的功能模块,如框架、组件和类库,如果想设出尽可能多的扩展点,就需要了解它们会被如何使用和使用者未来会有哪些功能需求等。

       但是,即使我们对业务和系统有足够的了解,也不可能识别出所有的扩展点。即便我们的够识别出所有的扩展点,但为了预留所有扩展点而付出的开发成本往往是不可接受的。因此我们没必要为一些未来不一定需要实现的需求提前“买单”,也就是说,不要进行过度设计。

        推荐的做法是,对于一些短期内可能进行的扩展,需求改动对代码结构影响比较大的扩展,或者实现成本不高的扩展,在编写代码时,我们可以事先进行可扩展性设计;但对于一些不确定未来是否要支持的需求,或者实现起来比较复杂的扩展,我们可以等到有需求驱动时,再通过重构的方式来满足扩展的需求。

        除此之外,我们还要认识到,开闭原则并不是“免费”的。代码的扩展性往往与代码的可读性冲突。例如上文提供的 Alert类的例子,为了更好地支持扩展性,我们对代码进行了重构,重构之后的代码比原始代码复杂很多,理解难度也增加不少。因此,在平时的开发中,我们需要权衡代码的扩展性和可读性。在一些场景下,代码的扩展性更重要,我们就适当地“牺牲”一些代码的可读性;在一些场景下,代码的可读性更重要,我们就适当地“牺牲”一些代码的扩展性。

        在上文提到的 Alert类的例子中,如果告警规则不是很多,也不复杂,那么check()函数中的if分支就不会有很多,对应的代码逻辑不会太复杂,代码行数也不会太多,因此,使用最初的代码实现即可。相反,如果告警规则多且复杂,那么check()函数中的if分支就会有很多对应的代码逻辑就会变复杂,代码行数也会增加,check()函数的可维护性和扩展性就会变差此时,重构代码就变得合理了。

6.总结

        开闭原则是面向对象设计中最基础的设计原则之一。其核心思想是:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。这意味着,当应用的需求改变时,在不修改软件实体的源代码或二进制代码的前提下,可以拓展模块的功能,使其满足需求。换句话说,强调的是用抽象构建框架,用实现扩展细节,从而提高软件系统的可复用性及可维护性。

        开闭原则并不是要求所有代码都不能修改,而是要求将变化的部分尽可能地封装和抽象出来。通过遵循这一原则,开发者可以构建出更加稳定、灵活且易于维护的软件系统。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python多任务处理---多线程

引入 生活中&#xff0c;我们在电脑上打开了一个word, 这个word对操作系统来说就是一个进程。我们在进行word操作的时候&#xff0c;比如在你打字的时候&#xff0c;该word同时可以进行文字检查。发现了没&#xff0c;在同一个进程中&#xff0c;我们也可以进行同时操作。…

【RV1126】瑞芯微RV1126开发板双网口问题

今天新拿到瑞芯微RV1126的开发板&#xff0c;是双网口双百兆接口&#xff0c;内存为2G&#xff0c;emmc flash为32G&#xff0c;一开始无法分辨物理接口和系统的eth是对应的&#xff0c;遂想用ethtool工具来查看网速&#xff0c;发现&#xff1a; ethtool eth0 # 可以正常返回…

Acwing.731 毕业旅行问题(状态压缩动态规划)

题目 小明目前在做一份毕业旅行的规划。 打算从北京出发&#xff0c;分别去若干个城市&#xff0c;然后再回到北京&#xff0c;每个城市之间均乘坐高铁&#xff0c;且每个城市只去一次。 由于经费有限&#xff0c;小明希望能够通过合理的路线安排尽可能的省些路上的花销。 …

如何在 Debian VPS 上添加、删除和授予用户 sudo 权限

简介 当你启动一个新的服务器时&#xff0c;会创建一个名为 root 的默认账户。这个用户拥有完全的系统访问权限&#xff0c;应该仅用于管理任务。作为 root 用户&#xff0c;你基本上可以对系统做任何操作&#xff0c;这很强大&#xff0c;但也极其危险。Linux 没有“撤销”按…

Leetcode 73 矩阵置零

题目信息 LeetCode地址: . - 力扣&#xff08;LeetCode&#xff09; 题目理解 矩阵是m*n的&#xff0c;如果某个元素(i,j)等于0&#xff0c;则将第i行和第j列的所有元素都置零。既然如此&#xff0c;我们可以便利每一个元素&#xff0c;并记录下哪一行哪一列有零。记录完毕后…

【C++进阶】二叉搜索树(来自二叉树的复仇)

&#x1fa90;&#x1fa90;&#x1fa90;欢迎来到程序员餐厅&#x1f4ab;&#x1f4ab;&#x1f4ab; 主厨&#xff1a;邪王真眼 主厨的主页&#xff1a;Chef‘s blog 所属专栏&#xff1a;c大冒险 总有光环在陨落&#xff0c;总有新星在闪烁 [本节目标] 1. 二叉搜索树…

KaTex 常用公式编辑

原文&#xff1a;https://blog.iyatt.com/?p7854 注&#xff1a;语法上和 Latex 差不多一样&#xff0c;我是因为 WordPress 上使用 WP Githuber MD 插件&#xff0c;才用的 KaTex&#xff08;插件里面的 LaTex 模块有 bug&#xff0c;无法渲染&#xff09; 希腊字母 大写代…

MyBatis 参数重复打印的bug

现象 最近有个需求&#xff0c;需要在mybatis对数据库进行写入操作的时候&#xff0c;根据条件对对象中的某个值进行置空&#xff0c;然后再进行写入&#xff0c;这样数据库中的值就会为空了。 根据网上查看的资料&#xff0c;选择在 StatementHandler 类执行 update 的时候进…

C++之调用Python

1、配置头文件 Python安装目录下的include目录加入头文件目录。Visual Studio2022中操作路径是&#xff1a;属性–> C/C -> 常规-> 附加包含目录 C:\Users \AppData\Local\Programs\Python\Python39\include 2、配置lib库目录 要将Python39.lib加入编译链接。Visua…

neo4j使用详解(七、cypher数学函数语法——最全参考)

Neo4j系列导航&#xff1a; neo4j及简单实践 cypher语法基础 cypher插入语法 cypher插入语法 cypher查询语法 cypher通用语法 cypher函数语法 5.数学函数 5.1.数值函数 数学函数仅对数字表达式进行运算&#xff0c;如果对任何其他值使用&#xff0c;将返回错误 abs()&#xf…

Nginx 基础

文章目录 Nginx概念安装下载上传安装包执行准备条件指定安装位置编译和安装启动服务创建启动脚本 linux文件目录nginx运行原理nginx配置域名概念和原理域名配置 Nginx 概念 Nginx 是一个高性能的HTTP和反向代理web服务器&#xff0c;同时也提供了IMAP/POP3/SMTP服务。Nginx是…

【Java八股面试系列】Arraylist和HashMap的底层原理

文章目录 ArrayList源码总&#xff1a;构造方法扩容机制remove HashMap总&#xff1a;构造方法细节问题putVal()方法resize()方法Hash值 HashMap常见问题 ConcurrentHashMap总&#xff1a;putVal()方法自己的测试 为什么重写HashCode和equals ArrayList源码 总&#xff1a; *…

3.28号arm

can总线相关理论 1. 概念 控制器局域网&#xff08;Controller Area Network&#xff0c;CAN&#xff09;&#xff0c;其特点是可拓展性好&#xff0c;可承受大量数据的高速通信&#xff0c;高度稳定可靠&#xff0c;因此常应用于汽车电子领域、工业自动化、医疗设备等高要求…

Java JSON字符串相关问题

一、依赖包 <!--json包--><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.15</version></dependency> 二、举例 1.实体对象转Json字符串 1.1 代码实现 Dog.java: pack…

python_web1(前端开发之HTML、CSS、Bootstap、Javascript、JQuery)

文章目录 一、Flask网页开发1.1创建一个名为web1.py的python文件1.2 templates目录创建文件index.html 二、html标签2.1 编码2.2title < head >2.3 标题< h>2.4 div和span2.5超链接1.在index.xml文件中补充。2.修改web1.py文件3.添加get_self.html4.效果 2.6图片1.…

Java 堆外内存及调优

文章目录 直接内存简介为什么DirectByteBuffer可以优化 IO 性能 直接内存的分配直接内存的回收直接内存跟踪与诊断 直接内存简介 直接内存(Direct Memory) 并不是虚拟机运行时数据区的一部分&#xff0c;并非Java虚拟机规范中定义的内存区域。但是这部分内存的频繁使用&#x…

ElasticSearch的DSL查询

ElasticSearch的DSL查询 准备工作 创建测试方法&#xff0c;初始化测试结构。 import org.apache.http.HttpHost; import org.apache.lucene.search.TotalHits; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchRespo…

AcWing22. 旋转数组的最小数字

把一个数组最开始的若干个元素搬到数组的末尾&#xff0c;我们称之为数组的旋转。 输入一个升序的数组的一个旋转&#xff0c;输出旋转数组的最小元素。 例如数组 {3,4,5,1,2}为 {1,2,3,4,5} 的一个旋转&#xff0c;该数组的最小值为 1。 数组可能包含重复项。 注意&#x…

【LeetCode】三月题解

文章目录 [2369. 检查数组是否存在有效划分](https://leetcode.cn/problems/check-if-there-is-a-valid-partition-for-the-array/)思路&#xff1a;代码&#xff1a; [1976. 到达目的地的方案数](https://leetcode.cn/problems/number-of-ways-to-arrive-at-destination/) 思路…

C++教学——从入门到精通 5.单精度实数float

众所周知&#xff0c;三角形的面积公式是(底*高)/2 那就来做个三角形面积计算器吧 到吗如下 #include"bits/stdc.h" using namespace std; int main(){int a,b;cin>>a>>b;cout<<(a*b)/2; } 这不对呀&#xff0c;明明是7.5而他却是7&#xff0c;…