Web 开发会话技术之 -Cookie介绍以及源码分析和图分析以及Cookie的生命周期--路径--中文乱码的分析和代码示例

news/2024/4/25 0:15:59/

目录

会话

基本介绍

1. 什么是会话?

2. 会话过程中要解决的一些问题?

cookie 技术

cookie 介绍

二说 cookie

cookie 可以用来做啥

cookie 基本使用

cookie 常用方法

cookie 底层实现机制-创建和读取 Cookie

CreateCookie

访问 CreateCookie.java, 使用浏览器抓包分析 , 创建 Cookie 的底层机制

 ReadCookie.java

  访问 ReadCookie.java, 使用浏览器抓包分析 读取 Cookie 的底层机制.

不同会话,jsessionid 不同 

代码

读取指定 Cookie

工具类

-修改 Cookie

cookie 生命周期

介绍

代码示例

 3. 完成测试 , 注意抓包看数据

cookie 有效路径

有效路径规则

3. 规则如下:

 代码示例

3. 完成测试 , 注意抓包看创建 Cookie 时,返回的数据

 4. 完成测试 , 注意抓包看读取 Cookie 时,返回的数据

 代码示例

html页面

java

Cookie 注意事项和细节

代码解决

设置

解码 


Web 开发会话技术之 -Cookie

会话

基本介绍

1. 什么是会话?

会话可简单理解为:用户开一个浏览器,点击多个超链接,访问服务器多个 web 资源,然

后关闭浏览器,整个过程称之为一个会话。

2. 会话过程中要解决的一些问题?

1) 每个用户在使用浏览器与服务器进行会话的过程中,不可避免各自会产生一些数据,服

务器要想办法为每个用户保存这些数据

2) 例如:多个用户点击超链接通过一个 servlet 各自购买了一个商品,服务器应该想办法

把每一个用户购买的商品保存在各自的地方,以便于这些用户点结帐 servlet 时,结帐

servlet 可以得到用户各自购买的商品为用户结帐。

cookie 技术

Cookie是客户端技术,服务器把每个用户的数据以 cookie 的形式写给用户各自的浏览器。当用户使用浏览器再去访问服务器中的 web 资源时,就会带着各自的数据去。这样,web 资源处理的就是用户各自的数据了。【简单示意图】

 

cookie 介绍

二说 cookie

1. Cookie 是服务器在客户端保存用户的信息,比如登录名,浏览历史等, 就可以以 cookie方式保存.

2. Cookie 信息就像是小甜饼(cookie 中文)一样,数据量并不大,服务器端在需要的时候可以从客户端/浏览器读取(http 协议),可以通过图来理解

 再次说明: cookie 数据是保存在浏览器的.

cookie 可以用来做啥

1. 保存上次登录时间等信息

2. 保存用户名,密码, 在一定时间不用重新登录

3. 网站的个性化,比如定制网站的服务,内容

cookie 基本使用

cookie 常用方法

1. Cookie 有点象一张表(K-V),分两列,一个是名字,一个是值,数据类型都是 String , 如图

2. 如何创建一个 Cookie(在服务端创建的)

Cookie c=new Cookie(String name,String val);

c.setMaxAge();//保存时间

3. 如何将一个 Cookie 添加到客户端

response.addCookie(c);

4. 如何读取 cookie(在服务器端读取到 cookie 信息)

request.getCookies();

cookie 底层实现机制-创建和读取 Cookie

1. 需求: 演示 Cookie 底层实现机制, 创建和读取Cookie

CreateCookie

/*** 演示如何创建cookie,并保存到浏览器*/
public class CreateCookie extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("CreateCookie 被调用...");//1. 创建一个Cookie对象//1) username 该cookie的名字 是唯一, 可以理解成是key//2) xxx : 该cookie的值//3) 可以创建多个cookie,老师就创建了一个//4) 这是cookie在服务器端, 还没有到浏览器Cookie cookie = new Cookie("username", "tom");Cookie cookie2 = new Cookie("email", "tom@qq.com");response.setContentType("text/html;charset=utf-8");//2. 将cookie发送给浏览器, 让浏览器将该cookie保存response.addCookie(cookie);response.addCookie(cookie2);PrintWriter writer = response.getWriter();writer.println("<h1>创建cookie成功~</h1>");writer.flush();writer.close();}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {doPost(request, response);}
}

访问 CreateCookie.java, 使用浏览器抓包分析 , 创建 Cookie 的底层机制

 ReadCookie.java

/*** 读取从浏览器发送来的cookie信息[底层仍然是http协议]*/
public class ReadCookies extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("ReadCookies 被调用..");//1. 通过request对象读取cookie信息Cookie[] cookies = request.getCookies();//2. 遍历cookieif (cookies != null && cookies.length != 0) {for (Cookie cookie : cookies) {System.out.println("cookie name= " + cookie.getName()+ " value= " + cookie.getValue());}}//3. 给浏览器返回信息response.setContentType("text/html;charset=utf-8");PrintWriter writer = response.getWriter();writer.println("<h1>读取cookie信息成功~</h1>");writer.flush();writer.close();}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {doPost(request, response);}
}

  访问 ReadCookie.java, 使用浏览器抓包分析 读取 Cookie 的底层机制.

不同会话,jsessionid 不同 

代码

读取指定 Cookie

public class ReadCookieByNameServlet extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//System.out.println("ReadCookieByNameServlet 被调用..");//得到指定的cookie的value//1. 先得到浏览器携带的所有cookieCookie[] cookies = request.getCookies();//2. 使用工具类来获取指定的cookieCookie emailCookie = CookieUtils.readCookieByName("username", cookies);if(null != emailCookie) {System.out.println("得到cookie name=" + emailCookie.getName()+ " value= " + emailCookie.getValue());} else {System.out.println("sorry, 没有这个cookie");}//3. 给浏览器返回信息response.setContentType("text/html;charset=utf-8");PrintWriter writer = response.getWriter();writer.println("<h1>完成读取cookie的任务..</h1>");writer.flush();writer.close();}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {doPost(request, response);}
}

工具类

public class CookieUtils {//编写一个方法,返回指定名字的cookie值public static Cookie readCookieByName(String name, Cookie[] cookies) {//判断传入的参数是否正确if (name == null || "".equals(name) || cookies == null || cookies.length == 0) {return null;}//遍历cookiesfor (Cookie cookie : cookies) {if(name.equals(cookie.getName())) {return cookie;}}return null;}
}

-修改 Cookie

public class UpdateCookie extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("UpdateCookie 被调用...");//需求/*** 1. 需求 演示如何修改Cookie* 1) 给定一个cookie的name, 找到该cookie, 如果找到, 则修改该cookie的值为 -hi* 2) 如果找不到指定的cookie , 则提示, 没有该cookie*///1. 根据name 去查找 cookieString cookieName = "emailx";Cookie[] cookies = request.getCookies();//如果我们直接创建了一个同名的cookie也相当于修改Cookie userNameCookie = new Cookie("username", "hahaha");Cookie cookie = CookieUtils.readCookieByName(cookieName, cookies);if (null == cookie) {//在该浏览器没有email cookieSystem.out.println("当前访问 服务端的 浏览器没有 该cookie");} else {cookie.setValue("hi");}System.out.println("=====修改后的cookies信息=======");//2. 编写cookiefor (Cookie cookie1 : cookies) {System.out.println("cookie name= " + cookie1.getName()+ " value= " + cookie1.getValue());}//3. 给浏览器返回信息response.setContentType("text/html;charset=utf-8");//4. 如果希望我们的浏览器本地的cookie也修改,则需要使用response.addCookie(cookie);if(cookie != null) {response.addCookie(cookie);}//把 新创建的userNameCookie 重新保存到浏览器//如果 保存的userNameCookie 和已经有的cookie同名,就等价于替换.if(userNameCookie != null) {response.addCookie(userNameCookie);}PrintWriter writer = response.getWriter();writer.println("<h1>完成修改cookie的任务..</h1>");writer.flush();writer.close();}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {doPost(request, response);}
}

cookie 生命周期

介绍

1. Cookie 的生命周期指的是如何管理 Cookie 什么时候被销毁(删除)

2. setMaxAge()

                ● 正数,表示在指定的秒数后过期

                ● 负数,表示浏览器关闭,Cookie 就会被删除(默认值是-1)

                ● 0,表示马上删除 Cookie

代码示例

public class CookieLive extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("CookieLive 被调用...");//演示创建一个cookie , 生命周期为 60sCookie cookie = new Cookie("job", "java");//解读:// 1. 从创建该cookie开始计时, 60秒后无效// 2. 浏览器来根据创建的时间,计时到60s秒,就认为该cookie无效// 3. 如果该cookie无效,那么浏览器在发出http请求时,就不在携带该cookiecookie.setMaxAge(60);//将cookie保存到浏览器response.addCookie(cookie);//演示如何删除一个cookie, 比如删除username//1 先得到username cookieCookie[] cookies = request.getCookies();Cookie usernameCookie =CookieUtils.readCookieByName("username", cookies);if(usernameCookie != null) {//2. 将其生命周期设置为0usernameCookie.setMaxAge(0);//3. 重新保存该cookie, 因为你将其生命周期设置0, 就等价于让浏览器删除该cookie//4. 说明:该cookie会被浏览器直接删除//   返回一个Set-Cookie: xxxxx => 一会抓包.//   Set-Cookie: username=tom; Expires=Thu, 01-Jan-1970 00:00:10 GMTresponse.addCookie(usernameCookie);//返回一个Set-Cookie: xxxxx => 一会抓包.}else{System.out.println("没有找到该cookie, 无法删除...");}/************************ 默认的会话级别的 Cookie [即浏览器关闭就销毁了]* 前面我们讲课时,都是默认会话级别的生命周期***********************/Cookie cookie3 = new Cookie("dkey", "dkey_value");/*** 解读 setMaxAge源码* public void setMaxAge(int expiry) {*         this.maxAge = expiry;* }* private int maxAge = -1; 默认就是-1*///cookie.setMaxAge(-1);//设置存活时间response.addCookie(cookie3);// 给浏览器返回信息response.setContentType("text/html;charset=utf-8");PrintWriter writer = response.getWriter();writer.println("<h1>设置cookie生命周期</h1>");writer.flush();writer.close();}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {doPost(request, response);}
}

 3. 完成测试 , 注意抓包看数据

cookie 有效路径

有效路径规则

1. Cookie 有效路径 Path 的设置

2. Cookie 的 path 属性可以有效的过滤哪些 Cookie 可以发送给服务器。哪些不发。path属性是通过请求的地址来进行有效的过滤

3. 规则如下:

cookie1.setPath = /工程路径

cookie2.setPath = /工程路径/aaa

请求地址: http://ip:端口/工程路径/资源

cookie1 会发给服务器

cookie2 不会发给服务器

请求地址: http://ip:端口/工程路径/aaa/资源

cookie1 会发给服务器

cookie2 会发给服务器

 代码示例

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;public class CookiePathServlet extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {doGet(request, response);}protected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {Cookie cookie = new Cookie("keyPath1", "keyPath1Value");// request.getContextPath() + "/aaa" 得到 /工程路径/aaacookie.setPath(request.getContextPath() + "/aaa");Cookie cookie2 = new Cookie("keyPath2", "keyPath2Value");cookie2.setPath(request.getContextPath());response.addCookie(cookie);response.addCookie(cookie2);response.setContentType("text/html;charset=utf-8");response.getWriter().write("<h1>创建 Cookie keyPath1 路径 /工程路径/aaa </h1>");response.getWriter().write("<h1>创建 Cookie keyPath2 路径 /工程路径 </h1>");}
}

3. 完成测试 , 注意抓包看创建 Cookie 时,返回的数据

 4. 完成测试 , 注意抓包看读取 Cookie 时,返回的数据

 代码示例

需求: 完成自动填写登录账户应用案例 , 如果用户登录成功,则下次登录自动填写登录 账户

html页面

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>登录页面</title>
</head>
<body>
<h1>用户登录界面</h1>
<form action="#" method="post">u:<input type="text" name="username"><br/>p:<input type="password" name="pwd"><br/><input type="submit" value="登录">
</form>
</body>
</html>

java

public class LoginServlet extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//System.out.println("LoginServlet 被调用...~~~");//1. 接收表单提交用户名和密码String username = request.getParameter("username");String pwd = request.getParameter("pwd");response.setContentType("text/html;charset=utf-8");PrintWriter writer = response.getWriter();//2. 判断是否合法if ("xxxx".equals(username) && "123456".equals(pwd)) {//将登录成功的用户名,以cookie的形式,保存到浏览器Cookie loginuserCookie = new Cookie("loginuser", username);//设置该cookie生命周期loginuserCookie.setMaxAge(3600 * 24 * 3);response.addCookie(loginuserCookie);//合法writer.println("<h1>登录OK</h1>");} else {//不合法writer.println("<h1>登录失败</h1>");}writer.flush();writer.close();}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {doPost(request, response);}
}

Cookie 注意事项和细节

1. 一个 Cookie 只能标识一种信息,它至少含有一个标识该信息的名称(NAME)和设置值(VALUE)。

2. 一个 WEB 站点可以给一个浏览器发送多个 Cookie,一个浏览器也可以存储多个 WEB 站点提供的 Cookie。

3. cookie 的总数量没有限制,但是每个域名的 COOKIE 数量和每个 COOKIE 的大小是有限制的 (不同的浏览器限制不同, 知道即可) , Cookie 不适合存放数据量大的信息。

4. 注意,删除 cookie 时,path 必须一致,否则不会删除

5. Java servlet 中 cookie 中文乱码

说明 如果存放中文的 cookie, 默认报错, 可以通过 URL 编码和解码来解决, 不建议存 放中文的 cookie 信息

代码解决

设置

public class EncoderCookie extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//System.out.println("EncoderCookie 被调用");//1. 创建cookie, 有中文//1) 如果直接存放中文的cookie, 报错 Control character in cookie value or attribute.//2) 解决方法,就是将中文 编程成 URL编码  英文: Encode=编码//3) 编码后,再保存即可String company = URLEncoder.encode("大家好", "utf-8");Cookie cookie = new Cookie("company", company);//2. 保存到浏览器response.addCookie(cookie);//3. 给浏览器返回信息response.setContentType("text/html;charset=utf-8");PrintWriter writer = response.getWriter();writer.println("<h1>设置中文cookie成功</h1>");writer.flush();writer.close();}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {doPost(request, response);}
}

解码 

public class ReadCookie2 extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("ReadCookie2 被调用..");//读取到中文cookieCookie[] cookies = request.getCookies();Cookie companyCookie = CookieUtils.readCookieByName("company",cookies);String companyVal = companyCookie.getValue();System.out.println("companyVal= " + companyVal);//URL//解码companyVal = URLDecoder.decode(companyVal, "utf-8");System.out.println("解码后 companyVal= " + companyVal);//中文//3. 给浏览器返回信息response.setContentType("text/html;charset=utf-8");PrintWriter writer = response.getWriter();writer.println("<h1>读取中文cookie解码成功~</h1>");writer.flush();writer.close();}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {doPost(request,response);}
}


http://www.ppmy.cn/news/46894.html

相关文章

【hello C++】内存管理

目录 前言&#xff1a; 1. C/C内存分布 2. C语言动态内存管理方式 3. C内存管理方式 3.1 new / delete 操作内置类型 3.2 new和delete操作自定义类型 4. operator new与operator delete函数 4.1 operator new与operator delete函数 5. new和delete的实现原理 5.1 内置类型 5.2…

顺序表的实现

思维导图&#xff1a; 一&#xff0c;顺序表 一&#xff0c;顺序表的创建&#xff08;位置&#xff1a;头文件内&#xff09; 1.1顺序表的结构体类型 要求&#xff1a;创建顺序表并使这个顺序表能够存放数据&#xff0c;能记录有效数据的个数&#xff0c;能够记录容量大小。…

python3 DataFrame一些好玩且高效的操作

pandas在处理Excel/DBs中读取出来&#xff0c;处理为DataFrame格式的数据时&#xff0c;处理方式和性能上有很大差异&#xff0c;下面是一些高效&#xff0c;方便处理数据的方法。 map/apply/applymaptransformagg遍历求和/求平均shift/diff透视表切片&#xff0c;索引&#x…

查询数据库空间(mysql和oracle)

Mysql版 1、查看所有数据库容量大小 -- 查看所有数据库容量大小 SELECTtable_schema AS 数据库,sum( table_rows ) AS 记录数,sum(TRUNCATE ( data_length / 1024 / 1024, 2 )) AS 数据容量(MB),sum(TRUNCATE ( index_length / 1024 / 1024, 2 )) AS 索引容量(MB) FROMinfor…

如何识别来自 ChatGPT 的文本输出

既然 ChatGPT 生成的内容需要和人类生成的内容有明确的区分&#xff0c;那如果我们拿到一个几经转手的、缺失标记的内容片段&#xff0c;有没有办法来判断他的作者&#xff0c;到底属于 ChatGPT&#xff0c;还是属于人类呢&#xff1f; openai 公司&#xff0c;为此主动推出了…

python中__init__.py文件

例子 不要使用相对路径。 __init__.py# from net.functions import * VERSION "1.0.0"import os, sys module_path os.path.dirname(__file__) module_parent_path os.path.dirname(module_path) sys.path.extend([module_path, module_parent_path]) from net i…

Matlab进阶绘图第17期—气泡热图

气泡热图是一种特殊的热图&#xff08;Heatmap&#xff09;。 与传统热图相比&#xff0c;气泡热图利用不同颜色、不同大小的圆形表示数据的大小&#xff0c;可以更加直观地对矩阵数据进行可视化表达。 本文使用自制的bubbleheatmap小工具进行气泡热图的绘制&#xff0c;先来…

赋值法写基础解系中解向量

赋值法写基础解系中解向量 &#xff08;一&#xff09;背景引入 通常解方程组时&#xff0c;将系数矩阵化为行阶梯型&#xff0c;进而可化为行最简型 &#xff08;说一嘴&#xff1a;行最简型是指阶梯口元素全是1&#xff0c;该1所在列全其余全为0&#xff1b;广义行阶梯的阶梯…

5G NR调制阶数与EVM关系以及对系统SNR要求分析

移动通信技术对数据传输速率要求越来越高。一种提高传输速率的思路是使用更高阶的QAM 调制方式&#xff0c;例如5G NR 的256QAM PDSCH&#xff0c;微波的1024QAM&#xff0c;2048QAM和4096QAM 调制。更高阶的QAM 调制方式对系统也提出了更高的要求。例如某个系统的EVM 测试结果…

【小程序分享篇 一 】开发了个JAVA小程序, 用于清除内存卡或者U盘里的垃圾文件非常有用

有一种场景, 手机内存卡空间被用光了,但又不知道哪个文件占用了太大,一个个文件夹去找又太麻烦,所以我开发了个小程序把手机所有文件(包括路径下所有层次子文件夹下的文件)进行一个排序,这样你就可以找出哪个文件占用了内存太大了。 使用例子如下,用JAVA 运行Sort 1,…

YOLOv8详解代码实战,附有效果图

YOLOv8架构 YOLOv8 是 ultralytics 公司在 2023 年 1月 10 号开源的 YOLOv5 的下一个重大更新版本&#xff0c;目前支持图像分类、物体检测和实例分割任务&#xff0c;鉴于Yolov5的良好表现&#xff0c;Yolov8在还没有开源时就收到了用户的广泛关注。yolov8的整体架构如下&…

tmux使用方法

tmux使用指南&#xff1a;比screen好用n倍&#xff01; - 知乎 当你开启一个session的时候会默认开启一个window&#xff0c;这个截图就是一个window&#xff0c;而这个window可以拆成很多分subwindow&#xff0c;在这里就是&#xff1a;左上角Asubwindow&#xff0c;左下角Bsu…

UE4 回放系统升级到UE5之后的代码报错问题解决

关键词&#xff1a; UE4 回放系统 升级 UE5 报错 DemoNetDriver GetDemoCurrentTime GetDemoTotalTime 背景 照着网上教的UE4的回放系统&#xff0c;也叫重播系统&#xff0c;英文Replay。做完了&#xff0c;测试运行正常&#xff0c;可升级到UE5却报了一堆 WorldSetting 和 …

Java数据结构与算法----动态规划(背包篇)

1. 0/1背包 1.1.算法思路 0/1背包是动态规划、背包问题中最经典的问题啦&#xff01;它主要的问题是&#xff1a; 给定n种物品、这n种物品的重量分别是&#xff0c;价值分别是 &#xff0c;而你有一个容量为C的背包&#xff0c;请问如何求出所能拿的最大价值呢&#xff1f; …

【Java EE】-网络编程(一) 网络初识

作者&#xff1a;学Java的冬瓜 博客主页&#xff1a;☀冬瓜的主页&#x1f319; 专栏&#xff1a;【JavaEE】 主要内容&#xff1a;单机、局域网、广域网、交换机、路由器。IP地址&#xff0c;端口号&#xff0c;协议&#xff0c;五元组。 协议分层&#xff0c;OSI七层网络模型…

ChatGPT团队中,3个清华学霸,1个北大学霸,共9位华人

众所周知&#xff0c;美国硅谷其实有着众多的华人&#xff0c;哪怕是芯片领域&#xff0c;华为也有着一席之地&#xff0c;比如AMD 的 CEO 苏姿丰、Nvidia 的 CEO 黄仁勋 都是华人。 还有更多的美国著名的科技企业中&#xff0c;都有着华人的身影&#xff0c;这些华人&#xff…

JVM:线上服务CPU爆满,如何排查(三)

0. 引言 前一段时间出现了一个正则表达式引起的线上CPU爆满的问题&#xff0c;一开始没有在第一时间定位到问题&#xff0c;这里也特此记录一下&#xff0c;同时也系统的梳理下CPU爆满问题的排查思路和方法&#xff0c;为后续的同学提供参考。 1. CPU爆满问题产生的原因 我们…

Cloud computing(后续慢慢补充)

Cloud computing 可以看到右侧的容器虚拟化架构中&#xff0c;不需要运行额外的OS&#xff0c;这样启动的服务性能会相比于通过虚拟化软件实现的架构更优秀。但是虚拟机同样也有它的优点&#xff0c;比如它的安全、隔离性&#xff0c;可以运行不同的操作系统等等。 Virtualiz…

Java对日开发成趋势?网友:找工作打开了新思路

近两年行业环境起起伏伏&#xff0c;企业降本增效&#xff0c;提高人才招聘的门槛&#xff0c;导致大家找工作时觉得越来越难&#xff0c;尤其是Java开发&#xff0c;主打的就是一个“卷”&#xff01; 不过行业变革&#xff0c;挑战与机遇并存。Java作为编程语言排行榜的常年第…

278. 第一个错误的版本

你是产品经理&#xff0c;目前正在带领一个团队开发新的产品。不幸的是&#xff0c;你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的&#xff0c;所以错误的版本之后的所有版本都是错的。 假设你有 n 个版本 [1, 2, ..., n]&#xff0c;你想找出导…