解密 Java Lambda 表达式中的 “effectively final“ 陷阱

文章目录

      • 1. 引言 (Introduction)
        • 1.1. 核心问题
        • 1.2. 博客目标
        • 1.3. 目标读者
        • 1.4. 阅读收获
      • 2. 重现错误 (Reproducing the Error)
        • 2.1. 代码示例 (LambdaErrorExampleCorrected.java)
        • 2.2. 逐步演示
          • 2.2.1. 没有错误的代码版本 (list 满足 effectively final)
          • 2.2.2. 导致错误的代码版本 (在 lambda 表达式之后 尝试重新赋值 names 变量的引用)
        • 2.3. 动手实践
      • 3. 理解 Lambda 表达式和变量捕获 (Understanding Lambda Expressions and Variable Capture)
        • 3.1. 什么是 Lambda 表达式?
        • 3.2. 什么是变量捕获?
        • 3.3. Lambda 表达式如何进行变量捕获?
          • 基本类型变量 (Primitive Type Variables)
          • 引用类型变量 (Reference Type Variables)
      • 4. `final` 和 Effectively Final (`final` vs. Effectively Final)
        • 4.1. `final` 关键字
        • 4.2. Effectively Final (Java 8 引入)
        • 4.3. 对比:`final` vs. Effectively Final
      • 5. 深入理解:为什么需要 Effectively Final? (Why Effectively Final?)
        • 5.1. 数据一致性问题
        • 5.2. 线程安全问题
      • 6. 总结 (Conclusion)

1. 引言 (Introduction)

您是否在编写 Java Lambda 表达式时遇到过类似这样的困惑:明明代码看起来只是简单地使用了一个变量,却被编译器报错 "Variable used in lambda expression should be final or effectively final"? 就像下面这段代码,我们希望在一个 Lambda 表达式中使用一个列表,然后在之后修改这个列表的 引用,却遭遇了编译器的阻拦:

import java.util.ArrayList;
import java.util.List;public class LambdaErrorExampleCorrected {public static void main(String[] args) {List<String> names = new ArrayList<>();names.add("Alice");names.add("Bob");names.forEach(name -> System.out.println("Hello, " + name));// **🔴 编译错误! 尝试重新赋值 names 变量的引用**names = new ArrayList<>();// 或者// names = null;}
}

这段代码的初衷是先使用一个名字列表,通过 Lambda 表达式打印问候语,然后再将 names 变量指向一个新的列表(或者 null)。 然而,Java 编译器却明确报错,阻止了我们重新赋值 names 变量的企图。

1.1. 核心问题

为什么 Java 会有如此看似苛刻的限制? 为什么 Lambda 表达式对变量的使用有 finaleffectively final 的要求? 我们经常听到的 finaleffectively final, 它们的真正含义和区别又是什么? 这背后蕴藏着 Java Lambda 表达式设计的核心原则:对数据一致性和行为可预测性的极致追求

1.2. 博客目标

在本篇博客中,我们将拨开迷雾,深入解析 Java Lambda 表达式中 “effectively final” 概念的本质。 我们将从重现这个常见的编译错误出发,深入剖析 Lambda 表达式的变量捕获机制,精确理解 finaleffectively final 的真正含义,最终掌握避开 “effectively final” 陷阱,编写健壮高效 Lambda 表达式的最佳实践方法。

1.3. 目标读者

本文的目标读者: 对 Java Lambda 表达式有基本了解,但对 “effectively final” 概念及其背后的原理感到困惑的 Java 开发者。

1.4. 阅读收获

阅读本文后,您将能够:

  • 准确理解 "Variable used in lambda expression should be final or effectively final" 错误的根本原因。
  • 彻底掌握 Lambda 表达式的变量捕获机制,特别是对于引用类型变量的捕获方式。
  • 精准区分 finaleffectively final 的概念,理解它们在 Lambda 表达式中的作用。
  • 识别并避免 “effectively final” 陷阱,编写更加可靠和易于维护的 Lambda 表达式代码。

让我们一起踏上解密 “effectively final” 之旅,彻底扫清 Java Lambda 表达式使用中的障碍!

2. 重现错误 (Reproducing the Error)

为了更深刻地理解 “effectively final” 错误,实践是最好的老师。 接下来,我们将提供一个完整的、可编译的代码示例,并逐步演示错误是如何被触发的。

2.1. 代码示例 (LambdaErrorExampleCorrected.java)
import java.util.ArrayList;
import java.util.List;public class LambdaErrorExampleCorrected {public static void main(String[] args) {List<String> names = new ArrayList<>();names.add("Alice");names.add("Bob");// **✅ 没有错误的代码版本 (list 满足 effectively final)**System.out.println("--- 没有错误的版本 ---");names.forEach(name -> System.out.println("Hello, " + name));// **❌ 导致错误的代码版本 (在 lambda 表达式之后 尝试重新赋值 names 变量的引用)**System.out.println("\n--- 导致错误的版本 ---");List<String> errorNames = new ArrayList<>();errorNames.add("Alice");errorNames.add("Bob");errorNames.forEach(name -> System.out.println("Hello, " + name));// **🔴 编译错误!  尝试重新赋值 errorNames 变量**// errorNames = new ArrayList<>();errorNames = null;}
}
2.2. 逐步演示
2.2.1. 没有错误的代码版本 (list 满足 effectively final)

首先,运行代码中 “没有错误的版本” 部分。 这段代码创建了一个 names 列表,并使用 forEach 方法和一个 Lambda 表达式来遍历打印每个名字的问候语。 关键在于,在这个版本中,我们在 Lambda 表达式之后,没有对 names 变量进行任何重新赋值引用的操作。 这段代码能够成功编译并运行,控制台输出如下:

--- 没有错误的版本 ---
Hello, Alice
Hello, Bob
2.2.2. 导致错误的代码版本 (在 lambda 表达式之后 尝试重新赋值 names 变量的引用)

现在,我们尝试运行代码中 “导致错误的版本” 部分。 这段代码与之前的版本极其相似,核心的区别在于,我们在 errorNames.forEach(...) 之后,尝试对 errorNames 变量进行重新赋值引用的操作, 例如 errorNames = new ArrayList<>(); 或者 errorNames = null;。 当我们尝试编译这段代码时,编译器会毫不犹豫地报错:

Error:(25, 24) java: Local variable errorNames defined in an enclosing scope must be final or effectively final

清晰的错误信息,精准定位问题:

编译器清楚地指出错误发生在尝试重新赋值 errorNames 变量的代码行,并给出明确的错误提示: Local variable errorNames defined in an enclosing scope must be final or effectively final (定义在封闭作用域中的局部变量 errorNames 必须是 final 或 effectively final)。 这有力地证明了,“effectively final” 限制的核心在于,Lambda 表达式引用的外部局部变量,其 引用 在 Lambda 表达式使用后,不能被重新赋值

2.3. 动手实践

动手实践,加深理解:

为了更深入地理解 “effectively final” 错误,我强烈建议您亲自操作。 将上述 LambdaErrorExampleCorrected.java 代码复制到您的 Java 开发环境 (IDE) 中,分别编译并运行 “没有错误的版本” 和 “导致错误的版本”。 观察编译结果和运行输出,尤其是编译报错信息。 亲身体验错误,能够帮助您从根本上理解 “effectively final” 限制的本质

3. 理解 Lambda 表达式和变量捕获 (Understanding Lambda Expressions and Variable Capture)

要彻底理解编译器报错的缘由,我们必须深入理解 Lambda 表达式的工作机制,尤其是 变量捕获 (Variable Capture) 这个至关重要的概念。

3.1. 什么是 Lambda 表达式?

简而言之,Lambda 表达式是 Java 8 引入的一种轻量级、简洁的方式来表示匿名函数。 您可以将其视为一段可传递的代码块,它可以作为参数传递给方法,也可以赋值给变量。 Lambda 表达式的强大之处在于其简洁性灵活性,它使得我们可以以更加函数式编程的风格来编写 Java 代码。

在本篇博客中,我们聚焦于 Lambda 表达式与 变量捕获 紧密相关的概念。 创建 Lambda 表达式不仅仅是定义一段独立的代码,它还可能需要访问和使用在其定义的作用域中声明的变量。 这就引出了变量捕获。

3.2. 什么是变量捕获?

变量捕获 是指 Lambda 表达式在其函数体内部访问和使用其定义所在作用域中声明的变量。 回顾之前的代码示例:

List<String> names = new ArrayList<>();
names.forEach(name -> System.out.println("Hello, " + name));

Lambda 表达式 name -> System.out.println("Hello, " + name) 访问了外部作用域中定义的 names 列表。 这个过程即为变量捕获。 Lambda 表达式 “捕获”names 变量,以便在 Lambda 表达式内部能够使用它。

3.3. Lambda 表达式如何进行变量捕获?

Java Lambda 表达式在处理变量捕获时,采取了以下关键策略:

基本类型变量 (Primitive Type Variables)

对于基本类型变量,Lambda 表达式捕获的是该变量 值的副本 (copy of value)这意味着,即使在 Lambda 表达式外部修改了基本类型变量的值,Lambda 表达式内部使用的仍然是最初捕获的副本值,外部的修改对 Lambda 表达式内部没有任何影响,反之亦然。 两者之间是完全隔离的。

引用类型变量 (Reference Type Variables)

对于引用类型变量,Lambda 表达式捕获的是该变量 引用的副本 (copy of reference)而非对象本身的副本这意味着,Lambda 表达式和外部作用域中的代码,实际上是通过 两个不同的引用副本 指向 堆内存中的同一个对象。因此,如果通过任何一个引用修改了对象 内部的状态 (例如,向 List 中添加元素,修改对象的字段值),另一个引用仍然能够访问到修改后的对象状态。 但是,至关重要的是,如果尝试修改 引用本身 (例如,将引用指向一个新的对象,或者赋值为 null), Lambda 表达式内部捕获的引用副本仍然指向 原来的对象,与外部作用域的新引用就此分离,互不影响。这正是 “effectively final” 限制起作用的关键所在。

4. final 和 Effectively Final (final vs. Effectively Final)

为了彻底弄清楚 “effectively final” 的概念,我们需要先理解 final 关键字,以及它与 effectively final 之间的联系和区别。

4.1. final 关键字

明确定义: final 关键字 在 Java 中是一个修饰符,可以用来修饰类、方法和变量。 当 final 用于修饰变量时,它表示被修饰的变量只能被 赋值一次

作用: final 关键字的主要作用是确保变量的值(或引用)在初始化后不会被改变。 一旦 final 变量被赋值,就不能再对它进行重新赋值。 这提供了一种不可变性的保证。

代码示例:

public class FinalExample {public static void main(String[] args) {final int number = 10; // 声明 final 变量并赋值System.out.println("Number: " + number);// **🔴 编译错误! 尝试重新赋值 final 变量**// number = 20;}
}

在上面的例子中,number 被声明为 final int 类型,并被赋值为 10。 任何尝试在之后重新赋值 number 的操作,都会导致编译错误。

4.2. Effectively Final (Java 8 引入)

定义: Effectively Final 是 Java 8 引入的一个新概念,它用来描述一种变量的状态。 即使一个局部变量没有被显式地声明为 final,但如果在初始化之后,它的值(或引用)事实上没有被修改,那么这个变量就是 effectively final 的

条件: 要成为 effectively final 的变量,需要满足以下条件:

  1. 只被赋值一次:变量必须在声明时或之后只被赋值一次。
  2. 在 Lambda 表达式中使用之前没有被修改:变量在被 Lambda 表达式捕获之前,不能被修改。

代码示例:

import java.util.ArrayList;
import java.util.List;public class EffectivelyFinalExample {public static void main(String[] args) {List<String> names = new ArrayList<>(); // names 变量没有声明为 finalnames.add("Alice"); // 第一次修改 names 指向的 ArrayList 对象的内容names.add("Bob");  // 第二次修改 names 指向的 ArrayList 对象的内容// **✅ names 变量是 effectively final 的,因为它的引用没有被重新赋值**names.forEach(name -> System.out.println("Hello, " + name));// **❌ 如果在这里重新赋值 names 变量的引用,names 就不再是 effectively final 的了 (如果 Lambda 在重新赋值后使用)**// names = new ArrayList<>();}
}

在上面的例子中,names 变量没有被声明为 final,但是它仍然是 effectively final 的,因为在 Lambda 表达式 names.forEach(...) 使用之前,names 变量的引用本身 没有被重新赋值。 我们只是修改了 names 指向的 ArrayList 对象的内容,但这并不影响 names 变量的 effectively final 状态。

4.3. 对比:final vs. Effectively Final

相同点: final 和 effectively final 的变量都保证了变量的值(或引用)在初始化后不会被改变。 对于 Lambda 表达式而言,它们都满足了 Lambda 表达式对捕获变量不可变性的要求。

不同点:

  1. 声明方式不同: final显式声明,需要使用 final 关键字来修饰变量。 effectively final 是 隐式推断,不需要显式关键字,由编译器根据变量的使用情况自动推断。

  2. 约束力不同: final 具有更强的约束力。 一旦变量被声明为 final,编译器会严格检查,确保在任何地方都不会对 final 变量进行重新赋值。 而 effectively final 的约束力相对较弱,它只在 Lambda 表达式的变量捕获上下文中起作用。 在 Lambda 表达式之外,即使变量是 effectively final 的,我们仍然可以修改它的值(只要不违反 effectively final 的条件)。

举例说明:

public class FinalVsEffectivelyFinal {public static void main(String[] args) {// **final 变量**final int finalNumber = 10;// finalNumber = 20; // 🔴 编译错误! final 变量不能重新赋值// **effectively final 变量**int effectivelyFinalNumber = 10;effectivelyFinalNumber = 20; // ✅  在 Lambda 表达式之外,effectively final 变量可以被修改 (只要 Lambda 表达式没有捕获它,或者在捕获之后没有修改)// 使用 Lambda 表达式捕获 effectively final 变量Runnable lambdaTask = () -> System.out.println("Effectively Final Number in Lambda: " + effectivelyFinalNumber);// **❌ 如果在 Lambda 表达式捕获 effectivelyFinalNumber 之后,再修改 effectivelyFinalNumber 变量的引用,就会报错 (如果在 Lambda 表达式中使用)**// effectivelyFinalNumber = 30; // 如果 lambdaTask 被执行,这里会导致数据不一致,因此 Java 编译器禁止这样做 (如果在 lambdaTask 中使用了 effectivelyFinalNumber)lambdaTask.run();}
}

final 和 effectively final 都服务于同一个目的: 确保 Lambda 表达式捕获的变量在 Lambda 表达式执行期间保持不变,从而保证数据一致性和行为的可预测性final 是显式的、强制性的,而 effectively final 是隐式的、更灵活的,它允许开发者在不显式使用 final 关键字的情况下,也能享受到 Lambda 表达式带来的便利。

5. 深入理解:为什么需要 Effectively Final? (Why Effectively Final?)

现在我们理解了什么是 effectively final,但更重要的是要理解 为什么 Java 要强制要求 Lambda 表达式捕获的变量必须是 final 或 effectively final 的? 这背后的原因主要与 数据一致性线程安全 这两个关键问题有关。

5.1. 数据一致性问题

详细解释: 如果允许 Lambda 表达式捕获的变量在其创建后被修改,就会导致 Lambda 表达式内部和外部的数据不一致,从而产生难以预测的行为和逻辑混乱

Lambda 表达式本质上是一个闭包 (Closure)。 闭包的一个重要特性是它可以“记住”创建时的环境,并访问和使用环境中的变量。 如果允许在 Lambda 表达式创建后修改被捕获的变量,那么 Lambda 表达式在不同时刻访问到的变量值可能会发生变化,这会破坏 Lambda 表达式的 “快照”特性,使其行为变得难以理解和调试。

代码示例:Counter 示例

我们来看一个经典的计数器示例,来生动地展示数据不一致可能导致的问题:

public class CounterExample {public static void main(String[] args) {int counter = 0;Runnable incrementCounter = () -> {// 假设允许修改外部变量counter++; // 🔴 如果允许,这里会修改外部的 counter 变量};for (int i = 0; i < 5; i++) {incrementCounter.run(); // 多次执行 Lambda 表达式System.out.println("Counter in loop: " + counter); // 打印循环中的 counter 值}System.out.println("Final Counter: " + counter); // 打印最终的 counter 值}
}

假设 Java 允许 Lambda 表达式修改外部变量 counter (实际上 Java 不允许,这里只是为了演示)。 在这种假设情况下,由于 Lambda 表达式每次执行都会修改外部的 counter 变量,循环内和循环外的 counter 值都会不断变化,程序的行为将变得非常难以预测。 Lambda 表达式的行为不再像一个“快照”,而是会随着外部变量的改变而动态变化,这会给程序的正确性和可维护性带来巨大的挑战。

代码示例:修改 List 示例

再回顾我们之前的 List 示例,虽然修改 List 的 内容 不会导致错误,但如果我们允许修改 List 的 引用,同样会产生数据不一致的问题:

import java.util.ArrayList;
import java.util.List;public class ListDataInconsistencyExample {public static void main(String[] args) {List<String> names = new ArrayList<>();names.add("Alice");names.add("Bob");Runnable printNames = () -> {System.out.println("Names in Lambda: " + names); // Lambda 表达式捕获 names 列表};printNames.run(); // 第一次执行 Lambda 表达式,打印 names 列表names = new ArrayList<>(); // 🔴  如果允许,在这里重新赋值 names 变量的引用names.add("Charlie");names.add("David");printNames.run(); // 第二次执行 Lambda 表达式,打印 names 列表}
}

同样假设 Java 允许 Lambda 表达式捕获的 names 变量在其创建后被重新赋值引用 (实际上 Java 不允许)。 在这种假设情况下,第一次执行 printNames.run() 时,Lambda 表达式会打印最初的 names 列表 (“Alice”, “Bob”)。 但是,由于我们在之后重新赋值了 names 变量的引用,第二次执行 printNames.run() 时,Lambda 表达式打印的 names 列表就会变成新的列表 (“Charlie”, “David”)。 同一个 Lambda 表达式,在不同的时刻执行,由于外部变量的改变,输出了不同的结果,这显然破坏了数据一致性,让程序的行为变得难以理解。

强调 “快照” 特性: 总结来说,Java 强制要求 Lambda 表达式捕获的变量必须是 final 或 effectively final, 核心目的就是为了维护 Lambda 表达式的 “快照” 特性, 保证数据一致性, 让 Lambda 表达式的行为更加可预测和易于理解。 Lambda 表达式应该像一个“函数”,给定输入,输出始终是确定的,而不会受到外部环境变化的影响。

5.2. 线程安全问题

简要提及: 除了数据一致性问题,线程安全 也是 Java 强制要求 effectively final 的一个重要考量因素,尤其是在多线程环境下。

竞态条件和不可预测的结果: 在多线程环境中,如果多个线程同时访问和修改同一个非 effectively final 的变量,就可能导致竞态条件 (Race Condition) 和不可预测的结果。 Lambda 表达式可能会在不同的线程中执行,如果 Lambda 表达式捕获的变量不是 effectively final 的,并且可以被外部线程修改,那么就可能出现线程安全问题。

示例说明:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ThreadSafetyExample {public static void main(String[] args) {int counter = 0; // 非 effectively final 变量ExecutorService executor = Executors.newFixedThreadPool(5);for (int i = 0; i < 10; i++) {executor.submit(() -> {// 假设允许修改外部变量for (int j = 0; j < 1000; j++) {counter++; // 🔴  多个线程同时修改非 effectively final 变量,可能导致线程安全问题 (如果允许)}});}executor.shutdown();// ... 等待所有线程执行完成 ...System.out.println("Final Counter (Expected 10000): " + counter); // 最终的 counter 值可能小于 10000,结果不可预测}
}

同样假设 Java 允许 Lambda 表达式修改外部变量 counter (实际上 Java 不允许)。 在这种假设情况下,多个线程会同时执行 Lambda 表达式,并尝试递增同一个非 effectively final 的 counter 变量。 由于 缺乏同步机制,不同线程对 counter 变量的修改操作可能会互相干扰,导致最终的 counter 值小于预期的 10000,甚至每次运行结果都可能不同,这显然是线程不安全的。

线程安全保证: 通过强制要求 Lambda 表达式捕获的变量必须是 final 或 effectively final,Java 可以有效地避免 Lambda 表达式在多线程环境下访问和修改共享的可变状态,从而提高 Lambda 表达式的线程安全性。 如果 Lambda 表达式捕获的变量是不可变的,那么多个线程同时访问这个变量是安全的,不会出现竞态条件。

Effectively Final 限制不仅是为了保证数据一致性,也是为了提高 Lambda 表达式在多线程环境下的安全性。它避免了 Lambda 表达式与外部作用域之间不必要的变量共享和修改,使得 Lambda 表达式更加可靠和易于在并发环境中使用。

6. 总结 (Conclusion)

本文深入探讨了 Java Lambda 表达式中 “Variable used in lambda expression should be final or effectively final” 错误的本质,全面解析了 effectively final 概念。 通过实例、变量捕获分析、final 与 effectively final 对比,以及对数据一致性和线程安全的理解,我们揭示了 effectively final 概念的重要性。

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

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

相关文章

ZooKeeper单节点详细部署流程

ZooKeeper单节点详细部署流程 文章目录 ZooKeeper单节点详细部署流程 一.下载稳定版本**ZooKeeper**二进制安装包二.安装并启动**ZooKeeper**1.安装**ZooKeeper**2.配置并启动**ZooKeeper** ZooKeeper 版本与 JDK 兼容性3.检查启动状态4.配置环境变量 三.可视化工具管理**Zooke…

【高级篇 / IPv6】(7.2) ❀ 04. 在60E上配置ADSL拨号宽带上网(IPv4) ❀ FortiGate 防火墙

【简介】除了单位用户以外&#xff0c;大部分个人用户目前使用的仍然是30E、50E、60E系列防火墙&#xff0c;固件无法达到目前最高版本7.6&#xff0c;这里以最常用的60E为例&#xff0c;演示固件版本7.2下实现ADSL拨号宽带的IPv6上网。由于内容比较多&#xff0c;文章分上、下…

51单片机07 串口通信

串口是一种应用十分广泛的通讯接口&#xff0c;串口成本低、容易使用、通信线路简单&#xff0c;可实现两个设备的互相通信。单片机的串口可以使单片机与单片机、单片机与电脑、单片机与各式各样的模块互相通信。51单片机内部自带UART&#xff08;Universal Asynchronous Recei…

【Kubernetes Pod间通信-第2篇】使用BGP实现Pod到Pod的通信

Kubernetes中Pod间的通信 本系列文章共3篇: 【Kubernetes Pod间通信-第1篇】在单个子网中使用underlay网络实现Pod到Pod的通信【Kubernetes Pod间通信-第2篇】使用BGP实现Pod到Pod的通信(本文介绍)【Kubernetes Pod间通信-第3篇】Kubernetes中Pod与ClusterIP服务之间的通信…

DeepSeek私有化本地部署图文(Win+Mac)

目录 一、DeepSeek私有化本地部署【Windows】 1、安装Ollama 2、配置环境变量 3、下载模型 4、使用示例 a、直接访问 b、chatbox网页访问 二、DeepSeek本地部署【Mac】 1、安装Ollama 2、配置环境变量 3、下载模型 4、使用示例 5、删除已下载的模型 三、DeepSeek…

点(线)集最小包围外轮廓效果赏析

“ 图像、点集、线集合最小外轮廓计算应用较为广泛&#xff0c;如抠图、神奇选择、LOD、碰撞检查等领域&#xff0c;提高场景效率” 1.前言 作者基于递归迭代求解实现点集的最小外轮廓计算&#xff0c;在CGLib库中实现&#xff0c;已集成于CGViewer&#xff0c;可联系作者试用&…

博客园-awescnb插件-geek皮肤优化-Markdown样式支持

&#x1f496;简介 博客园-awescnb插件-geek皮肤下&#xff0c;Markdown语法中对部分样式未正常支持&#xff0c;可以通过自定义CSS进行完善。 ✨定义列表 定义自定义CSS 博客园->管理->设置->页面定制 CSS 代码 添加代码 /* 定义列表 */ dl dt{font-size: 14px;f…

接口测试与常用接口测试工具

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 首先&#xff0c;什么是接口呢&#xff1f; 接口一般来说有两种&#xff0c;一种是程序内部的接口&#xff0c;一种是系统对外的接口。 系统对外的接口&#xff…

三维粒子滤波(Particle Filter)MATLAB例程,估计三维空间中匀速运动目标的位置(x, y, z),提供下载链接

三维粒子滤波(Particle Filter)MATLAB例程,估计三维空间中匀速运动目标的位置(x, y, z) 文章目录 介绍功能运行结果代码介绍 本 MATLAB 代码实现了三维粒子滤波( P a r t i c l e F i l t e

语言月赛 202311【基因】题解(AC)

》》》点我查看「视频」详解》》》 [语言月赛 202311] 基因 题目描述 有一个长度为 n n n 的字符串 S S S。其只包含有大写字母。 小 A 将 S S S 进行翻转后&#xff0c;得到另一个字符串 S ′ S S′。两个字符串 S S S 与 S ′ S S′ 对应配对。例如说&#xff0c;对…

PostgreSQL函数自动Commit/Rollback所带来的问题

一、综述 今天在PostgreSQL遇到一个奇怪的现象&#xff0c;简而言之&#xff0c;是想用函数&#xff08;存储过程&#xff09;实现插入记录&#xff0c;整个过程没报错但事后却没找到记录&#xff01;忙活半天&#xff0c;才发现原因是PostgreSQL函数&#xff08;存储过程&…

JVM为什么要指针压缩?为什么能指针压缩?原理是什么?

面试官&#xff1a;为什么要指针压缩&#xff0c;为什么能指针压缩&#xff1f;原理是什么&#xff1f; 指针压缩&#xff08;Pointer Compression&#xff09;是 Java 虚拟机&#xff08;JVM&#xff09;中用于优化内存使用的一种技术&#xff0c;特别是在 64 位环境下。 为…

Mac上搭建k8s环境——Minikube

1、在mac上安装Minikube可执行程序 brew cask install minikub 安装后使用minikube version命令查看版本 2、安装docker环境 brew install --cask --appdir/Applications docker #安装docker open -a Docker #启动docker 3、安装kubectl curl -LO https://storage.g…

Kafka 可靠性探究—副本刨析

Kafka 的多副本机制提升了数据容灾能力。 副本通常分为数据副本与服务副本。数据副本是指在不同的节点上持久化同一份数据&#xff1b;服务副本指多个节点提供同样的服务&#xff0c;每个节点都有能力接收来自外部的请求并进行相应的处理。 1 副本刨析 1.1 相关概念 AR&…

逻辑起源 - 比较DS与豆包对“逻辑”源头的提炼差异

政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 希望政安晨的博客能够对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff01; 问题&#xff1a;“逻辑”的源头是什么&#xff1f; 豆包回答&#xff1a; “逻辑” 一词源…

Qt:Qt环境配置安装

搭建Qt开发环境 Qt的开发工具概述 Qt 支持多种开发工具&#xff0c;其中比较常用的开发工具有&#xff1a;Qt Creator、Visual Studio、Eclipse。 Qt Creator Qt Creator是⼀个轻量级的跨平台集成开发环境&#xff08;IDE&#xff09;&#xff0c;专为使用Qt框架进行应用程…

文献阅读分享《新闻推荐中的审议式多样性:操作化与实验用户研究》

标题期刊年份Deliberative Diversity for News Recommendations: Operationalization and Experimental User StudyProceedings of the Seventeenth ACM Conference on Recommender Systems (RecSys 23)2023 &#x1f31f;论文背景 在信息爆炸的时代&#xff0c;新闻推荐系统…

AspectJ 中通知方法参数绑定

我们知道 AspectJ 中的通知方法可以携带参数&#xff0c;例如 Before 前置通知方法可以携带一个 JoinPoint 类型参数&#xff0c;那么还可以携带其它参数吗&#xff1f; 示例一 Before(value "execution(* *..UserServiceImpl.doSome(String))", argNames "…

bat脚本实现自动化漏洞挖掘

bat脚本 BAT脚本是一种批处理文件&#xff0c;可以在Windows操作系统中自动执行一系列命令。它们可以简化许多日常任务&#xff0c;如文件操作、系统配置等。 bat脚本执行命令 echo off#下面写要执行的命令 httpx 自动存活探测 echo off httpx.exe -l url.txt -o 0.txt nuc…

Golang 并发机制-6:掌握优雅的错误处理艺术

并发编程可能是提高软件系统效率和响应能力的一种强有力的技术。它允许多个工作负载同时运行&#xff0c;充分利用现代多核cpu。然而&#xff0c;巨大的能力带来巨大的责任&#xff0c;良好的错误管理是并发编程的主要任务之一。 并发代码的复杂性 并发编程增加了顺序程序所不…