提交 f76a81cc 编写于 作者: W wizardforcel

2021-10-01 23:26:15

上级 cbb7daef
# 第 1 章学习 Java 9 底层性能改进
当您认为自己掌握了 lambdas 和 Java8 的所有与性能相关的特性时,Java9 就出现了。以下是将其转化为 Java 9 的几个功能,您可以使用这些功能来帮助提高应用程序的性能。这些变化超出了字节级别的变化,比如字符串存储或垃圾收集的变化,而您几乎无法控制这些变化。另外,忽略实现更改,例如加快对象锁定的更改,因为您不必做任何不同的事情,您会自动获得这些改进。相反,新的库功能和全新的命令行工具将帮助您快速创建应用程序。
当您认为自己掌握了 Lambdas 和 Java8 的所有与性能相关的特性时,Java9 就出现了。以下是将其转化为 Java 9 的几个功能,您可以使用这些功能来帮助提高应用程序的性能。这些变化超出了字节级别的变化,比如字符串存储或垃圾收集的变化,而您几乎无法控制这些变化。另外,忽略实现更改,例如加快对象锁定的更改,因为您不必做任何不同的事情,您会自动获得这些改进。相反,新的库功能和全新的命令行工具将帮助您快速创建应用程序。
在本课中,我们将介绍以下主题:
......@@ -380,9 +380,9 @@ Java9 改进了争用锁定。您可能想知道什么是竞争锁定。让我
## 分层归属
提供编译器改进的首要变化与**分层归属****TA**)相关。此更改与 lambda 表达式更相关。目前,poly 表达式的类型检查是通过针对不同目标对同一棵树进行多次类型检查来完成的。这个过程被称为**推测归属****SA**),它允许使用不同的重载解析目标来检查 lambda 表达式。
提供编译器改进的首要变化与**分层归属****TA**)相关。此更改与 Lambda 表达式更相关。目前,poly 表达式的类型检查是通过针对不同目标对同一棵树进行多次类型检查来完成的。这个过程被称为**推测归属****SA**),它允许使用不同的重载解析目标来检查 Lambda 表达式。
这种类型检查方法虽然是一种健壮的技术,但对性能有很大的负面影响。例如,使用这种方法,`n`个重载候选者针对相同的参数表达式检查一次,最多为`n*3`个重载阶段,严格、松散和变参数。除此之外,还有一个最终检查阶段。当 lambda 返回一个 poly 方法调用时,会导致属性调用的组合爆炸,这会导致巨大的性能问题。因此,我们当然需要一种不同的多边形表达式类型检查方法。
这种类型检查方法虽然是一种健壮的技术,但对性能有很大的负面影响。例如,使用这种方法,`n`个重载候选者针对相同的参数表达式检查一次,最多为`n*3`个重载阶段,严格、松散和变参数。除此之外,还有一个最终检查阶段。当 Lambda 返回一个 poly 方法调用时,会导致属性调用的组合爆炸,这会导致巨大的性能问题。因此,我们当然需要一种不同的多边形表达式类型检查方法。
其核心思想是确保方法调用为每个多边形参数表达式创建具有每个细节的自底向上结构类型,在执行重载解析之前,执行重载解析适用性检查将需要这些类型。
......@@ -450,7 +450,7 @@ Java9 团队研究了一些优化,包括安全策略的实施和权限的评
3. Java 虚拟机
4. 类数据共享
5. 以下哪项允许使用不同的重载解析目标来检查 lambda 表达式?
5. 以下哪项允许使用不同的重载解析目标来检查 Lambda 表达式?
1. 分层归属
2. HotspotJVM
......
......@@ -215,7 +215,7 @@ System.out.println("Worker " + myRunnable.getId()
![Prerequisites](img/3_07.jpg)
前面的所有示例都将生成的结果存储在`class`属性中。但情况并非总是如此。通常,工作线程要么将其值传递给另一个线程,要么将其存储在数据库或外部的其他地方。在这种情况下,可以利用`Runnable`接口作为功能接口,将必要的处理函数作为 lambda 表达式传递到新线程中:
前面的所有示例都将生成的结果存储在`class`属性中。但情况并非总是如此。通常,工作线程要么将其值传递给另一个线程,要么将其存储在数据库或外部的其他地方。在这种情况下,可以利用`Runnable`接口作为功能接口,将必要的处理函数作为 Lambda 表达式传递到新线程中:
```java
System.out.print("demo_lambda_01(): ");
......@@ -237,7 +237,7 @@ try {
![Prerequisites](img/03_08.jpg)
根据首选样式,可以重新排列代码并在变量中隔离 lambda 表达式,如下所示:
根据首选样式,可以重新排列代码并在变量中隔离 Lambda 表达式,如下所示:
```java
Runnable r = () -> IntStream.rangeClosed(1, 99999)
......@@ -247,7 +247,7 @@ Runnable r = () -> IntStream.rangeClosed(1, 99999)
Thread t1 = new Thread(r);
```
或者,可以将 lambda 表达式放在单独的方法中:
或者,可以将 Lambda 表达式放在单独的方法中:
```java
void calculateAverage(String id) {
......@@ -1153,7 +1153,7 @@ Oracle 还建议使用 G1 及其默认设置,然后使用`-XX:MaxGCPauseMillis
在经历了几次错误的开始和几次灾难性的破坏之后,接着是英勇的恢复,金字塔的建造过程形成了,古代的建筑者能够完成一些项目。最终的形状有时看起来与预想的不完全一样(最初的金字塔最终弯曲了),但是,尽管如此,金字塔仍然装饰着今天的沙漠。这种经历代代相传,设计和工艺都经过了很好的调整,足以在 4000 多年后生产出一些华丽、宜人的东西。
软件实践也会随着时间的推移而改变,尽管自图灵先生编写第一个现代程序以来,我们只有大约 70 年的时间。起初,当世界上只有少数几个程序员时,计算机程序曾经是一个连续的指令列表。函数式编程(像一等公民一样推动函数)也很早就被引入,但尚未成为主流。相反,`goto`指令允许您在意大利面碗中滚动代码。随后是结构化编程,然后是面向对象编程,函数式编程正在发展,甚至在某些领域蓬勃发展。对按键生成的事件的异步处理成为许多程序员的例行程序。JavaScript 试图使用所有的最佳实践,并获得了很多功能,即使在调试(乐趣)阶段以程序员的挫折为代价。最后,由于线程池和 lambda 表达式是 JDKSE 的一部分,将反应流 API 添加到 jdk9 使 Java 成为允许使用异步数据流进行反应式编程的家族的一部分。
软件实践也会随着时间的推移而改变,尽管自图灵先生编写第一个现代程序以来,我们只有大约 70 年的时间。起初,当世界上只有少数几个程序员时,计算机程序曾经是一个连续的指令列表。函数式编程(像一等公民一样推动函数)也很早就被引入,但尚未成为主流。相反,`goto`指令允许您在意大利面碗中滚动代码。随后是结构化编程,然后是面向对象编程,函数式编程正在发展,甚至在某些领域蓬勃发展。对按键生成的事件的异步处理成为许多程序员的例行程序。JavaScript 试图使用所有的最佳实践,并获得了很多功能,即使在调试(乐趣)阶段以程序员的挫折为代价。最后,由于线程池和 Lambda 表达式是 JDKSE 的一部分,将反应流 API 添加到 jdk9 使 Java 成为允许使用异步数据流进行反应式编程的家族的一部分。
公平地说,即使没有这个新 API,我们也能够异步处理数据——通过旋转工作线程、使用线程池和可调用项(如前几节所述),或者通过传递回调(即使有时在调用谁的人的迷宫中丢失一次)。但是,在编写了几次这样的代码之后,我们注意到,大多数这样的代码只是一个管道,可以包装在一个框架内,从而大大简化异步处理。[这就是反应流倡议的方式](http://www.reactive-streams.org),工作范围定义如下:
......@@ -1252,7 +1252,7 @@ void demo_Observable1(){
}
```
RxJava 基于`Observable`对象(扮演`Publisher`角色)和`Observer`订阅`Observable`并等待数据发出。发射数据的每一项(从`Observable`到`Observer`的过程中)都可以通过以流畅的方式链接的操作进行处理(参见前面的代码)。每个操作都采用一个 lambda 表达式。从名称上看,操作功能是显而易见的。
RxJava 基于`Observable`对象(扮演`Publisher`角色)和`Observer`订阅`Observable`并等待数据发出。发射数据的每一项(从`Observable`到`Observer`的过程中)都可以通过以流畅的方式链接的操作进行处理(参见前面的代码)。每个操作都采用一个 Lambda 表达式。从名称上看,操作功能是显而易见的。
尽管能够表现出与流相似的行为,`Observable`具有显著不同的能力。例如,流一旦关闭就不能重新打开,而`Observable`可以重用。以下是一个例子:
......@@ -1359,7 +1359,7 @@ observable.subscribeOn(Schedulers.io())
3. `newSingleThreadScheduledExecutor()`
4. `newFixedThreadPool()`
3. 说明是真是假:可以利用`Runnable`接口作为函数接口,将必要的处理函数作为 lambda 表达式传递到新线程中。
3. 说明是真是假:可以利用`Runnable`接口作为函数接口,将必要的处理函数作为 Lambda 表达式传递到新线程中。
4. 调用`__________`方法后,池中不再添加工作线程。
1. `shutdownNow()`
......
......@@ -165,7 +165,7 @@ System.out.println("Members of the senate voted Yes on Issue"
![Basic Filtering](img/05_03.jpg)
我们可以利用 Java 函数式编程功能(使用 lambda 表达式)并创建`countAndPrint()`方法来重构前面的示例:
我们可以利用 Java 函数式编程功能(使用 Lambda 表达式)并创建`countAndPrint()`方法来重构前面的示例:
```java
long countAndPrint(List<Senator> senators,
......
......@@ -14,7 +14,7 @@
第 3 章“模块化编程”介绍了拼图作为 Java 生态系统的一个主要特性和巨大飞跃。本章将演示如何使用工具(如 JDEP 和 jlink)创建简单的模块化应用程序和相关工件(如模块化 JAR),以及如何模块化预 Jigsaw 应用程序。
第 4 章“开始函数式”介绍了一种称为函数式编程的编程范式。所涵盖的主题包括函数接口、lambda 表达式和 lambda 友好的 api。
第 4 章“开始函数式”介绍了一种称为函数式编程的编程范式。所涵盖的主题包括函数接口、Lambda 表达式和 Lambda 友好的 api。
第 5 章“流和管道”展示了如何利用流和链接集合上的多个操作来创建管道,使用工厂方法创建集合对象,创建和操作流,以及在
流上创建操作管道,包括并行计算。
......@@ -38,7 +38,7 @@
第 14 章“测试”解释了如何在 API 与其他组件集成之前对其进行单元测试,包括使用一些虚拟数据的桩依赖和模拟依赖。我们还将向您展示如何编写夹具来填充测试数据,以及如何通过集成不同的 api 并测试它们来测试您的应用程序行为。
第 15 章“使用 Java 10 和 Java 11 进行编码的新方法”,演示了如何使用局部变量类型推断,以及何时和如何使用局部变量语法进行 lambda 参数。
第 15 章“使用 Java 10 和 Java 11 进行编码的新方法”,演示了如何使用局部变量类型推断,以及何时和如何使用局部变量语法进行 Lambda 参数。
第 16 章“使用 JavaFX 的 GUI 编程”,说明如何使用 JavaFX 使用 FXML 标记和 CSS 创建 GUI。它将演示如何创建条形图、饼图、折线图和面积图。它还将展示如何在应用程序和媒体源中嵌入 HTML,以及如何向控件添加效果。此外,我们还将在 OpenJFX11 更新中了解最新发布的机器人 API。
......
......@@ -222,13 +222,13 @@ Epsilon 是一个所谓的无操作垃圾收集器,基本上什么也不做。
# JEP 321–HTTP 客户端(标准)
JDK 18.9 标准化了 JDK 9 中引入并在 JDK 10 中更新的孵化 HTTP API 客户端。基于 CompletableFuture,它支持非阻塞请求和响应。新的实现是异步的,提供了更好的可跟踪数据流。
JDK 18.9 标准化了 JDK 9 中引入并在 JDK 10 中更新的孵化 HTTP API 客户端。基于`CompletableFuture`,它支持非阻塞请求和响应。新的实现是异步的,提供了更好的可跟踪数据流。
第 10 章“网络”在几个食谱中更详细地解释了这一特性。
# JEP 323–Lambda 参数的局部变量语法
lambda 参数的局部变量语法与使用 Java11 中引入的保留`var`类型的局部变量声明的语法相同。详见第 15 章中的“对 lambda 参数使用局部变量语法”配方、“Java 10 和 Java 11 编码的新方式”。
Lambda 参数的局部变量语法与使用 Java11 中引入的保留`var`类型的局部变量声明的语法相同。详见第 15 章中的“对 Lambda 参数使用局部变量语法”配方、“Java 10 和 Java 11 编码的新方式”。
# JEP 333–ZGC
......@@ -343,8 +343,8 @@ JDK 18.9 中还引入了许多其他更改:
* `util.jar`中的 Pack200 和 UNCAP200 工具以及 Pack200 API 已弃用
* Nashorn JavaScript 引擎以及 JJS 工具都不推荐使用,目的是将来删除它们
* Java 类文件格式被扩展以支持新的常量池形式`CONSTANT_Dynamic`
* Aarch64 内部函数得到了改进,在 Aarch64 处理器 JEP 309 动态类文件常量上为`java.lang.Math`sin、cos 和 log 函数实现了新的内部函数
* Flight Recorder 提供了一个低开销的数据收集框架,用于对 Java 应用程序和 HotSpot JVM 进行故障排除
* Aarch64 内部函数得到了改进,在 Aarch64 处理器 JEP 309 动态类文件常量上为`java.lang.Math``sin``cos``log`函数实现了新的内部函数
* 飞行记录器提供了一个低开销的数据收集框架,用于对 Java 应用程序和 HotSpot JVM 进行故障排除
* Java 启动器现在可以运行作为单个 Java 源代码文件提供的程序,因此这些程序可以直接从源代码运行
* 通过 JVM 工具接口可以访问低开销堆评测,它提供了一种对 Java 堆分配进行采样的方法
* **传输层安全****TLS**)1.3 增加了安全性并提高了性能
......
......@@ -341,7 +341,7 @@ public static void main(String... arg) {
![](img/3b0f5b59-fcd0-4847-8141-cb56a9c81995.png)
如果接口只有一个抽象方法(称为函数接口),则可以使用另一个构造(称为 *lambda 表达式*,而不是匿名内部类)。它提供了一个较短的表示法。我们将在第 4 章“开始函数式”中讨论功能接口和 lambda 表达式。
如果接口只有一个抽象方法(称为函数接口),则可以使用另一个构造(称为 *Lambda 表达式*,而不是匿名内部类)。它提供了一个较短的表示法。我们将在第 4 章“开始函数式”中讨论功能接口和 Lambda 表达式。
# 还有更多。。。
......@@ -707,7 +707,7 @@ public class Vehicle {
此外,在现实生活中,基于机器学习和其他先进技术的建模变得如此复杂和专业化,以至于汽车加速建模通常由不同的团队完成,而不是由组装汽车模型的团队完成。
为了避免子类的激增和车辆构建者和 speed 模型开发人员之间的代码合并冲突,我们可以使用聚合创建更具扩展性的设计。
为了避免子类的激增和车辆构建者和速度模型开发人员之间的代码合并冲突,我们可以使用聚合创建更具扩展性的设计。
聚合是一种 OOD 原则,用于使用不属于继承层次结构的类的行为来实现必要的功能。该行为可以独立于聚合功能而存在。
......@@ -868,7 +868,7 @@ public class FactorySpeedModel {
为了简洁起见,我们将注释放在伪代码中,`...`符号代替了实际代码。
如您所见,factory 类可能隐藏许多不同的私有类,每个私有类都包含特定驾驶条件的专用模型。每个模型产生不同的结果。
如您所见,工厂类可能隐藏许多不同的私有类,每个私有类都包含特定驾驶条件的专用模型。每个模型产生不同的结果。
`FactoryVehicle`类的实现可能如下所示:
......@@ -1076,7 +1076,7 @@ public static void main(String... arg) {
如您所见,这次使用了`TruckImpl`类中的方法实现。它已经覆盖了`Truck`接口中的默认实现。
7. 增强`Truck`接口,在不改变`FactoryVehicle``Truck`接口实现的情况下,以千克为单位输入有效载荷。另外,我们不想添加 setter 方法。有了所有这些限制,我们唯一的办法就是将`convertKgToPounds(int kgs)`添加到`Truck`接口中,它必须是`static`,因为我们将在构建实现`Truck`接口的对象之前使用它:
7. 增强`Truck`接口,在不改变`FactoryVehicle``Truck`接口实现的情况下,以千克为单位输入有效载荷。另外,我们不想添加设置器方法。有了所有这些限制,我们唯一的办法就是将`convertKgToPounds(int kgs)`添加到`Truck`接口中,它必须是`static`,因为我们将在构建实现`Truck`接口的对象之前使用它:
```java
public interface Truck extends Vehicle {
......@@ -1275,7 +1275,7 @@ void checkResultOpt(Optional<Integer> lotteryPrize){
}
```
显然,`Optional`前面的用法无助于改进代码或使编码更容易。在 Lambda 表达式和流管道中使用`Optional`更有潜力,因为`Optional`对象提供了可以通过点运算符调用的方法,并且可以插入到 fluent 风格的处理代码中。
显然,`Optional`前面的用法无助于改进代码或使编码更容易。在 Lambda 表达式和流管道中使用`Optional`更有潜力,因为`Optional`对象提供了可以通过点运算符调用的方法,并且可以插入到流畅风格的处理代码中。
# 怎么做。。。
......@@ -1501,7 +1501,7 @@ useFlatMap(list);
* `requireNonNull()`:如果提供的对象为`null`,则五个方法抛出异常
* `hash()``hashCode():`计算单个对象或对象数组哈希值的两种方法
* `isNull()``nonNull()`:包装`obj == null``obj != null`表达式的两种方法
* `equals()``deepEquals()`:两种方法,用于比较两个可以为 null 或数组的对象
* `equals()``deepEquals()`:两种方法,用于比较两个可以为`null`或数组的对象
我们将在前面的序列中编写使用这些方法的代码。
......@@ -1559,7 +1559,7 @@ System.out.println(res); //prints: 0
//Objects.compare("a", null, Comparator.naturalOrder());
```
如果需要将对象与 null 进行比较,最好使用`org.apache.commons.lang3.ObjectUtils.compare(T o1, T o2)`
如果需要将对象与`null`进行比较,最好使用`org.apache.commons.lang3.ObjectUtils.compare(T o1, T o2)`
2.`obj`对象引用为`null`值时,`toString(Object obj)`方法很有用:
* `String toString(Object obj)`:非`null`时返回调用第一个参数`toString()`的结果,第一个参数值为`null`时返回调用`null`的结果
......
此差异已折叠。
# 流和管道
在 Java8 和 Java9 中,CollectionsAPI 通过利用 lambda 表达式引入流和内部迭代进行了重大的改头换面。在 Java10(JDK18.3)中,添加了新方法-`List.copyOf``Set.copyOf``Map.copyOf`,允许我们从现有实例创建新的不可变集合。此外,新方法`toUnmodifiableList``toUnmodifiableSet``toUnmodifiableMap`被添加到`java.util.stream`包中的`Collectors`类中,允许将`Stream`中的元素收集到一个不可变的集合中。本章介绍如何使用流和链多个操作来创建管道。此外,读者还将了解如何并行完成这些操作。配方列表包括以下内容:
在 Java8 和 Java9 中,CollectionsAPI 通过利用 Lambda 表达式引入流和内部迭代进行了重大的改头换面。在 Java10(JDK18.3)中,添加了新方法-`List.copyOf``Set.copyOf``Map.copyOf`,允许我们从现有实例创建新的不可变集合。此外,新方法`toUnmodifiableList``toUnmodifiableSet``toUnmodifiableMap`被添加到`java.util.stream`包中的`Collectors`类中,允许将`Stream`中的元素收集到一个不可变的集合中。本章介绍如何使用流和链多个操作来创建管道。此外,读者还将了解如何并行完成这些操作。配方列表包括以下内容:
* 使用`of()``copyOf()`工厂方法创建不可变集合
* 创建和操作流
......@@ -17,7 +17,7 @@
这种库的一个例子是`java.util.stream`包,这将是本章的重点。该包允许您对随后可以应用于数据的过程进行声明性表示,也可以并行应用;这些过程以流的形式呈现,流是`Stream`接口的对象。为了更好地从传统集合过渡到流,在`java.util.Collection`接口中添加了两种默认方法`stream()``parallelStream()`,同时在`Stream`接口中添加了流生成的新工厂方法。
这种方法利用了聚合的能力,如第 2 章“OOP 快速通道——类和接口”中所述。与其他设计原则(封装、接口和多态)一起,它促进了高度可扩展和灵活的设计,而 lambda 表达式允许您以简洁的方式实现它。
这种方法利用了聚合的能力,如第 2 章“OOP 快速通道——类和接口”中所述。与其他设计原则(封装、接口和多态)一起,它促进了高度可扩展和灵活的设计,而 Lambda 表达式允许您以简洁的方式实现它。
如今,当海量数据处理和操作微调的机器学习需求变得无处不在时,这些新特性加强了 Java 在几种现代编程语言中的地位。
......@@ -250,9 +250,9 @@ setA = new HashSet<>(listB);
# 还有更多。。。
lambda 表达式和流被引入后不久,非空值和不变性被强制执行,这不是偶然的。正如您将在后续配方中看到的,函数式编程和流管道鼓励流畅的编码风格(使用方法链接,以及在本配方示例中使用`forEach()`方法)。Fluent 样式提供了更紧凑、可读性更强的代码。不需要检查`null`值有助于保持紧凑,并专注于主要处理过程。
Lambda 表达式和流被引入后不久,非空值和不变性被强制执行,这不是偶然的。正如您将在后续配方中看到的,函数式编程和流管道鼓励流畅的编码风格(使用方法链接,以及在本配方示例中使用`forEach()`方法)。Fluent 样式提供了更紧凑、可读性更强的代码。不需要检查`null`值有助于保持紧凑,并专注于主要处理过程。
不变性特性反过来与 lambda 表达式使用的变量的`finale`概念非常一致。例如,可变集合允许我们绕过此限制:
不变性特性反过来与 Lambda 表达式使用的变量的`finale`概念非常一致。例如,可变集合允许我们绕过此限制:
```java
List<Integer> list = Arrays.asList(1,2,3,4,5);
......@@ -267,7 +267,7 @@ System.out.println();
list.forEach(System.out::print); //prints: 12545
```
在前面的代码中,第二个`forEach()`操作使用的 lambda 表达式在原始列表的第三个(索引为 2)元素中保持状态。它可以有意或无意地在 lambda 表达式中引入状态,并在不同的上下文中导致相同函数的不同结果。这在并行处理中尤其危险,因为无法预测每个可能上下文的状态。这就是为什么集合的不变性是一个有益的补充,它使代码更加健壮和可靠。
在前面的代码中,第二个`forEach()`操作使用的 Lambda 表达式在原始列表的第三个(索引为 2)元素中保持状态。它可以有意或无意地在 Lambda 表达式中引入状态,并在不同的上下文中导致相同函数的不同结果。这在并行处理中尤其危险,因为无法预测每个可能上下文的状态。这就是为什么集合的不变性是一个有益的补充,它使代码更加健壮和可靠。
# 创建和操作流
......@@ -1286,7 +1286,7 @@ class Person {
//prints: [Person{name:John,age:30}, Person{name:Jill,age:20}]
```
在前面的示例中,对累加器和组合器的注释演示了如何将这些函数表示为 lambda 表达式,而不仅仅是方法引用。
在前面的示例中,对累加器和组合器的注释演示了如何将这些函数表示为 Lambda 表达式,而不仅仅是方法引用。
第一个参数`Supplier<R>`返回结果的容器。在我们的例子中,我们将其定义为`ArrayList<Person>`类的构造函数,因为它实现了`List<Person>`接口—我们想要构造的对象的类型。
......@@ -1434,7 +1434,7 @@ set.add(new Person(30, "Bob")); //UnsupportedOperationException
```
从前面代码中的注释可以看出,使用`Collector<T, ?, List<T>> Collectors.toUnmodifiableList()``Collector<T, ?, Set<T>> Collectors.toUnmodifiableSet()`方法生成的收集器创建的对象会创建不可变的对象。这些对象在 lambda 表达式中使用时非常有用,因为这样我们可以保证它们不会被修改,因此,即使在不同的上下文中传递并执行相同的表达式,也会产生仅依赖于其输入参数的结果,并且不会因修改其使用的`List``Set`对象而产生意外的副作用。
从前面代码中的注释可以看出,使用`Collector<T, ?, List<T>> Collectors.toUnmodifiableList()``Collector<T, ?, Set<T>> Collectors.toUnmodifiableSet()`方法生成的收集器创建的对象会创建不可变的对象。这些对象在 Lambda 表达式中使用时非常有用,因为这样我们可以保证它们不会被修改,因此,即使在不同的上下文中传递并执行相同的表达式,也会产生仅依赖于其输入参数的结果,并且不会因修改其使用的`List``Set`对象而产生意外的副作用。
例如:
......@@ -2273,7 +2273,7 @@ System.out.println(map2); //prints: {false=2, true=1}
# 准备
在上一章第 4 章“开始函数式”中,在创建一个 lambda 友好的 API 时,我们最终采用了以下 API 方法:
在上一章第 4 章“开始函数式”中,在创建一个 Lambda 友好的 API 时,我们最终采用了以下 API 方法:
```java
public interface Traffic {
......@@ -2294,7 +2294,7 @@ public interface Traffic {
# 怎么做。。。
让我们回忆一下用户是如何调用 lambda 友好 API 的:
让我们回忆一下用户是如何调用 Lambda 友好 API 的:
```java
double timeSec = 10.0;
......
......@@ -568,7 +568,7 @@ ds.addDataSourceProperty("useServerPrepStmts", Boolean.TRUE);
```
我们已经注释掉了密码,因为我们没有为数据库设置密码。在属性`jdbcUrl``dataSourceClassName`之间,一次只能使用其中一个属性,除非使用一些可能需要同时设置这两个属性的旧驱动程序。另外,请注意,当特定属性没有专用的 setter 时,我们是如何使用通用方法`addDataSourceProperty()`的。
我们已经注释掉了密码,因为我们没有为数据库设置密码。在属性`jdbcUrl``dataSourceClassName`之间,一次只能使用其中一个属性,除非使用一些可能需要同时设置这两个属性的旧驱动程序。另外,请注意,当特定属性没有专用的设置器时,我们是如何使用通用方法`addDataSourceProperty()`的。
要从 PostgreSQL 切换到另一个关系数据库,只需更改驱动程序类名和数据库 URL。还有许多其他财产;其中一些是特定于数据库的,但我们不打算深入讨论这些细节,因为这个配方演示了如何使用 HikariCP。阅读数据库文档,了解特定于数据库的池配置属性,以及如何使用这些属性优化池以获得最佳性能,这在很大程度上取决于特定应用程序与数据库的交互方式。
......@@ -981,7 +981,7 @@ traverseRS("select * from test");
`Clob`允许您存储字符数据。`NClob`存储 Unicode 字符数据以支持国际化。它扩展了`Clob`接口并提供了相同的方法。这两个接口都允许您查找 LOB 的长度,并在值中获取子字符串。
`ResultSet``CallableStatement`(我们将在下一个配方中讨论)和`PreparedStatement`接口中的方法允许应用程序以各种方式存储和访问存储的值,其中一些方式是通过相应对象的 setter 和 getter,而另一些方式是`bytes[]`或二进制、字符或 ASCII 流。
`ResultSet``CallableStatement`(我们将在下一个配方中讨论)和`PreparedStatement`接口中的方法允许应用程序以各种方式存储和访问存储的值,其中一些方式是通过相应对象的设置器和 getter,而另一些方式是`bytes[]`或二进制、字符或 ASCII 流。
# 怎么做。。。
......@@ -1007,9 +1007,9 @@ execute("create table lobs (id integer, lob oid)");
execute("create table texts (id integer, text text)");
```
查看 JDBC 接口`PreparedStatement``ResultSet`,您会注意到对象的 setter 和 getter—`get/setBlob()``get/setClob()``get/setNClob()``get/setBytes()`——以及使用`InputStream``Reader``get/setBinaryStream()``get/setAsciiStream()``get/setCharacterStream()`的方法。流式方法的最大优点是,它们在数据库和源之间移动数据,而无需将整个 LOB 存储在内存中。
查看 JDBC 接口`PreparedStatement``ResultSet`,您会注意到对象的设置器和 getter—`get/setBlob()``get/setClob()``get/setNClob()``get/setBytes()`——以及使用`InputStream``Reader``get/setBinaryStream()``get/setAsciiStream()``get/setCharacterStream()`的方法。流式方法的最大优点是,它们在数据库和源之间移动数据,而无需将整个 LOB 存储在内存中。
然而,对象的 setter 和 getter 更接近我们的心,与面向对象的编码保持一致。因此,我们将从它们开始,使用不太大的对象进行演示。我们希望以下代码能够正常工作:
然而,对象的设置器和获取器更接近我们的心,与面向对象的编码保持一致。因此,我们将从它们开始,使用不太大的对象进行演示。我们希望以下代码能够正常工作:
```java
try (Connection conn = getDbConnection()) {
......@@ -1057,7 +1057,7 @@ try (Connection conn = getDbConnection()) {
![](img/321c623c-b082-4b55-abf2-fd0a1de83494.png)
我们也可以尝试通过 getter `ResultSet`检索对象:
我们也可以尝试通过获取器`ResultSet`检索对象:
```java
String sql = "select image from images";
......@@ -1085,7 +1085,7 @@ To use the Large Object functionality you can use either the `LargeObject` class
在不了解这些细节的情况下,找出处理 LOB 的方法将需要大量时间,并且会导致很多挫折。
在处理 LOB 时,流方法是首选的,因为它们直接将数据从源传输到数据库(或者相反),并且不像 setter 和 getter 那样消耗内存(它们必须首先在内存中加载所有 LOB)。以下是从 PostgreSQL 数据库中传输`Blob`的代码:
在处理 LOB 时,流方法是首选的,因为它们直接将数据从源传输到数据库(或者相反),并且不像设置器和获取器那样消耗内存(它们必须首先在内存中加载所有 LOB)。以下是从 PostgreSQL 数据库中传输`Blob`的代码:
```java
traverseRS("select * from images");
......@@ -2101,7 +2101,7 @@ create table person1 (
使用`<select>`的 ID 属性和类似的标记来调用它们,以及映射器名称空间值。我们将很快向您展示这是如何完成的。构造`#{id}`是指作为参数传入的值,如果该值是基元类型。否则,传入的对象应该有这样一个字段。不需要在对象上具有 getter。如果存在 getter,它必须符合 JavaBean 方法格式。
对于返回值,默认情况下,列的名称与对象字段或 setter 的名称匹配(必须符合 JavaBean 方法格式)。如果字段(或 setter 名称)和列名不同,可以使用标记`<resultMap>`提供映射。例如,如果表`person``person_id``person_name`列,而域对象`Person``id``name`字段,我们可以创建一个映射:
对于返回值,默认情况下,列的名称与对象字段或设置器的名称匹配(必须符合 JavaBean 方法格式)。如果字段(或设置器名称)和列名不同,可以使用标记`<resultMap>`提供映射。例如,如果表`person``person_id``person_name`列,而域对象`Person``id``name`字段,我们可以创建一个映射:
```java
<resultMap id="personResultMap" type="Person">
......@@ -2577,7 +2577,7 @@ public class Person1 {
该类及其任何持久实例变量都不能声明为 final。通过这种方式,实现框架可以扩展实体类并实现所需的功能。
或者,可以将持久性注释添加到 getter 和 setter,而不是实例字段(如果方法名称遵循 JavaBean 约定)。但不允许混淆字段和方法注释,这可能会导致意外的后果。
或者,可以将持久性注释添加到获取器和 setter,而不是实例字段(如果方法名称遵循 JavaBean 约定)。但不允许混淆字段和方法注释,这可能会导致意外的后果。
也可以使用 XML 文件而不是注释来定义 Java 类与数据库表和列之间的映射,但我们将继续使用字段级注释,以便提供最紧凑和清晰的方法来表达意图。
......
......@@ -14,7 +14,7 @@
并发随着大数据分析进入现代应用程序的主流,并行执行多个过程的能力变得越来越重要。在一个 CPU 中使用 CPU 或多个内核有助于提高吞吐量,但数据量的增长速度总是超过硬件的发展速度。此外,即使在多 CPU 系统中,也必须构造代码并考虑资源共享,以利用可用的计算能力。
在前面的章节中,我们演示了具有功能接口和并行流的 lambda 如何使并发处理成为每个 Java 程序员工具包的一部分。人们可以很容易地利用这一功能,只需最少的指导(如果有的话)。
在前面的章节中,我们演示了具有功能接口和并行流的 Lambda 如何使并发处理成为每个 Java 程序员工具包的一部分。人们可以很容易地利用这一功能,只需最少的指导(如果有的话)。
在本章中,我们将描述其他一些旧的(在 Java9 之前)和新的 Java 特性和 API,它们允许对并发进行更多的控制。自 Java5 以来,高级并发 JavaAPI 就一直存在。JDK 增强方案(JEP)266,[*更多并发更新*](http://openjdk.java.net/jeps/266),在 Java 9 引入到`java.util.concurrent`包中。
......@@ -115,7 +115,7 @@ IntStream.range(21, 24)
```
我们还可以利用`Runnable`作为函数接口的优势,通过传递 lambda 表达式来避免创建中间类:
我们还可以利用`Runnable`作为函数接口的优势,通过传递 Lambda 表达式来避免创建中间类:
```java
Thread thr1 = new Thread(() -> IntStream.range(1, 4)
......@@ -136,7 +136,7 @@ IntStream.range(21, 24)
哪种实现更好取决于您的目标和风格。实现`Runnable`有一个优势(在某些情况下,是唯一可能的选择),允许实现扩展另一个类。当您想向现有类添加类似线程的行为时,它特别有用。您甚至可以直接调用`run()`方法,而无需将对象传递给`Thread`构造函数。
当只需要`run()`方法实现时,使用 lambda 表达式胜过`Runnable`实现,不管它有多大。如果它太大,可以使用单独的方法将其隔离:
当只需要`run()`方法实现时,使用 Lambda 表达式胜过`Runnable`实现,不管它有多大。如果它太大,可以使用单独的方法将其隔离:
```java
public static void main(String arg[]) {
......@@ -376,7 +376,7 @@ class Calculator{
thr2.start();
```
这与使 lambda 表达式独立于创建它们的上下文的总体思想是一致的。这是因为,在多线程环境中,人们永远不知道上下文在执行过程中会是什么样子。除非必须处理大量数据,否则每次创建新对象的成本可以忽略不计,并且测试确保对象创建开销是显著的。
这与使 Lambda 表达式独立于创建它们的上下文的总体思想是一致的。这是因为,在多线程环境中,人们永远不知道上下文在执行过程中会是什么样子。除非必须处理大量数据,否则每次创建新对象的成本可以忽略不计,并且测试确保对象创建开销是显著的。
在多线程环境中,内存一致性错误可能有多种形式和原因。在`java.util.concurrent`包的 Javadoc 中对它们进行了详细的讨论。在这里,我们将只提到最常见的情况,这是由于缺乏可见性造成的。当一个线程更改属性值时,另一个线程可能不会立即看到更改,并且不能对基元类型使用`synchronized`关键字。在这种情况下,考虑对属性使用`volatile`关键字;它保证了不同线程之间的读/写可见性。
......@@ -436,7 +436,7 @@ l.add(i, "last");
对象不变性意味着在创建对象后,缺少更改其状态的方法。它不能保证线程安全,但有助于显著提高线程安全性,并在许多实际应用程序中提供足够的并发保护。
创建新对象而不是重用现有对象(通过 setter 和 getter 更改其状态)通常被认为是一种昂贵的方法。但随着现代计算机的强大,必须进行大量的对象创建,才能对性能产生重大影响。即使是这样,程序员也常常选择性能下降作为获得可预测结果的代价。
创建新对象而不是重用现有对象(通过设置器和获取器更改其状态)通常被认为是一种昂贵的方法。但随着现代计算机的强大,必须进行大量的对象创建,才能对性能产生重大影响。即使是这样,程序员也常常选择性能下降作为获得可预测结果的代价。
# 怎么做。。。
......@@ -457,7 +457,7 @@ class MutableClass{
}
```
要使其不可变,我们需要删除 setter 并将`final`关键字添加到其唯一属性和类本身:
要使其不可变,我们需要删除设置器并将`final`关键字添加到其唯一属性和类本身:
```java
final class ImmutableClass{
......@@ -473,7 +473,7 @@ final class ImmutableClass{
`final`关键字添加到类中会阻止对其进行扩展,因此无法重写其方法。将`final`添加到私有财产并不那么明显。动机有点复杂,与编译器在对象构造过程中重新排序字段的方式有关。如果该字段被声明为`final`,编译器将其视为已同步。这就是为什么向私有属性添加`final`是使对象完全不可变的必要条件。
如果该类由其他类组成,尤其是可变类,那么挑战就会增加。发生这种情况时,注入的类可能会引入影响包含类的代码。此外,内部(可变)类(由引用通过 getter 检索)可以被修改并在包含类内传播更改。关闭此类孔的方法是在对象检索的合成过程中生成新对象。以下是一个例子:
如果该类由其他类组成,尤其是可变类,那么挑战就会增加。发生这种情况时,注入的类可能会引入影响包含类的代码。此外,内部(可变)类(由引用通过获取器检索)可以被修改并在包含类内传播更改。关闭此类孔的方法是在对象检索的合成过程中生成新对象。以下是一个例子:
```java
final class ImmutableClass{
......@@ -657,7 +657,7 @@ int getSomething(AnotherMutableClass amc, String whatever){
当升级应用程序使其在多线程环境中工作时,我们应该记住这一点,如果我们使用迭代器删除列表元素,仅仅从`ArrayList()`更改为`CopyOnWriteArrayList`是不够的。
自 Java 8 以来,有一种更好的方法可以使用 lambda 从集合中删除元素,因为它将管道细节留给库代码使用:
自 Java 8 以来,有一种更好的方法可以使用 Lambda 从集合中删除元素,因为它将管道细节留给库代码使用:
```java
void demoRemoveIf(Collection<String> collection) {
......@@ -687,7 +687,7 @@ int getSomething(AnotherMutableClass amc, String whatever){
![](img/f11bd846-7dc6-4cd3-9d78-701ecafaba2a.png)
它很短,对任何集合都没有问题,并且符合无状态并行计算的一般趋势,即使用具有 lambda 和函数接口的流。
它很短,对任何集合都没有问题,并且符合无状态并行计算的一般趋势,即使用具有 Lambda 和函数接口的流。
此外,在我们升级应用程序以使用`CopyOnWriteArrayList`类之后,我们可以利用一种更简单的方法将新元素添加到列表中(无需首先检查它是否已经存在):
......@@ -1180,7 +1180,7 @@ Javadoc 中的`ConcurrentHashMap`类。
}
```
4. 通过使`Runnable`(使用 lambda 表达式)休眠一段时间(模拟要完成的有用工作)来增强示例:
4. 通过使`Runnable`(使用 Lambda 表达式)休眠一段时间(模拟要完成的有用工作)来增强示例:
```java
void executeAndSubmit(ExecutorService execService,
......@@ -1356,7 +1356,7 @@ interface CallableWorker<Result> extends Callable<Result> {
}
```
我们必须将这些方法设置为默认值并返回一些数据(它们无论如何都会被类实现覆盖)以保持其`functional interface`状态。否则,我们将无法在 lambda 表达式中使用它。
我们必须将这些方法设置为默认值并返回一些数据(它们无论如何都会被类实现覆盖)以保持其`functional interface`状态。否则,我们将无法在 Lambda 表达式中使用它。
我们还将创建一个工厂,生成工人列表:
......
......@@ -692,7 +692,7 @@ $ java -p mods -m process/com.packt.process.SpawnedProcessInfoDemo
ProcessHandle.allProcesses();
```
2. 使用`forEach()`在流上迭代,并传递 lambda 表达式以打印可用的详细信息:
2. 使用`forEach()`在流上迭代,并传递 Lambda 表达式以打印可用的详细信息:
```java
liveProcesses.forEach(ph -> {
......
......@@ -181,7 +181,7 @@ values('David', 'John', 'Delhi');
# 怎么做。。。
1. 创建一个模型类`com.packt.boot_db_demo.Person`,以表示一个人。我们将使用 Lombok 注释为我们生成 getter 和 setter:
1. 创建一个模型类`com.packt.boot_db_demo.Person`,以表示一个人。我们将使用 Lombok 注释为我们生成获取器和 setter:
```java
@Data
......
......@@ -341,7 +341,7 @@ public class UsernamePasswordAuthenticator
HttpResponse.BodyHandlers.ofString());
```
4. 我们提供`CompletionStage`以在前一阶段完成后处理响应。为此,我们使用了采用 lambda 表达式的`thenAccept`方法:
4. 我们提供`CompletionStage`以在前一阶段完成后处理响应。为此,我们使用了采用 Lambda 表达式的`thenAccept`方法:
```java
CompletableFuture<Void> processedFuture =
......
......@@ -407,7 +407,7 @@ public class VehicleTest {
# 怎么做。。。
让我们仔细看一下`Vehicle`类。测试 getter 没有什么价值,但我们仍然可以这样做,确保传递给构造函数的值由相应的 getter 返回。构造函数中的异常属于必须测试特性以及`getSpeedMph()`方法。还有一个`Engine`类的对象具有`getHorsePower()`方法。它能返回`null`吗?为了回答这个问题,让我们看看`Engine`课程:
让我们仔细看一下`Vehicle`类。测试获取器没有什么价值,但我们仍然可以这样做,确保传递给构造函数的值由相应的获取器返回。构造函数中的异常属于必须测试特性以及`getSpeedMph()`方法。还有一个`Engine`类的对象具有`getHorsePower()`方法。它能返回`null`吗?为了回答这个问题,让我们看看`Engine`课程:
```java
public class Engine {
......
......@@ -3,7 +3,7 @@
在本章中,我们将介绍以下配方:
* 使用局部变量类型推断
*lambda 参数使用局部变量语法
*Lambda 参数使用局部变量语法
# 介绍
......@@ -184,9 +184,9 @@ public void var(int i){
package com.packt.cookbook.var;
```
# 对 lambda 参数使用局部变量语法
# 对 Lambda 参数使用局部变量语法
在本配方中,您将学习如何对 lambda 参数使用局部变量语法(在上一配方中讨论),以及引入此功能的动机。它是在 Java11 中引入的。
在本配方中,您将学习如何对 Lambda 参数使用局部变量语法(在上一配方中讨论),以及引入此功能的动机。它是在 Java11 中引入的。
# 准备
......@@ -253,9 +253,9 @@ Exception in thread "main" java.lang.IllegalArgumentException:
Argument for @NotNull parameter 'x' of com/packt/cookbook/ch17_new_way/b_lambdas/Chapter15Var.lambda$main$4 must not be null
```
lambda 表达式甚至没有执行。
Lambda 表达式甚至没有执行。
3. 如果当参数是具有很长名称的类的对象时,我们需要使用注释,那么在 lambda 参数的情况下使用局部变量语法的优势就显而易见了。在 Java 11 之前,代码可能如下所示:
3. 如果当参数是具有很长名称的类的对象时,我们需要使用注释,那么在 Lambda 参数的情况下使用局部变量语法的优势就显而易见了。在 Java 11 之前,代码可能如下所示:
```java
BiFunction<SomeReallyLongClassName,
......@@ -281,4 +281,4 @@ BiFunction<SomeReallyLongClassName,
(@NotNull var x, @NotNull var y) -> x.doSomething(y);
```
这就是为 lambda 参数的声明引入局部变量语法的优点和动机。
\ No newline at end of file
这就是为 Lambda 参数的声明引入局部变量语法的优点和动机。
\ No newline at end of file
......@@ -259,7 +259,7 @@ java -p "PATH_TO_JAVAFX_SDK_LIB:COMPILED_CODE" <other parts of the command line>
在将组件添加到`javafx.scene.layout.GridPane`时,我们必须提到列号、行号和列跨度,即组件占用的列数,以及行跨度,即组件按该顺序占用的行数。列跨距和行跨距是可选的。在我们的配方中,我们将`appTitle`放在第一行和第一列,它占用两列空间和一行空间,如这里的代码所示:`appTitle.setFont(Font.font("Arial", FontWeight.NORMAL, 15));`
本配方的另一个重要部分是`ageCalculator`按钮事件的设置。我们使用`javafx.scene.control.Button`类的`setOnAction()`方法来设置单击按钮时执行的操作。这接受`javafx.event.EventHandler<ActionEvent>`接口的实现。由于`javafx.event.EventHandler`是一个功能接口,其实现可以写成 lambda 表达式的形式,如下图:
本配方的另一个重要部分是`ageCalculator`按钮事件的设置。我们使用`javafx.scene.control.Button`类的`setOnAction()`方法来设置单击按钮时执行的操作。这接受`javafx.event.EventHandler<ActionEvent>`接口的实现。由于`javafx.event.EventHandler`是一个功能接口,其实现可以写成 Lambda 表达式的形式,如下图:
```java
ageCalculator.setOnAction((event) -> {
......@@ -267,7 +267,7 @@ ageCalculator.setOnAction((event) -> {
});
```
前面的语法看起来类似于 Swing 期间广泛使用的匿名内部类。您可以在第 4 章、“开始函数式”中的配方中了解更多关于功能接口和 lambda 表达式的信息。
前面的语法看起来类似于 Swing 期间广泛使用的匿名内部类。您可以在第 4 章、“开始函数式”中的配方中了解更多关于功能接口和 Lambda 表达式的信息。
在我们的事件处理代码中,我们分别使用`getText()``getValue()`方法从`nameField``dateOfBirthPicker`获取值。`DatePicker`返回选择作为`java.time.LocalDate`实例的日期。这是添加到 Java8 中的新日期时间 API 之一。它表示一个日期,即年、月和日,没有任何时区相关信息。然后,我们使用`java.time.Period`类查找当前日期和所选日期之间的持续时间,如下所示:
......@@ -396,7 +396,7 @@ java -p "PATH_TO_JAVAFX_SDK_LIB:COMPILED_CODE" <other parts of the command line>
@FXML private TextField nameField;
```
11. 下一步是实现`calculateAge`方法,该方法注册为`Calculate`按钮的动作事件处理程序。该实现类似于上一个配方中的实现。唯一的区别是,它是一种方法,不同于之前的配方,它是一个 lambda 表达式:
11. 下一步是实现`calculateAge`方法,该方法注册为`Calculate`按钮的动作事件处理程序。该实现类似于上一个配方中的实现。唯一的区别是,它是一种方法,不同于之前的配方,它是一个 Lambda 表达式:
```java
@FXML
......
......@@ -113,7 +113,7 @@ public class MyRuleTest {
JUnit Lambda 众筹活动
JUnit Lambda 是该项目的名称,它是当前 JUnit 5 框架的种子。在项目名称中包含 lambda 一词强化了从项目一开始就使用 Java8 的想法。引用 JUnit Lambda 项目站点:
JUnit Lambda 是该项目的名称,它是当前 JUnit 5 框架的种子。在项目名称中包含 Lambda 一词强化了从项目一开始就使用 Java8 的想法。引用 JUnit Lambda 项目站点:
目标是为 JVM 上的开发人员端测试创建最新的基础。这包括关注 Java8 及以上版本,以及支持多种不同风格的测试。
......@@ -125,7 +125,7 @@ JUnit Lambda 项目成为 JUnit 5,指导开发过程的设计原则如下:
* 功能强大的扩展模型,重点关注可组合性:可扩展性是现代测试框架所必须的。因此,JUnit5 应该提供与第三方框架(例如 Spring 或 Mockito)的无缝集成。
* API 分离:将测试发现和执行与测试定义分离。
* 与旧版本的兼容性:支持在新的 JUnit5 平台上执行旧的 Java3 和 Java4。
* 用于编写测试的现代编程模型(Java8):如今,越来越多的开发人员使用 Java8 的新特性编写代码,例如 lambda 表达式。JUnit4 是在 Java5 上构建的,但是 JUnit5 是使用 Java8 从头创建的。
* 用于编写测试的现代编程模型(Java8):如今,越来越多的开发人员使用 Java8 的新特性编写代码,例如 Lambda 表达式。JUnit4 是在 Java5 上构建的,但是 JUnit5 是使用 Java8 从头创建的。
# JUnit 5 社区
......
......@@ -307,9 +307,9 @@ class StandardAssertionsTest {
一个重要的木星断言是`assertAll`。此方法允许同时对不同的断言进行分组。在分组断言中,始终执行所有断言,任何失败都将一起报告。
方法`assertAll`接受 lambda 表达式的 vargargs(`Executable…`)或这些表达式的流(`Stream<Executable>`)。可选地,`assertAll`的第一个参数可以是用于标记断言组的字符串消息。
方法`assertAll`接受 Lambda 表达式的 vargargs(`Executable…`)或这些表达式的流(`Stream<Executable>`)。可选地,`assertAll`的第一个参数可以是用于标记断言组的字符串消息。
让我们看一个例子。在下面的测试中,我们使用 lambda 表达式将几个`assertEquals`分组:
让我们看一个例子。在下面的测试中,我们使用 Lambda 表达式将几个`assertEquals`分组:
```java
package io.github.bonigarcia;
......@@ -342,7 +342,7 @@ class GroupedAssertionsTest {
# 断言异常
另一个重要的木星断言是`assertThrows`。此断言允许验证代码段中是否引发了给定的异常。为此,方法`assertThrows`接受两个参数。第一个是预期的异常类,第二个是预期发生异常的可执行对象(lambda 表达式):
另一个重要的木星断言是`assertThrows`。此断言允许验证代码段中是否引发了给定的异常。为此,方法`assertThrows`接受两个参数。第一个是预期的异常类,第二个是预期发生异常的可执行对象(Lambda 表达式):
```java
package io.github.bonigarcia;
......@@ -366,7 +366,7 @@ class ExceptionTest {
}
```
希望抛出`IllegalArgumentException`,这实际上是在这个 lambda 表达式中发生的。以下屏幕截图显示测试实际成功:
希望抛出`IllegalArgumentException`,这实际上是在这个 Lambda 表达式中发生的。以下屏幕截图显示测试实际成功:
![](img/00047.gif)
......@@ -411,7 +411,7 @@ class TimeoutExceededTest {
assertTimeout第一例控制台输出
让我们看看更多使用`assertTimeout`的测试。在第一个测试中,`assertTimeout`在给定的超时时间内将一段代码作为 lambda 表达式进行求值,得到结果。在第二个测试中,`assertTimeout`在给定的超时时间内评估一个方法,获得其结果:
让我们看看更多使用`assertTimeout`的测试。在第一个测试中,`assertTimeout`在给定的超时时间内将一段代码作为 Lambda 表达式进行求值,得到结果。在第二个测试中,`assertTimeout`在给定的超时时间内评估一个方法,获得其结果:
```java
package io.github.bonigarcia;
......@@ -1052,7 +1052,7 @@ class AssumptionsTest {
}
```
请注意,在本例中,由于未满足假设,因此跳过了前两个测试(`assumeTrueTest``assumeFalseTest`。然而,在`assummingThatTest`测试中,只有这部分测试(本例中为 lambda 表达式)没有执行,但整个测试没有跳过:
请注意,在本例中,由于未满足假设,因此跳过了前两个测试(`assumeTrueTest``assumeFalseTest`。然而,在`assummingThatTest`测试中,只有这部分测试(本例中为 Lambda 表达式)没有执行,但整个测试没有跳过:
![](img/00059.gif)
......
......@@ -228,7 +228,7 @@ public class ParameterizedTest {
另一方面,JUnit5 允许在运行时通过一个工厂方法生成测试,该工厂方法带有`@TestFactory`注释。与`@Test`相比,`@TestFactory`方法不是测试,而是工厂。`@TestFactory`方法必须返回`DynamicTest`实例的`Stream``Collection``Iterable``Iterator`。这些`DynamicTest`实例是惰性执行的,可以动态生成测试用例。
为了创建动态测试,我们可以使用`org.junit.jupiter.api`包中`DynamicTest`类的静态方法`dynamicTest`。如果我们检查这个类的源代码,我们可以看到一个`DynamicTest`由一个字符串形式的显示名和一个可执行对象组成,可以作为 lambda 表达式或方法引用提供。
为了创建动态测试,我们可以使用`org.junit.jupiter.api`包中`DynamicTest`类的静态方法`dynamicTest`。如果我们检查这个类的源代码,我们可以看到一个`DynamicTest`由一个字符串形式的显示名和一个可执行对象组成,可以作为 Lambda 表达式或方法引用提供。
让我们看几个动态测试的例子。在下面的示例中,由于我们没有返回预期的`DynamicTests`集合,第一个动态测试将失败。接下来的三种方法是非常简单的示例,演示了`DynamicTest`实例的`Collection``Iterable``Iterator`的生成:
......@@ -324,7 +324,7 @@ class DynamicExampleTest {
在 JUnit5 中还可以使用类`DynamicTest`的静态方法`stream`创建动态测试。此方法需要一个输入生成器、一个基于输入值生成显示名称的函数和一个测试执行器。
让我们看另一个例子。我们创建了一个测试工厂,将输入数据提供为一个`Iterator`、一个使用 lambda 表达式的显示名称函数,最后是一个使用另一个 lambda 表达式实现的测试执行器。在本例中,测试执行器基本上判断输入整数是偶数还是奇数:
让我们看另一个例子。我们创建了一个测试工厂,将输入数据提供为一个`Iterator`、一个使用 Lambda 表达式的显示名称函数,最后是一个使用另一个 Lambda 表达式实现的测试执行器。在本例中,测试执行器基本上判断输入整数是偶数还是奇数:
```java
package io.github.bonigarcia;
......
......@@ -768,7 +768,7 @@ public class MessageComponent {
此时,值得回顾一下执行 Spring 组件依赖项注入的不同方式:
1. 字段注入:注入的组件是一个带有`@Autowired`注释的类字段,就像前面的示例一样。作为一个好处,这种注入可以消除杂乱的代码,例如 setter 方法或构造函数参数。
1. 字段注入:注入的组件是一个带有`@Autowired`注释的类字段,就像前面的示例一样。作为一个好处,这种注入可以消除杂乱的代码,例如设置器方法或构造函数参数。
2. Setter 注入:注入的组件被声明为类中的一个字段,然后为该字段创建一个 Setter 并用`@Autowired`注释。
3. 构造函数注入:在类构造函数中注入依赖项,并用`@Autowired`(图中为 3-a)注释。这是前面示例中显示的方式。从 Spring 4.3 开始,不再需要用`@Autowired`注释构造函数来执行注入(3-b)。
......@@ -849,7 +849,7 @@ Spring 是一个名为`spring-test`的模块,支持 Spring 组件的单元测
* `org.springframework.mock.web`包包含一组 Servlet API 模拟对象,用于测试 Web 上下文。例如,对象`MockMvc`允许执行 HTTP 请求(`POST``GET``PUT``DELETE`等)并验证响应(状态码、内容类型或响应主体)。
* `org.springframework.mock.jndi`包包含**Java 命名和目录接口****JNDI**)SPI 的实现,可用于建立简单的 JNDI 测试环境。例如,使用`SimpleNamingContextBuilder`类,我们可以在测试中使用 JNDI 数据源。
* `org.springframework.test.jdbc`包包含类`JdbcTestUtils`,它是 JDBC 实用程序函数的集合,旨在简化标准数据库访问。
* `org.springframework.test.util`包包含类`ReflectionTestUtils`,它是一组实用方法的集合,用于在测试应用程序代码时设置非公共字段或调用私有/受保护的 setter 方法。
* `org.springframework.test.util`包包含类`ReflectionTestUtils`,它是一组实用方法的集合,用于在测试应用程序代码时设置非公共字段或调用私有/受保护的设置器方法。
# 测试 Spring 启动应用程序
......
......@@ -135,7 +135,7 @@ Eclipse4.7 中 EclEmma 的测试覆盖率(氧气)
因此,据说软件系统中不存在缺陷是无法证明的。这是由计算机科学先驱 Edsger W.Dijkstra 所说的(见本章开头的引文)。因此,测试充其量只是抽样,必须在任何软件项目中进行,以降低系统故障的风险(参见第 1 章、“软件质量和 Java 测试回顾”、回顾软件缺陷分类法)。因为我们不能测试所有的东西,所以我们需要正确地测试。在本节中,我们将回顾一组编写高效测试用例的最佳实践,即:
* **测试应该简单**:编写测试的软件工程师(打电话给他或她的测试人员、程序员、开发人员或其他人)应该避免尝试测试他或她的程序。关于测试,问题“是谁在看看守员?”的正确答案应该是无名小卒。我们的测试逻辑应该足够简单,以避免任何类型的元测试,因为这将导致任何逻辑的递归问题。间接地说,如果我们保持测试简单,我们还可以获得另一个理想的特性:测试将易于维护。
* **不要实现简单测试**:一件事是进行简单测试,另一件非常不同的事情是实现伪代码,比如 getter 或 setter。正如前面介绍的,测试充其量只是抽样,我们不能浪费宝贵的时间来评估代码库的这类部分。
* **不要实现简单测试**:一件事是进行简单测试,另一件非常不同的事情是实现伪代码,比如获取器或 setter。正如前面介绍的,测试充其量只是抽样,我们不能浪费宝贵的时间来评估代码库的这类部分。
* **易读**:第一步是为我们的测试方法提供一个有意义的名称。此外,由于 JUnit 5`@DisplayName`注释,我们可以提供丰富的文本描述,它在没有 Java 命名约束的情况下定义了测试的目标。
* **单一责任原则**:这是计算机编程的一般原则,规定每个类都应该对单一功能负责。它与内聚度密切相关。这一原则在编写测试代码时非常重要:单个测试只应引用给定的系统需求。
* **测试数据是关键**:如前一节所述,SUT 的预期结果是测试的核心部分。正确管理这些数据对于创建有效的测试至关重要。幸运的是,JUnit 5 提供了一个丰富的工具箱来处理测试数据(参见第 4 章“使用高级 JUnit 特性简化测试”中的“参数化测试”。
......
......@@ -188,13 +188,13 @@ public final BinaryOperator<Integer> add =
任何 Java 开发人员都应该完全熟悉第一个示例;它遵循函数的通用语法,该函数将两个整数作为参数并返回它们的总和。然而,第二个例子与我们习惯的传统代码有点不同。在这个新版本中,函数是一个计算为值的对象,可以将其指定给字段。这在某些情况下非常方便,因为它在某些情况下仍然可以用作函数,在某些情况下还可以用作返回值、函数中的参数或类中的字段。
有人可能认为函数的第一个版本更合适,因为它较短,并且不需要创建新对象。没错,但函数也可以是对象这一事实通过一系列新功能增强了它们。关于代码的详细程度,可以通过使用 lambda 表达式将其大大减少到一行:
有人可能认为函数的第一个版本更合适,因为它较短,并且不需要创建新对象。没错,但函数也可以是对象这一事实通过一系列新功能增强了它们。关于代码的详细程度,可以通过使用 Lambda 表达式将其大大减少到一行:
```java
public final BinaryOperator<Integer> addLambda = (a, b) -> a + b;
```
在下一节中,将介绍一种可能的解决方案**反向波兰符号****RPN**)。我们将使用函数编程的强大功能和表达能力,特别是 lambda 表示法,当函数需要作为某些函数的参数时,lambda 表示法非常方便。使用 lambdas 使我们的代码非常简洁优雅,增加了可读性。
在下一节中,将介绍一种可能的解决方案**反向波兰符号****RPN**)。我们将使用函数编程的强大功能和表达能力,特别是 Lambda 表示法,当函数需要作为某些函数的参数时,Lambda 表示法非常方便。使用 Lambdas 使我们的代码非常简洁优雅,增加了可读性。
# Kata–反向波兰符号
......@@ -434,7 +434,7 @@ lambdas 非常适合的另一个很好的例子是 Streams API,因为大多数
# 流
Java8 中包含的一个顶级实用程序是流。在本章中,我们将结合使用 lambda 和小代码片段中的流,并创建一个测试来验证它们。
Java8 中包含的一个顶级实用程序是流。在本章中,我们将结合使用 Lambda 和小代码片段中的流,并创建一个测试来验证它们。
为了更好地了解流是什么、做什么以及不做什么,强烈建议阅读 Oracle 的流页面。一个好的起点是[这里](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html)
......@@ -470,7 +470,7 @@ List<String> filteredNames = names.stream()
.collect(Collectors.toList());
```
那是最容易的。简而言之,`filter`过滤输入并返回一个没有过滤掉所有元素的值。使用 lambdas 可以使代码变得优雅且易于阅读。
那是最容易的。简而言之,`filter`过滤输入并返回一个没有过滤掉所有元素的值。使用 Lambdas 可以使代码变得优雅且易于阅读。
# 地图
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册