Skip to content

Commit

Permalink
16-19章 纠错 (lingcoder#443)
Browse files Browse the repository at this point in the history
* 第15章 异常: 错别字更改

update typos

* 第19章 类型信息 纠错

第19章 类型信息 纠错

* 第17章 文件 纠错

第17章 文件 纠错

* 第16章 代码校验 纠错

第16章 代码校验 纠错
  • Loading branch information
unclesesame committed Apr 26, 2020
1 parent 94f4e80 commit 5d7462a
Show file tree
Hide file tree
Showing 3 changed files with 13 additions and 13 deletions.
6 changes: 3 additions & 3 deletions docs/book/16-Validating-Your-Code.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ Cleaning up 4

**gradlew test**

尽管可以用最简单的方法,如 **CountedListTest.java** 所示没那样,JUnit 还包括大量的测试结构,你可以到[官网](junit.org)上学习它们。
尽管可以用最简单的方法,如 **CountedListTest.java** 所示那样,JUnit 还包括大量的测试结构,你可以到[官网](junit.org)上学习它们。

JUnit 是 Java 最流行的单元测试框架,但也有其它可以替代的。你可以通过互联网发现更适合的那一个。

Expand Down Expand Up @@ -1240,7 +1240,7 @@ public class SLF4JLogging {
**Aug 16, 2016 5:40:31 PM InfoLogging main**
**INFO: hello logging**

日志系统会检测日志消息处所在的的类名和方法名。 但它不能保证这些名称是正确的,所以不要纠结于其准确性。
日志系统会检测日志消息处所在的类名和方法名。 但它不能保证这些名称是正确的,所以不要纠结于其准确性。

### 日志等级

Expand Down Expand Up @@ -1654,7 +1654,7 @@ N:数组的大小:**10^(2*k)**,通常来说,**k=1..7** 足够来练习
Q:setter 的操作成本
这个 C/P/N/Q 模型在早期 JDK 8 的 Lambda 开发期间付出水面,大多数并行的 Stream 操作(**parallelSetAll()** 也基本相似)都满足这些结论:**N*Q**(主要工作量)对于并发性能尤为重要。并行算法在工作量较少时可能实际运行得更慢。
这个 C/P/N/Q 模型在早期 JDK 8 的 Lambda 开发期间浮出水面,大多数并行的 Stream 操作(**parallelSetAll()** 也基本相似)都满足这些结论:**N*Q**(主要工作量)对于并发性能尤为重要。并行算法在工作量较少时可能实际运行得更慢。
在一些情况下操作竞争如此激烈使得并行毫无帮助,而不管 **N*Q** 有多大。当 **C** 很大时,**P** 就变得不太相关(内部并行在大量的外部并行面前显得多余)。此外,在一些情况下,并行分解会让相同的 **C** 个客户端运行得比它们顺序运行代码更慢。
Expand Down
10 changes: 5 additions & 5 deletions docs/book/17-Files.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# 第十七章 文件
>在丑陋的 Java I/O 编程方式诞生多年以后,Java终于简化了文件读写的基本操作。
这种"困难方式"的全部细节都在 [Appendix: I/O Streams](./Appendix-IO-Streams.md)。如果你读过这个部分,就会认同 Java 设计者毫不在意他们的使用者的体验这一观念。打开并读取文件对于大多数编程语言来是非常常用的,由于 I/O 糟糕的设计以至于
这种"困难方式"的全部细节都在 [Appendix: I/O Streams](./Appendix-IO-Streams.md)。如果你读过这个部分,就会认同 Java 设计者毫不在意他们的使用者的体验这一观念。打开并读取文件对于大多数编程语言来说是非常常用的,由于 I/O 糟糕的设计以至于
很少有人能够在不依赖其他参考代码的情况下完成打开文件的操作。

好像 Java 设计者终于意识到了 Java 使用者多年来的痛苦,在 Java7 中对此引入了巨大的改进。这些新元素被放在 **java.nio.file** 包下面,过去人们通常把 **nio** 中的 **n** 理解为 **new** 即新的 **io**,现在更应该当成是 **non-blocking** 非阻塞 **io**(**io**就是*input/output输入/输出*)。**java.nio.file** 库终于将 Java 文件操作带到与其他编程语言相同的水平。最重要的是 Java8 新增的 streams 与文件结合使得文件操作编程变得更加优雅。我们将看一下文件操作的两个基本组件:
Expand Down Expand Up @@ -124,7 +124,7 @@ true
我已经在这一章第一个程序的 **main()** 方法添加了第一行用于展示操作系统的名称,因此你可以看到不同操作系统之间存在哪些差异。理想情况下,差别会相对较小,并且使用 **/** 或者 **\\** 路径分隔符进行分隔。你可以看到我运行在Windows 10 上的程序输出。

**toString()** 方法生成完整形式的路径,你可以看到 **getFileName()** 方法总是返回当前文件名。
通过使用 **Files** 工具类(我们接下类将会更多地使用它),可以测试一个文件是否存在,测试是否是一个"普通"文件还是一个目录等等。"Nofile.txt"这个示例展示我们描述的文件可能并不在指定的位置;这样可以允许你创建一个新的路径。"PathInfo.java"存在于当前目录中,最初它只是没有路径的文件名,但它仍然被检测为"存在"。一旦我们将其转换为绝对路径,我们将会得到一个从"C:"盘(因为我们是在Windows机器下进行测试)开始的完整路径,现在它也拥有一个父路径。“真实”路径的定义在文档中有点模糊,因为它取决于具体的文件系统。例如,如果文件名不区分大小写,即使路径由于大小写的缘故而不是完全相同,也可能得到肯定的匹配结果。在这样的平台上,**toRealPath()** 将返回实际情况下的 **Path**,并且还会删除任何冗余元素。
通过使用 **Files** 工具类(我们接下来将会更多地使用它),可以测试一个文件是否存在,测试是否是一个"普通"文件还是一个目录等等。"Nofile.txt"这个示例展示我们描述的文件可能并不在指定的位置;这样可以允许你创建一个新的路径。"PathInfo.java"存在于当前目录中,最初它只是没有路径的文件名,但它仍然被检测为"存在"。一旦我们将其转换为绝对路径,我们将会得到一个从"C:"盘(因为我们是在Windows机器下进行测试)开始的完整路径,现在它也拥有一个父路径。“真实”路径的定义在文档中有点模糊,因为它取决于具体的文件系统。例如,如果文件名不区分大小写,即使路径由于大小写的缘故而不是完全相同,也可能得到肯定的匹配结果。在这样的平台上,**toRealPath()** 将返回实际情况下的 **Path**,并且还会删除任何冗余元素。

这里你会看到 **URI** 看起来只能用于描述文件,实际上 **URI** 可以用于描述更多的东西;通过 [维基百科](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) 可以了解更多细节。现在我们成功地将 **URI** 转为一个 **Path** 对象。

Expand Down Expand Up @@ -465,7 +465,7 @@ test\Hello.txt

我们尝试使用 **createDirectory()** 来创建多级路径,但是这样会抛出异常,因为这个方法只能创建单级路径。我已经将 **populateTestDir()** 作为一个单独的方法,因为它将在后面的例子中被重用。对于每一个变量 **variant**,我们都能使用 **createDirectories()** 创建完整的目录路径,然后使用此文件的副本以不同的目标名称填充该终端目录。然后我们使用 **createTempFile()** 生成一个临时文件。

在调用 **populateTestDir()** 之后,我们在 **test** 目录下面下面创建一个临时目录。请注意,**createTempDirectory()** 只有名称的前缀选项。与 **createTempFile()** 不同,我们再次使用它将临时文件放入新的临时目录中。你可以从输出中看到,如果未指定后缀,它将默认使用".tmp"作为后缀。
在调用 **populateTestDir()** 之后,我们在 **test** 目录下面创建一个临时目录。请注意,**createTempDirectory()** 只有名称的前缀选项。与 **createTempFile()** 不同,我们再次使用它将临时文件放入新的临时目录中。你可以从输出中看到,如果未指定后缀,它将默认使用".tmp"作为后缀。

为了展示结果,我们首次使用看起来很有希望的 **newDirectoryStream()**,但事实证明这个方法只是返回 **test** 目录内容的 Stream 流,并没有更多的内容。要获取目录树的全部内容的流,请使用 **Files.walk()**

Expand Down Expand Up @@ -589,7 +589,7 @@ evt.kind(): ENTRY_DELETE

此时,**watcher.take()** 将等待并阻塞在这里。当目标事件发生时,会返回一个包含 **WatchEvent****Watchkey** 对象。展示的这三种方法是能对 **WatchEvent** 执行的全部操作。

查看输出的具体内容。即使我们正在删除以 **.txt** 结尾的文件,在 **Hello.txt** 被删除之前,**WatchService** 也不会被触发。你可能认为,如果说"监视这个目录",自然会包含整个目录和下面子目录,但实际上的:只会监视给定的目录,而不是下面的所有内容。如果需要监视整个树目录,必须在整个树的每个子目录上放置一个 **Watchservice**
查看输出的具体内容。即使我们正在删除以 **.txt** 结尾的文件,在 **Hello.txt** 被删除之前,**WatchService** 也不会被触发。你可能认为,如果说"监视这个目录",自然会包含整个目录和下面子目录,但实际上:只会监视给定的目录,而不是下面的所有内容。如果需要监视整个树目录,必须在整个树的每个子目录上放置一个 **Watchservice**

```java
// files/TreeWatcher.java
Expand Down Expand Up @@ -851,4 +851,4 @@ Java 7 和 8 对于处理文件和目录的类库做了大量改进。如果您

<!-- 分页 -->

<div style="page-break-after: always;"></div>
<div style="page-break-after: always;"></div>
10 changes: 5 additions & 5 deletions docs/book/19-Type-Information.md
Original file line number Diff line number Diff line change
Expand Up @@ -785,11 +785,11 @@ public class ForNameCreator extends PetCreator {
}
```

`loader()` 方法使用 `Class.forName()` 创建了 `Class` 对象的 `List`。这可能会导致 `ClassNotFoundException` 异常,因为你传入的是一个 `String` 类型的参数,它不能再编译期间被确认是否合理。由于 `Pet` 相关的文件在 `typeinfo` 包里面,所以使用它们的时候需要填写完整的包名。
`loader()` 方法使用 `Class.forName()` 创建了 `Class` 对象的 `List`。这可能会导致 `ClassNotFoundException` 异常,因为你传入的是一个 `String` 类型的参数,它不能在编译期间被确认是否合理。由于 `Pet` 相关的文件在 `typeinfo` 包里面,所以使用它们的时候需要填写完整的包名。

为了使得 `List` 装入的是具体的 `Class` 对象,类型转换是必须的,它会产生一个编译时警告。`loader()` 方法是分开编写的,然后它被放入到一个静态代码块里,因为 `@SuppressWarning` 注解不能够直接放置在静态代码块之上。

为了对 `Pet` 进行计数,我们需要一个能跟踪不同类型的 `Pet` 的工具。`Map` 的是这个需求的首选,我们将 `Pet` 类型名作为键,将保存 `Pet` 数量的 `Integer` 作为值。通过这种方式,你就看可以询问:“有多少个 `Hamster` 对象?”我们可以使用 `instanceof` 来对 `Pet` 进行计数:
为了对 `Pet` 进行计数,我们需要一个能跟踪不同类型的 `Pet` 的工具。`Map` 是这个需求的首选,我们将 `Pet` 类型名作为键,将保存 `Pet` 数量的 `Integer` 作为值。通过这种方式,你就可以询问:“有多少个 `Hamster` 对象?”我们可以使用 `instanceof` 来对 `Pet` 进行计数:

```java
// typeinfo/PetCount.java
Expand Down Expand Up @@ -1052,7 +1052,7 @@ EgyptianMau=2, Rodent=5, Hamster=1, Manx=7, Pet=20}

### 递归计数

`PetCount3.Counter` 中的 `Map` 预先加载了所有不同的 `Pet` 类。我们可以使用 `Class.isAssignableFrom()` 而不是预加载地图,并创建一个不限于计数 `Pet` 的通用工具:
`PetCount3.Counter` 中的 `Map` 预先加载了所有不同的 `Pet` 类。我们可以使用 `Class.isAssignableFrom()` 而不是预加载 `Map` ,并创建一个不限于计数 `Pet` 的通用工具:

```java
// onjava/TypeCounter.java
Expand Down Expand Up @@ -1336,7 +1336,7 @@ x.getClass().equals(Derived.class)) true

如果你不知道对象的确切类型,RTTI 会告诉你。但是,有一个限制:必须在编译时知道类型,才能使用 RTTI 检测它,并对信息做一些有用的事情。换句话说,编译器必须知道你使用的所有类。

起初,这看起来并没有那么大的限制,但是假设你引用了一个对不在程序空间中的对象。实际上,该对象的类在编译时甚至对程序都不可用。也许你从磁盘文件或网络连接中获得了大量的字节,并被告知这些字节代表一个类。由于这个类在编译器为你的程序生成代码后很长时间才会出现,你如何使用这样的类?
起初,这看起来并没有那么大的限制,但是假设你引用了一个不在程序空间中的对象。实际上,该对象的类在编译时甚至对程序都不可用。也许你从磁盘文件或网络连接中获得了大量的字节,并被告知这些字节代表一个类。由于这个类在编译器为你的程序生成代码后很长时间才会出现,你如何使用这样的类?

在传统编程环境中,这是一个牵强的场景。但是,当我们进入一个更大的编程世界时,会有一些重要的情况发生。第一个是基于组件的编程,你可以在应用程序构建器*集成开发环境*中使用*快速应用程序开发*(RAD)构建项目。这是一种通过将表示组件的图标移动到窗体上来创建程序的可视化方法。然后,通过在编程时设置这些组件的一些值来配置这些组件。这种设计时配置要求任何组件都是可实例化的,它公开自己的部分,并且允许读取和修改其属性。此外,处理*图形用户界面*(GUI)事件的组件必须公开有关适当方法的信息,以便 IDE 可以帮助程序员覆写这些事件处理方法。反射提供了检测可用方法并生成方法名称的机制。

Expand Down Expand Up @@ -1826,7 +1826,7 @@ caught EmptyTitleException

`EmptyTitleException` 是一个 `RuntimeException`,因为它意味着程序存在错误。在这个方案里边,你仍然可能会得到一个异常。但不同的是,在错误产生的那一刻(向 `setTitle()` 传 `null` 值时)就会抛出异常,而不是发生在其它时刻,需要你通过调试才能发现问题所在。另外,使用 `EmptyTitleException` 还有助于定位 BUG

`Person` 字段的限制又不太一样:如果你把它的值设为 `null`,程序会自动把将它赋值成一个空的 `Person` 对象。先前我们也用过类似的方法把字段转换成 `Option`,但这里我们是在返回结果的时候使用 `orElse(new Person())` 插入一个空的 `Person` 对象替代了 `null`。
`Person` 字段的限制又不太一样:如果你把它的值设为 `null`,程序会自动把将它赋值成一个空的 `Person` 对象。先前我们也用过类似的方法把字段转换成 `Optional`,但这里我们是在返回结果的时候使用 `orElse(new Person())` 插入一个空的 `Person` 对象替代了 `null`。

在 `Position` 里边,我们没有创建一个表示“空”的标志位或者方法,因为 `person` 字段的 `Person` 对象为空,就表示这个 `Position` 是个空缺位置。之后,你可能会发现你必须添加一个显式的表示“空位”的方法,但是正如 YAGNI[^2] (You Aren't Going to Need It,你永远不需要它)所言,在初稿时“实现尽最大可能的简单”,直到程序在某些方面要求你为其添加一些额外的特性,而不是假设这是必要的。
Expand Down

0 comments on commit 5d7462a

Please sign in to comment.