提交 25a56aee 编写于 作者: W wizardforcel

2021-09-20 20:46:22

上级 7a0721e4
......@@ -144,7 +144,7 @@ Vehicle vehicle = new Car();
Vehicle vehicle = new Truck();
```
但这里有两个问题。首先,我们的类应该为扩展而开放,为修改而关闭(开闭原则)。第二,每个班级应该只有一个改变的理由(单一责任原则)。每次添加一个新类时更改主代码将打破开放/关闭原则,让主类除了功能外还负责实例化`vehicle`对象将打破单一责任原则。
但这里有两个问题。首先,我们的类应该为扩展而开放,为修改而关闭(开闭原则)。第二,每个应该只有一个改变的理由(单一责任原则)。每次添加一个新类时更改主代码将打破开放/关闭原则,让主类除了功能外还负责实例化`vehicle`对象将打破单一责任原则。
在这种情况下,我们需要为代码提供更好的设计。我们可以添加一个新类来负责实例化`vehicle`对象。我们将基于这个`SimpleFactory`类调用模式。
......
......@@ -504,7 +504,7 @@ System.out.println(item);
# 备忘录模式
封装是面向对象设计的基本原则之一。我们也知道每个班级都应该有一个单一的责任。当我们向对象添加功能时,我们可能会意识到我们需要保存其内部状态,以便能够在稍后的阶段恢复它。如果我们直接在类中实现这样的功能,那么类可能会变得太复杂,最终可能会打破单一责任原则。同时,封装阻止我们直接访问需要记忆的对象的内部状态。
封装是面向对象设计的基本原则之一。我们也知道每个都应该有一个单一的责任。当我们向对象添加功能时,我们可能会意识到我们需要保存其内部状态,以便能够在稍后的阶段恢复它。如果我们直接在类中实现这样的功能,那么类可能会变得太复杂,最终可能会打破单一责任原则。同时,封装阻止我们直接访问需要记忆的对象的内部状态。
# 意图
......@@ -658,7 +658,7 @@ public class CarCaretaker
让我们回到我们在讨论命令模式时介绍的形状应用。我们应用了命令模式,所以我们必须重做所实现的操作。是时候添加保存功能了。
我们可能会认为,如果我们向`Shape`基类添加一个抽象的`Save`方法,并对每个形状进行扩展,那么问题就解决了。这个解决方案也许是最直观的,但不是最好的。首先,每个班级应该有一个单一的职责。
我们可能会认为,如果我们向`Shape`基类添加一个抽象的`Save`方法,并对每个形状进行扩展,那么问题就解决了。这个解决方案也许是最直观的,但不是最好的。首先,每个应该有一个单一的职责。
其次,如果需要更改保存每个形状的格式,会发生什么情况?如果我们要实现相同的方法来生成 XML 输出,那么我们是否必须更改为 JSON 格式?这种设计绝对不遵循开/关原则。
......
......@@ -44,7 +44,7 @@
* **流**:它提供了数据管道,就像列车轨道一样,为列车运行提供了基础设施。
* **数据流变量**:这些是应用于流函数的输入变量的函数的结果,就像电子表格单元格一样,通过对两个给定的输入参数应用加号数学函数来设置。
* **节流**:该机制用于实时处理环境,包括**数字信号处理器****DSP**)等硬件,通过丢弃元件来调节输入处理的速度,以赶上输入速度;用作背压策略。
* **推送机制**:这与好莱坞原理相似,因为它反转了呼叫方向。一旦数据可用,就调用流中的相关观察者来处理数据;相反,拉机制以同步方式获取信息。
* **推送机制**:这与好莱坞原理相似,因为它反转了调用方向。一旦数据可用,就调用流中的相关观察者来处理数据;相反,拉机制以同步方式获取信息。
有许多 Java 库和框架允许程序员编写反应式代码,如 Reactor、Ratpack、RxJava、Spring Framework 5 和 Vert.x。通过添加 JDK9 Flow API,开发人员可以使用反应式编程,而无需安装其他 API。
......
......@@ -410,7 +410,7 @@ public Employee EmployeeDataService(@PathVariable("id") String id) throws Servle
服务 X 将根据服务 Y 执行的操作的重要性来优雅地处理此故障。例如,如果服务 Y 负责更新帐户详细信息,服务 X 将向调用服务报告故障,或者对于 Y 正在执行的记录事务详细信息的所有服务,服务 X 将添加日志详细信息到回退队列,当服务 Y 备份时,它可以被清除。
这里的重要因素是不要让一次服务故障导致整个系统瘫痪。呼叫服务应该找出哪些是不健康的服务,并管理备用方法。
这里的重要因素是不要让一次服务故障导致整个系统瘫痪。调用服务应该找出哪些是不健康的服务,并管理备用方法。
# 故障处理模式
......
......@@ -181,7 +181,7 @@ public List<Employee> getEmployeeList()
* `3`:控制器根据给定的请求操作或更新模型,并返回最终用户请求的模型
* `4`:然后框架选择要处理当前请求的视图,并将模型传递给它
* `5`:视图通常是 JSP,根据提供的模型呈现数据
* `6`:最后的响应通常是 HTML,发送回呼叫代理或浏览器
* `6`:最后的响应通常是 HTML,发送回调用代理或浏览器
# MVC 架构及其应用实例
......
......@@ -918,7 +918,7 @@ public static String removeCharacter(String str, String ch) {
2. 其次,计算`HashMap`中表示最大出现次数的最大值(例如,使用`Collections.max()`
3. 最后,通过循环`HashMap`条目集,得到出现次数最多的字符
工具方法返回`Pair<Character, Integer>`,其中包含出现次数最多的字符和出现次数(注意,忽略了空格)。如果你不喜欢这个额外的课程,也就是说,`Pair`,那就依赖`Map.Entry<K, V>`
工具方法返回`Pair<Character, Integer>`,其中包含出现次数最多的字符和出现次数(注意,忽略了空格)。如果你不喜欢这个额外的,也就是说,`Pair`,那就依赖`Map.Entry<K, V>`
```java
public static Pair<Character, Integer> maxOccurenceCharacter(
......
......@@ -227,7 +227,7 @@ public static <T> void bubbleSortWithComparator(
}
```
还记得以前的吗?好吧,我们可以通过实现`Comparator`接口为它写一个`Comparator`
还记得以前的吗?好吧,我们可以通过实现`Comparator`接口为它写一个`Comparator`
```java
public class MelonComparator implements Comparator<Melon> {
......@@ -931,7 +931,7 @@ int i13 = Arrays.compare(integers1, integers3); // 1
int is13 = Arrays.compare(integers1, 3, 6, integers3, 3, 6); // 1
```
对于`Object`的数组,`Arrays`类还提供了一组专用的`compare()`方法。还记得`Melon`吗?好吧,为了比较两个没有显式`Comparator``Melon`数组,我们需要实现`Comparable`接口和`compareTo()`方法。假设我们依赖于瓜的重量,如下所示:
对于`Object`的数组,`Arrays`类还提供了一组专用的`compare()`方法。还记得`Melon`吗?好吧,为了比较两个没有显式`Comparator``Melon`数组,我们需要实现`Comparable`接口和`compareTo()`方法。假设我们依赖于瓜的重量,如下所示:
```java
public class Melon implements Comparable {
......
......@@ -43,7 +43,7 @@ public class Melon {
}
```
假设我们有一个客户——我们叫他马克——他想开一家卖瓜的公司。我们根据他的描述塑造了前面的班级。他的主要目标是拥有一个库存应用来支持他的想法和决策,因此需要创建一个必须基于业务需求和发展的应用。我们将在下面几节中查看每天开发此应用所需的时间。
假设我们有一个客户——我们叫他马克——他想开一家卖瓜的公司。我们根据他的描述塑造了前面的。他的主要目标是拥有一个库存应用来支持他的想法和决策,因此需要创建一个必须基于业务需求和发展的应用。我们将在下面几节中查看每天开发此应用所需的时间。
# 第 1 天(按瓜的类型过滤)
......@@ -1090,7 +1090,7 @@ public class CakeDecorator {
}
```
门课主要完成两件事:
个类主要完成两件事:
* 在构造器中,它调用`reduceDecorations()`方法。此方法将通过`Stream.reduce()``Function.andThen()`方法链接传递的`Function`数组。结果是由给定的`Function`数组组成的单个`Function`
* 当组合的`Function``apply()`方法从`decorate()`方法调用时,它将逐一应用给定函数的链。由于给定数组中的每一个`Function`都是一个修饰符,因此合成的`Function`将逐个应用每个修饰符。
......@@ -1252,7 +1252,7 @@ sequence.recordSequence(new DeleteCommand(hd));
sequence.runSequence();
```
显然,我们这里有很多*样板*代码。查看命令类。我们真的需要所有这些课程吗?好吧,如果我们意识到`Command`接口实际上是一个函数式接口,那么我们可以删除它的实现并通过 Lambda 提供行为(命令类只是行为块,因此它们可以通过 Lambda 表示),如下所示:
显然,我们这里有很多*样板*代码。查看命令类。我们真的需要所有这些吗?好吧,如果我们意识到`Command`接口实际上是一个函数式接口,那么我们可以删除它的实现并通过 Lambda 提供行为(命令类只是行为块,因此它们可以通过 Lambda 表示),如下所示:
```java
HardDisk hd = new HardDisk();
......
......@@ -1555,7 +1555,7 @@ double sumWeightsKg = melons.stream()
* `reducing​(T identity, BinaryOperator<T> op)`
* `reducing​(U identity, Function<? super T,​? extends U> mapper, BinaryOperator<U> op)`
`reducing()`论点很直截了当。我们有用于减少的`identity`值(以及没有输入元素时返回的值)、应用于每个输入值的映射函数和用于减少映射值的函数。
`reducing()`参数很直截了当。我们有用于减少的`identity`值(以及没有输入元素时返回的值)、应用于每个输入值的映射函数和用于减少映射值的函数。
例如,让我们通过`reducing()`重写前面的代码片段。请注意,我们从 0 开始求和,通过映射函数将其从克转换为千克,并通过 Lambda 减少值(结果千克):
......
......@@ -1057,7 +1057,7 @@ private static class Consumer implements Runnable {
不要混淆`nrOfConsumers``threadGroup.activeCount()``nrOfConsumers`变量存储当前打包灯泡的使用者(线程)的数量,而`threadGroup.activeCount()`表示所有活动使用者(线程),包括那些当前不工作(空闲)并且正等待从缓存中重用或调度的使用者(线程)。
现在,在一个真实的案例中,一个主管将监控装配线,当他们注意到当前数量的消费者无法面对即将到来的涌入时,他们将呼叫更多的消费者加入(最多允许 50 个消费者)。此外,当他们注意到一些消费者只是停留在附近,他们会派遣他们到其他工作。下图是此场景的图形表示:
现在,在一个真实的案例中,一个主管将监控装配线,当他们注意到当前数量的消费者无法面对即将到来的涌入时,他们将调用更多的消费者加入(最多允许 50 个消费者)。此外,当他们注意到一些消费者只是停留在附近,他们会派遣他们到其他工作。下图是此场景的图形表示:
![](img/a7ddcb56-2877-4575-96ed-e3e9b7aef403.png)
......
......@@ -1497,7 +1497,7 @@ List<String> results = cfDownloaded.thenApply(e -> {
`join()`方法类似于`get()`,但是,如果基础`CompletableFuture`异常完成,则抛出非受检异常。
由于我们在所有相关`CompletableFuture`完成后呼叫`join()`,因此没有阻塞点。
由于我们在所有相关`CompletableFuture`完成后调用`join()`,因此没有阻塞点。
返回的`List<String>`包含调用`downloadInvoices()`方法得到的结果,如下所示:
......
......@@ -280,7 +280,7 @@ method.invoke(bookInstance.orElse(null));
# 232 使用当前`Optional`类
有时候,我们想要的只是消费一节课。如果`Optional`不存在,则无需进行任何操作。不熟练的解决方案将依赖于`isPresent()`-`get()`对,如下所示:
有时候,我们想要的只是消费一个类。如果`Optional`不存在,则无需进行任何操作。不熟练的解决方案将依赖于`isPresent()`-`get()`对,如下所示:
```java
// Avoid
......
......@@ -806,7 +806,7 @@ System.out.println("Status code: " + responseOfFile.statusCode());
System.out.println("Body: " + responseOfFile.body());
```
如需指定开放式选项,请呼叫`ofFile(Path file, OpenOption... openOptions)`
如需指定开放式选项,请调用`ofFile(Path file, OpenOption... openOptions)`
# 将响应体作为字节数组处理
......
......@@ -602,7 +602,7 @@ Hello World
我们编辑的文件只包含代码片段,我们删除了大部分行,除了`main`方法的声明,并在其周围插入了类的声明。
在 Java 中,不能像在许多其他语言中那样拥有独立的方法或函数。每个方法都属于某个类,每个类都应该在一个单独的文件中声明(好吧,差不多,但现在,让我们跳过异常)。文件名必须与类名相同。编译器对`public`类要求这样。即使是非公立的课程,我们也通常遵循这个惯例。如果您将文件从`HelloWorld.java`重命名为`Hello.java`,则当您尝试用新名称编译文件时,编译器将显示一个错误:
在 Java 中,不能像在许多其他语言中那样拥有独立的方法或函数。每个方法都属于某个类,每个类都应该在一个单独的文件中声明(好吧,差不多,但现在,让我们跳过异常)。文件名必须与类名相同。编译器对`public`类要求这样。即使是非公共类,我们也通常遵循这个惯例。如果您将文件从`HelloWorld.java`重命名为`Hello.java`,则当您尝试用新名称编译文件时,编译器将显示一个错误:
```java
$ mv HelloWorld.java Hello.java
......@@ -615,9 +615,9 @@ public class HelloWorld {
那么,让我们把它移回原来的名字,也就是,`mv Hello.java HelloWorld.java`
类的声明以`class`关键字开始,然后是类的名称,一个大括号开始,直到匹配的大括号结束。中间的一切都属于班级
类的声明以`class`关键字开始,然后是类的名称,一个大括号开始,直到匹配的大括号结束。中间的一切都属于
现在,让我们跳过为什么我在课堂前写了`public`,重点讨论其中的`main`方法。该方法不返回任何值,因此返回值为`void`。参数,名为`args`,是一个字符串数组。当 JVM 启动`main`方法时,它将命令行参数传递给这个数组中的程序。然而,这次我们没有用。`main`方法包含打印出`Hello World`的行。现在,让我们再检查一下这条线。
现在,让我们跳过为什么我在前写了`public`,重点讨论其中的`main`方法。该方法不返回任何值,因此返回值为`void`。参数,名为`args`,是一个字符串数组。当 JVM 启动`main`方法时,它将命令行参数传递给这个数组中的程序。然而,这次我们没有用。`main`方法包含打印出`Hello World`的行。现在,让我们再检查一下这条线。
在其他语言中,将内容打印到控制台只需要一个`print`语句,或者一个非常类似的命令。我记得有些初级解释器甚至允许我们输入`?`而不是`print`,因为在屏幕上打印是很常见的。这在过去的 40 年里已经发生了很大的变化。我们使用图形屏幕、互联网和许多其他输入和输出通道。现在,在控制台上写东西已经不是很常见了。
......
......@@ -780,7 +780,7 @@ Java 语言中有一些我们不使用的特性。当语言被创建时,这些
类是使用`class`关键字定义的,每个类都必须有一个名称。名称在包中应该是唯一的(请参阅下一节),并且必须与文件名相同。一个类可以实现一个接口或扩展另一个类,我们将在后面看到一个示例。类也可以是`abstract``final``public`。这些是用适当的关键字定义的,您将在下面的示例中看到。
我们的课程有两个班。它们都是`public``public`类可以从任何地方访问。不是`public`的类只在包内可见。内部类和嵌套类也可以`private`仅在文件级定义的顶级类中可见。
我们的项目有两个类。它们都是`public``public`类可以从任何地方访问。不是`public`的类只在包内可见。内部类和嵌套类也可以`private`仅在文件级定义的顶级类中可见。
包含要由 Java 环境调用的`main()`方法的类应该是`public`。这是因为它们是由 JVM 调用的。
......@@ -917,7 +917,7 @@ public static void main(String[] args) {
在其他情况下,当对象是可修改的时,该方法可以有效地处理传递给它的对象。这也是我们示例中的`sort()`方法在数组上的工作方式。同一个数组本身也是一个对象,会被修改。
这种论点的传递比其他语言要简单得多。其他语言允许开发人员混合传递引用和**传递值**参数。在 Java 中,当您单独使用一个变量作为表达式将一个参数传递给一个方法时,您可以确保变量本身不会被修改。但是,如果对象是可变的,则可以修改它。
这种参数的传递比其他语言要简单得多。其他语言允许开发人员混合传递引用和**传递值**参数。在 Java 中,当您单独使用一个变量作为表达式将一个参数传递给一个方法时,您可以确保变量本身不会被修改。但是,如果对象是可变的,则可以修改它。
一个对象是可变的,如果它可以被修改,直接或通过一些方法调用改变它的一些字段的值。当一个类被设计成在对象创建之后没有正常的方式来修改对象的状态时,对象是不可变的。类`Byte``Short``Integer``Long``Float``Double``Boolean``Character`以及`String`在 JDK 中被设计成对象是不可变的。使用反射可以克服某些类的不变性实现的限制,但这样做是黑客行为,而不是专业的编码。这样做的目的只有一个,即更好地了解和理解一些 Java 类的内部工作原理,而不是别的。
......
......@@ -68,7 +68,7 @@ $ mvn -Dtest=SimplestStringListSortTest test
但是,如果我们想要对包含`Student`对象的列表进行排序,`Student`类必须实现`Comparable`接口。但是等等!你如何比较两个学生?按姓名、年龄或平均分数?
把一个学生和另一个学生作比较并不是这门课的基本特征。每个类或包、库或编程单元都应该有一个职责,它应该只实现这个职责,而不实现其他职责。这并不确切。这不是数学。有时,很难判断一个特性是否适合这个职责。可比性可能是某些数据类型的固有特征,例如`Integer``Double`。其他类没有这种固有的比较特性。
把一个学生和另一个学生作比较并不是这个类的基本特征。每个类或包、库或编程单元都应该有一个职责,它应该只实现这个职责,而不实现其他职责。这并不确切。这不是数学。有时,很难判断一个特性是否适合这个职责。可比性可能是某些数据类型的固有特征,例如`Integer``Double`。其他类没有这种固有的比较特性。
有一些简单的技术可以确定特性是否应该是类的一部分。例如,对于一个学生,你可以问真人他们的名字和年龄,他们也可以告诉你他们的平均分。如果你让他们中的一个去`compareTo`(另一个学生),因为`Comparable`接口需要这个方法,他们很可能会问,“用什么属性或者怎么做?”如果他们不是有礼貌的类型,他们可以简单地回答“什么?”(更不用说缩写 WTF,它代表一周的最后三个工作日,在这种情况下很流行。)在这种情况下,您可能会怀疑实现该特性可能不在该类及其关注的领域;比较应该与原始类的实现分离开来。这也称为**关注点分离**,与 SRP 密切相关。
......@@ -492,7 +492,7 @@ sort.setSwapper(new SwapActualNamesArrayElements());
sort.sort(names);
```
测试的最后但最重要的部分是断言结果是我们期望的结果。JUnit 在`Assert`课程的帮助下帮助我们做到这一点:
测试的最后但最重要的部分是断言结果是我们期望的结果。JUnit 在`Assert`的帮助下帮助我们做到这一点:
```java
Assert.assertEquals(List.of(
......@@ -594,7 +594,7 @@ public class ArrayListSortable implements Sortable {
这个类封装了`ArrayList`,然后实现了`gets``size`方法对`ArrayList`的访问。`ArrayList`本身声明为`final`。回想一下,`final`字段必须在构造器完成时定义。这保证了当我们开始使用对象时字段就在那里,并且在对象生存期内它不会改变。然而,注意,对象的内容,在这种情况下,`ArrayList`的元素可以改变。如果不是这样的话,我们就无法整理它。
下一节课`StringComparator`。这非常简单,我不在这里列出它;我将把它留给您来实现可以比较两个`Strings``java.util.Comparator`接口。这应该不难,特别是因为这个类已经是以前版本的`BubbleSortTest`类的一部分(提示这是一个匿名类,我们存储在名为`stringCompare`的变量中)。
下一个类`StringComparator`。这非常简单,我不在这里列出它;我将把它留给您来实现可以比较两个`Strings``java.util.Comparator`接口。这应该不难,特别是因为这个类已经是以前版本的`BubbleSortTest`类的一部分(提示这是一个匿名类,我们存储在名为`stringCompare`的变量中)。
我们还必须实现`ArrayListSwapper`,这也不应该是一个很大的惊喜:
......
......@@ -55,7 +55,7 @@ RGBY 4/0
当我们用面向对象的思想开发一段代码时,我们会尝试对真实世界建模,并将真实世界的对象映射到程序中的对象。你肯定听过面向对象的解释,用非常典型的几何物体的例子,或者用汽车和马达的东西来解释组成。就我个人而言,我认为这些例子太简单了,无法得到很好的理解。他们可能是好的开始,但我们已经在这本书的第四章。策划者的游戏好多了。它比矩形和三角形要复杂一些,但没有电信计费应用或原子能发电厂控制那么复杂。
在这个游戏中,我们有哪些真实世界的物体?我们有一张桌子,我们有不同颜色的别针。我们当然需要两个 Java 类。桌子里有什么?每行有四个位置。也许我们需要一节课。表将有行。我们还需要一些隐藏秘密的东西。这也可以是一行,并且每行还可以保存关于有多少位置和多少颜色匹配的信息。在秘密行的情况下,这个信息是明显的 -4 和 0。
在这个游戏中,我们有哪些真实世界的物体?我们有一张桌子,我们有不同颜色的别针。我们当然需要两个 Java 类。桌子里有什么?每行有四个位置。也许我们需要一个类。表将有行。我们还需要一些隐藏秘密的东西。这也可以是一行,并且每行还可以保存关于有多少位置和多少颜色匹配的信息。在秘密行的情况下,这个信息是明显的 -4 和 0。
什么是别针?每个别针都有一种颜色,通常就是它。除了可以插入桌子上的孔之外,没有其他的销钉的特性,但这是我们不会建模的真实特性。基本上,别针是一种颜色,而不是别的。这样,我们可以在早期就从模型中消除别针类,甚至在我们用 Java 创建别针类之前。相反,我们有颜色。
......@@ -1079,7 +1079,7 @@ public class PrettyPrintRow {
}
```
这是这个的核心。当一种颜色要打印时,它会得到一个指定的字母,除非它已经有了一个。由于在 JVM 中运行的每个游戏中包含分配的`Map`将使用相同的映射,因此新的`Game`被启动。它分配新的`Color`对象,很快就会用完我们在`String`常量中分配的六个字符。
这是这个的核心。当一种颜色要打印时,它会得到一个指定的字母,除非它已经有了一个。由于在 JVM 中运行的每个游戏中包含分配的`Map`将使用相同的映射,因此新的`Game`被启动。它分配新的`Color`对象,很快就会用完我们在`String`常量中分配的六个字符。
如果`Game`实例并行运行,那么我们的麻烦就更大了。这个类根本不是线程安全的。如果两个线程同时调用同一个`Color`实例的`colorToChar`方法(这不太可能,因为每个`Game`都使用自己的颜色,但请注意,编程中的**不太可能**非常像墓碑上有名的最后一句话),那么两个线程可能都会看到此时没有为颜色分配字母同时,两者都会指定字母(相同的字母或两个不同的字母,取决于运气)并增加计数器一到两次。至少,我们可以说,执行是不确定的。
......
......@@ -410,7 +410,7 @@ public boolean isUnique() {
如您所见,缓存可能会变得复杂。要专业地做到这一点,最好使用一些现成的缓存实现。我们在这里使用的缓存只是冰山一角。或者,它甚至只是在它身上瞥见的阳光。
其余的课程都相当标准,我们已经详细讨论了一些内容——对您的知识的一个很好的检查就是理解`equals()``hashCode()``toString()`方法是如何以这种方式实现的。我实现了`toString()`方法来帮助我进行调试,但它也被用于接下来的示例输出中。方法如下:
其余的都相当标准,我们已经详细讨论了一些内容——对您的知识的一个很好的检查就是理解`equals()``hashCode()``toString()`方法是如何以这种方式实现的。我实现了`toString()`方法来帮助我进行调试,但它也被用于接下来的示例输出中。方法如下:
```java
@Override
......@@ -612,7 +612,7 @@ t1.start();
# `ExecutorService`
`ExecutorService`是 JDK 中的一个接口。接口的实现可以异步执行一个`Runnable``Callable`类。接口只定义实现的 API,不要求调用是异步的。实际上,这就是为什么我们使用这样的服务。以同步方式调用`Runnable`接口的`run`方法只是调用一个方法。我们不需要特殊的课程
`ExecutorService`是 JDK 中的一个接口。接口的实现可以异步执行一个`Runnable``Callable`类。接口只定义实现的 API,不要求调用是异步的。实际上,这就是为什么我们使用这样的服务。以同步方式调用`Runnable`接口的`run`方法只是调用一个方法。我们不需要特殊的
`Runnable`接口定义了一个`run`方法。它没有参数,不返回值,也不引发异常。`Callable`接口是参数化的,它定义的唯一方法`call`没有参数,但返回泛型值,还可能抛出`Exception`。在代码中,如果我们只想运行某个东西,我们就实现了`Runnable`,如果我们想返回某个东西,我们就实现了`Callable`。这两个接口都是函数式接口;因此,它们是使用 Lambda 实现的很好的候选接口。
......@@ -666,7 +666,7 @@ public class ThreadIntermingling {
如果线程在启动前被设置为守护进程(调用`setDaemon(true)`),那么它就是守护线程。一个自动成为启动它的守护线程的线程也是守护线程。当所有其他线程都完成并且 JVM 想要完成时,守护线程被 JVM 停止。JVM 本身执行的一些线程是守护线程,但是在应用中创建守护线程可能没有实际用途。
不关闭服务只会阻止 JVM 停止。在`main`方法完成后,代码将挂起。为了告诉`ExecutorService`不需要它拥有的线程,我们必须`shutdown`服务。呼叫只会启动关机并立即返回。在这种情况下,我们不想等待。无论如何,JVM 都会这样做。如果我们需要等待,我们将不得不调用`awaitTermination`
不关闭服务只会阻止 JVM 停止。在`main`方法完成后,代码将挂起。为了告诉`ExecutorService`不需要它拥有的线程,我们必须`shutdown`服务。调用只会启动关机并立即返回。在这种情况下,我们不想等待。无论如何,JVM 都会这样做。如果我们需要等待,我们将不得不调用`awaitTermination`
# `CompletableFuture`
......@@ -929,7 +929,7 @@ public class SynchronizedDemo implements Runnable {
使用者在循环中使用队列中的对象,并且只有在队列中没有可读取的内容时才调用`wait`。当生产者调用`notifyAll`时,没有消费者等待,通知被忽略。它飞走了,但这不是问题;消费者没有等待。当消费者消费了一个对象并调用了`notifyAll`,并且没有生产者等待时,情况也是一样的。这不是问题。
消费者消费,呼叫`notifyAll`,在通知悬而未决后,找不到等待的生产者,生产者就开始等待,这是不可能发生的。这不可能发生,因为整个代码都在一个`synchronized`块中,它确保没有生产者在关键部分。这就是为什么只有在获取`Object`类的锁时才能调用`wait``notify``notifyAll`的原因。
消费者消费,调用`notifyAll`,在通知悬而未决后,找不到等待的生产者,生产者就开始等待,这是不可能发生的。这不可能发生,因为整个代码都在一个`synchronized`块中,它确保没有生产者在关键部分。这就是为什么只有在获取`Object`类的锁时才能调用`wait``notify``notifyAll`的原因。
如果有许多使用者执行相同的代码,并且他们同样擅长使用对象,那么调用`notify`而不是`notifyAll`就是一种优化。在这种情况下,`notifyAll`只会唤醒所有使用者线程。然而,只有幸运的人才会意识到他们被吵醒了;其他人会看到其他人已经逃脱了诱饵。
......
......@@ -755,7 +755,7 @@ Guice 足够聪明,您不必指定任何需要`Table`的地方,我们都将
# `MastermindHandler`类
我们已经开始列出`MastermindHandler`类,因为这个类有一百多行,所以我不把它作为一个整体包括在这里。这门课最重要的方法是`handle`
我们已经开始列出`MastermindHandler`类,因为这个类有一百多行,所以我不把它作为一个整体包括在这里。这个类最重要的方法是`handle`
```java
public void handle(HttpServletRequest request,
......@@ -1043,7 +1043,7 @@ Java 有几种可用的日志框架,每种都有优点和缺点。`java.util.l
日志框架通常使用配置文件进行配置。配置可能会限制日志记录,关闭某些级别。在正常的操作环境中,前三级通常是开启的,`INFO``DEBUG``TRACE`在真正需要时开启。也可以只为某些记录器打开和关闭某些级别。如果我们知道错误肯定在`GameSessionSaver`类中,那么我们可以为该类打开`DEBUG`级别。
日志文件还可能包含我们没有直接编码的其他信息,打印到标准输出时会非常麻烦。通常,每条日志消息都包含创建消息的精确时间、记录器的名称,在许多情况下,还包含线程的标识符。想象一下,如果你被迫把所有这些都放到每一个论点中,你很可能很快就会写一些额外的类来做这件事。不要!它已经做了专业它是记录器框架。
日志文件还可能包含我们没有直接编码的其他信息,打印到标准输出时会非常麻烦。通常,每条日志消息都包含创建消息的精确时间、记录器的名称,在许多情况下,还包含线程的标识符。想象一下,如果你被迫把所有这些都放到每一个参数中,你很可能很快就会写一些额外的类来做这件事。不要!它已经做了专业它是记录器框架。
记录器还可以配置为将消息发送到不同的位置。登录到控制台只是一种可能性。日志框架准备将消息发送到文件、数据库、Windows 事件记录器、SysLog 服务或任何其他目标。这种灵活性,即打印哪条消息、打印哪些额外信息以及打印到哪里,是通过按照单一责任原则将记录器框架执行的不同任务分为几个类来实现的。
......
......@@ -413,7 +413,7 @@ depth=18
`properties`文件用于存储简单的配置值,并且几乎只用于包含特定于语言的常量。这是一个非常扭曲的用法,因为`properties`文件是 **ISO Latin-1** 编码的文件,如果您需要使用一些特殊的 UTF-8 字符,您必须使用`uXXXX`格式或使用 native2ascii 转换器程序来键入它们。不能简单地将它们保存为 UTF-8。不过,这是该格式用于程序国际化的特定于语言的字符串的文件(也缩写为 i18n,因为国际化一词的起始 i 和最后 n 之间有 18 个字符)。
为了得到`Properties`对象,我们必须读取项目中的文件,并将它们打包成 JAR 文件。春季班`PathMatchingResourcePatternResolver`帮助我们这样做。
为了得到`Properties`对象,我们必须读取项目中的文件,并将它们打包成 JAR 文件。Spring 类`PathMatchingResourcePatternResolver`帮助我们这样做。
天哪,是的,我知道!当我们使用 Spring 时,我们必须习惯这些长名称。无论如何,这种长而描述性的名称在企业环境中被广泛使用,并且需要它们来解释类的功能。
......
......@@ -464,7 +464,7 @@ public class Checker {
通常,一个好的框架在逻辑上工作。我不知道Spring的这个特征,但我认为这是合乎逻辑的,而且神奇地,它起作用了。如果事情是合乎逻辑的,并且只是工作的话,你不需要阅读和记住文档。不过,稍微小心一点也不会有任何危害。在我意识到这个功能是这样工作的之后,我在文档中查阅了它,以看到这确实是 Spring 的一个保证特性,而不是仅仅发生在工作中的特性,而是在未来版本中可能会发生更改而不需要注意。仅使用保证功能是非常重要的,但在我们的行业中经常被忽略。
调用`isConsistent()`方法时,首先将产品信息收集到`HashMap`中,为每个`OrderItem`分配一个`ProductInformation`实例。这是在一个单独的班级里完成的。在此之后,`ProductsCheckerCollector`收集一个或多个产品项所需的`ConsistencyChecker`实例。当我们拥有这个集合时,我们只需要调用那些用这个集合中的注解之一进行注解的检查器。我们循环着做。
调用`isConsistent()`方法时,首先将产品信息收集到`HashMap`中,为每个`OrderItem`分配一个`ProductInformation`实例。这是在一个单独的里完成的。在此之后,`ProductsCheckerCollector`收集一个或多个产品项所需的`ConsistencyChecker`实例。当我们拥有这个集合时,我们只需要调用那些用这个集合中的注解之一进行注解的检查器。我们循环着做。
在这段代码中,我们使用反射。我们循环每个检查器都有的注解。为了获取注解集合,我们调用`checker.getClass().getAnnotations()`。此调用返回对象集合。每个对象都是一些 JDK 运行时生成的类的实例,这些类实现了我们在其源文件中声明为注解的接口。但是,没有保证动态创建的类只实现我们的`@interface`,而不是其他接口。因此,要获得实际的注解类,必须调用`annotationType()`方法。
......@@ -885,7 +885,7 @@ return order.getItems().stream()
可以说,`Map`接口以静态方式将键映射到数据结构中的值,流方法`map()`动态地将一种值映射到另一种(或相同)类型的值。
我们已经看到可以以 Lambda 表达式的形式提供函数式接口的实例。此参数不是 Lambda 表达式。这是一个方法引用。它说`map()`方法应该调用`Map piMap`上的`get()`方法,使用实际的流元素作为参数。我们很幸运`get()`也需要一个论点,不是吗?我们也可以这样写:
我们已经看到可以以 Lambda 表达式的形式提供函数式接口的实例。此参数不是 Lambda 表达式。这是一个方法引用。它说`map()`方法应该调用`Map piMap`上的`get()`方法,使用实际的流元素作为参数。我们很幸运`get()`也需要一个参数,不是吗?我们也可以这样写:
```java
.map( orderItem ->piMap.get(orderItem))
......
......@@ -184,7 +184,7 @@ JDK 中有一个现成的`Publisher`类,我们将在后面讨论。当调用`P
将订阅管理为一个抽象可以想象为一个复杂的任务,但是在反应流的情况下,它非常简单。订阅者所能做和应该做的就是设置它当前可以接收的项目数,并且可以取消订阅。
为什么`Publisher`要回调`Subscriber``onSubscribe`方法?为什么它不直接返回订阅或者抛出一些错误呢?产生这种复杂行为的原因是,调用`subscribe()`方法的可能不是`Subscriber`。就像在现实生活中一样,我可以订阅一本杂志并支付一年的费用作为圣诞礼物。(这是我写这部分书的季节)在我们的代码中,一些负责向谁通知某些数据更改的布线组件呼叫`subscribe`,而不一定是用户。`Subscriber`只负责订户应该负责的最小的事情。另一个原因是整个方法是异步的。当我们订阅某些东西时,订阅可能不会立即可用并准备就绪。可能有一些长时间运行的进程需要在订阅可用之前完成,而调用`subscribe`的调用方不需要等待进程完成。当订阅准备就绪时,它将被传递给订阅服务器,传递给真正需要它的实体。
为什么`Publisher`要回调`Subscriber``onSubscribe`方法?为什么它不直接返回订阅或者抛出一些错误呢?产生这种复杂行为的原因是,调用`subscribe()`方法的可能不是`Subscriber`。就像在现实生活中一样,我可以订阅一本杂志并支付一年的费用作为圣诞礼物。(这是我写这部分书的季节)在我们的代码中,一些负责向谁通知某些数据更改的布线组件调用`subscribe`,而不一定是用户。`Subscriber`只负责订户应该负责的最小的事情。另一个原因是整个方法是异步的。当我们订阅某些东西时,订阅可能不会立即可用并准备就绪。可能有一些长时间运行的进程需要在订阅可用之前完成,而调用`subscribe`的调用方不需要等待进程完成。当订阅准备就绪时,它将被传递给订阅服务器,传递给真正需要它的实体。
`Subscriber`接口定义了`onSubscribe()``onError()`(我们已经讨论过)、`onComplete()``onNext()`方法。
......@@ -276,7 +276,7 @@ public class InventoryItem {
请注意,例如,在 Groovy 中,我们可以简单地使用一个`Long d`变量并在闭包内更改该变量。可以说,Groovy 将 Lambda 调用为闭包。在 Java 中,我们不能这样做,因为我们可以从方法内部访问的变量应该是有效的`final`。然而,这只不过是属于闭包环境的更显式的表示法。`ClosureData d`对象是`final`,与类具有的字段相反,可以在 Lambda 中修改该字段。
本章中我们真正感兴趣的最有趣的课程`InventoryKeeper`。此类实现了`Subscriber`接口,能够使用订单维护库存:
本章中我们真正感兴趣的最有趣的`InventoryKeeper`。此类实现了`Subscriber`接口,能够使用订单维护库存:
```java
package packt.java11.mybusiness.inventory;
......@@ -330,7 +330,7 @@ public class InventoryKeeper implements Flow.Subscriber<Order> {
}
```
订阅对象后调用`onSubscribe()`方法。订阅将传递给对象,并存储在字段中。由于用户在以后的呼叫中需要这个订阅,所以当处理传入`onNext`的项目并且可以接受新的项目时,字段是存储这个对象的好地方。在这个方法中,我们还将初始请求设置为三项。实际值只是说明性的。企业环境应该能够配置这样的参数:
订阅对象后调用`onSubscribe()`方法。订阅将传递给对象,并存储在字段中。由于用户在以后的调用中需要这个订阅,所以当处理传入`onNext`的项目并且可以接受新的项目时,字段是存储这个对象的好地方。在这个方法中,我们还将初始请求设置为三项。实际值只是说明性的。企业环境应该能够配置这样的参数:
```java
private ExecutorService service =
......
......@@ -516,7 +516,7 @@ try(InputStream is = InputOutputStream.class.getResourceAsStream("/hello.txt")){
`ObjectInputStream`类的方法集比任何其他`InputStream`实现的方法集大得多。原因是它是围绕读取对象字段的值构建的,对象字段可以是各种类型的。为了使`ObjectInputStream`能够从输入的数据流构造一个对象,该对象必须是*可反序列化的*,这意味着它首先必须是*可序列化的*,可以转换成字节流。通常,这样做是为了通过网络传输对象。在目标位置,序列化对象被反序列化,原始对象的值被还原。
基本类型和大多数 Java 类,包括`String`类和基本类型包装器,都是可序列化的。如果类具有自定义类型的字段,则必须通过实现`java.io.Serizalizable`使其可序列化。怎么做不在这本书的范围之内。现在,我们只使用可序列化类型。我们来看看这门课
基本类型和大多数 Java 类,包括`String`类和基本类型包装器,都是可序列化的。如果类具有自定义类型的字段,则必须通过实现`java.io.Serizalizable`使其可序列化。怎么做不在这本书的范围之内。现在,我们只使用可序列化类型。我们来看看这个类
```java
class SomeClass implements Serializable {
......
......@@ -858,7 +858,7 @@ for(String e: list){
</dependency>
```
`ObjectUtils`的所有方法可分为七组:
`ObjectUtils`的所有方法可分为七组:
* 对象克隆方法
* 比较两个对象的方法
......@@ -885,7 +885,7 @@ for(String e: list){
# `LocalDate`类
班级`LocalDate`不带时间。它表示 ISO 8601 格式的日期(YYYY-MM-DD):
`LocalDate`不带时间。它表示 ISO 8601 格式的日期(YYYY-MM-DD):
```java
System.out.println(LocalDate.now()); //prints: 2019-03-04
......
......@@ -395,7 +395,7 @@ Apache Commons 项目包括以下三个部分:
* `ThreadUtils`类:查找当前正在运行的线程的信息
* `Validate`类:验证单个值和集合,比较它们,检查`null`,匹配,并执行许多其他验证
* `RandomStringUtils`类:根据不同字符集的字符生成`String`对象
* `StringUtils`课堂:我们在第 5 章中讨论了“字符串、输入/输出和文件”
* `StringUtils`:我们在第 5 章中讨论了“字符串、输入/输出和文件”
# `collections4`
......
......@@ -1054,7 +1054,7 @@ mps.play();
所有效果共享父级-抽象类`Effect`。`Node`类具有`setEffect(Effect e)`方法,这意味着可以将任何效果添加到任何节点。这是将效果应用于节点的主要方式,演员在舞台上产生场景(如果我们回想一下本章开头介绍的类比)
唯一的例外是`Blend`效果,这使得它的使用比其他效果的使用更加复杂。除了使用`setEffect(Effect e)`方法外,一些`Node`班的孩子还有`setBlendMode(BlendMode bm)`方法,可以调节图像重叠时如何相互融合。因此,可以以不同的方式设置不同的混合效果,以相互覆盖,并产生可能难以调试的意外结果。这就是为什么`Blend`效果的使用更加复杂,这就是为什么我们要开始概述`Blend`效果如何使用的原因。
唯一的例外是`Blend`效果,这使得它的使用比其他效果的使用更加复杂。除了使用`setEffect(Effect e)`方法外,一些`Node`类的子项还有`setBlendMode(BlendMode bm)`方法,可以调节图像重叠时如何相互融合。因此,可以以不同的方式设置不同的混合效果,以相互覆盖,并产生可能难以调试的意外结果。这就是为什么`Blend`效果的使用更加复杂,这就是为什么我们要开始概述`Blend`效果如何使用的原因。
有四个方面可以控制两个图像重叠区域的外观(我们在示例中使用两个图像使其更简单,但实际上,许多图像可以重叠):
......
......@@ -227,7 +227,7 @@ public static interface Flow.Processor<T,R>
`Flow.Processor`接口描述了一个既可以充当订阅者又可以充当发布者的实体。它允许创建此类处理器的链(管道),以便订阅者可以从发布者接收项目,对其进行转换,然后将结果传递给下一个订阅者或处理器。
在推送模式中,发布者可以在没有来自订户的任何请求的情况下呼叫`onNext()`。如果处理速度低于项目发布速度,订阅者可以使用各种策略来缓解压力。例如,它可以跳过项目或为临时存储创建一个缓冲区,希望项目生产速度会减慢,订户能够赶上
在推送模式中,发布者可以在没有来自订户的任何请求的情况下调用`onNext()`。如果处理速度低于项目发布速度,订阅者可以使用各种策略来缓解压力。例如,它可以跳过项目或为临时存储创建一个缓冲区,希望项目生产速度会减慢,订户能够赶上
这是 ReactiveStreams 计划为支持具有非阻塞背压的异步数据流而定义的最小接口集。如您所见,它允许订阅者和发布者相互交谈并协调传入数据的速率,从而为我们在“反应式”部分讨论的背压问题提供了多种解决方案。
......@@ -859,7 +859,7 @@ Observable.error(new RuntimeException("MyException"))
这些操作符在管道中任何位置发生的特定事件上都被调用。它们的工作方式类似于“处理”部分中描述的操作符。
这些操作符的格式是`doXXX()`,其中`XXX`是事件的名称:`onComplete``onNext``onError`等。并不是所有的课程都有,有些课程`Observable``Flowable``Single``Maybe``Completable`上略有不同。但是,我们没有空间列出所有这些类的所有变体,我们的概述将局限于`Observable`类的生命周期事件处理操作符的几个示例:
这些操作符的格式是`doXXX()`,其中`XXX`是事件的名称:`onComplete``onNext``onError`等。并不是所有的类都有,有些类`Observable``Flowable``Single``Maybe``Completable`上略有不同。但是,我们没有空间列出所有这些类的所有变体,我们的概述将局限于`Observable`类的生命周期事件处理操作符的几个示例:
* `doOnSubscribe(Consumer<Disposable> onSubscribe)`:当观察者订阅时执行
* `doOnNext(Consumer<T> onNext)`:当源可观测调用`onNext`时,应用提供的`Consumer`功能
......
......@@ -467,7 +467,7 @@ public interface Inner {
1. `SamplePackage.OuterPackage`的解析开始。
2. 处理`SamplePackage.OuterPackage.Nested`导入。
3. `SamplePackage.Outer.Nested`的决议开始。
3. `SamplePackage.Outer.Nested`的决议开始。
4. 内部接口是经过类型检查的,但由于此时它不在范围内,因此无法解析内部接口。
5. `SamplePackage.Thing`的解析开始。此步骤包括将`SamplePackage.Thing`的所有成员类型导入范围。
......
......@@ -380,7 +380,7 @@ JRE 和 JDK 映像之间不再有区别。在当前的 Java 平台上,JDK 映
# 保留现有行为
JEP-220 的最终目标是确保现有的课程不会受到负面影响。这是指不依赖于内部 JDK 或 JRE 运行时映像的应用。
JEP-220 的最终目标是确保现有的不会受到负面影响。这是指不依赖于内部 JDK 或 JRE 运行时映像的应用。
# 模块系统
......
......@@ -238,7 +238,7 @@ public class ProcessLister {
这是因为某些操作系统没有实现优雅的进程终止特性。在这种情况下,`destroy()`的实现与调用`destroyForcefully()`相同。接口`ProcessHandle`的系统特定实现必须实现方法`supportsNormalTermination()`,只有当实现支持正常(非强制)进程终止时,才应该是`true`。对于实际实现中的所有调用,该方法应返回相同的值,并且在执行 JVM 实例期间不应更改返回值。不需要多次调用该方法。
下面的示例演示了进程启动、进程终止和等待进程终止。在我们的示例中,我们使用两个类。第一节课演示了`.sleep()`方法:
下面的示例演示了进程启动、进程终止和等待进程终止。在我们的示例中,我们使用两个类。第一个类演示了`.sleep()`方法:
```java
public class WaitForChildToBeTerminated {
......
......@@ -289,7 +289,7 @@ stackwalker/packt.Main.simpleCall(Main.java:12)
stackwalker/packt.Main.main(Main.java:8)
```
此输出仅包含属于代码中的调用的帧。`main()`方法调用`simpleCall()`,后者调用`reflectCall()`,后者依次调用`lambdaCall()`,后者调用 Lambda 表达式,后者调用`walk()`,依此类推。我们没有指定任何选项的事实并没有从栈中删除 Lambda 调用。我们执行了那个呼叫,所以它一定在那里。它删除的是 JVM 实现 Lambda 所需的额外栈帧。我们可以在下一个输出中看到,当选项为`SHOW_REFLECT_FRAMES`时,反射帧已经存在:
此输出仅包含属于代码中的调用的帧。`main()`方法调用`simpleCall()`,后者调用`reflectCall()`,后者依次调用`lambdaCall()`,后者调用 Lambda 表达式,后者调用`walk()`,依此类推。我们没有指定任何选项的事实并没有从栈中删除 Lambda 调用。我们执行了那个调用,所以它一定在那里。它删除的是 JVM 实现 Lambda 所需的额外栈帧。我们可以在下一个输出中看到,当选项为`SHOW_REFLECT_FRAMES`时,反射帧已经存在:
```java
stackwalker/packt.Main.reflect(Main.java:58)
......
......@@ -1321,7 +1321,7 @@ public class OldSchool {
![](img/8d2afd93-4226-4018-a9b1-d009b4bd9362.png)
旧学校班级产
`OldSchool`类的输
不幸的是,这段代码非常冗长。我们在静态初始化器块中填充集合,而不是使用字段初始化器。还有其他方法填充我们的列表,但它们都比应该的更冗长。这些其他方法还有其他问题,比如需要创建额外的类、使用晦涩的代码和隐藏的引用。
......@@ -1386,9 +1386,9 @@ MacOSX`com.apple.eawt`包是一个内部 API,从 Java9 开始,就不能再
现代 Java 平台包括增强的方法句柄,作为改进以下列出的类的一种方法,以便通过改进的优化简化常见用法:
* `MethodHandle`
* `MethodHandles`
* `MethodHandles.Lookup`
* `MethodHandle`
* `MethodHandles`
* `MethodHandles.Lookup`
前面的类都是`java.lang.invoke`包的一部分,该包已针对现代 Java 平台进行了更新。这些改进是通过使用`MethodHandle`组合、`for`循环和`try...finally`块的查找细化实现的。
......
......@@ -246,7 +246,7 @@ Java 平台最近得到了增强,以改进并发性的使用。在本节中,
# Java 线程
Java 中的线程是一个程序执行,内置在 JVM 中。`Thread`类是`java.lang`包(`java.lang.Thread`的一部分。线程具有控制 JVM 执行它们的顺序的优先级。虽然概念很简单,但实现却不简单。让我们先来仔细看看`Thread`课程
Java 中的线程是一个程序执行,内置在 JVM 中。`Thread`类是`java.lang`包(`java.lang.Thread`的一部分。线程具有控制 JVM 执行它们的顺序的优先级。虽然概念很简单,但实现却不简单。让我们先来仔细看看`Thread`
`Thread`类有一个嵌套类:
......
......@@ -602,7 +602,7 @@ public final class String extends Object implements
# `java.text`包
`Bidi``BreakIterator``Normalizer`类的应用不如`Character``String`类广泛。以下是这些课程的简要概述:
`Bidi``BreakIterator``Normalizer`类的应用不如`Character``String`类广泛。以下是这些的简要概述:
这是`Bidi`类:
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册