From 71512c65c875c047941cd7117dc053a3568e6b28 Mon Sep 17 00:00:00 2001
From: WangHengwei <35023376@qq.com>
Date: Thu, 1 Aug 2019 12:02:01 +0800
Subject: [PATCH 001/461] =?UTF-8?q?=E5=B0=86=20High-Precision=20Numbers=20?=
=?UTF-8?q?=E7=9A=84=E7=BF=BB=E8=AF=91=E4=BB=8E=E2=80=9D=E9=AB=98=E7=B2=BE?=
=?UTF-8?q?=E6=95=B0=E5=80=BC=E7=9A=84=E5=AD=98=E5=82=A8=E2=80=9C=E6=94=B9?=
=?UTF-8?q?=E4=B8=BA=E2=80=9D=E9=AB=98=E7=B2=BE=E5=BA=A6=E6=95=B0=E5=80=BC?=
=?UTF-8?q?=E2=80=9C?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
docs/book/03-Objects-Everywhere.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/book/03-Objects-Everywhere.md b/docs/book/03-Objects-Everywhere.md
index ee8f7dd9..2941c371 100644
--- a/docs/book/03-Objects-Everywhere.md
+++ b/docs/book/03-Objects-Everywhere.md
@@ -100,7 +100,7 @@ char c = ch;
-### 高精数值的存储
+### 高精度数值
在 Java 中有两种类型的数据可用于高精度的计算。它们是 `BigInteger` 和 `BigDecimal`。尽管它们大致可以划归为“包装类型”,但是它们并没有对应的基本类型。
@@ -641,4 +641,4 @@ class AllTheColorsOfTheRainbow {
[^10]: 为了保持本书的代码排版紧凑,我并没完全遵守规范,但我尽量会做到符合 Java 标准。
-
\ No newline at end of file
+
From 197ac68ec18c67f724114471670833365fe9d878 Mon Sep 17 00:00:00 2001
From: 1326670425 <1326670425@qq.com>
Date: Thu, 1 Aug 2019 20:11:35 +0800
Subject: [PATCH 002/461] =?UTF-8?q?=E6=A0=A1=E8=AE=A2=20=E7=AC=AC=E5=8D=81?=
=?UTF-8?q?=E4=BA=8C=E7=AB=A0=EF=BC=9A=E9=9B=86=E5=90=88?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
docs/book/12-Collections.md | 173 ++++++++++++++++++------------------
1 file changed, 86 insertions(+), 87 deletions(-)
diff --git a/docs/book/12-Collections.md b/docs/book/12-Collections.md
index d95b3c81..44872d57 100644
--- a/docs/book/12-Collections.md
+++ b/docs/book/12-Collections.md
@@ -22,9 +22,9 @@ MyType aReference;
## 泛型和类型安全的集合
-使用 Java 5 之前的集合的一个主要问题是编译器允许你向集合中插入不正确的类型。例如,考虑一个 **Apple** 对象的集合,这里使用最基本最可靠的 **ArrayList** 。现在,可以把 **ArrayList** 看作“可以自动扩充自身尺寸的数组”来看待。使用 **ArrayList** 相当简单:创建一个实例,用 **add()** 插入对象;然后用 **get()** 来访问这些对象,此时需要使用索引,就像数组那样,但是不需要方括号。[^2] **ArrayList** 还有一个 **size()** 方法,来说明集合中包含了多少个元素,所以不会不小心因数组越界而引发错误(通过抛出*运行时异常*,[异常]()章节介绍了异常)。
+使用 Java 5 之前的集合的一个主要问题是编译器允许你向集合中插入不正确的类型。例如,考虑一个 **Apple** 对象的集合,这里使用最基本最可靠的 **ArrayList** 。现在,可以把 **ArrayList** 看作“可以自动扩充自身尺寸的数组”来看待。使用 **ArrayList** 相当简单:创建一个实例,用 `add()` 插入对象;然后用 `get()` 来访问这些对象,此时需要使用索引,就像数组那样,但是不需要方括号。[^2] **ArrayList** 还有一个 `size()` 方法,来说明集合中包含了多少个元素,所以不会不小心因数组越界而引发错误(通过抛出*运行时异常*,[异常]()章节介绍了异常)。
-在本例中, **Apple** 和 **Orange** 都被放到了集合中,然后将它们取出。正常情况下,Java编译器会给出警告,因为这个示例没有使用泛型。在这里,使用特定的注解来抑制警告信息。注解以“@”符号开头,可以带参数。这里的 **@SuppressWarning** 注解及其参数表示只抑制“unchecked”类型的警告([注解]()章节将介绍更多有关注解的信息):
+在本例中, **Apple** 和 **Orange** 都被放到了集合中,然后将它们取出。正常情况下,Java编译器会给出警告,因为这个示例没有使用泛型。在这里,使用特定的注解来抑制警告信息。注解以“@”符号开头,可以带参数。这里的 `@SuppressWarning` 注解及其参数表示只抑制“unchecked”类型的警告([注解]()章节将介绍更多有关注解的信息):
```java
// collections/ApplesAndOrangesWithoutGenerics.java
@@ -64,11 +64,11 @@ ndOrangesWithoutGenerics.java:23)
*/
```
-**Apple** 和 **Orange** 是截然不同的,它们除了都是 **Object** 之外没有任何共同点(如果一个类没有显式地声明继承自哪个类,那么它就自动继承自 **Object**)。因为 **ArrayList** 保存的是 **Object** ,所以不仅可以通过 **ArrayList** 的 **add()** 方法将 **Apple** 对象放入这个集合,而且可以放入 **Orange** 对象,这无论在编译期还是运行时都不会有问题。当使用 **ArrayList** 的 **get()** 方法来取出你认为是 **Apple** 的对象时,得到的只是 **Object** 引用,必须将其转型为 **Apple**。然后需要将整个表达式用括号括起来,以便在调用 **Apple** 的 **id()** 方法之前,强制执行转型。否则,将会产生语法错误。
+**Apple** 和 **Orange** 是截然不同的,它们除了都是 **Object** 之外没有任何共同点(如果一个类没有显式地声明继承自哪个类,那么它就自动继承自 **Object**)。因为 **ArrayList** 保存的是 **Object** ,所以不仅可以通过 **ArrayList** 的 `add()` 方法将 **Apple** 对象放入这个集合,而且可以放入 **Orange** 对象,这无论在编译期还是运行时都不会有问题。当使用 **ArrayList** 的 `get()` 方法来取出你认为是 **Apple** 的对象时,得到的只是 **Object** 引用,必须将其转型为 **Apple**。然后需要将整个表达式用括号括起来,以便在调用 **Apple** 的 `id()` 方法之前,强制执行转型。否则,将会产生语法错误。
在运行时,当尝试将 **Orange** 对象转为 **Apple** 时,会出现输出中显示的错误。
-在[泛型]()章节中,你将了解到使用 Java 泛型来创建类可能很复杂。但是,使用预先定义的泛型类却相当简单。例如,要定义一个用于保存 **Apple** 对象的 **ArrayList** ,只需要使用 **ArrayList** 来代替 **ArrayList** 。尖括号括起来的是*类型参数*(可能会有多个),它指定了这个集合实例可以保存的类型。
+在[泛型]()章节中,你将了解到使用 Java 泛型来创建类可能很复杂。但是,使用预先定义的泛型类却相当简单。例如,要定义一个用于保存 **Apple** 对象的 **ArrayList** ,只需要使用 **ArrayList\** 来代替 **ArrayList** 。尖括号括起来的是*类型参数*(可能会有多个),它指定了这个集合实例可以保存的类型。
通过使用泛型,就可以在编译期防止将错误类型的对象放置到集合中。[^3]下面还是这个示例,但是使用了泛型:
```java
@@ -94,7 +94,7 @@ public class ApplesAndOrangesWithGenerics {
*/
```
-在 **apples** 定义的右侧,可以看到 **new ArrayList<>()** 。这有时被称为“菱形语法”(diamond syntax)。在 Java 7 之前,必须要在两端都进行类型声明,如下所示:
+在 **apples** 定义的右侧,可以看到 `new ArrayList<>()` 。这有时被称为“菱形语法”(diamond syntax)。在 Java 7 之前,必须要在两端都进行类型声明,如下所示:
```java
ArrayList apples = new ArrayList();
@@ -104,7 +104,7 @@ ArrayList apples = new ArrayList();
有了 **ArrayList** 声明中的类型指定,编译器会阻止将 **Orange** 放入 **apples** ,因此,这会成为一个编译期错误而不是运行时错误。
-使用泛型,从 **List** 中获取元素不需要强制类型转换。因为 **List** 知道它持有什么类型,因此当调用 **get()** 时,它会替你执行转型。这样,不仅可以知道编译器将会检查放入集合中的对象的类型,而且在使用集合中的对象时,可以使用更加清晰的语法。
+使用泛型,从 **List** 中获取元素不需要强制类型转换。因为 **List** 知道它持有什么类型,因此当调用 `get()` 时,它会替你执行转型。这样,不仅可以知道编译器将会检查放入集合中的对象的类型,而且在使用集合中的对象时,可以使用更加清晰的语法。
当指定了某个类型为泛型参数时,并不仅限于只能将确切类型的对象放入集合中。向上转型也可以像作用于其他类型一样作用于泛型:
```java
@@ -137,7 +137,7 @@ Braeburn@4e25154f
因此,可以将 **Apple** 的子类型添加到被指定为保存 **Apple** 对象的集合中。
-程序的输出是从 **Object** 默认的 **toString()** 方法产生的,该方法打印类名,后边跟着对象的散列码的无符号十六进制表示(这个散列码是通过 **hashCode()** 方法产生的)。将在[附录:理解equals和hashCode方法]()中了解有关散列码的内容。
+程序的输出是从 **Object** 默认的 `toString()` 方法产生的,该方法打印类名,后边跟着对象的散列码的无符号十六进制表示(这个散列码是通过 `hashCode()` 方法产生的)。将在[附录:理解equals和hashCode方法]()中了解有关散列码的内容。
## 基本概念
@@ -145,7 +145,7 @@ Braeburn@4e25154f
Java集合类库采用“持有对象”(holding objects)的思想,并将其分为两个不同的概念,表示为类库的基本接口:
1. **集合(Collection)** :一个独立元素的序列,这些元素都服从一条或多条规则。**List** 必须以插入的顺序保存元素, **Set** 不能包含重复元素, **Queue** 按照*排队规则*来确定对象产生的顺序(通常与它们被插入的顺序相同)。
-2. **映射(Map)** : 一组成对的“键值对”对象,允许使用键来查找值。 **ArrayList** 使用数字来查找对象,因此在某种意义上讲,它是将数字和对象关联在一起。 **map**允许我们使用一个对象来查找另一个对象,它也被称作*关联数组*(associative array),因为它将对象和其它对象关联在一起;或者称作*字典*(dictionary),因为可以使用一个键对象来查找值对象,就像在字典中使用单词查找定义一样。 **Map**s是强大的编程工具。
+2. **映射(Map)** : 一组成对的“键值对”对象,允许使用键来查找值。 **ArrayList** 使用数字来查找对象,因此在某种意义上讲,它是将数字和对象关联在一起。 **map** 允许我们使用一个对象来查找另一个对象,它也被称作*关联数组*(associative array),因为它将对象和其它对象关联在一起;或者称作*字典*(dictionary),因为可以使用一个键对象来查找值对象,就像在字典中使用单词查找定义一样。 **Map** 是强大的编程工具。
尽管并非总是可行,但在理想情况下,你编写的大部分代码都在与这些接口打交道,并且唯一需要指定所使用的精确类型的地方就是在创建的时候。因此,可以像下面这样创建一个 **List** :
@@ -182,9 +182,9 @@ public class SimpleCollection {
*/
```
-这个例子仅适用 **Collection** 方法,所以任何继承自 **Collection** 的类的对象都可以使用。但是 **ArrayList** 是最基本的序列类型。
+这个例子仅使用了 **Collection** 中的方法(即 `add()` ),所以使用任何继承自 **Collection** 的类的对象都可以正常工作。但是 **ArrayList** 是最基本的序列类型。
-**add()** 方法的名称就表明它是在 **Collection** 中添加一个新元素。但是,文档中非常详细地叙述到 **add()** “要确保这个 **Collection** 包含指定的元素。”这是因为考虑到了 **Set** 的含义,因为在 **Set**中,只有当元素不存在时才会添加元素。在使用 **ArrayList** ,或任何其他类型的 **List** 时,**add()** 总是表示“把它放进去”,因为 **List** 不关心是否存在重复元素。
+`add()` 方法的名称就表明它是在 **Collection** 中添加一个新元素。但是,文档中非常详细地叙述到 `add()` “要确保这个 **Collection** 包含指定的元素。”这是因为考虑到了 **Set** 的含义,因为在 **Set**中,只有当元素不存在时才会添加元素。在使用 **ArrayList** ,或任何其他类型的 **List** 时,`add()` 总是表示“把它放进去”,因为 **List** 不关心是否存在重复元素。
可以使用 *for-in* 语法来遍历所有的 **Collection** ,就像这里所展示的那样。在本章的后续部分,还将学习到一个更灵活的概念,*迭代器*。
@@ -194,7 +194,7 @@ public class SimpleCollection {
在 **java.util** 包中的 **Arrays** 和 **Collections** 类中都有很多实用的方法,可以在一个 **Collection** 中添加一组元素。
-**Arrays.asList()** 方法接受一个数组或是逗号分隔的元素列表(使用可变参数),并将其转换为 **List** 对象。 **Collections.addAll()** 方法接受一个 **Collection** 对象,以及一个数组或是一个逗号分隔的列表,将其中元素添加到 **Collection** 中。下边的示例展示了这两个方法,以及更通用的 **addAll()** 方法,所有 **Collection** 类型都包含该方法:
+`Arrays.asList()` 方法接受一个数组或是逗号分隔的元素列表(使用可变参数),并将其转换为 **List** 对象。 `Collections.addAll()` 方法接受一个 **Collection** 对象,以及一个数组或是一个逗号分隔的列表,将其中元素添加到 **Collection** 中。下边的示例展示了这两个方法,以及更通用的 `addAll()` 方法,所有 **Collection** 类型都包含该方法:
```java
// collections/AddingGroups.java
@@ -220,11 +220,11 @@ public class AddingGroups {
}
```
-**Collection** 的构造器可以接受另一个 **Collection**,用它来将自身初始化。因此,可以使用 **Arrays.asList()** 来为这个构造器产生输入。但是, **Collections.addAll()** 运行得更快,而且很容易构建一个不包含元素的 **Collection** ,然后调用 **Collections.addAll()** ,因此这是首选方式。
+**Collection** 的构造器可以接受另一个 **Collection**,用它来将自身初始化。因此,可以使用 `Arrays.asList()` 来为这个构造器产生输入。但是, `Collections.addAll()` 运行得更快,而且很容易构建一个不包含元素的 **Collection** ,然后调用 `Collections.addAll()` ,因此这是首选方式。
-**Collection.addAll()** 方法只能接受另一个 **Collection** 作为参数,因此它没有 **Arrays.asList()** 或 **Collections.addAll()** 灵活。这两个方法都使用可变参数列表。
+`Collection.addAll()` 方法只能接受另一个 **Collection** 作为参数,因此它没有 `Arrays.asList()` 或 `Collections.addAll()` 灵活。这两个方法都使用可变参数列表。
-也可以直接使用 **Arrays.asList()** 的输出作为一个 **List** ,但是这里的底层实现是数组,没法调整大小。如果尝试在这个 **List** 上调用 **add()** 或 **delete()**,由于这两个方法会尝试修改数组大小,所以会在运行时得到“Unsupported Operation(不支持的操作)”错误:
+也可以直接使用 `Arrays.asList()` 的输出作为一个 **List** ,但是这里的底层实现是数组,没法调整大小。如果尝试在这个 **List** 上调用 `add()` 或 `delete()`,由于这两个方法会尝试修改数组大小,所以会在运行时得到“Unsupported Operation(不支持的操作)”错误:
```java
// collections/AsListInference.java
@@ -260,12 +260,12 @@ public class AsListInference {
}
```
-在 **snow4** 中,注意 **Arrays.asList()** 中间的“hint”,告诉编译器 **Arrays.asList()** 生成的结果 **List** 类型的实际目标类型是什么。这称为*显式类型参数说明*(explicit type argument specification)。
+在 **snow4** 中,注意 `Arrays.asList()` 中间的“暗示”(即 `` ),告诉编译器 `Arrays.asList()` 生成的结果 **List** 类型的实际目标类型是什么。这称为*显式类型参数说明*(explicit type argument specification)。
## 集合的打印
-必须使用 **Arrays.toString()** 来生成数组的可打印形式。但是打印集合无需任何帮助。下面是一个例子,这个例子中也介绍了基本的Java集合:
+必须使用 `Arrays.toString()` 来生成数组的可打印形式。但是打印集合无需任何帮助。下面是一个例子,这个例子中也介绍了基本的Java集合:
```java
// collections/PrintingCollections.java
// Collections print themselves automatically
@@ -310,24 +310,23 @@ public class PrintingCollections {
*/
```
-这显示了Java集合库中的两个主要类型。它们的区别在于集合中的每个“槽”(slot)保存的元素个数。 **Collection** 类型在每个槽中只能保存一个元素。此类集合包括: **List**, 它以特定的顺序保存一组元素; **Set** ,其中元素不允许重复; **Queue** ,只能在集合一端插入对象,并从另一端移除对象(就本例而言,这只是查看序列的另一种方式,因此并没有显示它)。 **Map** 在每个槽中存放了两个元素,即*键*和与之关联的*值*。
+这显示了Java集合库中的两个主要类型。它们的区别在于集合中的每个“槽”(slot)保存的元素个数。 **Collection** 类型在每个槽中只能保存一个元素。此类集合包括: **List** ,它以特定的顺序保存一组元素; **Set** ,其中元素不允许重复; **Queue** ,只能在集合一端插入对象,并从另一端移除对象(就本例而言,这只是查看序列的另一种方式,因此并没有显示它)。 **Map** 在每个槽中存放了两个元素,即*键*和与之关联的*值*。
默认的打印行为,使用集合提供的 `toString()` 方法即可生成可读性很好的结果。 **Collection** 打印出的内容用方括号括住,每个元素由逗号分隔。 **Map** 则由大括号括住,每个键和值用等号连接(键在左侧,值在右侧)。
-第一个 **fill()** 方法适用于所有类型的 **Collection** ,这些类型都实现了 **add()** 方法以添加新元素。
+第一个 `fill()` 方法适用于所有类型的 **Collection** ,这些类型都实现了 `add()` 方法以添加新元素。
**ArrayList** 和 **LinkedList** 都是 **List** 的类型,从输出中可以看出,它们都按插入顺序保存元素。两者之间的区别不仅在于执行某些类型的操作时的性能,而且 **LinkedList** 包含的操作多于 **ArrayList** 。本章后面将对这些内容进行更全面的探讨。
**HashSet** , **TreeSet** 和 **LinkedHashSet** 是 **Set** 的类型。从输出中可以看到, **Set** 仅保存每个相同项中的一个,并且不同的 **Set** 实现存储元素的方式也不同。 **HashSet** 使用相当复杂的方法存储元素,这在[附录:集合主题]()中进行了探讨。现在只需要知道,这种技术是检索元素的最快方法,因此,存储顺序看上去没有什么意义(通常只关心某事物是否是 **Set** 的成员,而存储顺序并不重要)。如果存储顺序很重要,则可以使用 **TreeSet** ,它将按比较结果的升序保存对象)或 **LinkedHashSet** ,它按照被添加的先后顺序保存对象。
-**Map** (也称为*关联数组*)使用*键*来查找对象,就像一个简单的数据库。所关联的对象称为*值*。 假设有一个 **Map** 将美国州名与它们的首府联系在一起,如果想要俄亥俄州(Ohio)的首府,可以用“Ohio”作为键来查找,几乎就像使用数组下标一样。 这是由于这种行为,对于每个键, **Map** 只存储一次。
-
-**Map.put(key, value)** 添加一个所想要添加的值并将它与一个键(用来查找值)相关联。 **Map.get(key)** 生成与该键相关联的值。上面的示例仅添加键值对,并没有执行查找。这将在稍后展示。
+**Map** (也称为*关联数组*)使用*键*来查找对象,就像一个简单的数据库。所关联的对象称为*值*。 假设有一个 **Map** 将美国州名与它们的首府联系在一起,如果想要俄亥俄州(Ohio)的首府,可以用“Ohio”作为键来查找,几乎就像使用数组下标一样。正是由于这种行为,对于每个键, **Map** 只存储一次。
+`Map.put(key, value)` 添加一个所想要添加的值并将它与一个键(用来查找值)相关联。 `Map.get(key)` 生成与该键相关联的值。上面的示例仅添加键值对,并没有执行查找。这将在稍后展示。
请注意,这里没有指定(或考虑) **Map** 的大小,因为它会自动调整大小。 此外, **Map** 还知道如何打印自己,它会显示相关联的键和值。
-本例使用了 **Map** 的三种基本风格: **HashMap** , **TreeMap**和 **LinkedHashMap** 。
+本例使用了 **Map** 的三种基本风格: **HashMap** , **TreeMap** 和 **LinkedHashMap** 。
键和值保存在 **HashMap** 中的顺序不是插入顺序,因为 **HashMap** 实现使用了非常快速的算法来控制顺序。 **TreeMap** 通过比较结果的升序来保存键, **LinkedHashMap** 在保持 **HashMap** 查找速度的同时按键的插入顺序保存键。
@@ -345,7 +344,7 @@ public class PrintingCollections {
下面的示例导入 **typeinfo.pets** ,超前使用了[类型信息]()一章中的类库。这个类库包含了 **Pet** 类层次结构,以及用于随机生成 **Pet** 对象的一些工具类。此时不需要了解完整的详细信息,只需要知道两点:
1. 有一个 **Pet** 类,以及 **Pet** 的各种子类型。
-2. 静态的 **Pets.arrayList()** 方法返回一个填充了随机选取的 **Pet** 对象的 **ArrayList**:
+2. 静态的 `Pets.arrayList()` 方法返回一个填充了随机选取的 **Pet** 对象的 **ArrayList**:
```java
// collections/ListFeatures.java
@@ -442,44 +441,44 @@ sub: [Mouse, Pug]
打印行都编了号,因此可从输出追溯到源代码。 第 1 行输出展示了原始的由 **Pet** 组成的 **List** 。 与数组不同, **List** 可以在创建后添加或删除元素,并自行调整大小。这正是它的重要价值:一种可修改的序列。在第 2 行输出中可以看到添加一个 **Hamster** 的结果,该对象将被追加到列表的末尾。
-可以使用 **contains()** 方法确定对象是否在列表中。如果要删除一个对象,可以将该对象的引用传递给 **remove()** 方法。同样,如果有一个对象的引用,可以使用 **indexOf()** 在 **List** 中找到该对象所在位置的下标号,如第 4 行输出所示中所示。
+可以使用 `contains()` 方法确定对象是否在列表中。如果要删除一个对象,可以将该对象的引用传递给 `remove()` 方法。同样,如果有一个对象的引用,可以使用 `indexOf()` 在 **List** 中找到该对象所在位置的下标号,如第 4 行输出所示中所示。
-当确定元素是否是属于某个 **List** ,寻找某个元素的索引,以及通过引用从 **List** 中删除元素时,都会用到 **equals()** 方法(根类 **Object** 的一个方法)。每个 **Pet** 被定义为一个唯一的对象,所以即使列表中已经有两个 **Cymrics** ,如果再创建一个新的 **Cymric** 对象并将其传递给 **indexOf()** 方法,结果仍为 **-1** (表示未找到),并且尝试调用 **remove()** 方法来删除这个对象将返回 **false** 。对于其他类, **equals()** 的定义可能有所不同。例如,如果两个 **String** 的内容相同,则这两个 **String** 相等。因此,为了防止出现意外,请务必注意 **List** 行为会根据 **equals()** 行为而发生变化。
+当确定元素是否是属于某个 **List** ,寻找某个元素的索引,以及通过引用从 **List** 中删除元素时,都会用到 `equals()` 方法(根类 **Object** 的一个方法)。每个 **Pet** 被定义为一个唯一的对象,所以即使列表中已经有两个 **Cymrics** ,如果再创建一个新的 **Cymric** 对象并将其传递给 `indexOf()` 方法,结果仍为 **-1** (表示未找到),并且尝试调用 `remove()` 方法来删除这个对象将返回 **false** 。对于其他类, `equals()` 的定义可能有所不同。例如,如果两个 **String** 的内容相同,则这两个 **String** 相等。因此,为了防止出现意外,请务必注意 **List** 行为会根据 `equals()` 行为而发生变化。
第 7、8 行输出展示了删除与 **List** 中的对象完全匹配的对象是成功的。
-可以在 **List** 的中间插入一个元素,就像在第 9 行输出和它之前的代码那样。但这会带来一个问题:对于 **LinkedList** ,在列表中间插入和删除都是廉价操作(在本例中,除了对列表中间进行的真正的随机访问),但对于 **ArrayList** ,这可是代价高昂的操作。这是否意味着永远不应该在 **ArrayList** 的中间插入元素,并最好是转换为 **LinkedList** ?不,它只是意味着你应该意识到这个问题,如果你开始在某个 **ArrayList** 中间进执行很多插入操作,并且程序开始变慢,那么你应该看看你的 **List** 实现有可能就是罪魁祸首(发现此类瓶颈的最佳方式是使用分析器 profiler)。优化是一个很棘手的问题,最好的策略就是置之不顾,直到发现必须要去担心它了(尽管去理解这些问题总是一个很好的主意)。
+可以在 **List** 的中间插入一个元素,就像在第 9 行输出和它之前的代码那样。但这会带来一个问题:对于 **LinkedList** ,在列表中间插入和删除都是廉价操作(在本例中,除了对列表中间进行的真正的随机访问),但对于 **ArrayList** ,这可是代价高昂的操作。这是否意味着永远不应该在 **ArrayList** 的中间插入元素,并最好是转换为 **LinkedList** ?不,它只是意味着你应该意识到这个问题,如果你开始在某个 **ArrayList** 中间执行很多插入操作,并且程序开始变慢,那么你应该看看你的 **List** 实现有可能就是罪魁祸首(发现此类瓶颈的最佳方式是使用分析器 profiler)。优化是一个很棘手的问题,最好的策略就是置之不顾,直到发现必须要去担心它了(尽管去理解这些问题总是一个很好的主意)。
-**subList()** 方法可以轻松地从更大的列表中创建切片,当将切片结果传递给原来这个较大的列表的 **containsAll()** 方法时,很自然地会得到 **true**。请注意,顺序并不重要,在第 11、12 行输出中可以看到,在 **sub** 上调用直观命名的 **Collections.sort()** 和 **Collections.shuffle()** 方法,不会影响 **containsAll()** 的结果。 **subList()** 所产生的列表的幕后支持就是原始列表。因此,对所返回列表的更改都将会反映在原始列表中,反之亦然。
+`subList()` 方法可以轻松地从更大的列表中创建切片,当将切片结果传递给原来这个较大的列表的 `containsAll()` 方法时,很自然地会得到 **true**。请注意,顺序并不重要,在第 11、12 行输出中可以看到,在 **sub** 上调用直观命名的 `Collections.sort()` 和 `Collections.shuffle()` 方法,不会影响 `containsAll()` 的结果。 `subList()` 所产生的列表的幕后支持就是原始列表。因此,对所返回列表的更改都将会反映在原始列表中,反之亦然。
-**retainAll()** 方法实际上是一个“集合交集”操作,在本例中,它保留了同时在 **copy** 和 **sub** 中的所有元素。请再次注意,所产生的结果行为依赖于 **equals()** 方法。
+`retainAll()` 方法实际上是一个“集合交集”操作,在本例中,它保留了同时在 **copy** 和 **sub** 中的所有元素。请再次注意,所产生的结果行为依赖于 `equals()` 方法。
-第 14 行输出展示了使用索引号来删除元素的结果,与通过对象引用来删除元素相比,它显得更加直观,因为在使用索引时,不必担心 **equals()** 的行为。
+第 14 行输出展示了使用索引号来删除元素的结果,与通过对象引用来删除元素相比,它显得更加直观,因为在使用索引时,不必担心 `equals()` 的行为。
-**removeAll()** 方法也是基于 **equals()** 方法运行的。 顾名思义,它会从 **List** 中删除在参数 **List** 中的所有元素。
+`removeAll()` 方法也是基于 `equals()` 方法运行的。 顾名思义,它会从 **List** 中删除在参数 **List** 中的所有元素。
-**set()** 方法的命名显得很不合时宜,因为它与 **Set** 类存在潜在的冲突。在这里使用“replace”可能更适合,因为它的功能是用第二个参数替换索引处的元素(第一个参数)。
+`set()` 方法的命名显得很不合时宜,因为它与 **Set** 类存在潜在的冲突。在这里使用“replace”可能更适合,因为它的功能是用第二个参数替换索引处的元素(第一个参数)。
-第 17 行输出表明,对于 **List** ,有一个重载的 **addAll()** 方法可以将新列表插入到原始列表的中间,而不仅仅只能用 **Collection** 的 **addAll()** 方法将追加到列表末尾。
+第 17 行输出表明,对于 **List** ,有一个重载的 `addAll()` 方法可以将新列表插入到原始列表的中间,而不仅仅只能用 **Collection** 的 `addAll()` 方法将追加到列表末尾。
-第 18 - 20 行输出展示了 **isEmpty()** 和 **clear()** 方法的效果。
+第 18 - 20 行输出展示了 `isEmpty()` 和 `clear()` 方法的效果。
-第 22、23 行输出展示了如何使用 **toArray()** 方法将任意的 **Collection** 转换为数组。这是一个重载方法,其无参版本返回一个 **Object** 数组,但是如果将目标类型的数组传递给这个重载版本,那么它会生成一个指定类型的数组(假设它通过了类型检查)。如果参数数组太小而无法容纳 **List** 中的所有元素(就像本例一样),则 **toArray()** 会创建一个具有合适尺寸的新数组。 **Pet** 对象有一个 **id()** 方法,可以在所产生的数组中的对象上调用这个方法。
+第 22、23 行输出展示了如何使用 `toArray()` 方法将任意的 **Collection** 转换为数组。这是一个重载方法,其无参版本返回一个 **Object** 数组,但是如果将目标类型的数组传递给这个重载版本,那么它会生成一个指定类型的数组(假设它通过了类型检查)。如果参数数组太小而无法容纳 **List** 中的所有元素(就像本例一样),则 `toArray()` 会创建一个具有合适尺寸的新数组。 **Pet** 对象有一个 `id()` 方法,可以在所产生的数组中的对象上调用这个方法。
## 迭代器Iterators
-在任何集合中,都必须有某种方式可以插入元素并再次获取它们。毕竟,保存事物是集合最基本的工作。对于 **List** , **add()** 是插入元素的一种方式, **get()** 是获取元素的一种方式。
+在任何集合中,都必须有某种方式可以插入元素并再次获取它们。毕竟,保存事物是集合最基本的工作。对于 **List** , `add()` 是插入元素的一种方式, `get()` 是获取元素的一种方式。
如果从更高层次的角度考虑,会发现这里有个缺点:要使用集合,必须对集合的确切类型编程。这一开始可能看起来不是很糟糕,但是考虑下面的情况:如果原本是对 **List** 编码的,但是后来发现如果能够将相同的代码应用于 **Set** 会更方便,此时应该怎么做?或者假设想从一开始就编写一段通用代码,它不知道或不关心它正在使用什么类型的集合,因此它可以用于不同类型的集合,那么如何才能不重写代码就可以应用于不同类型的集合?
-*迭代器*(也是一种设计模式)的概念实现了这种抽象。迭代器是一个对象,它在一个序列中移动并选择该序列中的每个对象,而客户端程序员不知道或不关心该序列的底层结构。另外,迭代器通常被称为*轻量级对象*(lightweight object):创建它的代价小。因此,经常可以看到一些对迭代器有些奇怪的约束。例如,Java的 **Iterator** 只能单向移动。这个 **Iterator** 只能用来:
+*迭代器*(也是一种设计模式)的概念实现了这种抽象。迭代器是一个对象,它在一个序列中移动并选择该序列中的每个对象,而客户端程序员不知道或不关心该序列的底层结构。另外,迭代器通常被称为*轻量级对象*(lightweight object):创建它的代价小。因此,经常可以看到一些对迭代器有些奇怪的约束。例如,Java 的 **Iterator** 只能单向移动。这个 **Iterator** 只能用来:
-1. 使用 **iterator()** 方法要求集合返回一个 **Iterator**。 **Iterator** 将准备好返回序列中的第一个元素。
-2. 使用 **next()** 方法获得序列中的下一个元素。
-3. 使用 **hasNext()** 方法检查序列中是否还有元素。
-4. 使用 **remove()** 方法将迭代器最近返回的那个元素删除。
+1. 使用 `iterator()` 方法要求集合返回一个 **Iterator**。 **Iterator** 将准备好返回序列中的第一个元素。
+2. 使用 `next()` 方法获得序列中的下一个元素。
+3. 使用 `hasNext()` 方法检查序列中是否还有元素。
+4. 使用 `remove()` 方法将迭代器最近返回的那个元素删除。
为了观察它的工作方式,这里再次使用[类型信息]()章节中的 **Pet** 工具:
@@ -519,15 +518,15 @@ public class SimpleIteration {
*/
```
-有了 **Iterator** ,就不必再为集合中元素的数量操心了。这是由 **hasNext()** 和 **next()** 关心的事情。
+有了 **Iterator** ,就不必再为集合中元素的数量操心了。这是由 `hasNext()` 和 `next()` 关心的事情。
如果只是想向前遍历 **List** ,并不打算修改 **List** 对象本身,那么使用 *for-in* 语法更加简洁。
-**Iterator** 还可以删除由 **next()** 生成的最后一个元素,这意味着在调用 **remove()** 之前必须先调用 **next()** 。[^4]
+**Iterator** 还可以删除由 `next()` 生成的最后一个元素,这意味着在调用 `remove()` 之前必须先调用 `next()` 。[^4]
在集合中的每个对象上执行操作,这种思想十分强大,并且贯穿于本书。
-现在考虑创建一个 **display()** 方法,它不必知晓集合的确切类型:
+现在考虑创建一个 `display()` 方法,它不必知晓集合的确切类型:
```java
// collections/CrossCollectionIteration.java
@@ -565,7 +564,7 @@ public class CrossCollectionIteration {
*/
```
-**display()** 方法不包含任何有关它所遍历的序列的类型信息。这也展示了 **Iterator** 的真正威力:能够将遍历序列的操作与该序列的底层结构分离。出于这个原因,我们有时会说:迭代器统一了对集合的访问方式。
+`display()` 方法不包含任何有关它所遍历的序列的类型信息。这也展示了 **Iterator** 的真正威力:能够将遍历序列的操作与该序列的底层结构分离。出于这个原因,我们有时会说:迭代器统一了对集合的访问方式。
我们可以使用 **Iterable** 接口生成上一个示例的更简洁版本,该接口描述了“可以产生 **Iterator** 的任何东西”:
@@ -606,12 +605,12 @@ public class CrossCollectionIteration2 {
*/
```
-这里所有的类都是 **Iterable** ,所以现在对 **display()** 的调用显然更简单。
+这里所有的类都是 **Iterable** ,所以现在对 `display()` 的调用显然更简单。
### ListIterator
-**ListIterator** 是一个更强大的 **Iterator** 子类型,它只能由各种 **List** 类生成。虽然 **Iterator** 只能向前移动,但 **ListIterator** 可以双向移动。它还可以生成相对于迭代器在列表中指向的当前位置的后一个和前一个元素的索引,并且可以使用 **set()** 方法替换它访问过的最后一个元素。可以通过调用 **listIterator()** 方法来生成指向 **List** 开头处的 **ListIterator** ,还可以通过调用 **listIterator(n)** 创建一个一开始就指向列表索引号为 **n** 的元素处的 **ListIterator** 。 下面的示例演示了所有这些能力:
+**ListIterator** 是一个更强大的 **Iterator** 子类型,它只能由各种 **List** 类生成。 **Iterator** 只能向前移动,而 **ListIterator** 可以双向移动。它还可以生成相对于迭代器在列表中指向的当前位置的后一个和前一个元素的索引,并且可以使用 `set()` 方法替换它访问过的最近一个元素。可以通过调用 `listIterator()` 方法来生成指向 **List** 开头处的 **ListIterator** ,还可以通过调用 `listIterator(n)` 创建一个一开始就指向列表索引号为 **n** 的元素处的 **ListIterator** 。 下面的示例演示了所有这些能力:
```java
// collections/ListIteration.java
@@ -650,7 +649,7 @@ EgyptianMau]
*/
```
-**Pets.get()** 方法用来从位置 3 开始替换 **List** 中的所有 Pet 对象。
+`Pets.get()` 方法用来从位置 3 开始替换 **List** 中的所有 Pet 对象。
@@ -660,11 +659,11 @@ EgyptianMau]
**LinkedList 还添加了一些方法,使其可以被用作栈、队列或双端队列(deque)** 。在这些方法中,有些彼此之间可能只是名称有些差异,或者只存在些许差异,以使得这些名字在特定用法的上下文环境中更加适用(特别是在 **Queue** 中)。例如:
-- **getFirst()** 和 **element()** 是相同的,它们都返回列表的头部(第一个元素)而并不删除它,如果 **List** 为空,则抛出 **NoSuchElementException** 异常。 **peek()** 方法与这两个方法只是稍有差异,它在列表为空时返回 **null** 。
-- **removeFirst()** 和 **remove()** 也是相同的,它们删除并返回列表的头部元素,并在列表为空时抛出 **NoSuchElementException** 异常。 **poll()** 稍有差异,它在列表为空时返回 **null** 。
-- **addFirst()** 在列表的开头插入一个元素。
-- **offer()** 与 **add()** 和 **addLast()** 相同。 它们都在列表的尾部(末尾)添加一个元素。
-- **removeLast()** 删除并返回列表的最后一个元素。
+- `getFirst()` 和 `element()` 是相同的,它们都返回列表的头部(第一个元素)而并不删除它,如果 **List** 为空,则抛出 **NoSuchElementException** 异常。 `peek()` 方法与这两个方法只是稍有差异,它在列表为空时返回 **null** 。
+- `removeFirst()` 和 `remove()` 也是相同的,它们删除并返回列表的头部元素,并在列表为空时抛出 **NoSuchElementException** 异常。 `poll()` 稍有差异,它在列表为空时返回 **null** 。
+- `addFirst()` 在列表的开头插入一个元素。
+- `offer()` 与 `add()` 和 `addLast()` 相同。 它们都在列表的尾部(末尾)添加一个元素。
+- `removeLast()` 删除并返回列表的最后一个元素。
下面的示例展示了这些功能之间基本的相似性和差异性。它并不是重复执行 **ListFeatures.java** 中所示的行为:
@@ -722,7 +721,7 @@ pets.removeLast(): Hamster
*/
```
-**Pets.list()** 的结果被传递给 **LinkedList** 的构造器,以便使用它来填充 **LinkedList** 。如果查看 **Queue** 接口就会发现,它在 **LinkedList** 的基础上添加了 **element()** , **offer()** , **peek()** , **poll()** 和 **remove()** 方法,以使其可以成为一个 **Queue** 的实现。 **Queue** 的完整示例将在本章稍后给出。
+`Pets.list()` 的结果被传递给 **LinkedList** 的构造器,以便使用它来填充 **LinkedList** 。如果查看 **Queue** 接口就会发现,它在 **LinkedList** 的基础上添加了 `element()` , `offer()` , `peek()` , `poll()` 和 `remove()` 方法,以使其可以成为一个 **Queue** 的实现。 **Queue** 的完整示例将在本章稍后给出。
@@ -772,7 +771,7 @@ public class Stack {
}
```
-这里引入了使用泛型的类定义的最简单的可能示例。类名称后面的 **** 告诉编译器这是一个参数化类型,而其中的类型参数 **T** 会在使用类时被实际类型替换。基本上,这个类是在声明“我们在定义一个可以持有 **T** 类型对象的 **Stack** 。” **Stack** 是使用 **ArrayDeque** 实现的,而 **ArrayDeque** 也被告知它将持有 **T** 类型对象。注意, **push()** 接受类型为 **T** 的对象,而 **peek()** 和 **pop()** 返回类型为 **T** 的对象。 **peek()** 方法将返回栈顶元素,但并不将其从栈顶删除,而 **pop()** 删除并返回顶部元素。
+这里引入了使用泛型的类定义的最简单的可能示例。类名称后面的 **\** 告诉编译器这是一个参数化类型,而其中的类型参数 **T** 会在使用类时被实际类型替换。基本上,这个类是在声明“我们在定义一个可以持有 **T** 类型对象的 **Stack** 。” **Stack** 是使用 **ArrayDeque** 实现的,而 **ArrayDeque** 也被告知它将持有 **T** 类型对象。注意, `push()` 接受类型为 **T** 的对象,而 `peek()` 和 `pop()` 返回类型为 **T** 的对象。 `peek()` 方法将返回栈顶元素,但并不将其从栈顶删除,而 `pop()` 删除并返回顶部元素。
如果只需要栈的行为,那么使用继承是不合适的,因为这将产生一个具有 **ArrayDeque** 的其它所有方法的类(在[附录:集合主题]()中将会看到, **Java 1.0** 设计者在创建 **java.util.Stack** 时,就犯了这个错误)。使用组合,可以选择要公开的方法以及如何命名它们。
@@ -837,7 +836,7 @@ import onjava.Stack;
**Set** 不保存重复的元素。 如果试图将相同对象的多个实例添加到 **Set** 中,那么它会阻止这种重复行为。 **Set** 最常见的用途是测试归属性,可以很轻松地询问某个对象是否在一个 **Set** 中。因此,查找通常是 **Set** 最重要的操作,因此通常会选择 **HashSet** 实现,该实现针对快速查找进行了优化。
-**Set** 具有与 **Collection** 相同的接口,因此没有任何额外的功能,不像前面两种不同类型的 **List** 那样。实际上, **Set** 就是一个 **Collection** ,只是行为不同。(这是继承和多态思想的典型应用:表现不同的行为。)**Set** 根据对象的“值”确定归属性,更复杂的问题将在[附录:集合主题]()中介绍。
+**Set** 具有与 **Collection** 相同的接口,因此没有任何额外的功能,不像前面两种不同类型的 **List** 那样。实际上, **Set** 就是一个 **Collection** ,只是行为不同。(这是继承和多态思想的典型应用:表现不同的行为。)**Set** 根据对象的“值”确定归属性,更复杂的内容将在[附录:集合主题]()中介绍。
下面是使用存放 **Integer** 对象的 **HashSet** 的示例:
@@ -862,7 +861,7 @@ public class SetOfInteger {
在 0 到 29 之间的 10000 个随机整数被添加到 **Set** 中,因此可以想象每个值都重复了很多次。但是从结果中可以看到,每一个数只有一个实例出现在结果中。
-早期 Java 版本中的 **HashSet** 产生的输出没有可辨别的顺序。这是因为出于对速度的追求, **HashSet** 使用了散列,请参阅[附录:集合主题]()一章。由 **HashSet** 维护的顺序与 **TreeSet** 或 **LinkedHashSet** 不同,因为它们的实现具有不同的元素存储方式。 **TreeSet** 将元素存储在红-黑树数据结构中,而 **HashSet** 使用散列函数。 **LinkedHashSet** 因为查询速度的原因也使用了散列,但是看起来使用了链表来维护元素的插入顺序。显然,哈希算法已经更改,现在 **Integer** 按顺序排序。 但是,您不应该依赖此行为:
+早期 Java 版本中的 **HashSet** 产生的输出没有可辨别的顺序。这是因为出于对速度的追求, **HashSet** 使用了散列,请参阅[附录:集合主题]()一章。由 **HashSet** 维护的顺序与 **TreeSet** 或 **LinkedHashSet** 不同,因为它们的实现具有不同的元素存储方式。 **TreeSet** 将元素存储在红-黑树数据结构中,而 **HashSet** 使用散列函数。 **LinkedHashSet** 因为查询速度的原因也使用了散列,但是看起来使用了链表来维护元素的插入顺序。看起来散列算法好像已经改变了,现在 **Integer** 按顺序排序。但是,您不应该依赖此行为:
```java
// collections/SetOfString.java
@@ -916,7 +915,7 @@ public class SortedSetOfString {
*/
```
-最常见的操作之一是使用 **contains()** 测试成员归属性,但也有一些其它操作,可能会让你想起在小学学过的维恩图(译者注:利用图形的交合表示多个集合之间的逻辑关系):
+最常见的操作之一是使用 `contains()` 测试成员归属性,但也有一些其它操作,这可能会让你想起在小学学过的维恩图(译者注:利用图形的交合表示多个集合之间的逻辑关系):
```java
// collections/SetOperations.java
@@ -958,9 +957,9 @@ Z]
*/
```
-这些方法名都是自解释的,JDK文档中还有一些其它的方法。
+这些方法名都是自解释的,JDK 文档中还有一些其它的方法。
-能够产生每个元素都唯一的列表是相当有用的功能。例如,假设想要列出上面的 **SetOperations.java** 文件中的所有单词,通过使用本书后面介绍的 **java.nio.file.Files.readAllLines()** 方法,可以打开一个文件,并将其作为一个 **List\** 读取,每个 **String** 都是输入文件中的一行:
+能够产生每个元素都唯一的列表是相当有用的功能。例如,假设想要列出上面的 **SetOperations.java** 文件中的所有单词,通过使用本书后面介绍的 `java.nio.file.Files.readAllLines()` 方法,可以打开一个文件,并将其作为一个 **List\** 读取,每个 **String** 都是输入文件中的一行:
```java
// collections/UniqueWords.java
@@ -991,7 +990,7 @@ void]
*/
```
-我们逐步浏览文件中的每一行,并使用 **String.split()** 将其分解为单词,这里使用正则表达式 **\\\ W +** ,这意味着它会依据一个或多个(即 **+** )非单词字母来拆分字符串(正则表达式将在[字符串]()章节介绍)。每个结果单词都会添加到 **Set words** 中。因为它是 **TreeSet** ,所以对结果进行排序。这里,排序是按*字典顺序*(lexicographically)完成的,因此大写和小写字母位于不同的组中。如果想按*字母顺序*(alphabetically)对其进行排序,可以向 **TreeSet** 构造器传入 **String.CASE_INSENSITIVE_ORDER** 比较器(比较器是一个建立排序顺序的对象):
+我们逐步浏览文件中的每一行,并使用 `String.split()` 将其分解为单词,这里使用正则表达式 **\\\ W +** ,这意味着它会依据一个或多个(即 **+** )非单词字母来拆分字符串(正则表达式将在[字符串]()章节介绍)。每个结果单词都会添加到 **Set words** 中。因为它是 **TreeSet** ,所以对结果进行排序。这里,排序是按*字典顺序*(lexicographically)完成的,因此大写和小写字母位于不同的组中。如果想按*字母顺序*(alphabetically)对其进行排序,可以向 **TreeSet** 构造器传入 **String.CASE_INSENSITIVE_ORDER** 比较器(比较器是一个建立排序顺序的对象):
```java
// collections/UniqueWordsAlphabetic.java
@@ -1058,9 +1057,9 @@ public class Statistics {
*/
```
-- **[1]** 自动包装机制将随机生成的 **int** 转换为可以与 **HashMap** 一起使用的 **Integer** 引用(不能使用基本类型的集合)。如果键不在集合中,则 **get()** 返回 **null** (这意味着这是第一次找到该数字)。否则, **get()** 会为键生成与之关联的 **Integer** 值,然后该值被递增(自动包装机制再次简化了表达式,但实际上确实发生了对 **Integer** 的装箱和拆箱)。
+- **[1]** 自动包装机制将随机生成的 **int** 转换为可以与 **HashMap** 一起使用的 **Integer** 引用(不能使用基本类型的集合)。如果键不在集合中,则 `get()` 返回 **null** (这意味着这是第一次找到该数字)。否则, `get()` 会为键生成与之关联的 **Integer** 值,然后该值被递增(自动包装机制再次简化了表达式,但实际上确实发生了对 **Integer** 的装箱和拆箱)。
-接下来的示例将使用一个 **String** 描述来查找 **Pet** 对象。它还展示了通过使用 **containsKey()** 和 **containsValue()** 方法去测试一个 **Map** ,以查看它是否包含某个键或某个值:
+接下来的示例将使用一个 **String** 描述来查找 **Pet** 对象。它还展示了通过使用 `containsKey()` 和 `containsValue()` 方法去测试一个 **Map** ,以查看它是否包含某个键或某个值:
```java
// collections/PetMap.java
@@ -1089,7 +1088,7 @@ true
*/
```
-**Map** 与数组和其他的 **Collection** 一样,可以轻松地扩展到多个维度,只需要创建一个值为 **Map** 的 **Map**(这些 **Map** 的值可以是其他集合,甚至是其他 **Map**)。因此,能够很容易地将集合组合起来以快速生成强大的数据结构。例如,假设你正在追踪有多个宠物的人,只需要一个 **Map\\>** 即可:
+**Map** 与数组和其他的 **Collection** 一样,可以轻松地扩展到多个维度,只需要创建一个值为 **Map** 的 **Map**(这些 **Map** 的值可以是其他集合,甚至是其他 **Map**)。因此,能够很容易地将集合组合起来以快速生成强大的数据结构。例如,假设你正在追踪有多个宠物的人,只需要一个 **Map\>** 即可:
```java
@@ -1157,7 +1156,7 @@ Person Luke has:
*/
```
-**Map** 可以返回由其键组成的 **Set** ,由其值组成的 **Collection** ,或者其键值对的 **Set** 。 **keySet()** 方法生成由在 **petPeople** 中的所有键组成的 **Set** ,它在 *for-in* 语句中被用来遍历该 **Map** 。
+**Map** 可以返回由其键组成的 **Set** ,由其值组成的 **Collection** ,或者其键值对的 **Set** 。 `keySet()` 方法生成由在 **petPeople** 中的所有键组成的 **Set** ,它在 *for-in* 语句中被用来遍历该 **Map** 。
@@ -1196,9 +1195,9 @@ B r o n t o s a u r u s
*/
```
-**offer()** 是与 **Queue** 相关的方法之一,它在允许的情况下,在队列的尾部插入一个元素,或者返回 **false** 。 **peek()** 和 **element()** 都返回队头元素而不删除它,但是如果队列为空,则 **element()** 抛出 **NoSuchElementException** ,而 **peek()** 返回 **null** 。 **poll()** 和 **remove()** 都删除并返回队头元素,但如果队列为空,**poll()** 返回 **null** ,而 **remove()** 抛出 **NoSuchElementException** 。
+`offer()` 是与 **Queue** 相关的方法之一,它在允许的情况下,在队列的尾部插入一个元素,或者返回 **false** 。 `peek()` 和 `element()` 都返回队头元素而不删除它,但是如果队列为空,则 `element()` 抛出 **NoSuchElementException** ,而 `peek()` 返回 **null** 。 `poll()` 和 `remove()`* 都删除并返回队头元素,但如果队列为空,`poll()` 返回 **null** ,而 `remove()` 抛出 **NoSuchElementException** 。
-自动包装机制会自动将 **nextInt()** 的 **int** 结果转换为 **queue** 所需的 **Integer** 对象,并将 **char c** 转换为 **qc** 所需的 **Character** 对象。 **Queue** 接口窄化了对 **LinkedList** 方法的访问权限,因此只有适当的方法才能使用,因此能够访问到的 **LinkedList** 的方法会变少(这里实际上可以将 **Queue** 强制转换回 **LinkedList** ,但至少我们不鼓励这样做)。
+自动包装机制会自动将 `nextInt()` 的 **int** 结果转换为 **queue** 所需的 **Integer** 对象,并将 **char c** 转换为 **qc** 所需的 **Character** 对象。 **Queue** 接口窄化了对 **LinkedList** 方法的访问权限,因此只有适当的方法才能使用,因此能够访问到的 **LinkedList** 的方法会变少(这里实际上可以将 **Queue** 强制转换回 **LinkedList** ,但至少我们不鼓励这样做)。
与 **Queue** 相关的方法提供了完整而独立的功能。 也就是说,对于 **Queue** 所继承的 **Collection** ,在不需要使用它的任何方法的情况下,就可以拥有一个可用的 **Queue** 。
@@ -1209,7 +1208,7 @@ B r o n t o s a u r u s
优先级队列声明下一个弹出的元素是最需要的元素(具有最高的优先级)。例如,在机场,当飞机临近起飞时,这架飞机的乘客可以在办理登机手续时排到队头。如果构建了一个消息传递系统,某些消息比其他消息更重要,应该尽快处理,而不管它们何时到达。在Java 5 中添加了 **PriorityQueue** ,以便自动实现这种行为。
-当在 **PriorityQueue** 上调用 **offer()** 方法来插入一个对象时,该对象会在队列中被排序。[^5]默认的排序使用队列中对象的*自然顺序*(natural order),但是可以通过提供自己的 **Comparator** 来修改这个顺序。 **PriorityQueue** 确保在调用**peek()** , **poll()** 或 **remove()** 方法时,获得的元素将是队列中优先级最高的元素。
+当在 **PriorityQueue** 上调用 `offer()` 方法来插入一个对象时,该对象会在队列中被排序。[^5]默认的排序使用队列中对象的*自然顺序*(natural order),但是可以通过提供自己的 **Comparator** 来修改这个顺序。 **PriorityQueue** 确保在调用 `peek()` , `poll()` 或 `remove()` 方法时,获得的元素将是队列中优先级最高的元素。
让 **PriorityQueue** 与 **Integer** , **String** 和 **Character** 这样的内置类型一起工作易如反掌。在下面的示例中,第一组值与前一个示例中的随机值相同,可以看到它们从 **PriorityQueue** 中弹出的顺序与前一个示例不同:
@@ -1266,7 +1265,7 @@ C B A A
*/
```
-**PriorityQueue** 是允许重复的,最小的值具有最高的优先级(如果是 **String** ,空格也可以算作值,并且比字母的优先级高)。为了展示如何通过提供自己的 **Comparator** 对象来改变顺序,第三个对 **PriorityQueue\** 构造器的调用,和第二个对 **PriorityQueue\** 的调用使用了由 **Collections.reverseOrder()** (Java 5 中新添加的)产生的反序的 **Comparator** 。
+**PriorityQueue** 是允许重复的,最小的值具有最高的优先级(如果是 **String** ,空格也可以算作值,并且比字母的优先级高)。为了展示如何通过提供自己的 **Comparator** 对象来改变顺序,第三个对 **PriorityQueue\** 构造器的调用,和第二个对 **PriorityQueue\** 的调用使用了由 `Collections.reverseOrder()` (Java 5 中新添加的)产生的反序的 **Comparator** 。
最后一部分添加了一个 **HashSet** 来消除重复的 **Character**。
@@ -1277,7 +1276,7 @@ C B A A
**Collection** 是所有序列集合共有的根接口。它可能会被认为是一种“附属接口”(incidental interface),即因为要表示其他若干个接口的共性而出现的接口。此外,**java.util.AbstractCollection** 类提供了 **Collection** 的默认实现,使得你可以创建 **AbstractCollection** 的子类型,而其中没有不必要的代码重复。
-使用接口描述的一个理由是它可以使我们创建更通用的代码。通过针对接口而非具体实现来编写代码,我们的代码可以应用于更多类型的对象。[^6]因此,如果所编写的方法接受一个 **Collection** ,那么该方法可以应用于任何实现了 **Collection** 的类——这也就使得一个新类可以选择去实现 **Collection** 接口,以便该方法可以使用它。标准 C++ 类库中的的集合并没有共同的基类——集合之间的所有共性都是通过迭代器实现的。在 Java 中,遵循 C++ 的方式看起来似乎很明智,即用迭代器而不是 **Collection** 来表示集合之间的共性。但是,这两种方法绑定在了一起,因为实现 **Collection** 就意味着需要提供 **iterator()** 方法:
+使用接口描述的一个理由是它可以使我们创建更通用的代码。通过针对接口而非具体实现来编写代码,我们的代码可以应用于更多类型的对象。[^6]因此,如果所编写的方法接受一个 **Collection** ,那么该方法可以应用于任何实现了 **Collection** 的类——这也就使得一个新类可以选择去实现 **Collection** 接口,以便该方法可以使用它。标准 C++ 类库中的的集合并没有共同的基类——集合之间的所有共性都是通过迭代器实现的。在 Java 中,遵循 C++ 的方式看起来似乎很明智,即用迭代器而不是 **Collection** 来表示集合之间的共性。但是,这两种方法绑定在了一起,因为实现 **Collection** 就意味着需要提供 `iterator()` 方法:
```java
// collections/InterfaceVsIterator.java
@@ -1334,11 +1333,11 @@ Britney=Pug, Sam=Cymric, Spot=Pug, Fluffy=Manx}
*/
```
-两个版本的 **display()** 方法都可以使用 **Map** 或 **Collection** 的子类型来工作。 而且**Collection** 接口和 **Iterator** 都将 **display()** 方法与低层集合的特定实现解耦。
+两个版本的 `display()` 方法都可以使用 **Map** 或 **Collection** 的子类型来工作。 而且**Collection** 接口和 **Iterator** 都将 `display()` 方法与低层集合的特定实现解耦。
-在本例中,这两种方式都可以奏效。事实上, **Collection** 要更方便一点,因为它是 **Iterable** 类型,因此在 **display(Collection)** 的实现中可以使用 *for-in* 构造,这使得代码更加清晰。
+在本例中,这两种方式都可以奏效。事实上, **Collection** 要更方便一点,因为它是 **Iterable** 类型,因此在 `display(Collection)` 的实现中可以使用 *for-in* 构造,这使得代码更加清晰。
-当需要实现一个不是 **Collection** 的外部类时,由于让它去实现 **Collection** 接口可能非常困难或麻烦,因此使用 **Iterator** 就会变得非常吸引人。例如,如果我们通过继承一个持有 **Pet** 对象的类来创建一个 **Collection** 的实现,那么我们必须实现 **Collection** 所有的方法,即使我们不在 **display()** 方法中使用它们,也必须这样做。虽然这可以通过继承 **AbstractCollection** 而很容易地实现,但是无论如何还是要被强制去实现 **iterator()** 和 **size()** 方法,这些方法 **AbstractCollection** 没有实现,但是 **AbstractCollection** 中的其它方法会用到:
+当需要实现一个不是 **Collection** 的外部类时,由于让它去实现 **Collection** 接口可能非常困难或麻烦,因此使用 **Iterator** 就会变得非常吸引人。例如,如果我们通过继承一个持有 **Pet** 对象的类来创建一个 **Collection** 的实现,那么我们必须实现 **Collection** 所有的方法,即使我们不在 `display()` 方法中使用它们,也必须这样做。虽然这可以通过继承 **AbstractCollection** 而很容易地实现,但是无论如何还是要被强制去实现 `iterator()` 和 `size()` 方法,这些方法 **AbstractCollection** 没有实现,但是 **AbstractCollection** 中的其它方法会用到:
```java
// collections/CollectionSequence.java
@@ -1380,11 +1379,11 @@ extends AbstractCollection {
*/
```
-**remove()** 方法是一个“可选操作”,在[附录:集合主题]()中详细介绍。 这里可以不必实现它,如果你调用它,它将抛出异常。
+`remove()` 方法是一个“可选操作”,在[附录:集合主题]()中详细介绍。 这里可以不必实现它,如果你调用它,它将抛出异常。
-- **[1]** 你可能会认为,因为 **iterator()** 返回 **Iterator\** ,匿名内部类定义可以使用菱形语法,Java可以推断出类型。但这不起作用,类型推断仍然非常有限。
+- **[1]** 你可能会认为,因为 `iterator()` 返回 **Iterator\** ,匿名内部类定义可以使用菱形语法,Java可以推断出类型。但这不起作用,类型推断仍然非常有限。
-这个例子表明,如果实现了 **Collection** ,就必须实现 **iterator()** ,并且只拿实现 **iterator()** 与继承 **AbstractCollection** 相比,花费的代价只有略微减少。但是,如果类已经继承了其他的类,那么就不能继承再 **AbstractCollection** 了。 在这种情况下,要实现 **Collection** ,就必须实现该接口中的所有方法。 此时,继承并提供创建迭代器的能力要容易得多:
+这个例子表明,如果实现了 **Collection** ,就必须实现 `iterator()` ,并且只拿实现 `iterator()` 与继承 **AbstractCollection** 相比,花费的代价只有略微减少。但是,如果类已经继承了其他的类,那么就不能继承再 **AbstractCollection** 了。在这种情况下,要实现 **Collection** ,就必须实现该接口中的所有方法。此时,继承并提供创建迭代器的能力要容易得多:
```java
// collections/NonCollectionSequence.java
@@ -1451,7 +1450,7 @@ public class ForInCollections {
由于 **cs** 是一个 **Collection** ,因此该代码展示了使用 *for-in* 是所有 **Collection** 对象的特征。
-这样做的原因是 Java 5 引入了一个名为 **Iterable** 的接口,该接口包含一个能够生成 **Iterator** 的 **iterator()** 方法。*for-in* 使用此 **Iterable** 接口来遍历序列。因此,如果创建了任何实现了 **Iterable** 的类,都可以将它用于 *for-in* 语句中:
+这样做的原因是 Java 5 引入了一个名为 **Iterable** 的接口,该接口包含一个能够生成 **Iterator** 的 `iterator()` 方法。*for-in* 使用此 **Iterable** 接口来遍历序列。因此,如果创建了任何实现了 **Iterable** 的类,都可以将它用于 *for-in* 语句中:
```java
// collections/IterableClass.java
@@ -1488,7 +1487,7 @@ And that is how we know the Earth to be banana-shaped.
*/
```
-**iterator()** 返回的是实现了 **Iterator\** 的匿名内部类的实例,该匿名内部类可以遍历数组中的每个单词。在 **main()** 中,可以看到 **IterableClass** 确实可以用于 *for-in* 语句。
+`iterator()` 返回的是实现了 **Iterator\** 的匿名内部类的实例,该匿名内部类可以遍历数组中的每个单词。在主方法中,可以看到 **IterableClass** 确实可以用于 *for-in* 语句。
在 Java 5 中,许多类都是 **Iterable** ,主要包括所有的 **Collection** 类(但不包括各种 **Maps** )。 例如,下面的代码可以显示所有的操作系统环境变量:
@@ -1507,7 +1506,7 @@ public class EnvironmentVariables {
}
```
-**System.getenv()** [^7]返回一个 **Map** , **entrySet()** 产生一个由 **Map.Entry** 的元素构成的 **Set** ,并且这个 **Set** 是一个 **Iterable** ,因此它可以用于 *for-in* 循环。
+`System.getenv()` [^7]返回一个 **Map** , `entrySet()` 产生一个由 **Map.Entry** 的元素构成的 **Set** ,并且这个 **Set** 是一个 **Iterable** ,因此它可以用于 *for-in* 循环。
*for-in* 语句适用于数组或其它任何 **Iterable** ,但这并不意味着数组肯定也是个 **Iterable** ,也不会发生任何自动装箱:
@@ -1539,7 +1538,7 @@ public class ArrayIsNotIterable {
### 适配器方法惯用法
-如果现在有一个 **Iterable** 类,你想要添加一种或多种在 *for-in* 语句中使用这个类的方法,应该怎么做呢? 例如,假设你希望可以选择是在正向还是反向遍历一个单词列表。如果直接继承这个类,并覆盖 **iterator()** 方法,则只能替换现有的方法,而不能实现选择。
+如果现在有一个 **Iterable** 类,你想要添加一种或多种在 *for-in* 语句中使用这个类的方法,应该怎么做呢? 例如,假设你希望可以选择是在正向还是反向遍历一个单词列表。如果直接继承这个类,并覆盖 `iterator()` 方法,则只能替换现有的方法,而不能实现选择。
一种解决方案是所谓*适配器方法*(Adapter Method)的惯用法。“适配器”部分来自于设计模式,因为必须要提供特定的接口来满足 *for-in* 语句。如果已经有一个接口并且需要另一个接口时,则编写适配器就可以解决这个问题。
在这里,若希望在默认的正向迭代器的基础上,添加产生反向迭代器的能力,因此不能使用覆盖,相反,而是添加了一个能够生成 **Iterable** 对象的方法,该对象可以用于 *for-in* 语句。这使得我们可以提供多种使用 *for-in* 语句的方式:
@@ -1592,7 +1591,7 @@ be to not or be To
*/
```
-在 **main()** 中,如果直接将 **ral** 对象放在 *for-in* 语句中,则会得到(默认的)正向迭代器。但是如果在该对象上调用 **reversed()** 方法,它会产生不同的行为。
+在主方法中,如果直接将 **ral** 对象放在 *for-in* 语句中,则会得到(默认的)正向迭代器。但是如果在该对象上调用 `reversed()` 方法,它会产生不同的行为。
通过使用这种方式,可以在 **IterableClass.java** 示例中添加两种适配器方法:
@@ -1649,9 +1648,9 @@ And that is how we know the Earth to be banana-shaped.
*/
```
-注意,第二个方法 **random()** 没有创建它自己的 **Iterator** ,而是直接返回被打乱的 **List** 中的 **Iterator** 。
+注意,第二个方法 `random()` 没有创建它自己的 **Iterator** ,而是直接返回被打乱的 **List** 中的 **Iterator** 。
-从输出中可以看到, **Collections.shuffle()** 方法不会影响到原始数组,而只是打乱了 **shuffled** 中的引用。之所以这样,是因为 **randomized()** 方法用一个 **ArrayList** 将 **Arrays.asList()** 的结果包装了起来。如果这个由 **Arrays.asList()** 生成的 **List** 被直接打乱,那么它将修改底层数组,如下所示:
+从输出中可以看到, `Collections.shuffle()` 方法不会影响到原始数组,而只是打乱了 **shuffled** 中的引用。之所以这样,是因为 `randomized()` 方法用一个 **ArrayList** 将 `Arrays.asList()` 的结果包装了起来。如果这个由 `Arrays.asList()` 生成的 **List** 被直接打乱,那么它将修改底层数组,如下所示:
```java
// collections/ModifyingArraysAsList.java
@@ -1685,7 +1684,7 @@ array: [9, 1, 6, 3, 7, 2, 5, 10, 4, 8]
*/
```
-在第一种情况下, **Arrays.asList()** 的输出被传递给了 **ArrayList** 的构造器,这将创建一个引用 **ia** 的元素的 **ArrayList** ,因此打乱这些引用不会修改该数组。但是,如果直接使用 **Arrays.asList(ia)** 的结果,这种打乱就会修改 **ia** 的顺序。重要的是要注意 **Arrays.asList()** 生成一个 **List** 对象,该对象使用底层数组作为其物理实现。如果执行的会修改这个 **List** ,并且不希望修改原始数组,那么就应该在另一个集合中创建一个副本。
+在第一种情况下, `Arrays.asList()` 的输出被传递给了 **ArrayList** 的构造器,这将创建一个引用 **ia** 的元素的 **ArrayList** ,因此打乱这些引用不会修改该数组。但是,如果直接使用 `Arrays.asList(ia)` 的结果,这种打乱就会修改 **ia** 的顺序。重要的是要注意 `Arrays.asList()` 生成一个 **List** 对象,该对象使用底层数组作为其物理实现。如果执行的操作会修改这个 **List** ,并且不希望修改原始数组,那么就应该在另一个集合中创建一个副本。
## 本章小结
@@ -1791,7 +1790,7 @@ Serializable]
*/
```
-除 **TreeSet** 之外的所有 **Set** 都具有与 **Collection** 完全相同的接口。**List** 和 **Collection** 存在着明显的不同,尽管 **List** 所要求的方法都在 **Collection** 中。另一方面,在 **Queue** 接口中的方法是独立的,在创建具有 **Queue** 功能的实现时,不需要使用 **Collection** 方法。最后, **Map** 和 **Collection** 之间唯一的交集是 **Map** 可以使用 **entrySet()** 和 **values()** 方法来产生 **Collection** 。
+除 **TreeSet** 之外的所有 **Set** 都具有与 **Collection** 完全相同的接口。**List** 和 **Collection** 存在着明显的不同,尽管 **List** 所要求的方法都在 **Collection** 中。另一方面,在 **Queue** 接口中的方法是独立的,在创建具有 **Queue** 功能的实现时,不需要使用 **Collection** 方法。最后, **Map** 和 **Collection** 之间唯一的交集是 **Map** 可以使用 `entrySet()` 和 `values()` 方法来产生 **Collection** 。
请注意,标记接口 **java.util.RandomAccess** 附加到了 **ArrayList** 上,但不附加到 **LinkedList** 上。这为根据特定 **List** 动态改变其行为的算法提供了信息。
@@ -1799,19 +1798,19 @@ Serializable]
尽管存在这些问题,但 Java 集合仍是在日常工作中使用的基本工具,它可以使程序更简洁、更强大、更有效。你可能需要一段时间才能熟悉集合类库的某些方面,但我想你很快就会找到自己的路子,来获得和使用这个类库中的类。
-[^1]: 许多语言,例如Perl,Python和Ruby,都有集合的本地支持。
+[^1]: 许多语言,例如 Perl ,Python 和 Ruby ,都有集合的本地支持。
[^2]: 这里是操作符重载的用武之地,C++和C#的集合类都使用操作符重载生成了更简洁的语法。
[^3]: 在[泛型]()章节的末尾,有个关于这个问题是否很严重的讨论。但是,[泛型]()章节还将展示Java泛型远不止是类型安全的集合这么简单。
-[^4]: **remove()** 是一个所谓的“可选”方法(还有一些其它的这种方法),这意味着并非所有的 **Iterator** 实现都必须实现该方法。这个问题将在[附录:集合主题]()中介绍。但是,标准Java库集合实现了 **remove()** ,因此在[附录:集合主题]()章节之前,都不必担心这个问题。
+[^4]: `remove()` 是一个所谓的“可选”方法(还有一些其它的这种方法),这意味着并非所有的 **Iterator** 实现都必须实现该方法。这个问题将在[附录:集合主题]()中介绍。但是,标准 Java 库集合实现了 `remove()` ,因此在[附录:集合主题]()章节之前,都不必担心这个问题。
[^5]: 这实际上依赖于具体实现。优先级队列算法通常会按插入顺序排序(维护一个*堆*),但它们也可以在删除时选择最重要的元素。 如果对象的优先级在它在队列中等待时可以修改,那么算法的选择就显得很重要了。
[^6]: 有些人提倡这样一种自动创建机制,即对一个类中所有可能的方法组合都自动创建一个接口,有时候对于单个的类都是如此。 我相信接口的意义不应该仅限于方法组合的机械地复制,因此我在创建接口之前,总是要先看到增加接口带来的价值。
-[^7]: 这在Java 5之前是不可用的,因为该方法被认为与操作系统的耦合度过紧,因此违反“一次编写,处处运行”的原则。现在提供它这一事实表明,Java的设计者们更加务实了。
+[^7]: 这在 Java 5 之前是不可用的,因为该方法被认为与操作系统的耦合度过紧,因此违反“一次编写,处处运行”的原则。现在却提供它,这一事实表明, Java 的设计者们更加务实了。
From cd438293a616c913a09219c3296c22d23cc8d1b9 Mon Sep 17 00:00:00 2001
From: WangHengwei <35023376@qq.com>
Date: Fri, 2 Aug 2019 11:06:14 +0800
Subject: [PATCH 003/461] Update 03-Objects-Everywhere.md
---
docs/book/03-Objects-Everywhere.md | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/docs/book/03-Objects-Everywhere.md b/docs/book/03-Objects-Everywhere.md
index 2941c371..bd96286f 100644
--- a/docs/book/03-Objects-Everywhere.md
+++ b/docs/book/03-Objects-Everywhere.md
@@ -417,7 +417,7 @@ import java.util.*;
2. 创建一个与此类的任何对象无关的方法。也就是说,即使没有创建对象,也能调用该方法。
-**static** 关键字(从 C++ 采用)就符合上述两点要求。当我们说某个事物是静态时,就意味着该字段或方法不依赖于任何特定的对象实例 。 即使我们从未创建过该类的对象,也可以调用其静态方法或访问静态字段。相反,对于普通的非静态字段和方法,我们必须要先创建一个对象并使用该对象来访问字段或方法,因为非静态字段和方法必须与特定对象关联 [^6] 。
+**static** 关键字(从 C++ 采用)就符合上述两点要求。当我们说某个事物是静态时,就意味着该字段或方法不依赖于任何特定的对象实例 。 即使我们从未创建过该类的对象,也可以调用其静态方法或访问其静态字段。相反,对于普通的非静态字段和方法,我们必须要先创建一个对象并使用该对象来访问字段或方法,因为非静态字段和方法必须与特定对象关联 [^6] 。
一些面向对象的语言使用类数据(class data)和类方法(class method),表示静态数据和方法只是作为类,而不是类的某个特定对象而存在的。有时 Java 文献也使用这些术语。
@@ -436,7 +436,7 @@ StaticTest st1 = new StaticTest();
StaticTest st2 = new StaticTest();
```
-`st1.i` 和 `st2.i` 指向同一块存储空间,因此它们的值都是 47。引用静态变量有两种方法。在前面的示例中,我们通过一个对象来去定位它,例如 `st2.i`。我们也可以通过类名直接引用它,这种方式对于非静态成员则不行:
+`st1.i` 和 `st2.i` 指向同一块存储空间,因此它们的值都是 47。引用静态变量有两种方法。在前面的示例中,我们通过一个对象来定位它,例如 `st2.i`。我们也可以通过类名直接引用它,这种方式对于非静态成员则不可行:
```java
StaticTest.i++;
@@ -444,7 +444,7 @@ StaticTest.i++;
`++` 运算符将会使变量结果 + 1。此时 `st1.i` 和 `st2.i` 的值都变成了 48。
-使用类名直接引用静态变量是首选方法,因为它强调了变量的静态属性。类似的逻辑也适用于静态方法。我们可以通过对象引用静态方法,就像使用任何方法一样,也可以通过特殊的语法方式 `Classname.method()` 来直接调用静态属性或方法 [^7]。 代码示例:
+使用类名直接引用静态变量是首选方法,因为它强调了变量的静态属性。类似的逻辑也适用于静态方法。我们可以通过对象引用静态方法,就像使用任何方法一样,也可以通过特殊的语法方式 `Classname.method()` 来直接调用静态属性或方法 [^7]。 代码示例:
```java
class Incrementable {
@@ -454,7 +454,7 @@ class Incrementable {
}
```
-上例中,`Incrementable` 的 `increment()` 方法通过 `++` 运算符将静态数据 `i` 加1。我们依然可以先实例化对象再调用该方法。 代码示例:
+上例中,`Incrementable` 的 `increment()` 方法通过 `++` 运算符将静态数据 `i` 加 1。我们依然可以先实例化对象再调用该方法。 代码示例:
```java
Incrementable sf = new Incrementable();
@@ -489,17 +489,17 @@ public class HelloDate {
```
-在这本书中,所有代码示例的第一行都是注释行,其中包含文件的路径信息(比如本章的目录名是 **objects**),后跟文件名。我的工具可以根据这些信息自动提取和测试书籍的代码,你也可以通过参考第一行注释轻松地在 Github 库中找到对应的代码示例。
+在本书中,所有代码示例的第一行都是注释行,其中包含文件的路径信息(比如本章的目录名是 **objects**),后跟文件名。我的工具可以根据这些信息自动提取和测试书籍的代码,你也可以通过参考第一行注释轻松地在 Github 库中找到对应的代码示例。
如果你想在代码中使用一些额外的类库,那么就必须在程序文件的开始处使用 **import** 关键字来导入它们。之所以说是额外的,因为有一些类库已经默认自动导入到每个文件里了。例如:`java.lang` 包。
现在打开你的浏览器在 [Oracle](https://www.oracle.com/) 上查看文档。如果你还没有从 [Oracle](https://www.oracle.com/) 网站上下载 JDK 文档,那现在就去 [^8] 。查看包列表,你会看到 Java 附带的所有不同的类库。
-选择 `java.lang` ,你会看到该库中所有类的列表。由于 `java.lang` 隐式包含在每个 Java代码文件中,因此这些类是自动可用的。`java.lang` 类库中没有 **Date** 类,所以我们必须导入其他的类库(即 Date 所在的类库)。如果你不清楚某个类所在的类库或者想查看类库中所有的类,那么可以在 Java 文档中选择 “Tree” 查看。
+选择 `java.lang`,你会看到该库中所有类的列表。由于 `java.lang` 隐式包含在每个 Java 代码文件中,因此这些类是自动可用的。`java.lang` 类库中没有 **Date** 类,所以我们必须导入其他的类库(即 Date 所在的类库)。如果你不清楚某个类所在的类库或者想查看类库中所有的类,那么可以在 Java 文档中选择 “Tree” 查看。
现在,我们可以找到 Java 附带的每个类。使用浏览器的“查找”功能查找 **Date**,搜索结果中将会列出 **java.util.Date**,我们就知道了 **Date** 在 **util** 库中,所以必须导入 **java.util.*** 才能使用 **Date**。
-如果你在文档中选择 **java.lang**,然后选择 **System**,你会看到 **System** 类中有几个字段,如果你选择了 **out**,你会发现它是一个静态的 **PrintStream** 对象。 所以,即使我们不使用 **new** 创建, **out** 对象就已经存在并可以使用。 **out** 对象可以执行的操作取决于它的类型: **PrintStream** ,其在文档中是一个超链接,如果单击该链接,我们将可以看到 **PrintStream** 对应的方法列表(更多详情,将在本书后面介绍)。 现在我们重点说的是 **println()** 这个方法。 它的作用是 “将信息输出到控制台,并以换行符结束”。既然如此,我们可以这样编码来输出信息到控制台。 代码示例:
+如果你在文档中选择 **java.lang**,然后选择 **System**,你会看到 **System** 类中有几个字段,如果你选择了 **out**,你会发现它是一个静态的 **PrintStream** 对象。 所以,即使我们不使用 **new** 创建, **out** 对象就已经存在并可以使用。 **out** 对象可以执行的操作取决于它的类型: **PrintStream** ,其在文档中是一个超链接,如果单击该链接,我们将可以看到 **PrintStream** 对应的方法列表(更多详情,将在本书后面介绍)。 现在我们重点说的是 **println()** 这个方法。 它的作用是 “将信息输出到控制台,并以换行符结束”。既然如此,我们可以这样编码来输出信息到控制台。 代码示例:
```java
System.out.println("A String of things");
From 577fce56e29681766c9aa14c0076d96cd73de90b Mon Sep 17 00:00:00 2001
From: WangHengwei <35023376@qq.com>
Date: Fri, 2 Aug 2019 15:40:30 +0800
Subject: [PATCH 004/461] [hotfix] fix a typo
---
docs/book/04-Operators.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/book/04-Operators.md b/docs/book/04-Operators.md
index ebf19580..05ed80d5 100644
--- a/docs/book/04-Operators.md
+++ b/docs/book/04-Operators.md
@@ -1472,7 +1472,7 @@ public class AllOps {
}
```
-**注意** :**boolean** 类型的的运算是受限的。你能为其赋值 `true` 或 `false`,也可测试它的值是否是 `true` 或 `false`。但你不能对其作加减等其他运算。
+**注意** :**boolean** 类型的运算是受限的。你能为其赋值 `true` 或 `false`,也可测试它的值是否是 `true` 或 `false`。但你不能对其作加减等其他运算。
在 **char**,**byte** 和 **short** 类型中,我们可以看到算术运算符的“类型转换”效果。我们必须要显式强制类型转换才能将结果重新赋值为原始类型。对于 **int** 类型的运算则不用转换,因为默认就是 **int** 型。虽然我们不用再停下来思考这一切是否安全,但是两个大的 int 型整数相乘时,结果有可能超出 **int** 型的范围,这种情况下结果会发生溢出。下面的代码示例:
From 6b3fcca2ba3eef7236b8a67465c7de71d73dca3d Mon Sep 17 00:00:00 2001
From: WangHengwei <35023376@qq.com>
Date: Fri, 2 Aug 2019 16:17:16 +0800
Subject: [PATCH 005/461] [hotfix] fix a typo
---
docs/book/05-Control-Flow.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/book/05-Control-Flow.md b/docs/book/05-Control-Flow.md
index e2f0dcb3..9f5676e8 100644
--- a/docs/book/05-Control-Flow.md
+++ b/docs/book/05-Control-Flow.md
@@ -28,7 +28,7 @@ public class TrueFalse {
true false
```
-通过上一章的学习,我们知道任何关系运算符都可以产生条件语句。 **注意**:在 Java 中使用数值作为布尔值是非法的,即便这种操作在 C/C++ 中是被允许的(在这些语言中,“真”为非零,而“假”是零)。如果想在布尔测试中使用一个非布尔值,那么首先需要使用条件表达式来产生 **boolean** 类型的结果,例如 `if(a! = 0)`。
+通过上一章的学习,我们知道任何关系运算符都可以产生条件语句。 **注意**:在 Java 中使用数值作为布尔值是非法的,即便这种操作在 C/C++ 中是被允许的(在这些语言中,“真”为非零,而“假”是零)。如果想在布尔测试中使用一个非布尔值,那么首先需要使用条件表达式来产生 **boolean** 类型的结果,例如 `if(a != 0)`。
## if-else
From a96df2b6ba5b2e47e277db58fd24c96838d57639 Mon Sep 17 00:00:00 2001
From: jjm <1065658889@qq.com>
Date: Fri, 2 Aug 2019 20:40:59 +0800
Subject: [PATCH 006/461] =?UTF-8?q?=E6=A8=A1=E5=BC=8F=E6=A6=82=E5=BF=B5?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
docs/book/25-Patterns.md | 94 +++++++++++++++++++++++++++++++++++++++-
1 file changed, 93 insertions(+), 1 deletion(-)
diff --git a/docs/book/25-Patterns.md b/docs/book/25-Patterns.md
index 9ae1f0d2..345e46dc 100644
--- a/docs/book/25-Patterns.md
+++ b/docs/book/25-Patterns.md
@@ -6,10 +6,102 @@
## 概念
+最初,你可以将模式视为解决特定类问题的一种特别巧妙且有深刻见解的方法。这就像前辈已经从所有角度去解决问题,并提出了最通用,最灵活的解决方案。问题可能是你之前看到并解决过的问题,但你的解决方案可能没有你在模式中体现的那种完整性。
+虽然它们被称为“设计模式”,但它们实际上并不与设计领域相关联。模式似乎与传统的分析、设计和实现的思维方式不同。相反,模式在程序中体现了一个完整的思想,因此它有时会出现在分析阶段或高级设计阶段。因为模式在代码中有一个直接的实现,所以你可能不会期望模式在低级设计或实现之前出现(而且通常在到达这些阶段之前,你不会意识到需要一个特定的模式)。
+
+模式的基本概念也可以看作是程序设计的基本概念:添加抽象层。当你抽象一些东西的时候,就像在剥离特定的细节,而这背后最重要的动机之一是:
+> **将易变的事物与不变的事物分开**
+
+另一种方法是,一旦你发现程序的某些部分可能因某种原因而发生变化,你要保持这些变化不会引起整个代码中其他变化。 如果代码更容易理解,那么维护起来会更容易。
+
+通常,开发一个优雅且易维护设计中最困难的部分是发现我称之为变化的载体(也就是最易改变的地方)。这意味着找到系统中最重要的变化,换而言之,找到变化会导致最严重后果的地方。一旦发现变化载体,就可以围绕构建设计的焦点。
+
+因此,设计模式的目标是隔离代码中的更改。 如果以这种方式去看,你已经在本书中看到了设计模式。 例如,继承可以被认为是一种设计模式(虽然是由编译器实现的)。它允许你表达所有具有相同接口的对象(即保持相同的行为)中的行为差异(这就是变化的部分)。组合也可以被视为一种模式,因为它允许你动态或静态地更改实现类的对象,从而改变类的工作方式。
+
+你还看到了设计模式中出现的另一种模式:迭代器(Java 1.0和1.1随意地将其称为枚举; Java 2 集合才使用Iterator)。当你逐个选择元素时并逐步处理,这会隐藏集合的特定实现。迭代器允许你编写通用代码,该代码对序列中的所有元素执行操作,而不考虑序列的构建方式。因此,你的通用代码可以与任何可以生成迭代器的集合一起使用。
+
+即使模式是非常有用的,但有些人断言:
+> **设计模式代表语言的失败。**
+
+这是一个非常重要的见解,因为一个模式在 C++ 有意义,可能在JAVA或者其他语言中就没有意义。出于这个原因,所以一个模式可能出现在设计模式书上,不意味着应用于你的编程语言是有用的。
+
+我认为“语言失败”这个观点是有道理的,但是我也认为这个观点过于简单化。如果你试图解决一个特定的问题,而你使用的语言没有直接提供支持你使用的技巧,你可以说这个是语言的失败。但是,你使用特定的技巧的频率的是多少呢?也许平衡是对的:当你使用特定的技巧的时候,你必须付出更多的努力,但是你又没有足够的理由去使得语言支持这个技术。另一方面,没有语言的支持,使用这种技术常常会很混乱,但是在语言支持下,你可能会改变编程方式(例如,Java 8流实现此目的)。
+
+### 单例模式
+也许单例模式是最简单的设计模式,它是一种提供一个且只有一个对象实例的方法。这在java库中使用,但是这有个更直接的示例:
+
+```java
+// patterns/SingletonPattern.java
+interface Resource {
+ int getValue();
+ void setValue(int x);
+}
+
+/*
+* 由于这不是从Cloneable基类继承而且没有添加可克隆性,
+* 因此将其设置为final可防止通过继承添加可克隆性。
+* 这也实现了线程安全的延迟初始化:
+*/
+final class Singleton {
+ private static final class ResourceImpl implements Resource {
+ private int i;
+ private ResourceImpl(int i) {
+ this.i = i;
+ }
+ public synchronized int getValue() {
+ return i;
+ }
+ public synchronized void setValue(int x) {
+ i = x;
+ }
+ }
+
+ private static class ResourceHolder {
+ private static Resource resource = new ResourceImpl(47);
+ }
+ public static Resource getResource() {
+ return ResourceHolder.resource;
+ }
+}
+
+public class SingletonPattern {
+ public static void main(String[] args) {
+ Resource r = Singleton.getResource();
+ System.out.println(r.getValue());
+ Resource s2 = Singleton.getResource();
+ s2.setValue(9);
+ System.out.println(r.getValue());
+ try {
+ // 不能这么做,会发生:compile-time error(编译时错误).
+ // Singleton s3 = (Singleton)s2.clone();
+ } catch(Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+} /* Output: 47 9 */
+```
+创建单例的关键是防止客户端程序员直接创建对象。 在这里,这是通过在Singleton类中将Resource的实现作为私有类来实现的。
+
+此时,你将决定如何创建对象。在这里,它是按需创建的,在第一次访问的时候创建。 该对象是私有的,只能通过public getResource()方法访问。
+
+
+懒惰地创建对象的原因是它嵌套的私有类resourceHolder在首次引用之前不会加载(在getResource()中)。当Resource对象加载的时候,静态初始化块将被调用。由于JVM的工作方式,这种静态初始化是线程安全的。为保证线程安全,Resource中的getter和setter是同步的。
+
+### 模式分类
+
+“设计模式”一书讨论了23种不同的模式,分为以下三种类别(所有这些模式都围绕着可能变化的特定方面)。
+
+1. **创建型**:如何创建对象。 这通常涉及隔离对象创建的细节,这样你的代码就不依赖于具体的对象的类型,因此在添加新类型的对象时不会更改。单例模式(Singleton)被归类为创作模式,本章稍后你将看到Factory Method的示例。
+
+2. **构造型**:设计对象以满足特定的项目约束。它们处理对象与其他对象连接的方式,以确保系统中的更改不需要更改这些连接。
+
+3. **行为型**:处理程序中特定类型的操作的对象。这些封装要执行的过程,例如解释语言、实现请求、遍历序列(如在迭代器中)或实现算法。本章包含观察者和访问者模式的例子。
+
+《设计模式》一书中每个设计模式都有单独的一个章节,每个章节都有一个或者多个例子,通常使用C++,但有时也使用SmallTalk。 本章不重复设计模式中显示的所有模式,因为该书独立存在,应单独研究。 相反,你会看到一些示例,可以为你提供关于模式的理解以及它们如此重要的原因。
-## 构建型
+## 构建应用程序框架
From f06b850470c9b14ca0e7ebe6a671eaee12dd4ef7 Mon Sep 17 00:00:00 2001
From: AlfredAlan <854723360@mail.com>
Date: Sun, 4 Aug 2019 17:53:56 +0800
Subject: [PATCH 007/461] =?UTF-8?q?=E9=99=84=E5=BD=95=EF=BC=9A=E5=B9=B6?=
=?UTF-8?q?=E5=8F=91=E5=BA=95=E5=B1=82=E5=8E=9F=E7=90=86=20=E7=BF=BB?=
=?UTF-8?q?=E8=AF=91=E8=87=B3=20DelayQueue?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
docs/book/Appendix-Low-Level-Concurrency.md | 141 ++++++++++++++++++--
1 file changed, 133 insertions(+), 8 deletions(-)
diff --git a/docs/book/Appendix-Low-Level-Concurrency.md b/docs/book/Appendix-Low-Level-Concurrency.md
index f7ef3ecc..e7f701dc 100644
--- a/docs/book/Appendix-Low-Level-Concurrency.md
+++ b/docs/book/Appendix-Low-Level-Concurrency.md
@@ -547,13 +547,13 @@ public class EvenProducer extends IntGenerator {
417 not even!
*/
```
-* [1] 一个任务有可能在另外一个任务执行第一个对 **currentEvenValue** 的递增操作之后,但是没有执行第二个操作之前,调用 `next()` 方法。这将使这个值处于 “不恰当” 的状态。
+* [1] 一个任务有可能在另外一个任务执行第一个对 **currentEvenValue** 的自增操作之后,但是没有执行第二个操作之前,调用 `next()` 方法。这将使这个值处于 “不恰当” 的状态。
为了证明这是可能发生的, `EvenChecker.test()` 创建了一组 **EventChecker** 对象,以连续读取 **EvenProducer** 的输出并测试检查每个数值是否都是偶数。如果不是,就会报告错误,而程序也将关闭。
多线程程序的部分问题是,即使存在 bug ,如果失败的可能性很低,程序仍然可以正确显示。
-重要的是要注意到递增操作自身需要多个步骤,并且在递增过程中任务可能会被线程机制挂起 - 也就是说,在 Java 中,递增不是原子性的操作。因此,如果不保护任务,即使单纯的递增也不是线程安全的。
+重要的是要注意到自增操作自身需要多个步骤,并且在自增过程中任务可能会被线程机制挂起 - 也就是说,在 Java 中,自增不是原子性的操作。因此,如果不保护任务,即使单纯的自增也不是线程安全的。
该示例程序并不总是在第一次非偶数产生时终止。所有任务都不会立即关闭,这是并发程序的典型特征。
@@ -584,7 +584,7 @@ synchronized void g() { /* ... */ }
在使用并发时,将字段设为 **private** 特别重要;否则,**synchronized** 关键字不能阻止其他任务直接访问字段,从而产生资源冲突。
-一个线程可以获取对象的锁多次。如果一个方法调用在同一个对象上的第二个方法,而后者又在同一个对象上调用另一个方法,就会发生这种情况。 JVM 会跟踪对象被锁定的次数。如果对象已解锁,则其计数为 0 。当一个线程首次获得锁时,计数变为 1 。每次同一线程在同一对象上获取另一个锁时,计数就会递增。显然,只有首先获得锁的线程才允许多次获取多个锁。每当线程离开 **synchronized** 方法时,计数递减,直到计数变为 0 ,完全释放锁以给其他线程使用。每个类也有一个锁(作为该类的 **Class** 对象的一部分),因此 **synchronized** 静态方法可以在类范围的基础上彼此锁定,不让同时访问静态数据。
+一个线程可以获取对象的锁多次。如果一个方法调用在同一个对象上的第二个方法,而后者又在同一个对象上调用另一个方法,就会发生这种情况。 JVM 会跟踪对象被锁定的次数。如果对象已解锁,则其计数为 0 。当一个线程首次获得锁时,计数变为 1 。每次同一线程在同一对象上获取另一个锁时,计数就会自增。显然,只有首先获得锁的线程才允许多次获取多个锁。每当线程离开 **synchronized** 方法时,计数递减,直到计数变为 0 ,完全释放锁以给其他线程使用。每个类也有一个锁(作为该类的 **Class** 对象的一部分),因此 **synchronized** 静态方法可以在类范围的基础上彼此锁定,不让同时访问静态数据。
你应该什么时候使用同步呢?可以永远 *Brian* 的同步法则[^2]。
@@ -620,7 +620,7 @@ No odd numbers discovered
*/
```
-在两个递增操作之间插入 `Nap()` 构造器方法,以提高在 **currentEvenValue** 是奇数的状态时上下文切换的可能性。因为互斥锁可以阻止多个任务同时进入临界区,所有这不会产生失败。第一个进入 `next()` 方法的任务将获得锁,任何试图获取锁的后续任务都将被阻塞,直到第一个任务释放锁。此时,调度机制选择另一个等待锁的任务。通过这种方式,任何时刻只能有一个任务通过互斥锁保护的代码。
+在两个自增操作之间插入 `Nap()` 构造器方法,以提高在 **currentEvenValue** 是奇数的状态时上下文切换的可能性。因为互斥锁可以阻止多个任务同时进入临界区,所有这不会产生失败。第一个进入 `next()` 方法的任务将获得锁,任何试图获取锁的后续任务都将被阻塞,直到第一个任务释放锁。此时,调度机制选择另一个等待锁的任务。通过这种方式,任何时刻只能有一个任务通过互斥锁保护的代码。
## volatile 关键字
@@ -631,7 +631,7 @@ No odd numbers discovered
### 字分裂
-当你的 Java 数据类型足够大(在 Java 中 **long** 和 **double** 类型都是 64 位),写入变量的过程分两步进行,就会发生 *Word tearing* (字分裂)情况。 JVM 被允许将64位数量的读写作为两个单独的32位操作执行[^3],这增加了在读写过程中发生上下文切换的可能性,因此其他任务会看到不正确的结果。这被称为 *Word tearing* (字分裂),因为你可能只看到其中一部分修改后的值。基本上,任务有时可以在第一步之后但在第二步之前读取变量,从而产生垃圾值(对于例如 **boolean** 或 **int** 类型的小变量是没有问题的;任何 **long** 或 **double** 类型则除外)。
+当你的 Java 数据类型足够大(在 Java 中 **long** 和 **double** 类型都是 64 位),写入变量的过程分两步进行,就会发生 *Word tearing* (字分裂)情况。 JVM 被允许将64位数量的读写作为两个单独的32位操作执行 [^3] ,这增加了在读写过程中发生上下文切换的可能性,因此其他任务会看到不正确的结果。这被称为 *Word tearing* (字分裂),因为你可能只看到其中一部分修改后的值。基本上,任务有时可以在第一步之后但在第二步之前读取变量,从而产生垃圾值(对于例如 **boolean** 或 **int** 类型的小变量是没有问题的;任何 **long** 或 **double** 类型则除外)。
在缺乏任何其他保护的情况下,用 **volatile** 修饰符定义一个 **long** 或 **double** 变量,可阻止字分裂情况。然而,如果使用 **synchronized** 或 **java.util.concurrent.atomic** 类之一保护这些变量,则 **volatile** 将被取代。此外,**volatile** 不会影响到增量操作并不是原子操作的事实。
@@ -701,7 +701,7 @@ public class ReOrdering implements Runnable {
在 Java 线程的讨论中,经常反复提交但不正确的知识是:“原子操作不需要同步”。 一个 *原子操作* 是不能被线程调度机制中断的操作;一旦操作开始,那么它一定可以在可能发生的“上下文切换”之前(切换到其他线程执行)执行完毕。依赖于原子性是很棘手且很危险的,如果你是一个并发编程专家,或者你得到了来自这样的专家的帮助,你才应该使用原子性来代替同步,如果你认为自己足够聪明可以应付这种玩火似的情况,那么请接受下面的测试:
-> Goetz 测试:如果你可以编写用于现代微处理器的高性能 JVM ,那么就有资格考虑是否可以避免同步[^4]。
+> Goetz 测试:如果你可以编写用于现代微处理器的高性能 JVM ,那么就有资格考虑是否可以避免同步 [^4] 。
了解原子性是很有用的,并且知道它与其他高级技术一起用于实现一些更加巧妙的 **java.util.concurrent** 库组件。 但是要坚决抵制自己依赖它的冲动。
@@ -774,7 +774,7 @@ i:I
每条指令都会产生一个 “get” 和 “put”,它们之间还有一些其他指令。因此在获取指令和放置指令之间,另有一个任务可能会修改这个属性,所有,这些操作不是原子性的。
-让我们通过定义一个抽象类来测试原子性的概念,这个抽象类的方法是将一个整数类型进行偶数递增,并且 `run()` 不断地调用这个方法:
+让我们通过定义一个抽象类来测试原子性的概念,这个抽象类的方法是将一个整数类型进行偶数自增,并且 `run()` 不断地调用这个方法:
```java
// lowlevel/IntTestable.java
@@ -876,7 +876,7 @@ public class SerialNumbers {
}
```
-**SerialNumbers** 是你可以想象到最简单的类,如果你具备 C++ 或者其他底层的知识背景,你可能会认为递增是一个原子操作,因为 C++ 的递增操作通常被单个微处理器指令所实现(尽管不是以任何一致,可靠,跨平台的方式)。但是,正如前面所提到的,Java 递增操作不是原子性的,并且操作同时涉及读取和写入,因此即使在这样一个简单的操作中,也存在有线程问题的空间。
+**SerialNumbers** 是你可以想象到最简单的类,如果你具备 C++ 或者其他底层的知识背景,你可能会认为自增是一个原子操作,因为 C++ 的自增操作通常被单个微处理器指令所实现(尽管不是以任何一致,可靠,跨平台的方式)。但是,正如前面所提到的,Java 自增操作不是原子性的,并且操作同时涉及读取和写入,因此即使在这样一个简单的操作中,也存在有线程问题的空间。
我们在这里加入 volatile ,看看它是否有帮助。然而,真正的问题是 `nextSerialNumber()` 方法在不进行线程同步的情况下访问共享的可变变量值。
@@ -1363,12 +1363,137 @@ tryLock(2, TimeUnit.SECONDS): false
## 库组件
+**java.util.concurrent** 库提供大量旨在解决并发问题的类,可以帮助你生成更简单,更鲁棒的并发程序。但请注意,这些工具是比起并行流和 **CompletableFuture** 更底层的机制。
+
+在本节中,我们将看一些使用不同组件的示例,然后讨论一下 *lock-free*(无锁) 库组件是如何工作的。
+
### DelayQueue
+这是一个无界阻塞队列 ( **BlockingQueue** ),用于放置实现了 **Delayed** 接口的对象,其中的对象只能在其到期时才能从队列中取走。这种队列是有序的,因此队首对象的延迟到期的时间最长。如果没有任何延迟到期,那么就不会有队首元素,并且 `poll()` 将返回 **null**(正因为这样,你不能将 **null** 放置到这种队列中)。
+
+下面是一个示例,其中的 **Delayed** 对象自身就是任务,而 **DelayedTaskConsumer** 将最“紧急”的任务(到期时间最长的任务)从队列中取出,然后运行它。注意的是这样 **DelayQueue** 就成为了优先级队列的一种变体。
+
+```java
+// lowlevel/DelayQueueDemo.java
+import java.util.*;
+import java.util.stream.*;
+import java.util.concurrent.*;
+import static java.util.concurrent.TimeUnit.*;
+
+class DelayedTask implements Runnable, Delayed {
+ private static int counter = 0;
+ private final int id = counter++;
+ private final int delta;
+ private final long trigger;
+ protected static List sequence =
+ new ArrayList<>();
+ DelayedTask(int delayInMilliseconds) {
+ delta = delayInMilliseconds;
+ trigger = System.nanoTime() +
+ NANOSECONDS.convert(delta, MILLISECONDS);
+ sequence.add(this);
+ }
+ @Override
+ public long getDelay(TimeUnit unit) {
+ return unit.convert(
+ trigger - System.nanoTime(), NANOSECONDS);
+ }
+ @Override
+ public int compareTo(Delayed arg) {
+ DelayedTask that = (DelayedTask)arg;
+ if(trigger < that.trigger) return -1;
+ if(trigger > that.trigger) return 1;
+ return 0;
+ }
+ @Override
+ public void run() {
+ System.out.print(this + " ");
+ }
+ @Override
+ public String toString() {
+ return
+ String.format("[%d] Task %d", delta, id);
+ }
+ public String summary() {
+ return String.format("(%d:%d)", id, delta);
+ }
+ public static class EndTask extends DelayedTask {
+ EndTask(int delay) { super(delay); }
+ @Override
+ public void run() {
+ sequence.forEach(dt ->
+ System.out.println(dt.summary()));
+ }
+ }
+}
+
+public class DelayQueueDemo {
+ public static void
+ main(String[] args) throws Exception {
+ DelayQueue tasks =
+ Stream.concat( // Random delays:
+ new Random(47).ints(20, 0, 4000)
+ .mapToObj(DelayedTask::new),
+ // Add the summarizing task:
+ Stream.of(new DelayedTask.EndTask(4000)))
+ .collect(Collectors
+ .toCollection(DelayQueue::new));
+ while(tasks.size() > 0)
+ tasks.take().run();
+ }
+}
+/* Output:
+[128] Task 12 [429] Task 6 [551] Task 13 [555] Task 2
+[693] Task 3 [809] Task 15 [961] Task 5 [1258] Task 1
+[1258] Task 20 [1520] Task 19 [1861] Task 4 [1998] Task
+17 [2200] Task 8 [2207] Task 10 [2288] Task 11 [2522]
+Task 9 [2589] Task 14 [2861] Task 18 [2868] Task 7
+[3278] Task 16 (0:4000)
+(1:1258)
+(2:555)
+(3:693)
+(4:1861)
+(5:961)
+(6:429)
+(7:2868)
+(8:2200)
+(9:2522)
+(10:2207)
+(11:2288)
+(12:128)
+(13:551)
+(14:2589)
+(15:809)
+(16:3278)
+(17:1998)
+(18:2861)
+(19:1520)
+(20:1258)
+*/
+```
+
+**DelayedTask** 包含一个称为 **sequence** 的 **List<DelayedTask>** ,它保存了任务被创建的顺序,因此我们可以看到排序是按照实际发生的顺序执行的。
+
+**Delay** 接口有一个方法, `getDelay()` , 该方法用来告知延迟到期有多长时间,或者延迟在多长时间之前已经到期了。这个方法强制我们去使用 **TimeUnit** 类,因为这就是参数类型。这会产生一个非常方便的类,因为你可以很容易地转换单位而无需作任何声明。例如,**delta** 的值是以毫秒为单位存储的,但是 `System.nanoTime()` 产生的时间则是以纳秒为单位的。你可以转换 **delta** 的值,方法是声明它的单位以及你希望以什么单位来表示,就像下面这样:
+
+```java
+NANOSECONDS.convert(delta, MILLISECONDS);
+```
+
+在 `getDelay()` 中, 所希望的单位是作为 **unit** 参数传递进来的,你使用它将当前时间与触发时间之间的差转换为调用者要求的单位,而无需知道这些单位是什么(这是*策略*设计模式的一个简单示例,在这种模式中,算法的一部分是作为参数传递进来的)。
+
+为了排序, **Delayed** 接口还继承了 **Comparable** 接口,因此必须实现 `compareTo()` , 使其可以产生合理的比较。
+
+从输出中可以看到,任务创建的顺序对执行顺序没有任何影响 - 相反,任务是按照所期望的延迟顺序所执行的。
+
### PriorityBlockingQueue
### Lock-Free Collections
+#### The Copying Strategy
+
+#### Compare-And-Swap (CAS)
+
## 本章小结
From 1863ae189f4d5db03e2e2d62cfe316f21d98459c Mon Sep 17 00:00:00 2001
From: AlfredAlan <854723360@mail.com>
Date: Sun, 4 Aug 2019 19:06:09 +0800
Subject: [PATCH 008/461] =?UTF-8?q?=E9=99=84=E5=BD=95=20=E5=B9=B6=E5=8F=91?=
=?UTF-8?q?=E5=BA=95=E5=B1=82=E5=8E=9F=E7=90=86=20=E7=BF=BB=E8=AF=91?=
=?UTF-8?q?=E8=87=B3=20PriorityBlockingQueue?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
docs/book/Appendix-Low-Level-Concurrency.md | 157 +++++++++++++++++++-
1 file changed, 153 insertions(+), 4 deletions(-)
diff --git a/docs/book/Appendix-Low-Level-Concurrency.md b/docs/book/Appendix-Low-Level-Concurrency.md
index e7f701dc..21e23571 100644
--- a/docs/book/Appendix-Low-Level-Concurrency.md
+++ b/docs/book/Appendix-Low-Level-Concurrency.md
@@ -631,7 +631,7 @@ No odd numbers discovered
### 字分裂
-当你的 Java 数据类型足够大(在 Java 中 **long** 和 **double** 类型都是 64 位),写入变量的过程分两步进行,就会发生 *Word tearing* (字分裂)情况。 JVM 被允许将64位数量的读写作为两个单独的32位操作执行 [^3] ,这增加了在读写过程中发生上下文切换的可能性,因此其他任务会看到不正确的结果。这被称为 *Word tearing* (字分裂),因为你可能只看到其中一部分修改后的值。基本上,任务有时可以在第一步之后但在第二步之前读取变量,从而产生垃圾值(对于例如 **boolean** 或 **int** 类型的小变量是没有问题的;任何 **long** 或 **double** 类型则除外)。
+当你的 Java 数据类型足够大(在 Java 中 **long** 和 **double** 类型都是 64 位),写入变量的过程分两步进行,就会发生 *Word tearing* (字分裂)情况。 JVM 被允许将64位数量的读写作为两个单独的32位操作执行[^3],这增加了在读写过程中发生上下文切换的可能性,因此其他任务会看到不正确的结果。这被称为 *Word tearing* (字分裂),因为你可能只看到其中一部分修改后的值。基本上,任务有时可以在第一步之后但在第二步之前读取变量,从而产生垃圾值(对于例如 **boolean** 或 **int** 类型的小变量是没有问题的;任何 **long** 或 **double** 类型则除外)。
在缺乏任何其他保护的情况下,用 **volatile** 修饰符定义一个 **long** 或 **double** 变量,可阻止字分裂情况。然而,如果使用 **synchronized** 或 **java.util.concurrent.atomic** 类之一保护这些变量,则 **volatile** 将被取代。此外,**volatile** 不会影响到增量操作并不是原子操作的事实。
@@ -701,7 +701,7 @@ public class ReOrdering implements Runnable {
在 Java 线程的讨论中,经常反复提交但不正确的知识是:“原子操作不需要同步”。 一个 *原子操作* 是不能被线程调度机制中断的操作;一旦操作开始,那么它一定可以在可能发生的“上下文切换”之前(切换到其他线程执行)执行完毕。依赖于原子性是很棘手且很危险的,如果你是一个并发编程专家,或者你得到了来自这样的专家的帮助,你才应该使用原子性来代替同步,如果你认为自己足够聪明可以应付这种玩火似的情况,那么请接受下面的测试:
-> Goetz 测试:如果你可以编写用于现代微处理器的高性能 JVM ,那么就有资格考虑是否可以避免同步 [^4] 。
+> Goetz 测试:如果你可以编写用于现代微处理器的高性能 JVM ,那么就有资格考虑是否可以避免同步[^4] 。
了解原子性是很有用的,并且知道它与其他高级技术一起用于实现一些更加巧妙的 **java.util.concurrent** 库组件。 但是要坚决抵制自己依赖它的冲动。
@@ -719,7 +719,7 @@ i++; // Might be atomic in C++
i += 2; // Might be atomic in C++
```
-但是在 C++ 中,这取决于编译器和处理器。你无法编写出依赖于原子性的 C++ 跨平台代码,因为 C++ [^5] 没有像 Java 那样的一致 *内存模型* (memory model)。
+但是在 C++ 中,这取决于编译器和处理器。你无法编写出依赖于原子性的 C++ 跨平台代码,因为 C++ [^5]没有像 Java 那样的一致 *内存模型* (memory model)。
在 Java 中,上面的操作肯定不是原子性的,正如下面的方法产生的 JVM 指令中可以看到的那样:
@@ -1488,6 +1488,155 @@ NANOSECONDS.convert(delta, MILLISECONDS);
### PriorityBlockingQueue
+这是一个很基础的优先级队列,它具有可阻塞的读取操作。在下面的示例中, **Prioritized** 对象会被赋予优先级编号。几个 **Producer** 任务的实例会插入 **Prioritized** 对象到 **PriorityBlockingQueue** 中,但插入之间会有随机延时。然后,单个 **Consumer** 任务在执行 `take()` 时会显示多个选项,**PriorityBlockingQueue** 会将当前具有最高优先级的 **Prioritized** 对象提供给它。
+
+在 **Prioritized** 中的静态变量 **counter** 是 **AtomicInteger** 类型。这是必要的,因为有多个 **Producer** 并行运行;如果不是 **AtomicInteger** 类型,你将会看到重复的 **id** 号。 这个问题在 [并发编程](./24-Concurrent-Programming.md) 的 [构造函数非线程安全](./24-Concurrent-Programming.md) 一节中讨论过。
+
+```java
+// lowlevel/PriorityBlockingQueueDemo.java
+import java.util.*;
+import java.util.stream.*;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.*;
+import onjava.Nap;
+
+class Prioritized implements Comparable {
+ private static AtomicInteger counter =
+ new AtomicInteger();
+ private final int id = counter.getAndIncrement();
+ private final int priority;
+ private static List sequence =
+ new CopyOnWriteArrayList<>();
+ Prioritized(int priority) {
+ this.priority = priority;
+ sequence.add(this);
+ }
+ @Override
+ public int compareTo(Prioritized arg) {
+ return priority < arg.priority ? 1 :
+ (priority > arg.priority ? -1 : 0);
+ }
+ @Override
+ public String toString() {
+ return String.format(
+ "[%d] Prioritized %d", priority, id);
+ }
+ public void displaySequence() {
+ int count = 0;
+ for(Prioritized pt : sequence) {
+ System.out.printf("(%d:%d)", pt.id, pt.priority);
+ if(++count % 5 == 0)
+ System.out.println();
+ }
+ }
+ public static class EndSentinel extends Prioritized {
+ EndSentinel() { super(-1); }
+ }
+}
+
+class Producer implements Runnable {
+ private static AtomicInteger seed =
+ new AtomicInteger(47);
+ private SplittableRandom rand =
+ new SplittableRandom(seed.getAndAdd(10));
+ private Queue queue;
+ Producer(Queue q) {
+ queue = q;
+ }
+ @Override
+ public void run() {
+ rand.ints(10, 0, 20)
+ .mapToObj(Prioritized::new)
+ .peek(p -> new Nap(rand.nextDouble() / 10))
+ .forEach(p -> queue.add(p));
+ queue.add(new Prioritized.EndSentinel());
+ }
+}
+
+class Consumer implements Runnable {
+ private PriorityBlockingQueue q;
+ private SplittableRandom rand =
+ new SplittableRandom(47);
+ Consumer(PriorityBlockingQueue q) {
+ this.q = q;
+ }
+ @Override
+ public void run() {
+ while(true) {
+ try {
+ Prioritized pt = q.take();
+ System.out.println(pt);
+ if(pt instanceof Prioritized.EndSentinel) {
+ pt.displaySequence();
+ break;
+ }
+ new Nap(rand.nextDouble() / 10);
+ } catch(InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+}
+
+public class PriorityBlockingQueueDemo {
+ public static void main(String[] args) {
+ PriorityBlockingQueue queue =
+ new PriorityBlockingQueue<>();
+ CompletableFuture.runAsync(new Producer(queue));
+ CompletableFuture.runAsync(new Producer(queue));
+ CompletableFuture.runAsync(new Producer(queue));
+ CompletableFuture.runAsync(new Consumer(queue))
+ .join();
+ }
+}
+/* Output:
+[15] Prioritized 2
+[17] Prioritized 1
+[17] Prioritized 5
+[16] Prioritized 6
+[14] Prioritized 9
+[12] Prioritized 0
+[11] Prioritized 4
+[11] Prioritized 12
+[13] Prioritized 13
+[12] Prioritized 16
+[14] Prioritized 18
+[15] Prioritized 23
+[18] Prioritized 26
+[16] Prioritized 29
+[12] Prioritized 17
+[11] Prioritized 30
+[11] Prioritized 24
+[10] Prioritized 15
+[10] Prioritized 22
+[8] Prioritized 25
+[8] Prioritized 11
+[8] Prioritized 10
+[6] Prioritized 31
+[3] Prioritized 7
+[2] Prioritized 20
+[1] Prioritized 3
+[0] Prioritized 19
+[0] Prioritized 8
+[0] Prioritized 14
+[0] Prioritized 21
+[-1] Prioritized 28
+(0:12)(2:15)(1:17)(3:1)(4:11)
+(5:17)(6:16)(7:3)(8:0)(9:14)
+(10:8)(11:8)(12:11)(13:13)(14:0)
+(15:10)(16:12)(17:12)(18:14)(19:0)
+(20:2)(21:0)(22:10)(23:15)(24:11)
+(25:8)(26:18)(27:-1)(28:-1)(29:16)
+(30:11)(31:6)(32:-1)
+*/
+```
+
+与前面的示例一样,**Prioritized** 对象的创建顺序在 **sequence** 的 **list** 对象上所记入,以便与实际执行顺序进行比较。 **EndSentinel** 是用于告知 **Consumer** 对象关闭的特殊类型。
+
+**Producer** 使用 **AtomicInteger** 变量为 **SplittableRandom** 设置随机生成种子,以便不同的 **Producer** 生成不同的队列。 这是必需的,因为多个生产者并行创建,如果不是这样,创建过程并不会是线程安全的。
+
+**Producer** 和 **Consumer** 通过 **PriorityBlockingQueue** 相互连接。因为阻塞队列的性质提供了所有必要的同步,因为阻塞队列的性质提供了所有必要的同步,请注意,显式同步是并不需要的 — 从队列中读取数据时,你不用考虑队列中是否有任何元素,因为队列在没有元素时将阻塞读取。
+
### Lock-Free Collections
#### The Copying Strategy
@@ -1517,7 +1666,7 @@ NANOSECONDS.convert(delta, MILLISECONDS);
[^4]: 这个测试的推论是,“如果某人表示线程是容易并且简单的,请确保这个人没有对你的项目做出重要的决策。如果那个人已经做出,那么你就已经陷入麻烦之中了。”
-[^5]: 这在即将产生的 C++ 的标准中得到了补救
+[^5]: 这在即将产生的 C++ 的标准中得到了补救。
From 293ebe34ee4f6e31b9f8fa862fdaaeea305a7623 Mon Sep 17 00:00:00 2001
From: AlfredAlan <854723360@mail.com>
Date: Mon, 5 Aug 2019 00:26:56 +0800
Subject: [PATCH 009/461] =?UTF-8?q?=E9=99=84=E5=BD=95=20=E5=B9=B6=E5=8F=91?=
=?UTF-8?q?=E5=BA=95=E5=B1=82=E5=8E=9F=E7=90=86=20=E7=BF=BB=E8=AF=91?=
=?UTF-8?q?=E8=87=B3=20=E6=97=A0=E9=94=81=E9=9B=86=E5=90=88?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
docs/book/Appendix-Low-Level-Concurrency.md | 24 ++++++++++++++++++---
1 file changed, 21 insertions(+), 3 deletions(-)
diff --git a/docs/book/Appendix-Low-Level-Concurrency.md b/docs/book/Appendix-Low-Level-Concurrency.md
index 21e23571..d85e110d 100644
--- a/docs/book/Appendix-Low-Level-Concurrency.md
+++ b/docs/book/Appendix-Low-Level-Concurrency.md
@@ -1637,11 +1637,29 @@ public class PriorityBlockingQueueDemo {
**Producer** 和 **Consumer** 通过 **PriorityBlockingQueue** 相互连接。因为阻塞队列的性质提供了所有必要的同步,因为阻塞队列的性质提供了所有必要的同步,请注意,显式同步是并不需要的 — 从队列中读取数据时,你不用考虑队列中是否有任何元素,因为队列在没有元素时将阻塞读取。
-### Lock-Free Collections
+### 无锁集合
-#### The Copying Strategy
+[集合](./12-Collections.md) 章节强调集合是基本的编程工具,这也要求包含并发性。因此,早期的集合比如 **Vector** 和 **Hashtable** 有许多使用 **synchronized** 机制的方法。当这些集合不是在多线程应用中使用时,这就导致了不可接收的开销。在 Java 1.2 版本中,新的集合库是非同步的,而给 **Collection** 类赋予了各种 **static** **synchronized** 修饰的方法来同步不同的集合类型。虽然这是一个改进,因为它让你可以选择是否对集合使用同步,但是开销仍然基于同步锁定。 Java 5 版本添加新的集合类型,专门用于增加线程安全性能,使用巧妙的技术来消除锁定。
-#### Compare-And-Swap (CAS)
+无锁集合有一个有趣的特性:只要读取者仅能看到已完成修改的结果,对集合的修改就可以同时发生在读取发生时。这是通过一些策略实现的。为了让你了解它们是如何工作的,我们来看看其中的一些。
+
+#### 复制策略
+
+使用“复制”策略,修改是在数据结构一部分的单独副本(或有时是整个数据的副本)上进行的,并且在整个修改过程期间这个副本是不可见的。仅当修改完成时,修改后的结构才与“主”数据结构安全地交换,然后读取者才会看到修改。
+
+在 **CopyOnWriteArrayList** ,写入操作会复制整个底层数组。保留原来的数组,以便在修改复制的数组时可以线程安全地进行读取。当修改完成后,原子操作会将其交换到新数组中,以便新的读取操作能够看到新数组内容。 **CopyOnWriteArrayList** 的其中一个好处是,当多个迭代器遍历和修改列表时,它不会抛出 **ConcurrentModificationException** 异常,因此你不用就像过去必须做的那样,编写特殊的代码来防止此类异常。
+
+**CopyOnWriteArraySet** 使用 **CopyOnWriteArrayList** 来实现其无锁行为。
+
+**ConcurrentHashMap** 和 **ConcurrentLinkedQueue** 使用类似的技术来允许并发读写,但是只复制和修改集合的一部分,而不是整个集合。然而,读取者仍然不会看到任何不完整的修改。**ConcurrentHashMap** **不会抛出concurrentmodificationexception** 异常。
+
+#### 比较并交换 (CAS)
+
+在 比较并交换 (CAS) 中,你从内存中获取一个值,并在计算新值时保留原始值。然后使用 CAS 指令,它将原始值与当前内存中的值进行比较,如果这两个值是相等的,则将内存中的旧值替换为计算新值的结果,所有操作都在一个原子操作中完成。如果原始值比较失败,则不会进行交换,因为这意味着另一个线程同时修改了内存。在这种情况下,你的代码必须再次尝试,获取一个新的原始值并重复该操作。
+
+如果内存仅轻量竞争,CAS操作几乎总是在没有重复尝试的情况下完成,因此它非常快。相反,**synchronized** 操作需要考虑每次获取和释放锁的成本,这要昂贵得多,而且没有额外的好处。随着内存竞争的增加,使用 CAS 的操作会变慢,因为它必须更频繁地重复自己的操作,但这是对更多资源竞争的动态响应。这确实是一种优雅的方法。
+
+最重要的是,许多现代处理器的汇编语言中都有一条 CAS 指令,并且也被 JVM 中的 CAS 操作(例如 **Atomic** 类中的操作)所使用。CAS 指令在硬件层面中是原子性的,并且与你所期望的操作一样快。
## 本章小结
From 5c83895b645ed08dc39d08b495e43c7de5cd1f60 Mon Sep 17 00:00:00 2001
From: WangHengwei <35023376@qq.com>
Date: Mon, 5 Aug 2019 02:07:05 +0800
Subject: [PATCH 010/461] fix typos
---
docs/book/06-Housekeeping.md | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/docs/book/06-Housekeeping.md b/docs/book/06-Housekeeping.md
index 4a3d2636..b589ee4f 100644
--- a/docs/book/06-Housekeeping.md
+++ b/docs/book/06-Housekeeping.md
@@ -712,7 +712,7 @@ Error: checked out
如前文所述,这里讨论的 Java 虚拟机中,内存分配以较大的"块"为单位。如果对象较大,它会占用单独的块。严格来说,"停止-复制"要求在释放旧对象之前,必须先将所有存活对象从旧堆复制到新堆,这导致了大量的内存复制行为。有了块,垃圾回收器就可以把对象复制到废弃的块。每个块都有年代数来记录自己是否存活。通常,如果块在某处被引用,其年代数加 1,垃圾回收器会对上次回收动作之后新分配的块进行整理。这对处理大量短命的临时对象很有帮助。垃圾回收器会定期进行完整的清理动作——大型对象仍然不会复制(只是年代数会增加),含有小型对象的那些块则被复制并整理。Java 虚拟机会监视,如果所有对象都很稳定,垃圾回收的效率降低的话,就切换到"标记-清扫"方式。同样,Java 虚拟机会跟踪"标记-清扫"的效果,如果堆空间出现很多碎片,就会切换回"停止-复制"方式。这就是"自适应"的由来,你可以给它个啰嗦的称呼:"自适应的、分代的、停止-复制、标记-清扫"式的垃圾回收器。
-Java 虚拟机中有许多附加技术用来提升速度。尤其是与加载器操作有关的,被称为"即时"(Just-In-Time, JIT)编译器的技术。这种技术可以把程全部或部分翻译成本地机器码,所以不需要 JVM 来进行翻译,因此运行得更快。当需要装载某个类(通常是创建该类的第一个对象)时,编译器会先找到其 **.class** 文件,然后将该类的字节码装入内存。你可以让即时编译器编译所有代码,但这种做法有两个缺点:一是这种加载动作贯穿整个程序生命周期内,累加起来需要花更多时间;二是会增加可执行代码的长度(字节码要比即时编译器展开后的本地机器码小很多),这会导致页面调度,从而一定降低程序速度。另一种做法称为*惰性评估*,意味着即时编译器只有在必要的时候才编译代码。这样,从未被执行的代码也许就压根不会被 JIT 编译。新版 JDK 中的 Java HotSpot 技术就采用了类似的做法,代码每被执行一次就优化一些,所以执行的次数越多,它的速度就越快。
+Java 虚拟机中有许多附加技术用来提升速度。尤其是与加载器操作有关的,被称为"即时"(Just-In-Time, JIT)编译器的技术。这种技术可以把程序全部或部分翻译成本地机器码,所以不需要 JVM 来进行翻译,因此运行得更快。当需要装载某个类(通常是创建该类的第一个对象)时,编译器会先找到其 **.class** 文件,然后将该类的字节码装入内存。你可以让即时编译器编译所有代码,但这种做法有两个缺点:一是这种加载动作贯穿整个程序生命周期内,累加起来需要花更多时间;二是会增加可执行代码的长度(字节码要比即时编译器展开后的本地机器码小很多),这会导致页面调度,从而一定降低程序速度。另一种做法称为*惰性评估*,意味着即时编译器只有在必要的时候才编译代码。这样,从未被执行的代码也许就压根不会被 JIT 编译。新版 JDK 中的 Java HotSpot 技术就采用了类似的做法,代码每被执行一次就优化一些,所以执行的次数越多,它的速度就越快。
@@ -1054,7 +1054,7 @@ f3(1)
概括一下创建对象的过程,假设有个名为 **Dog** 的类:
-1. 即使没有显式地使用 **static** 关键字,构造器实际上也是静态方法。所以,当首次创建 **Dog** 类型的对象时,或是首次访问 **Dog** 类的静态方法或属性,Java 解释器必须在类路径中查找,以定位 **Dog.class**。
+1. 即使没有显式地使用 **static** 关键字,构造器实际上也是静态方法。所以,当首次创建 **Dog** 类型的对象或是首次访问 **Dog** 类的静态方法或属性时,Java 解释器必须在类路径中查找,以定位 **Dog.class**。
2. 当加载完 **Dog.class** 后(后面会学到,这将创建一个 **Class** 对象),有关静态初始化的所有动作都会执行。因此,静态初始化只会在首次加载 **Class** 对象时初始化一次。
3. 当用 `new Dog()` 创建对象时,首先会在堆上为 **Dog** 对象分配足够的存储空间。
4. 分配的存储空间首先会被清零,即会将 **Dog** 对象中的所有基本类型数据设置为默认值(数字会被置为 0,布尔型和字符型也相同),引用被置为 **null**。
@@ -1812,4 +1812,4 @@ This burrito is maybe too hot.
-
\ No newline at end of file
+
From 8f7a8b9b4bb5c134490cd37d814f6471d2041878 Mon Sep 17 00:00:00 2001
From: 1326670425 <1326670425@qq.com>
Date: Mon, 5 Aug 2019 18:41:39 +0800
Subject: [PATCH 011/461] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=AC=AC=E5=8D=81?=
=?UTF-8?q?=E4=BA=8C=E7=AB=A0=20=E9=9B=86=E5=90=88=E7=9A=84=E6=A1=86?=
=?UTF-8?q?=E6=9E=B6=E5=9B=BE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
docs/book/12-Collections.md | 6 +++++-
docs/images/collection.png | Bin 0 -> 58326 bytes
docs/images/map.png | Bin 0 -> 20386 bytes
3 files changed, 5 insertions(+), 1 deletion(-)
create mode 100644 docs/images/collection.png
create mode 100644 docs/images/map.png
diff --git a/docs/book/12-Collections.md b/docs/book/12-Collections.md
index 44872d57..199300a8 100644
--- a/docs/book/12-Collections.md
+++ b/docs/book/12-Collections.md
@@ -1707,7 +1707,7 @@ Java 提供了许多保存对象的方法:
8. 不要在新代码中使用遗留类 **Vector** ,**Hashtable** 和 **Stack** 。
-浏览一下Java集合的简图(不包含抽象类或遗留组件)会很有帮助。这里仅包括在一般情况下会碰到的接口和类。
+浏览一下Java集合的简图(不包含抽象类或遗留组件)会很有帮助。这里仅包括在一般情况下会碰到的接口和类。(译者注:下图为原著PDF中的截图,可能由于未知原因存在问题。这里可参考译者绘制版[^8])

@@ -1812,6 +1812,10 @@ Serializable]
[^7]: 这在 Java 5 之前是不可用的,因为该方法被认为与操作系统的耦合度过紧,因此违反“一次编写,处处运行”的原则。现在却提供它,这一事实表明, Java 的设计者们更加务实了。
+[^8]: 下面是译者绘制的 Java 集合框架简图,黄色为接口,绿色为抽象类,蓝色为具体类。虚线箭头表示实现关系,实线箭头表示继承关系。
+
+
+
diff --git a/docs/images/collection.png b/docs/images/collection.png
new file mode 100644
index 0000000000000000000000000000000000000000..ab24df4466e7b844ae90f45cfba61a346e564802
GIT binary patch
literal 58326
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