解放代码:识别与消除循环依赖的实战指南

目录

一、对循环依赖的基本认识

(一)代码中形成循环依赖的说明

(二)无环依赖的原则

二、识别和消除循环依赖的方法

(一)使用JDepend识别循环依赖

使用 Maven 集成 JDepend

分析报告识别循环依赖

(二)消除循环依赖的三大方法思考

提取中介者

转移业务逻辑

采用回调接口

三、案件实战分析

(一)具体案列介绍

HealthRecord 类

HealthTask 类

循环依赖的产生

(二)具体解决方案一:提取中介者

提取中介者的实现

简化的 HealthTask 类

测试用例示例

(三)具体解决方案二:转移业务逻辑

转移业务逻辑的实现

改造后的 HealthRecord 类

改造后的 HealthTask 类

测试用例示例

(四)具体解决方案三:采用回调接口

使用回调接口的实现

HealthLevelHandler 接口的定义

改造后的 HealthTask 类

改造后的 HealthRecord 类

测试用例示例

参考书籍、文献和链接等


本文讨论软件开发中常见的循环依赖问题及其解决方法。首先介绍了循环依赖在代码中的形成原因,并提出了避免循环依赖的基本原则。其次,详细介绍了使用工具如JDepend来识别项目中的循环依赖,并通过具体案例分析了三种消除循环依赖的方法:提取中介者、转移业务逻辑和采用回调接口。每种方法都结合了实际的代码改造示例和测试用例,帮助读者理解和应用这些技术以优化自己的软件架构和设计。主要思想的编排思路来自极客时间《如何有效识别和解决代码中存在的循环依赖问题?》,当然也有其他的参考和自身的一些思考和优化。

一、对循环依赖的基本认识

循环依赖是指两个或多个模块或对象彼此之间相互依赖形成闭环,如 A 依赖 B,B 又依赖 A。这就像是两个人互相拉着对方,无法确定谁先开始走。

循环依赖最大的问题是,在运行时,可能会导致无限递归、栈溢出或者对象创建循环等问题,因为每个类的构造函数都试图创建依赖的对象,形成了一个死锁。所以一般情况下我们都希望在编译时,编译器就可能会报告循环依赖错误或无法解析的问题。

(一)代码中形成循环依赖的说明

循环依赖是指两个或多个模块、类、或组件之间相互依赖形成闭环。这种情况可能导致编译错误、运行时错误,甚至导致系统崩溃或无法正常工作。随着系统复杂性的增加,循环依赖问题可能会变得更加严重和难以检测。

简单来说,假设当前只有存在两个类A和类B,这个时候的识别还是较简单的,但如果此时在增加C,那么这个时候其可能产生依赖的情况就会很多了,比如如下的三种情况:

显然我们平时的系统中不会只是三个类这么简单,在多类情况下这种依赖问题就会不断放大(随着业务发展只会不断扩大),比如简单的依赖会变为如下依赖:

现实中,随着类和模块数量的增加,系统中的依赖关系会变得复杂,容易形成循环依赖,不仅增加了系统的复杂性和维护难度,还可能导致运行时错误和系统不稳定。因此,在设计和实现系统时,应尽量避免循环依赖,并采取合适的解决策略来管理依赖关系。

(二)无环依赖的原则

无环依赖原则(ADP)规定,在系统的依赖关系图中,不应该存在任何依赖环。换句话说,一个模块或组件的依赖链中不应出现环形依赖结构。这样,任何一个模块的变更不会导致循环依赖的问题,从而避免了系统的复杂性和难以维护性。

也就是基本原则要求如下:

  • 在软件设计中,模块之间的依赖关系可以用图来表示,节点表示模块,边表示依赖关系。无环依赖原则要求这个图必须是一个有向无环图(DAG)。
  • 通过确保依赖关系是无环的,可以防止系统中出现依赖的闭环,避免模块之间的强耦合。这样,修改一个模块不会引发对其依赖模块的连锁反应。

二、识别和消除循环依赖的方法

识别和消除循环依赖是软件开发中的一个重要任务,可以通过重构代码、引入中介者模式、使用接口和依赖注入,以及遵循设计原则等方法来实现。借助 Maven Enforcer Plugin、Madge、Deptrac、JDepend、PyDepGraph 和 SonarQube 等开源工具,可以更高效地检测和管理项目中的依赖关系,从而提高代码的可维护性和可扩展性。

(一)使用JDepend识别循环依赖

JDepend 是一个用于分析 Java 包之间依赖关系的工具。它能够生成报告,帮助开发者理解项目的架构和依赖关系,并识别可能的设计问题,如循环依赖。JDepend 提供了关于包的耦合度、内聚性、抽象度和稳定性等方面的信息,从而帮助改进代码质量和系统结构。可以从 JDepend 的官方网站 下载 JDepend,并将 JDepend 集成到构建工具中,例如 Maven 或 Gradle,以便在构建过程中自动分析依赖关系。

使用 Maven 集成 JDepend

在Maven 项目的 pom.xml 文件中添加 JDepend 插件:

<project>
  <!-- 其他配置 -->
  <build>
    <plugins>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>jdepend-maven-plugin</artifactId>
        <version>2.0-beta-2</version>
        <executions>
          <execution>
            <goals>
              <goal>generate</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>

然后,运行以下命令来执行 JDepend 分析:

mvn jdepend:generate

分析报告识别循环依赖

JDepend 生成的报告包括以下几个方面:

  • 包的稳定性:度量包的变化频率,越不稳定的包越容易发生变化。
  • 包的抽象度:度量包的抽象程度,抽象包通常包含更多的接口和抽象类。
  • 包的耦合度:度量包之间的依赖关系。
  • 循环依赖:检测包之间是否存在循环依赖。

一旦生成 JDepend 报告就可以在报告中查找是否存在循环依赖,循环依赖通常会被标记为 “Cycles” 或 “Dependency Cycles”:

(二)消除循环依赖的三大方法思考

提取中介者

通过引入一个独立的中介者类来管理和协调其他类之间的交互,从而避免直接的循环依赖。

适用于需要集中管理复杂依赖关系的场景,可以有效地降低类之间的耦合度,增强系统的可维护性和灵活性。

转移业务逻辑

将涉及循环依赖的业务逻辑转移到一个独立的类或模块中,从而解除类之间的直接依赖关系。

适用于可以重构业务逻辑的场景,通过模块化设计提高系统的可理解性和可维护性。

采用回调接口

通过定义接口来抽象依赖关系,使类之间的依赖通过接口进行交互,从而避免直接的循环依赖。

适用于需要解耦具体实现的场景,可以增强系统的扩展性和灵活性。

三、案件实战分析

(一)具体案列介绍

在医疗健康类系统中,每个用户都有一份健康档案,记录着他们当前的健康状况(以健康等级表示)和一系列健康任务。用户每天可以通过完成医生指定的任务来获得健康积分。健康积分的计算取决于用户的当前等级,不同等级下完成同一个任务获得的积分不同。同时,用户的健康等级也取决于他们当前需要完成的任务数量,任务越多说明越不健康,等级也就越低。

针对这个场景,我们可以抽象出两个类:HealthRecordHealthTask

HealthRecord

HealthRecord 类代表健康档案,包含一个 HealthTask 列表以及添加 HealthTask 的方法。这个类还包含一个获取健康等级的方法,该方法根据任务数量来判断用户的健康等级。

import java.util.ArrayList;
import java.util.List;

public class HealthRecord {
    private List<HealthTask> tasks = new ArrayList<HealthTask>();

    public Integer getHealthLevel() {
        // 根据健康任务数量来判断健康等级
        // 任务越多说明越不健康,健康等级就越低
        if (tasks.size() > 5) {
            return 1;
        }
        if (tasks.size() < 2) {
            return 3;
        }
        return 2;
    }

    public void addTask(String taskName, Integer initialHealthPoint) {
        HealthTask task = new HealthTask(this, taskName, initialHealthPoint);
        tasks.add(task);
    }

    public List<HealthTask> getTasks() {
        return tasks;
    }
}

HealthTask

HealthTask 类代表健康任务,它包含对 HealthRecord 的引用,并实现了一个计算任务所能获得积分的方法,这个方法需要使用 HealthRecord 中的健康等级信息。

public class HealthTask {
    private HealthRecord record;
    private String taskName;
    private Integer initialHealthPoint;

    public HealthTask(HealthRecord record, String taskName, Integer initialHealthPoint) {
        this.record = record;
        this.taskName = taskName;
        this.initialHealthPoint = initialHealthPoint;
    }

    public Integer calculateHealthPointForTask() {
        // 计算该任务所能获取的积分需要等级信息
        // 等级越低积分越高,以鼓励多做任务
        Integer healthPointFromHealthLevel = 12 / record.getHealthLevel();
        // 最终积分为初始积分加上与等级相关的积分
        return initialHealthPoint + healthPointFromHealthLevel;
    }

    public String getTaskName() {
        return taskName;
    }

    public int getInitialHealthPoint() {
        return initialHealthPoint;
    }
}

循环依赖的产生

从代码中可以看出,HealthRecordHealthTask 之间存在循环依赖:

  • HealthRecord 需要知道 HealthTask 列表来确定健康等级。
  • HealthTask 需要引用 HealthRecord 来计算任务的健康积分。

这种循环依赖在系统扩展和维护时会带来很多问题,例如增加新的功能可能导致意想不到的耦合和复杂度。

(二)具体解决方案一:提取中介者

提取中介者的核心思想是把两个相互依赖的组件中的交互部分抽象出来形成一个新的组件,而新组件同时包含着原有两个组件的引用,这样就把循环依赖关系剥离出来并提取到一个专门的中介者组件中。

在医疗健康类系统中,我们面对 HealthRecordHealthTask 两个类之间的循环依赖问题:HealthRecord 类负责管理健康档案和健康任务列表,同时根据任务数量确定用户的健康等级。而 HealthTask 类则依赖于 HealthRecord 的健康等级信息来计算任务的健康积分,这导致了双向的依赖关系。

提取中介者的实现

为消除 HealthRecordHealthTask 的直接依赖,引入中介者类 HealthPointMediator。该中介者类负责协调 HealthRecordHealthTask 的交互,并提供了计算任务健康积分的方法。

public class HealthPointMediator {
    private HealthRecord record;

    public HealthPointMediator(HealthRecord record) {
        this.record = record;
    }

    public Integer calculateHealthPointForTask(HealthTask task) {
        Integer healthLevel = record.getHealthLevel();
        Integer initialHealthPoint = task.getInitialHealthPoint();
        Integer healthPoint = 12 / healthLevel + initialHealthPoint;
        return healthPoint;
    }
}

简化的 HealthTask

为了减少 HealthTaskHealthRecord 的依赖,可简化 HealthTask 类的实现。现在它专注于任务的描述和初始化积分,不再直接引用 HealthRecord

public class HealthTask {
    private String taskName;
    private Integer initialHealthPoint;

    public HealthTask(String taskName, Integer initialHealthPoint) {
        this.taskName = taskName;
        this.initialHealthPoint = initialHealthPoint;
    }

    public String getTaskName() {
        return taskName;
    }

    public Integer getInitialHealthPoint() {
        return initialHealthPoint;
    }
}

测试用例示例

编写了一个简单的测试用例来验证 HealthPointMediator 的功能,确保它能正确计算每个任务的健康积分。

public class HealthPointTest {
    public static void main(String[] args) {
        HealthRecord record = new HealthRecord();
        record.addTask("忌烟酒", 5);
        record.addTask("一周慢跑三次", 4);
        record.addTask("一天喝两升水", 2);
        record.addTask("坐1小时起来活动5分钟", 2);
        record.addTask("晚上10点按时睡觉", 3);
        record.addTask("晚上8点之后不再饮食", 1);

        HealthPointMediator mediator = new HealthPointMediator(record);
        List<HealthTask> tasks = record.getTasks();

        for (HealthTask task : tasks) {
            Integer healthPoint = mediator.calculateHealthPointForTask(task);
            System.out.println("任务:" + task.getTaskName() + ",积分:" + healthPoint);
        }
    }
}

通过提取中介者的方法,我们成功地消除了 HealthRecordHealthTask 之间的循环依赖关系。中介者模式使得系统更加灵活和可扩展,同时降低了类之间的耦合度,提高了代码的可维护性和可读性。这种设计方式不仅符合面向对象设计的原则,也使得系统更易于理解和维护。

(三)具体解决方案二:转移业务逻辑

这种方法的实现思路在于提取一个专门的业务组件来完成对等级的计算过程。这样,HealthTask原有的对HealthRecord的依赖就转移到了对这个业务组件的依赖,而这个业务组件本身不需要依赖任何对象。

转移业务逻辑的实现

提取一个专门的业务组件 HealthLevelHandler,负责计算健康等级而不依赖于任何其他对象。这样一来,HealthTask 类的原有对 HealthRecord 的依赖转移到了对 HealthLevelHandler 的依赖,从而消除了循环依赖。

public class HealthLevelHandler {
    private Integer taskCount;

    public HealthLevelHandler(Integer taskCount) {
        this.taskCount = taskCount;
    }

    public Integer getHealthLevel() {
        if (taskCount > 5) {
            return 1;
        } else if (taskCount < 2) {
            return 3;
        } else {
            return 2;
        }
    }
}

改造后的 HealthRecord

HealthRecord 类现在封装了对 HealthLevelHandler 的创建过程,并提供了获取健康任务列表的方法。

public class HealthRecord {
    private List<HealthTask> tasks = new ArrayList<>();

    public void addTask(String taskName, Integer initialHealthPoint) {
        HealthTask task = new HealthTask(taskName, initialHealthPoint);
        tasks.add(task);
    }

    public HealthLevelHandler getHealthPointHandler() {
        return new HealthLevelHandler(tasks.size());
    }

    public List<HealthTask> getTasks() {
        return tasks;
    }
}

改造后的 HealthTask

HealthTask 类改为接受 HealthLevelHandler 对象作为参数,并利用其计算任务的健康积分。

public class HealthTask {
    private String taskName;
    private Integer initialHealthPoint;

    public HealthTask(String taskName, Integer initialHealthPoint) {
        this.taskName = taskName;
        this.initialHealthPoint = initialHealthPoint;
    }

    public Integer calculateHealthPointForTask(HealthLevelHandler handler) {
        Integer healthPointFromHealthLevel = 12 / handler.getHealthLevel();
        return initialHealthPoint + healthPointFromHealthLevel;
    }

    public String getTaskName() {
        return taskName;
    }
}

测试用例示例

编写一个简单的测试用例来验证改造后的 HealthRecordHealthTask 的功能。现在,系统中不存在任何循环依赖,且代码更易于理解和维护。

public class HealthPointTest {
    public static void main(String[] args) {
        HealthRecord record = new HealthRecord();
        record.addTask("忌烟酒", 5);
        record.addTask("一周慢跑三次", 4);
        record.addTask("一天喝两升水", 2);
        record.addTask("坐1小时起来活动5分钟", 2);
        record.addTask("晚上10点按时睡觉", 3);
        record.addTask("晚上8点之后不再饮食", 1);

        HealthLevelHandler handler = record.getHealthPointHandler();
        List<HealthTask> tasks = record.getTasks();

        for (HealthTask task : tasks) {
            Integer healthPoint = task.calculateHealthPointForTask(handler);
            System.out.println("任务:" + task.getTaskName() + ",积分:" + healthPoint);
        }
    }
}

通过转移业务逻辑的方法,我们成功消除了 HealthRecordHealthTask 之间的循环依赖。通过引入 HealthLevelHandler 业务组件,我们将等级计算的责任集中在一个地方,简化了系统的设计并提高了其灵活性和可维护性。这种设计方式符合单一职责原则,使得各个类的功能更加清晰和独立。

(四)具体解决方案三:采用回调接口

所谓回调本质上就是一种双向调用模式,也就是说,被调用方在被调用的同时也会调用对方。在实现上,我们可以提取一个用于计算等级的业务接口,然后让HealthRecord去实现这个接口。这样,HealthTask在计算积分时只需要依赖这个业务接口,而不需要关心这个接口的具体实现类。

使用回调接口的实现

首先定义了一个名为 HealthLevelHandler 的接口,该接口包含了计算健康等级的方法声明,作为回调接口使用。接着,HealthRecord 类实现了这个接口,并提供了具体的等级计算逻辑。在创建 HealthTask 对象时,我们将 HealthRecord 对象作为参数传入 HealthTask 的构造函数,并在 HealthTask 中使用 HealthLevelHandler 接口来获取健康等级信息,从而消除了对 HealthRecord 的直接依赖。

HealthLevelHandler 接口的定义
public interface HealthLevelHandler {
    Integer getHealthLevel();
}
改造后的 HealthTask

HealthTask 类不再直接依赖 HealthRecord,而是依赖 HealthLevelHandler 接口,并通过该接口获取健康等级信息来计算任务的健康积分。

public class HealthTask {
    private String taskName;
    private Integer initialHealthPoint;
    private HealthLevelHandler handler;

    public HealthTask(String taskName, Integer initialHealthPoint, HealthLevelHandler handler) {
        this.taskName = taskName;
        this.initialHealthPoint = initialHealthPoint;
        this.handler = handler;
    }

    public Integer calculateHealthPointForTask() {
        Integer healthPointFromHealthLevel = 12 / handler.getHealthLevel();
        return initialHealthPoint + healthPointFromHealthLevel;
    }

    public String getTaskName() {
        return taskName;
    }
}
改造后的 HealthRecord

HealthRecord 类实现了 HealthLevelHandler 接口,并提供了具体的健康等级计算逻辑。在创建 HealthTask 对象时,将 this 作为 HealthLevelHandler 的实例传入 HealthTask 的构造函数。

public class HealthRecord implements HealthLevelHandler {
    private List<HealthTask> tasks = new ArrayList<>();

    @Override
    public Integer getHealthLevel() {
        if (tasks.size() > 5) {
            return 1;
        } else if (tasks.size() < 2) {
            return 3;
        } else {
            return 2;
        }
    }

    public void addTask(String taskName, Integer initialHealthPoint) {
        HealthTask task = new HealthTask(taskName, initialHealthPoint, this);
        tasks.add(task);
    }

    public List<HealthTask> getTasks() {
        return tasks;
    }
}

测试用例示例

编写一个简单的测试用例来验证改造后的 HealthRecordHealthTask 的功能。现在,系统中不存在任何循环依赖,且代码更易于理解和维护。

public class HealthRecord implements HealthLevelHandler {
    private List<HealthTask> tasks = new ArrayList<>();

    @Override
    public Integer getHealthLevel() {
        if (tasks.size() > 5) {
            return 1;
        } else if (tasks.size() < 2) {
            return 3;
        } else {
            return 2;
        }
    }

    public void addTask(String taskName, Integer initialHealthPoint) {
        HealthTask task = new HealthTask(taskName, initialHealthPoint, this);
        tasks.add(task);
    }

    public List<HealthTask> getTasks() {
        return tasks;
    }
}

通过引入 HealthLevelHandler 接口,HealthTask 类不再直接依赖于 HealthRecord,而是依赖于一个通用的接口,提高了系统的灵活性和可维护性。这种设计方式符合面向接口编程的思想,使得各个组件之间的耦合度降低,同时也使得代码更具扩展性和测试性。

参考书籍、文献和链接等

如何有效识别和解决代码中存在的循环依赖问题?-极客时间

JDepend 的官方网站

GitHub - clarkware/jdepend: A Java package dependency analyzer that generates design quality metrics.

JDepend Task

Managing Your Dependencies with JDepend – Craftsmanship

六、如何解决循环依赖_循环依赖解决方式-CSDN博客

https://jiapan.me/2020/circular-dependence/

https://blog.51cto.com/u_16038001/6159153

中介者模式解决Spring循环依赖_哔哩哔哩_bilibili

Spring Boot 系统学习第四天:Spring循环依赖案例分析_springboot 循环以来例子-CSDN博客

深入理解循环依赖:如何避免和解决

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

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

相关文章

超越中心化:Web3如何塑造未来数字生态

随着技术的不断发展&#xff0c;人们对于网络和数字生态的期望也在不断提升。传统的中心化互联网模式虽然带来了便利&#xff0c;但也暴露出了诸多问题&#xff0c;比如数据滥用、信息泄露、权力集中等。在这样的背景下&#xff0c;Web3技术应运而生&#xff0c;旨在打破传统中…

Shopee API接口:获取搜索栏生成的商品结果列表

一、平台介绍 Shopee&#xff0c;作为东南亚领先的电商平台&#xff0c;一直致力于为卖家和买家提供便捷、高效的在线购物体验。为了满足广大开发者的需求&#xff0c;Shopee提供了丰富的API接口服务&#xff0c;帮助卖家和第三方开发者更好地与平台进行数据交互&#xff0c;实…

ucos抢占式实时多任务操作系统 (RTOS)。

介绍 uCOS (也称为 μC/OS 或 Micro-Controller Operating System) 是一个开源的、可移植的、可裁剪的、抢占式实时多任务操作系统 (RTOS)。它最初由 Jean J. Labrosse 编写&#xff0c;并广泛用于嵌入式系统设计中。uCOS 是一个小型的 RTOS&#xff0c;非常适合那些需要实时性…

区间预测 | Matlab实现CNN-ABKDE卷积神经网络自适应带宽核密度估计多变量回归区间预测

区间预测 | Matlab实现CNN-ABKDE卷积神经网络自适应带宽核密度估计多变量回归区间预测 目录 区间预测 | Matlab实现CNN-ABKDE卷积神经网络自适应带宽核密度估计多变量回归区间预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现CNN-ABKDE卷积神经网络自适应…

基于深度学习网络的USB摄像头实时视频采集与人脸检测matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 将摄像头对这播放视频的显示器&#xff0c;然后进行识别&#xff0c;识别结果如下&#xff1a; 本课题中&#xff0c;使用的USB摄像头为&#xff…

30.保存游戏配置到文件

上一个内容&#xff1a;29.添加录入注入信息界面 以 29.添加录入注入信息界面 它的代码为基础进行修改 效果图&#xff1a; 首先在我们辅助程序所在目录下创建一个ini文件 文件内容 然后首先编写一个获取辅助程序路径的代码 TCHAR FileModule[0x100]{};GetModuleFileName(NUL…

【教学类-12-12】20240617通义万相-动物图片6张编故事(A4一页4条)

背景需求 【教学类-12-11】20240612通义万相-动物图片连连看&#xff08;A4一页3套&#xff09;-CSDN博客文章浏览阅读891次&#xff0c;点赞34次&#xff0c;收藏11次。【教学类-12-11】20240612通义万相-动物图片连连看&#xff08;A4一页3套&#xff09;https://blog.csdn.n…

Web前端项目-拼图游戏【附源码】

拼图游戏 拼图游戏是一种经典的益智游戏&#xff0c;通过HTML、CSS和JavaScript等前端技术的综合运用来实现&#xff1b;拼图游戏可以锻炼玩家的观察能力、空间认知能力和逻辑思维能力。游戏开始时&#xff0c;一张图片会被切割成多个小块&#xff0c;并以随机顺序排列在游戏区…

【第三篇】SpringSecurity请求流程分析

简介 本篇文章主要分析一下SpringSecurity在系统启动的时候做了那些事情、第一次请求执行的流程是什么、以及SpringSecurity的认证流程是怎么样的,主要的过滤器有哪些? SpringSecurity初始化流程 1.加载配置文件web.xml 当Web服务启动的时候,会加载我们配置的web.xml文件…

你是否感受到AI就在身边?

人工智能&#xff08;AI&#xff09;是一项革命性的技术&#xff0c;旨在模仿人类智慧并执行通常需要人类认知能力的任务。它覆盖了多个子领域&#xff0c;如机器学习、自然语言处理、计算机视觉和机器人技术。AI系统设计用于分析大量数据、从模式中学习、做出预测&#xff0c;…

ue5创建地图瓦片

先在虚幻商城下载免费的paperzd插件&#xff0c;并启用。 导入资源后&#xff0c;先通过应用paper2d纹理资源&#xff0c;将去掉导入ue时产生的边缘模糊&#xff0c;再点击下面的创建瓦片集&#xff0c; 打开瓦片集&#xff0c;发现选中不对&#xff0c; 改变瓦片大小为16*…

Java——IO流(字符流,字节流)

JavaIO的整体框架图 IO流从方向上来说&#xff0c;可以分为输入流和输出流&#xff1b; 从传输内容上来说&#xff0c;可以分为字符流和字节流 防止记混的口诀 所谓的IO&#xff0c;说白了就是数据在内存和硬盘之间的传输 输入流 %Reader %InputStream&#xff0c;从硬盘写…

如何应对缺失值带来的分布变化?探索填充缺失值的最佳插补算法

本文将探讨了缺失值插补的不同方法&#xff0c;并比较了它们在复原数据真实分布方面的效果&#xff0c;处理插补是一个不确定性的问题&#xff0c;尤其是在样本量较小或数据复杂性高时的挑战&#xff0c;应选择能够适应数据分布变化并准确插补缺失值的方法。 我们假设存在一个…

华为----RIP- RIPv2的认证配置

8.2 配置RIPv2的认证 8.2.1 原理概述 配置协议的认证可以降低设备接受非法路由选择更新消息的可能性&#xff0c;也可称为“验证”。非法的更新消息可能来自试图破坏网络的攻击者&#xff0c;或试图通过欺骗路由器发送数据到错误的目的地址的方法来捕获数据包。RIPv2协议能够…

Pycharm社区版搭建Django环境及Django简单项目、操控mysql数据库

Web应用开发&#xff08;Django&#xff09; 一、配置Django环境 1、先通过Pycharm社区版创建一个普通的项目 2、依次点击”file"-->"Settings" 3、点击"Project:项目名"-"Python Interpreter"-"号" 4、在搜索框输入要安装的…

CSS选择符和可继承属性

属性选择符&#xff1a; 示例&#xff1a;a[target"_blank"] { text-decoration: none; }&#xff08;选择所有target"_blank"的<a>元素&#xff09; /* 选择所有具有class属性的h1元素 */ h1[class] { color: silver; } /* 选择所有具有hre…

渗透测试和红蓝对抗是什么?二者之间有何区别?

在网络安全这个庞大的体系中&#xff0c;渗透测试、红蓝对抗是比较常见的专业名词&#xff0c;承担着非常重要的作用&#xff0c;那么什么是渗透测试、红蓝对抗?红蓝对抗和渗透测试有什么区别?小编通过这篇文章为大家介绍一下。 渗透测试 渗透测试&#xff0c;是通过模拟黑…

Java基础 - 练习(一)打印等腰三角形

Java基础练习 打印等腰三角形&#xff0c;先上代码&#xff1a; public static void main(String[] args) {// 打印等腰三角形System.out.println("打印等腰三角形&#xff1a;");isoscelesTriangle(); } public static void isoscelesTriangle() {// for循环控制行…

java:一个springfox swagger2的简单例子

# 示例程序 【pom.xml】 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>2.3.12.RELEASE</version> </dependency> <dependency><groupId>…

《Cloud Native Data Center Networking》(云原生数据中心网络设计)读书笔记 -- 02 Clos拓扑

本章回答以下问题&#xff1a; 什么是 Clos 拓扑&#xff0c;它与“接入 - 汇聚 - 核心”拓扑有何不同?Clos 拓扑的特征是什么?Clos 拓扑对数据中心网络的影响是什么? Clos拓扑 云原生数据中心基础设施的先行者们想要构建一种支持大规模水平扩展网络。 基本的Clos拓扑如图…