07. HTTP接口请求重试怎么处理?

 目录

1、前言

2、实现方式

2.1、循环重试

2.2、递归重试

2.3、Spring Retry

2.4、Resilience4j

2.5、http请求网络工具内置重试方式

2.6、自定义重试工具

2.7、并发框架异步重试

2.8、消息队列

3、小结


1、前言

HTTP接口请求重试是指在请求失败时,再次发起请求的机制。在实际应用中,由于网络波动、服务器故障等原因,HTTP接口请求可能会失败。为了保证系统的可用性和稳定性,需要对HTTP接口请求进行重试。

2、实现方式

今天给大家分享一些常见的接口请求重试的方式。本地模拟了一个请求接口,后面的代码示例均模拟请求该接口:

@GetMapping("http_test")
public String getHttpTest(){
    return "接口请求成功,返回:OK";
}

2.1、循环重试

循环重试是最简单最粗暴的方式,就是在请求接口代码中加入循环机制,如果接口请求失败,则循环继续发起接口请求,直到请求成功或接口重试次数达到上限。如果请求成功,则不进行重试。

简单代码示例如下:

@GetMapping("retry_demo_loop")
public String retry_demo_loop(){
    // 重试上限次数为3次
    int maxRetryTime = 3;
    String result = null;
    // 接口循环请求
    for (int i = 1; i <= maxRetryTime; i++) {
        try {
            // 模拟请求接口
            result = HttpUtil.get("http://localhost:8080/http_test");
            // 模拟一次请求失败
            if(i == 1){
                int co = i / 0;
            }
            // 请求成功,跳出循环
            break;
        } catch (Exception e) {
            log.error("接口请求异常,进行第{}次重试", i);
            result = "接口请求失败,请联系管理员";
        }
    }
    return result;
}

请求结果:

重试日志打印:

2.2、递归重试

除了循环,还可以使用递归来实现接口的请求重试。递归是我们都比较熟悉的编程技巧,在请求接口的方法中调用自身,如果请求失败则继续调用,直到请求成功或达到最大重试次数。

@GetMapping("retry_demo_rec")
public String retry_demo_rec(){
    // 重试上限次数为3次
    int maxRetryTime = 3;
    return retryRequest(maxRetryTime);
}

/**
 * 递归方法
 * @param maxRetryTime
 * @return
 */
private String retryRequest(int maxRetryTime){
    if (maxRetryTime <= 0) {
        return "接口请求失败,请联系管理员";
    }

    int retryTime = 0;
    try {
        // 模拟请求接口
        String result = HttpUtil.get("http://localhost:8080/http_test");
        // 模拟一次请求失败
        if(maxRetryTime == 3){
            int co = 1 / 0;
        }
        return result;
    } catch (Exception e) {
        // 处理异常
        log.error("接口请求异常,进行第{}次重试", ++retryTime);
        return retryRequest(maxRetryTime - 1);
    }
}

请求结果:

重试日志打印:

2.3、Spring Retry

第三种便是使用Spring Retry依赖实现。首先我们需要集成相关依赖:

<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
</dependency>
<!-- 由于retry使用到了aop,所以还需要加入aop依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

加入@EnableRetry启动:

@EnableRetry
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

添加retry方法注解:

public interface MyRetryService {

    /**
     * retryable注解表示该方法需要重试
     * value:出现该指定异常后,进行重试
     * maxAttempts:重试次数上限,这里指定为3次
     * backoff:重试策略,这里指定200ms间隔一次
     * @param code
     * @return
     * @throws Exception
     */
    @Retryable(value = {Exception.class}, maxAttempts = 3, backoff = @Backoff(200))
    String retry(int code) throws Exception;


    /**
     * 当重试达到上限后还是失败,则作为异常回调方法
     * @param th
     * @param code
     * @return
     */
    @Recover
    String recover(Throwable th, int code);

}

MyReretService实现类:

@Slf4j
@Service
public class MyRetryServiceImpl implements MyRetryService {

    @Override
    public String retry(int code) throws Exception {
        log.info("请求retry接口");
        String result = HttpUtil.get("http://localhost:8080/http_test");
        if(code != 200){
            throw new Exception("接口请求异常");
        }

        return result;
    }

    @Override
    public String recover(Throwable th, int code) {
        log.error("回调方法执行!!!!");
        return "异常码为:" + code + ",异常信息:" + th.getMessage();
    }
}

Controller:

@Autowired
private MyRetryService myRetryService;


/**
 * 当请求code参数为200时,直接成功
 * 当code参数!=200时,会出发重试
 * @param code
 * @return
 * @throws Exception
 */
@GetMapping("retry_demo_spring_retry")
public String retry_demo_spring_retry(Integer code) throws Exception {
    return myRetryService.retry(code);
}

访问地址:http://localhost:8080/retry_demo_spring_retry?code=123

查看结果:可以看到接口重试了3次,最后执行了@Recover方法最后的回调。

2.4、Resilience4j

Resilience4j是一个轻量级、易于使用的轻量级“容错”包。它受Neflix Hystrix启发但只有一个依赖(Vavr),而不像Hystrix很多很多的依赖。同时它是一个 Java 库,可以帮助我们构建弹性和容错的应用程序。Resilience4j在“容错”方面提供了各种模式:断路器(Circuit Breaker)、重试(Retry)、限时器(Time Limiter)、限流器(Rate Limiter)、隔板(BulkHead)。我们今天讨论的话题是重试,那么今天就来演示下Retry。

Github地址:GitHub - resilience4j/resilience4j: Resilience4j is a fault tolerance library designed for Java8 and functional programming

首先,添加相应依赖:

<dependency>
    <groupId>io.github.resilience4j</groupId>
    <artifactId>resilience4j-spring-boot2</artifactId>
    <version>2.1.0</version>
</dependency>

application.yml配置相关策略,配置官方文档:https://resilience4j.readme.io/docs/retry

resilience4j:
  retry:
    instances:
      retry_demo:
        max-attempts: 3     # 重试的上限次数
        wait-duration: 1s   # 重试的间隔时间,配置为1s

我们改造一下上面spring-retry的demo。

controller:

@GetMapping("retry_demo_spring_retry")
@Retry(name = "retry_demo", fallbackMethod = "recover")
public String retry_demo_spring_retry(Integer code) throws Exception {
    return myRetryService.retry(code);
}

public String recover(Throwable th) {
    log.error("回调方法执行!!!!");
    return "异常信息:" + th.getMessage();
}

myRetryService:

@Override
public String retry(int code) throws Exception {
    log.info("请求retry接口");
    String result = HttpUtil.get("http://localhost:8080/http_test");
    if(code != 200){
        throw new Exception("接口请求异常");
    }
    return result;
}

程序执行,打印结果:

同样接口请求了3次,均失败后执行了fallback回调方法。

2.5、http请求网络工具内置重试方式

通常一些外部的http网络工具,都会内置一些重试的策略。如Apache HttpClient。这里以httpclient5为例。

首先添加依赖:

<dependency>
    <groupId>org.apache.httpcomponents.client5</groupId>
    <artifactId>httpclient5</artifactId>
    <version>5.1.4</version>
</dependency>

定义HttpClient相关类,指定重试策略。可以使用默认的DefaultHttpRequestRetryStrategy,也可以自定义重试策略CustomRetryStrategy。

private static volatile CloseableHttpClient HTTP_CLIENT = null;
    static {
        if(HTTP_CLIENT == null){
            synchronized (HelloWorldController.class) {
                if(HTTP_CLIENT == null){
                    HTTP_CLIENT = HttpClients.custom()
                            // 设置重试策略
//                            .setRetryStrategy(new DefaultHttpRequestRetryStrategy(3, TimeValue.NEG_ONE_SECOND))
                            // 自定义重试策略
                            .setRetryStrategy(new CustomRetryStrategy())
                            .build();
                }
            }
        }
    }

CustomRetryStrategy:

public static class CustomRetryStrategy implements HttpRequestRetryStrategy {

    @Override
    public boolean retryRequest(HttpRequest httpRequest, IOException e, int executeCount, HttpContext httpContext) {
        return false;
    }

    @Override
    public boolean retryRequest(HttpResponse httpResponse, int executeCount, HttpContext httpContext) {
        System.out.println("进入重试策略");
        if(executeCount > 3){
            System.out.println("重试超过3次,终止重试");
            return false;
        }

        if(httpResponse.getCode() != 200){
            System.out.println("http状态码不等于200,进行重试");
            return true;
        }

        // 其他情况,不重试
        return false;
    }

    @Override
    public TimeValue getRetryInterval(HttpResponse httpResponse, int executeCount, HttpContext httpContext) {
        return null;
    }
}

Controller代码:

@GetMapping("retry_demo_httpclient")
public String retry_demo_httpclient(Integer code) throws Exception {
    return httpclientRetry(code);
}

private String httpclientRetry(int code) throws Exception {
    log.info("请求retry接口");
    // 这里模拟了一个不存在的地址
    HttpGet request = new HttpGet("http://localhost:8080/http_test1");
    CloseableHttpResponse httpResponse = HTTP_CLIENT.execute(request);
    String result = IoUtil.read(httpResponse.getEntity().getContent()).toString();
    if(code != 200){
        throw new Exception("接口请求异常");
    }

    return result;
}

访问接口地址:http://localhost:8080/retry_demo_httpclient?code=200。查看控制台日志打印:

2.6、自定义重试工具

装X的话,我们还可以自定义我们的重试工具。其实无非以下几个步骤:

  1. 自定义重试的工具类
  2. 接收一个方法调用,并对该方法进行异常捕获
  3. 如果捕获了该异常,则进行一定间隔,然后重新请求
  4. 记录请求次数,如果超过上限,则提示异常信息

直接定义一个重试的工具类RetryUtil.java:

import cn.hutool.core.thread.ThreadUtil;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.function.Supplier;

@Slf4j
public class RetryUtil {


    /**
     * 重试方法
     * @param invokeFunc   原方法调用
     * @param maxAttempts  重试次数上限
     * @param deplay   重试的间隔时间
     * @param timeUnit   重试的间隔时间单位
     * @param faultFunc   如果超过重试上限次数,那么会执行该错误回调方法
     * @return
     * @param <T>
     */
    public static <T> T retry(Supplier<T> invokeFunc, int maxAttempts, long deplay, TimeUnit timeUnit, Function<Throwable, T> faultFunc) {
        AtomicInteger retryTimes = new AtomicInteger(0);
        for(;;) {
            try{
                return invokeFunc.get();
            } catch (Throwable th) {
                if(retryTimes.get() > maxAttempts){
                    log.error("重试次数超过{}次,进入失败回调", retryTimes.get());
                    return faultFunc.apply(th);
                }
                ThreadUtil.sleep(deplay, timeUnit);
                retryTimes.getAndAdd(1);
            }
        }
    }

}

工具类使用:

@GetMapping("retry_demo_custom")
public String retry_demo_custom(Integer code)  {
    return RetryUtil.retry(() -> {
        String result = null;
        try {
            result = customRetry(code);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return result;
    }, 3, 1000, TimeUnit.MILLISECONDS, Throwable::getMessage);
}

private String customRetry(int code) throws Exception {
    log.info("请求customRetry接口");
    String result = HttpUtil.get("http://localhost:8080/http_test");
    if(code != 200){
        throw new Exception("接口请求异常");
    }

    return result;
}

执行完后,访问地址:http://localhost:8080/retry_demo_custom?code=2001

这里只是简单的进行了定义,如果项目中使用肯定需要考虑更复杂的因素。如进入重试时不一定只有异常的时候需要重试,可以指定重试策略,然后制定进入重试策略的规则。

2.7、并发框架异步重试

在 Java 并发框架中,异步重试通常涉及到使用线程池和定时器,以便在异步任务失败后进行重试。以下是一个简单的示例,演示了如何使用 CompletableFuture、ScheduledExecutorService 和 CompletableFuture.supplyAsync 来实现异步任务的重试。

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class AsyncRetryExample {

    private static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

    public static void main(String[] args) {
        // 示例异步任务,这里使用 supplyAsync,你可以根据实际情况选择其他异步任务
        CompletableFuture<String> asyncTask = CompletableFuture.supplyAsync(() -> performAsyncTask("Task"));

        // 异步任务失败后的重试逻辑
        retryAsyncTask(asyncTask, 3, 1, TimeUnit.SECONDS);
    }

    private static CompletableFuture<String> performAsyncTask(String taskName) {
        // 模拟异步任务,这里可以是任何异步操作
        System.out.println("Performing async task: " + taskName);
        // 这里模拟任务失败的情况
        throw new RuntimeException("Task failed");
    }

    private static <T> void retryAsyncTask(CompletableFuture<T> asyncTask, int maxRetries, long delay, TimeUnit timeUnit) {
        asyncTask.exceptionally(throwable -> {
            // 异步任务失败后的处理逻辑
            System.out.println("Task failed: " + throwable.getMessage());

            // 重试逻辑
            if (maxRetries > 0) {
                System.out.println("Retrying...");

                CompletableFuture<T> retryTask = CompletableFuture.supplyAsync(() -> performAsyncTask("Retry Task"));
                // 递归调用,进行重试
                retryAsyncTask(retryTask, maxRetries - 1, delay, timeUnit);
            } else {
                System.out.println("Max retries reached. Task failed.");
            }

            return null; // 必须返回 null,否则会影响链式调用
        });
    }
}

示例中,performAsyncTask 模拟了一个异步任务,如果任务失败,它会抛出一个运行时异常。retryAsyncTask 方法用于处理异步任务的失败情况,并进行重试。在重试时,它使用 CompletableFuture.supplyAsync 创建一个新的异步任务,模拟了重试的过程。请注意,这只是一个简单的示例,实际应用中可能需要更复杂的重试策略和错误处理逻辑。

2.8、消息队列

网上还有一种消息队列的方式来实现,这里没过多的去研究过,目前以上几种方式应该也是够用的了。这里直接贴出网上的部分代码,使用 RabbitMQ 作为消息队列,演示了请求重试的实现:

首先添加依赖:

<dependencies>
    <dependency>
        <groupId>com.rabbitmq</groupId>
        <artifactId>amqp-client</artifactId>
        <version>5.13.1</version>
    </dependency>
</dependencies>

然后,创建一个发送者和接收者类:

消息发送者(Producer)

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class MessageProducer {

    private static final String QUEUE_NAME = "retry_queue";

    public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");

        try (Connection connection = factory.newConnection(); Channel channel = connection.createChannel()) {
            channel.queueDeclare(QUEUE_NAME, false, false, false, null);

            // 模拟发送请求
            String request = "Your request data";

            // 将请求发送到队列
            channel.basicPublish("", QUEUE_NAME, null, request.getBytes());
            System.out.println(" [x] Sent '" + request + "'");
        }
    }
}

消息接收者(Consumer)

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class MessageConsumer {

    private static final String QUEUE_NAME = "retry_queue";

    public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");

        try (Connection connection = factory.newConnection(); Channel channel = connection.createChannel()) {
            channel.queueDeclare(QUEUE_NAME, false, false, false, null);

            // 设置消息监听器
            DeliverCallback deliverCallback = (consumerTag, delivery) -> {
                String request = new String(delivery.getBody(), "UTF-8");

                // 模拟处理请求,这里可能会出现处理失败的情况
                boolean processingSucceeded = processRequest(request);

                if (processingSucceeded) {
                    System.out.println(" [x] Received and processed: '" + request + "'");
                } else {
                    // 处理失败,将请求重新放入队列,进行重试
                    channel.basicPublish("", QUEUE_NAME, null, delivery.getBody());
                    System.out.println(" [x] Processing failed. Retrying: '" + request + "'");
                }
            };

            // 消费消息
            channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> {
            });
        }
    }

    private static boolean processRequest(String request) {
        // 模拟处理请求的方法
        // 在实际应用中,这里应该是对请求的处理逻辑
        // 返回 true 表示处理成功,返回 false 表示处理失败,需要进行重试
        // 这里简单地模拟了一个失败的情况
        return !request.equals("Your request data");
    }
}

示例中,消息发送者(MessageProducer)将请求发送到名为 "retry_queue" 的队列中。消息接收者(MessageConsumer)监听队列,当接收到消息时,模拟处理请求的逻辑。如果处理失败,将请求重新放入队列进行重试。

3、小结

接口请求重试机制对保证系统高可用非常关键,需要根据业务需求选择合适的重试策略。常用的组合策略包括带最大次数的定时/指数退避重试、故障转移重试等。重试机制需要综合设置以达到容错效果 又避免产生过大的系统负载。

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

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

相关文章

[python]matplotlib

整体图示 .ipynb 转换md时候图片不能通知携带&#xff0c;所有图片失效&#xff0c;不过直接运行代码可以执行 figure figure,axes与axis import matplotlib.pyplot as plt figplt.figure() fig2plt.subplots() fig3,axsplt.subplots(2,2) plt.show()<Figure size 640x480 …

C++模板进阶操作 ---非类型模板参数、模板的特化以及模板的分离编译

本专栏内容为&#xff1a;C学习专栏&#xff0c;分为初阶和进阶两部分。 通过本专栏的深入学习&#xff0c;你可以了解并掌握C。 &#x1f493;博主csdn个人主页&#xff1a;小小unicorn ⏩专栏分类&#xff1a;C &#x1f69a;代码仓库&#xff1a;小小unicorn的代码仓库&…

计算机网络复习1

概论 文章目录 概论计算机网络的组成功能分类性能指标&#xff08;搞清楚每个时延的具体定义&#xff09;分层结构协议、接口和服务服务的分类ISO/OSITCP/IP两者的不同 计算机网络的组成 组成部分&#xff1a;硬件&#xff0c;软件和协议&#xff08;协议&#xff1a;传输数据…

C++ stack使用、模拟实现、OJ题

目录 一、介绍 二、常用函数 三、模拟实现 四、OJ练习题 1、最小栈 2、栈的压入、弹出序列 3、逆波兰表达式(后缀转中缀) 4、中缀转后缀思路 5、用栈实现队列 一、介绍 stack是一种容器适配器&#xff0c;专门用在具有后进先出操作的上下文环境中&#xff0c;其删除…

二叉树的前序遍历 、二叉树的最大深度、平衡二叉树、二叉树遍历【LeetCode刷题日志】

目录 一、二叉树的前序遍历 方法一&#xff1a;全局变量记录节点个数 方法二&#xff1a;传址调用记录节点个数 二、二叉树的最大深度 三、平衡二叉树 四、二叉树遍历 一、二叉树的前序遍历 方法一&#xff1a;全局变量记录节点个数 计算树的节点数: 函数TreeSize用于…

[情商-5]:用IT直男擅长的流程图阐述高情商聊天过程与直男聊天过程

目录 一、目标与主要思想的差别 二、高情商聊天与直男聊天的流程图 1. 发起谈话主题Topic 2. 分析谈话的主题和内容 3. 确定谈话目的&#xff1a;解决问题还是情绪交流 4. 倾听&#xff1a;站在自己的角度倾听、捕获、理解对方的情绪状态与情绪诉求 5. 同理心&#xff1…

探索 CodeWave低代码技术的魅力与应用

目录 前言1 低代码平台2 CodeWave简介3 CodeWave 的独特之处3.1 高保真还原交互视觉需求3.2 擅长复杂应用开发3.3 支持应用导出&独立部署3.4 金融级安全要求3.5 可集成性高3.6 可拓展性强 4 平台架构和核心功能4.1 数据模型设计4.2 页面设计4.3 逻辑设计4.4 流程设计4.5 接…

milvus学习(一)cosin距离和欧式距离

参考&#xff1a;https://blog.csdn.net/qq_36560894/article/details/115408613 归一化以后的cosin距离和欧式距离可以相互转化&#xff0c;未归一化的不可以相互转化&#xff08;因为距离带单位&#xff09;。

IO DAY2

#include<my_head.h> //定义注册函数*************************************************** int do_register() { //以追加的形式打开文件 FILE *wfp 0; char name[20]; char pwd[20]; printf("请输入注册账号&#xff1a;"); fgets(…

VMware15安装Linux,CentOS-7x86_64

最近面试遇到很多Linux&#xff0c;咱就是实在糊弄不过去了&#xff0c;学一下吧 下载网站&#xff0c;官网&#xff1a;https://www.centos.org/download/ 第一步&#xff1a;点击x86_64 第二步&#xff1a;随便选个国内源&#xff0c;我选的清华 第三步&#xff1a;等待下…

【LeetCode每日一题】466. 统计重复个数

2024-1-2 文章目录 [466. 统计重复个数](https://leetcode.cn/problems/count-the-repetitions/)思路&#xff1a; 466. 统计重复个数 思路&#xff1a; ​ s1表示要重复的序列。n1表示要重复s1的次数。 ​ s2表示要判断的子序列。n2表示子序列s2在整个序列中重复的次数。返回…

基于微信小程序的停车预约系统设计与实现

基于微信小程序的停车预约系统设计与实现 项目概述 本项目旨在结合微信小程序、后台Spring Boot和MySQL数据库&#xff0c;打造一套高效便捷的停车预约系统。用户通过微信小程序进行注册、登录、预约停车位等操作&#xff0c;而管理员和超级管理员则可通过后台管理系统对停车…

2024 年政府和技术预测

新的一年即将来临&#xff0c;这意味着专家、技术专家和专栏作家应该尝试预测 2024 年政府和技术即将出现的一些最大趋势。今年可能使这些预测变得更加困难的是事实上&#xff0c;许多技术正在以惊人的速度向前发展。在某些情况下&#xff0c;过去需要多年才能慢慢发生的变化现…

Vue3-32-路由-重定向路由

什么是重定向 路由的重定向 &#xff1a;将匹配到的路由 【替换】 为另一个路由。 redirect : 重定向的关键字。 重定向的特点 1、重定向是路由的直接替换,路由的地址是直接改变的&#xff1b; 2、在没有子路由配置的情况下&#xff0c;重定向的路由可以省略 component 属性的配…

MySQL四大引擎建库建表账号管理

目录 一. 数据库四大引擎 1.1 引擎查看 1.2 InnoDB引擎 1.3 MyISAM引擎 1.4 MEMORY引擎 1.5 Archive引擎 二. 数据库管理 2.1 元数据库 2.2 数据库的增删改查及使用 2.3 权限相关表 三. 数据表管理 3.1 三大范式 3.2 基本数据类型 优化原则 分类 四. 数据库账号…

C++Qt6 多种排序算法的比较 数据结构课程设计 | JorbanS

一、 问题描述 在计算机科学与数学中&#xff0c;一个排序算法&#xff08;英语&#xff1a;Sorting algorithm&#xff09;是一种能将一串资料依照特定排序方式排列的算法。最常用到的排序方式是数值顺序以及字典顺序。有效的排序算法在一些算法&#xff08;例如搜索算法与合…

学习【Mysql基础篇】这一篇就够了

Mysql基础篇 1. Mysql概述1-1. 数据库相关概念1-2. Mysql数据库版本下载安装启动停止客户端连接数据模型 2. SQL2-1. SQL通用语法2-2. SQL分类2-3. DDL数据库操作表操作 - 查询创建表操作 - 修改表操作 - 删除数据类型 2-4. 图像化界面工具2-5. DML2-6. DQL2-7. DCL 3. 函数4. …

深度理解Flutter:有状态Widget与无状态Widget的详细对比

有状态Widget 什么是有状态Widget (StatefulWidget) 官方解释&#xff1a; 如果用户与 widget 交互&#xff0c;widget 会发生变化&#xff0c;那么它就是 有状态的。 有状态的 widget 自身是可动态改变的&#xff08;基于State&#xff09;。 例如用户交互而改变 Widget 的 s…

Java中的类和方法(方法重载)

目录 前言&#xff1a; 什么是面向对象&#xff1f; 什么是类&#xff1f; 类和方法的关系&#xff1a; 方法的定义&#xff1a; 方法重载&#xff1a; 类的定义&#xff1a; 修改类的名字&#xff1a; 实例化&#xff1a; 利用方法对其属性赋值&#xff1a; this…

C++多态性——(2)联编

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 成功的秘诀就在于多努力一次&#xff…