8. Bug 与 Error
计算机程序中的缺陷通常被称为 bug。把它们想象成偶然爬进我们工作中的小东西,会让程序员感觉良好。当然,实际上是我们自己把它们放进去的。
如果程序是思想的结晶,我们可以将错误大致分为思想混乱造成的错误和将思想转化为代码时引入错误造成的错误。前者通常比后者更难诊断和修复。
语言
如果计算机对我们要做的事情有足够的了解,那么很多错误都可以由计算机自动指出来。但在这方面,JavaScript 的松散性是一个障碍。它对绑定和属性的概念非常模糊,以至于在实际运行程序之前很少能发现错误。即便如此,它还是允许你做一些明显毫无意义的事情,而不会抱怨,比如计算 true * “monkey”。
JavaScript 也会抱怨一些事情。编写不符合语言语法的程序会立即引起计算机的抱怨。其他一些事情,比如调用一个非函数的东西,或者查找一个未定义值的属性,都会在程序尝试执行操作时报错。
不过,通常情况下,你的无意义计算只会产生 NaN(不是数字)或未定义的值,而程序会继续愉快地运行,坚信自己在做有意义的事情。只有在假值经过多个函数后,错误才会显现出来。它可能根本不会引发错误,但会悄无声息地导致程序输出错误。查找此类问题的根源可能很困难。
在程序中查找错误的过程称为调试。
严格模式
通过启用严格模式,JavaScript 可以变得更严格一些。这可以通过在文件或函数体的顶部加上 “use strict ”字符串来实现。下面是一个例子:
类和模块(我们将在第 10 章讨论)中的代码自动严格。旧的非严格行为之所以仍然存在,只是因为一些旧代码可能依赖于它,而语言设计者努力避免破坏任何现有程序。
通常情况下,当你忘记在绑定前面加上 let 时(如示例中的 counter),JavaScript 会悄悄创建一个全局绑定并使用它。而在严格模式下,则会报错。这非常有用。不过需要注意的是,如果绑定已经存在于作用域中的某个地方,那么这个方法就不起作用了。在这种情况下,循环仍会悄悄覆盖绑定的值。
严格模式下的另一个变化是,在未作为方法调用的函数中,该绑定值为未定义。在严格模式之外进行此类调用时,this 指的是全局作用域对象,而全局作用域对象的属性就是全局绑定。因此,如果在严格模式下不小心错误地调用了方法或构造函数,JavaScript 在试图从中读取内容时就会产生错误,而不是愉快地写入全局作用域。
例如,下面的代码在调用构造函数时没有使用 new 关键字,这样它的 this 就不会指向一个新构造的对象:
对 Person 的假调用成功了,但返回了一个未定义的值,并创建了全局绑定名称。在严格模式下,结果则不同。
我们立即被告知出了问题。这很有帮助。
幸运的是,使用类符号创建的构造函数如果在没有 new 的情况下被调用,总是会被抱怨,因此即使在非严格模式下,问题也不大。
严格模式还做了一些事情。它不允许给一个函数提供多个同名参数,并完全删除了某些有问题的语言特性(如 with 语句,这种语句是错误的,本书不再讨论)。
总之,在程序顶端加上 “use strict ”很少有坏处,而且可能会帮助你发现问题。
类型
有些语言希望在运行程序之前就知道所有绑定和表达式的类型。当某种类型的使用方式不一致时,它们会立即告诉你。JavaScript 只有在实际运行程序时才会考虑类型,而且即使在运行程序时也会经常尝试将值隐式地转换为它所期望的类型,因此帮不上什么忙。
不过,类型还是为讨论程序提供了一个有用的框架。很多错误都是由于对进入函数或从函数中出来的值的类型感到困惑而造成的。如果把这些信息写下来,就不容易混淆了。
你可以在上一章的 findRoute 函数前添加类似下面的注释来描述它的类型:
用类型注释 JavaScript 程序有许多不同的约定。
关于类型的一个问题是,它们需要引入自身的复杂性,才能描述出足够有用的代码。你认为从数组中随机返回一个元素的 randomPick 函数的类型是什么?你需要引入一个类型变量 T,它可以代表任何类型,这样你就可以给 randomPick 赋予 (T[]) → T(从 Ts 数组到 T 的函数)这样的类型。
当程序的类型已知时,计算机就有可能为你检查这些类型,在程序运行前指出错误。有几种 JavaScript 方言可以在语言中添加类型并对其进行检查。最流行的一种叫做 TypeScript。如果你有兴趣为你的程序增加更多的严谨性,我建议你试一试。
在本书中,我们将继续使用原始、危险、未键入的 JavaScript 代码。
测试
如果程序语言不能帮助我们发现错误,我们就只能通过运行程序,看看它是否做对了。
一次又一次地手工操作是个非常糟糕的主意。这不仅令人讨厌,而且往往效果不佳,因为每次更改都要花费大量时间对所有内容进行详尽测试。
计算机擅长重复性工作,而测试正是理想的重复性工作。自动测试是编写一个程序来测试另一个程序的过程。编写测试程序比手动测试要繁琐一些,但一旦完成,你就会获得一种超能力:只需几秒钟就能验证你的程序在你编写测试程序的所有情况下是否仍能正常运行。当你破坏了某些东西时,你会立即注意到,而不是在以后的某个时间随机遇到。
测试通常采用小标签程序的形式,用于验证代码的某些方面。例如,toUpperCase 方法(标准方法,可能已经有人测试过了)的测试集可能是这样的:
这样编写测试往往会产生相当重复、笨拙的代码。幸运的是,有一种软件可以帮助你构建和运行测试集合(测试套件),它提供了一种适合表达测试的语言(以函数和方法的形式),并在测试失败时输出信息。这些软件通常被称为测试运行程序。
有些代码比其他代码更容易测试。一般来说,与代码交互的外部对象越多,设置测试上下文就越难。上一章中展示的编程风格使用自足的持久值而不是不断变化的对象,这种风格往往容易测试。
调试
一旦你发现程序出了问题,因为它行为不端或产生错误,下一步就是找出问题所在。有时问题很明显。错误信息会指向程序中的某一行,如果查看错误描述和该行代码,通常就能发现问题所在。但并非总是如此。有时,引发问题的那行代码只是在其他地方产生的错误值以无效方式被使用的第一个地方。如果你已经解决了前面几章的练习,你可能已经经历过这种情况。
下面的示例程序试图将一个整数转换成一个给定基数(十进制、二进制等)的字符串,方法是反复挑出最后一位数字,然后除以该数字,去掉该数字。但它目前产生的奇怪输出结果表明它存在一个错误。
即使你已经看到了问题所在,也请暂时假装看不到。我们知道我们的程序出现了故障,我们想找出原因。这时,你必须克制住自己的冲动,不要随意修改代码,看看这样做是否能让程序变得更好。取而代之的是思考。分析正在发生的情况,并就可能发生的原因提出理论。然后进行更多的观察来验证这一理论--或者,如果你还没有理论,也可以进行更多的观察来帮助你提出一个理论。
在程序中调用一些策略性的 console.log 调用,是获取关于程序正在做什么的额外信息的好方法。在本例中,我们希望 n 取值为 13、1 和 0。
对。13 除以 10 不能得到整数。我们实际上需要的是 n = Math.floor(n/base),而不是 n /= base,这样数字就会正确地向右 “移动”。
除了使用 console.log 来窥探程序的行为外,另一种方法是使用浏览器的调试器功能。浏览器具有在特定代码行上设置断点的功能。当程序执行到带有断点的行时,程序会暂停,你可以检查该点的绑定值。由于不同浏览器的调试器不尽相同,我就不详细介绍了,但请查看浏览器的开发工具或在网上搜索相关说明。
另一种设置断点的方法是在程序中加入调试器语句(仅由该关键字组成)。如果浏览器的开发工具处于激活状态,程序在运行到该语句时就会暂停。
错误传播
遗憾的是,并非所有问题程序员都能避免。如果你的程序以任何方式与外界通信,就有可能获得畸形输入、工作负荷过重或网络故障。
如果你只是为自己编程,你可以忽略这些问题,直到它们发生。但如果你编写的程序将被其他人使用,你通常希望程序能做得更好,而不仅仅是崩溃。有时,正确的做法是接受不良输入并继续运行。在其他情况下,最好是向用户报告出错的原因,然后放弃。无论在哪种情况下,程序都必须积极采取措施来应对问题。
假设有一个 promptNumber 函数,向用户询问一个数字并返回该数字。如果用户输入 “orange”,它应该返回什么?
一种方法是让它返回一个特殊值。这种值的常见选择是 null、undefined 或-1。
现在,任何调用 promptNumber 的代码都必须检查是否读取了实际的数字,如果没有,则必须以某种方式恢复--可能是再次询问,也可能是填写默认值。或者,它可以再次向调用者返回一个特殊值,以表明它未能完成所要求的操作。
在许多情况下,主要是当错误很常见并且调用者应该明确考虑到这些错误时,返回一个特殊值是一种很好的提示错误的方法。不过,它也有缺点。首先,如果函数已经可以返回所有可能的值,那该怎么办?在这样的函数中,你就必须像迭代器接口的下一个方法那样,将结果封装在一个对象中,以便区分成功与失败。
返回特殊值的第二个问题是,它可能导致代码笨拙。如果一段代码调用 promptNumber 10 次,它就必须检查 10 次是否返回了空值。如果发现空值后的反应是简单地返回空值,那么函数的调用者就必须依次检查空值,以此类推。
异常
当函数无法正常运行时,我们通常希望停止正在进行的工作,并立即跳转到知道如何处理问题的地方。这就是异常处理的作用。
异常是一种机制,它可以让遇到问题的代码引发(或抛出)异常。异常可以是任何值。引发异常有点类似于函数的超级返回:异常不仅会跳出当前函数,还会跳出其调用者,一直跳到开始当前执行的第一个调用。这就是所谓的释放堆栈。你可能还记得第 3 章中提到的函数调用栈。异常会沿着堆栈向下缩放,丢弃它遇到的所有调用上下文。
如果异常总是直接放大到堆栈底部,那么它们就没有什么用处了。它们只是提供了一种炸毁程序的新方法。异常的强大之处在于,你可以沿着堆栈设置 “障碍”,以便在异常向下放大时捕获它。一旦捕捉到异常,你就可以对它进行处理,解决问题,然后继续运行程序。
这里有一个例子:
throw 关键字用于引发异常。捕获异常的方法是将一段代码封装在 try 代码块中,然后使用关键字 catch。当 try 代码块中的代码导致异常发生时,catch 代码块将被评估,括号中的名称将与异常值绑定。在 catch 代码块结束后,或者如果 try 代码块结束时没有问题,程序将在整个 try/catch 语句下继续执行。
在本例中,我们使用 Error 构造函数创建了异常值。这是一个标准的 JavaScript 构造函数,用于创建一个带有消息属性的对象。Error 实例还会收集创建异常时存在的调用堆栈信息,即所谓的堆栈跟踪。这些信息存储在堆栈属性中,在调试问题时很有帮助:它会告诉我们问题发生在哪个函数中,哪些函数进行了失败调用。
请注意,look 函数完全忽略了 promptDirection 可能出错的可能性。这就是异常的最大优势:只有在错误发生时和处理错误时才需要错误处理代码。中间的函数可以完全不用考虑。
嗯,几乎是...
清理例外情况
异常的影响是另一种控制流。每一个可能导致异常的操作(几乎是每一次函数调用和属性访问)都可能导致控制权突然离开代码。
这就意味着,当代码有多个副作用时,即使其 “常规 ”控制流看起来都会发生,异常也可能会阻止其中一些副作用的发生。
下面是一些非常糟糕的银行代码:
转账函数将一笔钱从一个给定的账户转入另一个账户,在此过程中会询问另一个账户的名称。如果给定的账户名称无效,getAccount 会抛出异常。
但是转账函数会先将钱从账户中取出,然后调用 getAccount,再将钱添加到另一个账户中。如果在这个过程中出现异常,钱就会消失。
这段代码本可以写得更智能一些,例如在开始移动资金之前调用 getAccount。但类似的问题往往以更微妙的方式出现。即使是看起来不会抛出异常的函数,在特殊情况下或包含程序员错误时也可能会抛出异常。
解决这一问题的方法之一是减少副作用。同样,计算新值而非更改现有数据的编程风格也有帮助。如果一段代码在创建新值的过程中停止运行,现有的数据结构不会受到破坏,这样就更容易恢复。
由于这并不总是切实可行,所以 try 语句还有另一个特点:它们后面可能会跟一个 finally 代码块,以代替 catch 代码块或作为 catch 代码块的补充。finally 代码块表示 “无论发生什么,在尝试运行 try 代码块中的代码后,再运行此代码”。
这个版本的函数会跟踪其运行进度,如果在离开时发现它在创建了不一致的程序状态时被终止,它就会修复所造成的损害。
请注意,即使 finally 代码在 try 代码块中抛出异常时运行,它也不会干扰异常。在 finally 代码块运行后,堆栈会继续展开。
编写即使异常在意想不到的地方出现也能可靠运行的程序是很困难的。很多人根本懒得去做,而且由于异常通常只在特殊情况下出现,因此问题可能很少发生,甚至从未被注意到。这究竟是好事还是坏事,取决于软件出现故障时会造成多大的损失。
选择性捕捉
当一个异常没有被捕获而一直到达堆栈底部时,它就会被环境处理。这在不同环境中的含义各不相同。在浏览器中,错误描述通常会写入 JavaScript 控制台(可通过浏览器的 “工具 ”或 “开发人员 ”菜单访问)。我们将在第 20 章讨论的无浏览器 JavaScript 环境 Node.js 对数据损坏更为谨慎。当出现无法处理的异常时,它会中止整个进程。
对于程序员的错误,让错误通过往往是最好的办法。未处理异常是程序崩溃的合理信号,在现代浏览器上,JavaScript 控制台会提供一些信息,告诉你问题发生时堆栈上有哪些函数调用。
对于在日常使用中预计会发生的问题,使用未处理异常导致程序崩溃是一种糟糕的策略。
语言的无效使用,如引用不存在的绑定、查找 null 属性或调用非函数,也会导致异常的产生。这些异常也可以被捕获。
当输入 catch body 时,我们只知道 try body 中的某些内容导致了异常。但我们不知道是什么引起了异常,也不知道是哪个异常。
JavaScript(一个相当明显的疏忽)并不直接支持选择性捕获异常:要么全部捕获,要么一个都不捕获。这就很容易让人认为你捕获到的异常就是你在编写捕获块时考虑到的异常。
但事实可能并非如此。其他一些假设可能被违反,或者你可能引入了一个导致异常的错误。下面是一个试图继续调用 promptDirection 直到得到有效答案的示例:
for (;;) 结构是一种有意创建不会自行终止的循环的方法。只有在给出有效方向时,我们才会跳出循环。不幸的是,我们拼错了 promptDirection,这将导致 “未定义变量 ”错误。由于 catch 代码块完全忽略了异常值 (e),以为自己知道问题所在,因此错误地将绑定错误视为输入错误。这不仅会导致无限循环,还会 “掩盖 ”有关拼写错误绑定的有用错误信息。
一般来说,除非是为了将异常 “路由 ”到其他地方--例如,通过网络告诉其他系统我们的程序崩溃了,否则不要一揽子捕获异常。即便如此,也要仔细考虑如何隐藏信息。
我们要捕获一种特定的异常。我们可以通过在 catch 块中检查我们得到的异常是否是我们感兴趣的异常,如果不是,就重新抛出。但如何识别异常呢?
我们可以将其信息属性与我们预期的错误信息进行比较。但这种编写代码的方式并不可靠--我们将使用供人类使用的信息(信息)来做出程序决策。一旦有人更改(或翻译)了信息,代码就会停止工作。
相反,让我们定义一种新的错误类型,并使用 instanceof 来识别它。
新的错误类扩展了 Error。它没有定义自己的构造函数,这意味着它继承了 Error 的构造函数,后者需要一个字符串消息作为参数。事实上,它根本没有定义任何东西--该类是空的。InputError 对象的行为与 Error 对象类似,只是它们有一个不同的类,我们可以通过这个类来识别它们。
现在,循环可以更仔细地捕捉这些对象了。
这将只捕获 InputError 实例,而让无关的异常通过。如果重新引入拼写错误,未定义的绑定错误将被正确报告。
断言
断言是程序内部的检查,用于验证某些事情是否符合其应有的状态。它们不是用来处理正常运行中可能出现的情况,而是用来发现程序员的错误。
例如,如果 firstElement 被描述为一个永远不应该在空数组上调用的函数,我们可以这样写:
现在,它不再默默地返回未定义(读取不存在的数组属性时会返回未定义),而是一旦你误用它,程序就会立即爆炸。这样,此类错误就不容易被忽视,也更容易在错误发生时找到原因。
我不建议为每一种可能的错误输入编写断言。这将是一项很大的工作量,而且会导致代码非常嘈杂。你需要为容易犯的错误(或你发现自己会犯的错误)保留断言。
总结
编程的一个重要部分就是查找、诊断和修复错误。如果有一个自动化测试套件或在程序中添加断言,问题就更容易被发现。
对于由程序控制之外的因素导致的问题,通常应积极加以规划。有时,当问题可以在本地处理时,特殊返回值是跟踪问题的好方法。否则,异常可能是更好的选择。
抛出异常会导致调用堆栈被释放,直到下一个外层 try/catch 块或堆栈底部。异常值将提供给捕获异常的 catch 块,该块应验证异常确实是预期的异常类型,然后对异常进行处理。为了帮助解决异常导致的控制流不可预测性问题,可以使用 finally 块来确保代码在块结束时始终运行。
练习
重试
假设你有一个函数 primitiveMultiply,它在 20% 的情况下可以实现两个数字相乘,而在另外 80% 的情况下会引发一个类型为 MultiplicatorUnitFailure 的异常。编写一个函数来封装这个笨重的函数,并不断尝试直到调用成功,然后返回结果。
确保只处理你要处理的异常。
代码:
function reliableMultiply(a, b) {try {return primitiveMultiply(a, b);} catch (e) {if (e instanceof MultiplicatorUnitFailure) {console.log(e.message);}}
}
console.log(reliableMultiply(8,2));
上锁的箱子
请看下面这个(相当臆造的)对象:
这是一个带锁的盒子。盒子里有一个数组,但只有当盒子被解锁时才能访问它。
编写一个名为 withBoxUnlocked 的函数,以函数值作为参数,解锁盒子,运行函数,然后在返回之前确保盒子再次被锁定,无论参数函数是正常返回还是抛出异常。
代码:
function withBoxUnlocked(body) {box.unlock();try {body();console.log(box.content);} finally {box.lock();}
}
为了获得额外的分数,请确保如果在盒子已经解锁的情况下调用 withBoxUnlocked,盒子将保持解锁状态。
相关文章:

8. Bug 与 Error
计算机程序中的缺陷通常被称为 bug。把它们想象成偶然爬进我们工作中的小东西,会让程序员感觉良好。当然,实际上是我们自己把它们放进去的。 如果程序是思想的结晶,我们可以将错误大致分为思想混乱造成的错误和将思想转化为代码时引入错误造成…...

论文 | Model-tuning Via Prompts Makes NLP Models Adversarially Robust
这篇论文研究了使用提示 (Prompting) 方法微调预训练语言模型,以提高其在对抗样本攻击下的鲁棒性。论文的主要贡献如下: 1.MVP 比 MLP-FT 更鲁棒: 论文比较了 MVP (Model-tuning Via Prompts) 和传统的 MLP-FT (Fine-tuning with an MLP head…...

828华为云征文|华为云Flexus云服务器X实例部署 即时通讯IM聊天交友软件——高性能服务器实现120W并发连接
营运版的即时通讯IM聊天交友系统:特点可发红包,可添加多条链接到用户网站和应用,安卓苹果APPPC端H5四合一 后端开发语言:PHP, 前端开发语言:uniapp混合开发。 集安卓苹果APPPC端H5四合一APP源码࿰…...

超好用的element的el-pagination分页组件二次封装-附源码及讲解
前言:在很多后台管理系统开发时总会有很多分页组件的使用,如果我们每次都用elementui官网的el-pagination去写的话,调整所有分页的样式就会很麻烦,而且页面内容也会很累赘繁琐。 讲解一个我经常使用的二次封装el-pagination组件&…...

【AIGC】通过OpenAi Canvas修改论文(附40条论文优化指令)
目录 1、用ChatGPT优化论文大纲和逻辑2、用ChatGPT充实论文内容3、用ChatGPT寻找案例和数据4、用ChatGPT检查语法和字词错误5、如何直接使用ChatGPT4o、o1、OpenAI Canvas6、OpenAI Canvas增强了啥?7、编程功能增强 在刚开始撰写学术论文时,很多小伙伴感…...

Kubernetes Pod详解
目录 1. Pod 介绍 1.1 Pod 结构 1.2 Pod 定义 2. Pod 配置 2.1 基本配置 2.2 镜像拉取 2.3 启动命令 2.4 环境变量 2.5 端口设置 2.6 资源配额 3. Pod 生命周期 3.1 创建和终止 3.2 初始化容器 3.3 钩子函数 3.4 容器探测 3.5 重启策略 4. Pod 调度 4.1 定向调…...

Vue2电商项目(七)、订单与支付
文章目录 一、交易业务Trade1. 获取用户地址2. 获取订单信息 二、提交订单三、支付1. 获取支付信息2. 支付页面--ElementUI(1) 引入Element UI(2) 弹框支付的业务逻辑(这个逻辑其实没那么全)(3) 支付逻辑知识点小总结 四、个人中心1. 搭建二级路由2. 展示动态数据(1). 接口(2).…...

你知道U盘怎么加密吗?
1、使用Windows BitLocker: 适用于Windows 10/11专业版及以上版本。 插入U盘,右键点击U盘图标,选择“启用BitLocker”。 设置密码,并选择加密选项,点击“开始加密”。 2、使用Mac的Disk Utility: 适用…...

【软件教程OBS下载使用】一篇文章教会你如何下载安装使用OBS-Studio
OBS Studio是全新的OBS(Open Broadcaster Software),是一款广泛应用的视频直播录制软件,跟经典版的区别就是,音频分路简单,在不出错的情况下性能优于经典版。可以说是高级版,目前仍然处于初期阶段,比起经典…...

鸿蒙next开发第一课03.ArkTs语法介绍-案例
前面已经学习了ArkTs的基本语法和DevEcoStudio的基本操作,接下来按照官方提示开发一个基本案例。 该案例是系统自带的demo,下载下来源代码后可以直接运行。 接下来我来演示如何运行demo。我在demo中加入了自己的注释。 切记:文件夹不能有中…...

HTML网页制作——设计系学生静态HTML网页设计作品
HTML网页制作——设计系学生静态HTML网页设计作品 网站主题为荷兰风格派,主要介绍荷兰风格设计的网站,由设计系学生亲自设计,独立开发网页,适用于学生自己的作品。 网站效果视频: 荷兰风格派(设计系学生网…...

智能翻译新纪元:4款英汉互译在线工具解析
大家好,我是一个喜欢找各种办公软件的人,今天咱们来聊聊那些让我们在英汉互译世界里如鱼得水的神器——福昕翻译在线、福昕翻译大师、海鲸AI论文翻译,还有DeepL翻译。这些家伙,简直就是我们跨语言交流的超级英雄! 1、…...

Cisco Meraki平台中国区注册
登陆下面网址注册cisco meraki中国区云平台账户 https://n4.meraki.cn/ 点击创建一个新账户 地区选择“china” 填写邮箱,名字,秘密,公司名称等信息,点击注册新账户 注册的邮箱会收到一封确认此邮箱的邮件,点击…...

分享国产RISC-V单片机通用
开源已经成为构建新技术生态的主流趋势。基于开源指令集 RISC-V 的软硬件生态正在飞速扩增,并且已经迅速扩展至个人 PC、服务器和人工智能等领域。RISC-V 的灵活性和可扩展性使其能够在应用处理器和AI加速领域迅速发展。 RAMSUN提供的RISC-V单片机,开源…...

java 网络知识 + 多线程问题
服务器: package p1007;import java.io.*; import java.net.*; import java.util.Random;public class Server {public static void main(String[] args) {int port 12345; // 服务端口try (ServerSocket serverSocket new ServerSocket(port)) {System.out.print…...

android 菜单不显示auto time zone菜单
packages\apps\Settings\res\xml\date_time_prefs.xml 有对应的xml packages\apps\Settings\src\com\android\settings\datetime\AutoTimeZonePreferenceController.java Overridepublic boolean isAvailable() {if (mIsFromSUW) {return false;}TimeZoneCapabilities time…...

51单片机的金属探测器【proteus仿真+程序+报告+原理图+演示视频】
1、主要功能 该系统由AT89C51/STC89C52单片机LCD1602显示模块金属检测传感器继电器LED、蜂鸣器等模块构成。适用于金属探测仪、检测金属、剔除金属等相似项目。 可实现功能: 1、LCD1602实时显示是否检测到金属 2、金属检测传感器(按键模拟)检测是否有…...

使用Spring Security实现用户-权限-资源的精细化控制
文章目录 一、基于权限的请求控制二、加载用户权限信息三、自定义异常处理四、注册自定义异常处理器五、总结 在开发Web应用时,权限管理是一个不可忽视的部分。最近在项目中,我使用了Spring Security来实现用户、权限、资源之间的精细化控制。这里我想分…...

动态规划10:174. 地下城游戏
动态规划解题步骤: 1.确定状态表示:dp[i]是什么 2.确定状态转移方程:dp[i]等于什么 3.初始化:确保状态转移方程不越界 4.确定填表顺序:根据状态转移方程即可确定填表顺序 5.确定返回值 题目链接:174.…...

【数据结构】链表-1
数组 数组在分配内存的时候需要先告诉系统它有多大,为什么呢?打个比方,我们有以一列的凳子,按顺序排布,一个位置只放一个,数组呢,是一个家庭,数组这个家庭呢,他们得挨着…...

Python进阶--正则表达式
目录 1. 基础匹配 2. 元字符匹配 1. 基础匹配 正则表达式,又称规则表达式(Regular Expression),是使用单个字符串来描述、匹配某个句法规则的字符串,常被用来检索、替换那些符合某个模式(规则ÿ…...

富格林:发现潜在欺诈安全交易
富格林指出,在全球经济不确定性加剧的背景下,黄金的避险优势再次吸引了投资者的关注。尤其是在今年,随着多种因素的变化,金价的走势引发了市场的广泛讨论。但事实上黄金与其他投资品类相似,也存在潜在的欺诈套路导致我…...

Linux复习--Linux服务管理类(SSH服务、DHCP+FTP、DNS服务、Apache服务、Nginx服务、HTTP状态码)
前言:本博客仅作记录学习使用,部分图片出自网络,如有侵犯您的权益,请联系删除 一、SSH服务 1、问题引出 哪些设置能够提升SSH远程管理的安全等级? 2、SSH的登录验证方式-口令登录 3、SSH的登录验证方式-密钥登录 4、…...

如何用大模型来提升学习效率?
自从2022年底ChatGPT横空出世以来,在过去的十几个月里,生成式人工智能的浪潮席卷并改变着各行各业。 2023年一月,在线课程供应商Study.com曾向1000名18岁以上的学生发起的一项调查显示,当时就已经有超过89%的学生使用ChatGPT来完…...

SQL进阶技巧:如何优雅求解指标累计去重问题?
目录 0 需求概述 1 数据准备 2 问题分析 3 小结 0 需求概述 近期公司开发某项学习功能,改功能有很多学习内容(如java,C,python等方向),每天都会有众多学习用户学习某一项或者多项学习内容。产生数据如下表: 产生数据如下表: 日期 内容 学习用户 2022…...

大数据毕业设计选题推荐-国产电影数据分析-Python数据可视化-Hive-Hadoop-Spark
✨作者主页:IT研究室✨ 个人简介:曾从事计算机专业培训教学,擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…...

Linux:无法为立即文档创建临时文件: 设备上没有空间
虚拟机磁盘空间不足解决记录 1、问题描述2、问题解决 1、问题描述 在命令行输入命令按Tab键时出现如下报错: 很明显,设备上没有空间,即磁盘空间不足。通过命令查看具体情况如下: df -h2、问题解决 首先想到的是虚拟机扩容。关机虚…...

【SQL】掌握SQL查询技巧:数据筛选与限制
目录 1. DISTINCT:避免重复记录1.1 示意图1.2 使用场景 2. LIMIT:控制查询结果的数量2.1 示意图2.2 使用场景 3. OFFSET:跳过前几行3.1 示意图3.2 使用场景 4. WHERE子句:精细控制数据过滤4.1 示意图4.2 运算符详细说明4.3 基本条…...

大学生社团活动系统小程序的设计
管理员账户功能包括:系统首页,个人中心,学生管理,社长管理,社团分类管理,社团信息管理,社团加入管理,社团活动管理,轮播图信息 微信端账号功能包括:系统首页…...

codetop标签双指针题目大全解析(三),双指针刷穿地心!!!!!
复习比学习更重要,更需要投入时间,更需要花费精力 1.字符串的排列2.找出字符串中第一个匹配的下标3.最大连续1的个数II4.数组中的山脉5.移除元素6.两个数组的交集II7.有序数组的平方8.删除有序数组中的重复项II9.寻找重复数10.水果成篮 1.字符串的排列 …...