## Chapter 6. Enums and Annotations(枚舉和注解)
### Item 37: Use EnumMap instead of ordinal indexing(使用 EnumMap 替換序數索引)
Occasionally you may see code that uses the ordinal method (Item 35) to index into an array or list. For example, consider this simplistic class meant to represent a plant:
偶爾你可能會看到使用 `ordinal()` 的返回值([Item-35](/Chapter-6/Chapter-6-Item-35-Use-instance-fields-instead-of-ordinals.md))作為數組或 list 索引的代碼。例如,考慮這個簡單的類,它表示一種植物:
```
class Plant {
enum LifeCycle { ANNUAL, PERENNIAL, BIENNIAL }
final String name;
final LifeCycle lifeCycle;
Plant(String name, LifeCycle lifeCycle) {
this.name = name;
this.lifeCycle = lifeCycle;
}
@Override public String toString() {
return name;
}
}
```
Now suppose you have an array of plants representing a garden, and you want to list these plants organized by life cycle (annual, perennial, or biennial). To do this, you construct three sets, one for each life cycle, and iterate through the garden, placing each plant in the appropriate set. Some programmers would do this by putting the sets into an array indexed by the life cycle’s ordinal:
現在假設你有一個代表花園全部植物的 Plant 數組,你想要列出按生命周期(一年生、多年生或兩年生)排列的植物。要做到這一點,你需要構造三個集合,每個生命周期一個,然后遍歷整個數組,將每個植物放入適當的集合中:
```
// Using ordinal() to index into an array - DON'T DO THIS!
Set<Plant>[] plantsByLifeCycle =(Set<Plant>[]) new Set[Plant.LifeCycle.values().length];
for (int i = 0; i < plantsByLifeCycle.length; i++)
plantsByLifeCycle[i] = new HashSet<>();
for (Plant p : garden)
plantsByLifeCycle[p.lifeCycle.ordinal()].add(p);
// Print the results
for (int i = 0; i < plantsByLifeCycle.length; i++) {
System.out.printf("%s: %s%n",
Plant.LifeCycle.values()[i], plantsByLifeCycle[i]);
}
```
**譯注:假設 Plant 數組如下:**
```
Plant[] garden = new Plant[]{
new Plant("A", LifeCycle.ANNUAL),
new Plant("B", LifeCycle.BIENNIAL),
new Plant("C", LifeCycle.PERENNIAL),
new Plant("D", LifeCycle.BIENNIAL),
new Plant("E", LifeCycle.PERENNIAL),
};
```
輸出結果為:
```
ANNUAL: [A]
PERENNIAL: [E, C]
BIENNIAL: [B, D]
```
This technique works, but it is fraught with problems. Because arrays are not compatible with generics (Item 28), the program requires an unchecked cast and will not compile cleanly. Because the array does not know what its index represents, you have to label the output manually. But the most serious problem with this technique is that when you access an array that is indexed by an enum’s ordinal, it is your responsibility to use the correct int value; ints do not provide the type safety of enums. If you use the wrong value, the program will silently do the wrong thing or—if you’re lucky—throw an ArrayIndexOutOfBoundsException.
這種技術是有效的,但它充滿了問題。因為數組與泛型不兼容([Item-28](/Chapter-5/Chapter-5-Item-28-Prefer-lists-to-arrays.md)),所以該程序需要 unchecked 的轉換,否則不能順利地編譯。因為數組不知道它的索引表示什么,所以必須手動標記輸出。但是這種技術最嚴重的問題是,當你訪問一個由枚舉序數索引的數組時,你有責任使用正確的 int 值;int 不提供枚舉的類型安全性。如果你使用了錯誤的值,程序將靜默執行錯誤的操作,如果幸運的話,才會拋出 ArrayIndexOutOfBoundsException。
There is a much better way to achieve the same effect. The array is effectively serving as a map from the enum to a value, so you might as well use a Map. More specifically, there is a very fast Map implementation designed for use with enum keys, known as java.util.EnumMap. Here is how the program looks when it is rewritten to use EnumMap:
有一種更好的方法可以達到同樣的效果。該數組有效地充當從枚舉到值的映射,因此你不妨使用 Map。更具體地說,有一種非常快速的 Map 實現,用于枚舉鍵,稱為 `java.util.EnumMap`。以下就是這個程序在使用 EnumMap 時的樣子:
```
// Using an EnumMap to associate data with an enum
Map<Plant.LifeCycle, Set<Plant>> plantsByLifeCycle =new EnumMap<>(Plant.LifeCycle.class);
for (Plant.LifeCycle lc : Plant.LifeCycle.values())
plantsByLifeCycle.put(lc, new HashSet<>());
for (Plant p : garden)
plantsByLifeCycle.get(p.lifeCycle).add(p);
System.out.println(plantsByLifeCycle);
```
This program is shorter, clearer, safer, and comparable in speed to the original version. There is no unsafe cast; no need to label the output manually because the map keys are enums that know how to translate themselves to printable strings; and no possibility for error in computing array indices. The reason that EnumMap is comparable in speed to an ordinal-indexed array is that EnumMap uses such an array internally, but it hides this implementation detail from the programmer, combining the richness and type safety of a Map with the speed of an array. Note that the EnumMap constructor takes the Class object of the key type: this is a bounded type token, which provides runtime generic type information (Item 33).
這個程序比原來的版本更短,更清晰,更安全,速度也差不多。沒有不安全的轉換;不需要手動標記輸出,因為 Map 的鍵是能轉換為可打印字符串的枚舉;在計算數組索引時不可能出錯。EnumMap 在速度上與有序索引數組相當的原因是,EnumMap 在內部使用這樣的數組,但是它向程序員隱藏了實現細節,將 Map 的豐富的功能和類型安全性與數組的速度結合起來。注意,EnumMap 構造函數接受鍵類型的 Class 對象:這是一個有界類型標記,它提供運行時泛型類型信息([Item-33](/Chapter-5/Chapter-5-Item-33-Consider-typesafe-heterogeneous-containers.md))。
The previous program can be further shortened by using a stream (Item 45) to manage the map. Here is the simplest stream-based code that largely duplicates the behavior of the previous example:
通過使用流([Item-45](/Chapter-7/Chapter-7-Item-45-Use-streams-judiciously.md))來管理映射,可以進一步縮短前面的程序。下面是基于流的最簡單的代碼,它在很大程度上復制了前一個示例的行為:
```
// Naive stream-based approach - unlikely to produce an EnumMap!
System.out.println(Arrays.stream(garden).collect(groupingBy(p -> p.lifeCycle)));
```
**譯注:以上代碼需要引入 `java.util.stream.Collectors.groupingBy`,輸出結果如下:**
```
{BIENNIAL=[B, D], ANNUAL=[A], PERENNIAL=[C, E]}
```
The problem with this code is that it chooses its own map implementation, and in practice it won’t be an EnumMap, so it won’t match the space and time performance of the version with the explicit EnumMap. To rectify this problem, use the three-parameter form of Collectors.groupingBy, which allows the caller to specify the map implementation using the mapFactory parameter:
這段代碼的問題在于它選擇了自己的 Map 實現,而實際上它不是 EnumMap,所以它的空間和時間性能與顯式 EnumMap 不匹配。要糾正這個問題,可以使用 `Collectors.groupingBy` 的三參數形式,它允許調用者使用 mapFactory 參數指定 Map 實現:
```
// Using a stream and an EnumMap to associate data with an enum
System.out.println(
Arrays.stream(garden).collect(groupingBy(p -> p.lifeCycle,() -> new EnumMap<>(LifeCycle.class), toSet()))
);
```
**譯注:以上代碼需要引入 `java.util.stream.Collectors.toSet`**
This optimization would not be worth doing in a toy program like this one but could be critical in a program that made heavy use of the map.
這種優化在示例程序中不值得去做,但在大量使用 Map 的程序中可能非常重要。
The behavior of the stream-based versions differs slightly from that of the EmumMap version. The EnumMap version always makes a nested map for each plant lifecycle, while the stream-based versions only make a nested map if the garden contains one or more plants with that lifecycle. So, for example, if the garden contains annuals and perennials but no biennials, the size of plantsByLifeCycle will be three in the EnumMap version and two in both of the stream-based versions.
基于流的版本的行為與 EmumMap 版本略有不同。EnumMap 版本總是為每個植物生命周期生成一個嵌套 Map,而基于流的版本只在花園包含具有該生命周期的一個或多個植物時才生成嵌套 Map。例如,如果花園包含一年生和多年生植物,但沒有兩年生植物,plantsByLifeCycle 的大小在 EnumMap 版本中為 3,在基于流的版本中為 2。
You may see an array of arrays indexed (twice!) by ordinals used to represent a mapping from two enum values. For example, this program uses such an array to map two phases to a phase transition (liquid to solid is freezing, liquid to gas is boiling, and so forth):
你可能會看到被序數索引(兩次!)的數組,序數用于表示兩個枚舉值的映射。例如,這個程序使用這樣的一個數組來映射兩個狀態到一個狀態的轉換過程(液體到固體是凍結的,液體到氣體是沸騰的,等等):
```
// Using ordinal() to index array of arrays - DON'T DO THIS!
public enum Phase {
SOLID, LIQUID, GAS;
public enum Transition {
MELT, FREEZE, BOIL, CONDENSE, SUBLIME, DEPOSIT;
// Rows indexed by from-ordinal, cols by to-ordinal
private static final Transition[][] TRANSITIONS = {
{ null, MELT, SUBLIME },
{ FREEZE, null, BOIL },
{ DEPOSIT, CONDENSE, null }
};
// Returns the phase transition from one phase to another
public static Transition from(Phase from, Phase to) {
return TRANSITIONS[from.ordinal()][to.ordinal()];
}
}
}
```
**譯注:固體、液體、氣體三態,對應的三組變化:融化 MELT,凍結 FREEZE(固態與液態);沸騰 BOIL,凝固 CONDENSE(液態與氣態);升華 SUBLIME,凝華 DEPOSIT(固態與氣態)。**
This program works and may even appear elegant, but appearances can be deceiving. Like the simpler garden example shown earlier, the compiler has no way of knowing the relationship between ordinals and array indices. If you make a mistake in the transition table or forget to update it when you modify the Phase or Phase.Transition enum type, your program will fail at runtime. The failure may be an ArrayIndexOutOfBoundsException, a NullPointerException, or (worse) silent erroneous behavior. And the size of the table is quadratic in the number of phases, even if the number of non-null entries is smaller.
這個程序可以工作,甚至可能看起來很優雅,但外表可能具有欺騙性。就像前面展示的更簡單的 garden 示例一樣,編譯器無法知道序數和數組索引之間的關系。如果你在轉換表中出錯,或者在修改 Phase 或 `Phase.Transition` 枚舉類型時忘記更新,你的程序將在運行時失敗。失敗可能是拋出 ArrayIndexOutOfBoundsException、NullPointerException 或(更糟糕的)靜默錯誤行為。并且即使非空項的數目更小,該表的大小也為狀態數量的二次方。
Again, you can do much better with EnumMap. Because each phase transition is indexed by a pair of phase enums, you are best off representing the relationship as a map from one enum (the “from” phase) to a map from the second enum (the “to” phase) to the result (the phase transition). The two phases associated with a phase transition are best captured by associating them with the phase transition enum, which can then be used to initialize the nested EnumMap:
同樣,使用 EnumMap 可以做得更好。因為每個階段轉換都由一對階段枚舉索引,所以最好將這個關系用 Map 表示,從一個枚舉(起始階段)到第二個枚舉(結束階段)到結果(轉換階段)。與階段轉換相關聯的兩個階段最容易捕捉到的是將它們與階段過渡的 enum 聯系起來,這樣就可以用來初始化嵌套的 EnumMap:
```
// Using a nested EnumMap to associate data with enum pairs
public enum Phase {
SOLID, LIQUID, GAS;
public enum Transition {
MELT(SOLID, LIQUID), FREEZE(LIQUID, SOLID),
BOIL(LIQUID, GAS), CONDENSE(GAS, LIQUID),
SUBLIME(SOLID, GAS), DEPOSIT(GAS, SOLID);
private final Phase from;
private final Phase to;
Transition(Phase from, Phase to) {
this.from = from;
this.to = to;
}
// Initialize the phase transition map
private static final Map<Phase_new, Map<Phase_new, Transition>> m = Stream.of(values())
.collect(groupingBy(
t -> t.from,
() -> new EnumMap<>(Phase_new.class),
toMap(t -> t.to, t -> t, (x, y) -> y, () -> new EnumMap<>(Phase_new.class))
)
);
public static Transition from(Phase from, Phase to) {
return m.get(from).get(to);
}
}
}
```
The code to initialize the phase transition map is a bit complicated. The type of the map is `Map<Phase, Map<Phase, Transition>>`, which means “map from (source) phase to map from (destination) phase to transition.” This map-of-maps is initialized using a cascaded sequence of two collectors. The first collector groups the transitions by source phase, and the second creates an EnumMap with mappings from destination phase to transition. The merge function in the second collector ((x, y) -> y)) is unused; it is required only because we need to specify a map factory in order to get an EnumMap, and Collectors provides telescoping factories. The previous edition of this book used explicit iteration to initialize the phase transition map. The code was more verbose but arguably easier to understand.
初始化階段變化 Map 的代碼有點復雜。Map 的類型是 `Map<Phase, Map<Phase, Transition>>`,這意味著「從(源)階段 Map 到(目標)階段 Map 的轉換過程」。這個 Map 嵌套是使用兩個收集器的級聯序列初始化的。第一個收集器按源階段對轉換進行分組,第二個收集器使用從目標階段到轉換的映射創建一個 EnumMap。第二個收集器 ((x, y) -> y) 中的 merge 函數未使用;之所以需要它,只是因為我們需要指定一個 Map 工廠來獲得 EnumMap,而 Collector 提供了伸縮工廠。本書的上一個版本使用顯式迭代來初始化階段轉換映射。代碼更冗長,但也更容易理解。
**譯注:第二版中的實現代碼如下:**
```
// Initialize the phase transition map
private static final Map<Phase, Map<Phase,Transition> m =
new EnumMap<Phase, Map<Phase ,Transition>>(Phase.class);
static{
for (Phase p : Phase. values())
m.put(p,new EnumMap<Phase,Transition (Phase.class));
for (Transition trans : Transition.values() )
m.get(trans. src).put(trans.dst, trans) ;
}
public static Transition from(Phase src, Phase dst) {
return m.get(src).get(dst);
}
```
Now suppose you want to add a new phase to the system: plasma, or ionized gas. There are only two transitions associated with this phase: ionization, which takes a gas to a plasma; and deionization, which takes a plasma to a gas. To update the array-based program, you would have to add one new constant to Phase and two to Phase.Transition, and replace the original nine-element array of arrays with a new sixteen-element version. If you add too many or too few elements to the array or place an element out of order, you are out of luck: the program will compile, but it will fail at runtime. To update the EnumMap-based version, all you have to do is add PLASMA to the list of phases, and IONIZE(GAS, PLASMA) and DEIONIZE(PLASMA, GAS) to the list of phase transitions:
現在假設你想向系統中加入一種新階段:等離子體,或電離氣體。這個階段只有兩個變化:電離,它把氣體轉為等離子體;去離子作用,把等離子體變成氣體。假設要更新基于數組版本的程序,必須向 Phase 添加一個新常量,向 `Phase.Transition` 添加兩個新常量,并用一個新的 16 個元素版本替換原來的數組中的 9 個元素數組。如果你向數組中添加了太多或太少的元素,或者打亂了元素的順序,那么你就麻煩了:程序將編譯,但在運行時將失敗。相比之下,要更新基于 EnumMap 的版本,只需將 PLASMA 添加到 Phase 列表中,將 `IONIZE(GAS, PLASMA)` 和 `DEIONIZE(PLASMA, GAS)` 添加到 `Phase.Transition` 中:
```
// Adding a new phase using the nested EnumMap implementation
public enum Phase {
SOLID, LIQUID, GAS, PLASMA;
public enum Transition {
MELT(SOLID, LIQUID), FREEZE(LIQUID, SOLID),
BOIL(LIQUID, GAS), CONDENSE(GAS, LIQUID),
SUBLIME(SOLID, GAS), DEPOSIT(GAS, SOLID),
IONIZE(GAS, PLASMA), DEIONIZE(PLASMA, GAS);
... // Remainder unchanged
}
}
```
The program takes care of everything else and leaves you virtually no opportunity for error. Internally, the map of maps is implemented with an array of arrays, so you pay little in space or time cost for the added clarity, safety, and ease of maintenance.
這個程序會處理所有其他事情,實際上不會給你留下任何出錯的機會。在內部,Map 的映射是用一個數組來實現的,因此你只需花費很少的空間或時間成本就可以獲得更好的清晰度、安全性并易于維護。
In the interest of brevity, the above examples use null to indicate the absence of a state change (wherein to and from are identical). This is not good practice and is likely to result in a NullPointerException at runtime. Designing a clean, elegant solution to this problem is surprisingly tricky, and the resulting programs are sufficiently long that they would detract from the primary material in this item.
為了簡潔起見,最初的示例使用 null 表示沒有狀態更改(其中 to 和 from 是相同的)。這不是一個好的方式,可能會在運行時導致 NullPointerException。針對這個問題設計一個干凈、優雅的解決方案是非常棘手的,并且生成的程序冗長,以至于它們會偏離條目中的主要內容。
In summary, **it is rarely appropriate to use ordinals to index into arrays: use EnumMap instead.** If the relationship you are representing is multidimensional, use `EnumMap<..., EnumMap<...>>`. This is a special case of the general principle that application programmers should rarely, if ever, use Enum.ordinal (Item 35).
總之,**用普通的序數索引數組是非常不合適的:應使用 EnumMap 代替。** 如果所表示的關系是多維的,則使用 `EnumMap<..., EnumMap<...>>`。這是一種特殊的基本原則,程序員很少(即使有的話)使用 `Enum.ordinal` ([Item-35](/Chapter-6/Chapter-6-Item-35-Use-instance-fields-instead-of-ordinals.md))。
---
**[Back to contents of the chapter(返回章節目錄)](/Chapter-6/Chapter-6-Introduction.md)**
- **Previous Item(上一條目):[Item 36: Use EnumSet instead of bit fields(用 EnumSet 替代位字段)](/Chapter-6/Chapter-6-Item-36-Use-EnumSet-instead-of-bit-fields.md)**
- **Next Item(下一條目):[Item 38: Emulate extensible enums with interfaces(使用接口模擬可擴展枚舉)](/Chapter-6/Chapter-6-Item-38-Emulate-extensible-enums-with-interfaces.md)**
- Chapter 2. Creating and Destroying Objects(創建和銷毀對象)
- Item 1: Consider static factory methods instead of constructors(考慮以靜態工廠方法代替構造函數)
- Item 2: Consider a builder when faced with many constructor parameters(在面對多個構造函數參數時,請考慮構建器)
- Item 3: Enforce the singleton property with a private constructor or an enum type(使用私有構造函數或枚舉類型實施單例屬性)
- Item 4: Enforce noninstantiability with a private constructor(用私有構造函數實施不可實例化)
- Item 5: Prefer dependency injection to hardwiring resources(依賴注入優于硬連接資源)
- Item 6: Avoid creating unnecessary objects(避免創建不必要的對象)
- Item 7: Eliminate obsolete object references(排除過時的對象引用)
- Item 8: Avoid finalizers and cleaners(避免使用終結器和清除器)
- Item 9: Prefer try with resources to try finally(使用 try-with-resources 優于 try-finally)
- Chapter 3. Methods Common to All Objects(對象的通用方法)
- Item 10: Obey the general contract when overriding equals(覆蓋 equals 方法時應遵守的約定)
- Item 11: Always override hashCode when you override equals(當覆蓋 equals 方法時,總要覆蓋 hashCode 方法)
- Item 12: Always override toString(始終覆蓋 toString 方法)
- Item 13: Override clone judiciously(明智地覆蓋 clone 方法)
- Item 14: Consider implementing Comparable(考慮實現 Comparable 接口)
- Chapter 4. Classes and Interfaces(類和接口)
- Item 15: Minimize the accessibility of classes and members(盡量減少類和成員的可訪問性)
- Item 16: In public classes use accessor methods not public fields(在公共類中,使用訪問器方法,而不是公共字段)
- Item 17: Minimize mutability(減少可變性)
- Item 18: Favor composition over inheritance(優先選擇復合而不是繼承)
- Item 19: Design and document for inheritance or else prohibit it(繼承要設計良好并且具有文檔,否則禁止使用)
- Item 20: Prefer interfaces to abstract classes(接口優于抽象類)
- Item 21: Design interfaces for posterity(為后代設計接口)
- Item 22: Use interfaces only to define types(接口只用于定義類型)
- Item 23: Prefer class hierarchies to tagged classes(類層次結構優于帶標簽的類)
- Item 24: Favor static member classes over nonstatic(靜態成員類優于非靜態成員類)
- Item 25: Limit source files to a single top level class(源文件僅限有單個頂層類)
- Chapter 5. Generics(泛型)
- Item 26: Do not use raw types(不要使用原始類型)
- Item 27: Eliminate unchecked warnings(消除 unchecked 警告)
- Item 28: Prefer lists to arrays(list 優于數組)
- Item 29: Favor generic types(優先使用泛型)
- Item 30: Favor generic methods(優先使用泛型方法)
- Item 31: Use bounded wildcards to increase API flexibility(使用有界通配符增加 API 的靈活性)
- Item 32: Combine generics and varargs judiciously(明智地合用泛型和可變參數)
- Item 33: Consider typesafe heterogeneous containers(考慮類型安全的異構容器)
- Chapter 6. Enums and Annotations(枚舉和注解)
- Item 34: Use enums instead of int constants(用枚舉類型代替 int 常量)
- Item 35: Use instance fields instead of ordinals(使用實例字段替代序數)
- Item 36: Use EnumSet instead of bit fields(用 EnumSet 替代位字段)
- Item 37: Use EnumMap instead of ordinal indexing(使用 EnumMap 替換序數索引)
- Item 38: Emulate extensible enums with interfaces(使用接口模擬可擴展枚舉)
- Item 39: Prefer annotations to naming patterns(注解優于命名模式)
- Item 40: Consistently use the Override annotation(堅持使用 @Override 注解)
- Item 41: Use marker interfaces to define types(使用標記接口定義類型)
- Chapter 7. Lambdas and Streams(λ 表達式和流)
- Item 42: Prefer lambdas to anonymous classes(λ 表達式優于匿名類)
- Item 43: Prefer method references to lambdas(方法引用優于 λ 表達式)
- Item 44: Favor the use of standard functional interfaces(優先使用標準函數式接口)
- Item 45: Use streams judiciously(明智地使用流)
- Item 46: Prefer side effect free functions in streams(在流中使用無副作用的函數)
- Item 47: Prefer Collection to Stream as a return type(優先選擇 Collection 而不是流作為返回類型)
- Item 48: Use caution when making streams parallel(謹慎使用并行流)
- Chapter 8. Methods(方法)
- Item 49: Check parameters for validity(檢查參數的有效性)
- Item 50: Make defensive copies when needed(在需要時制作防御性副本)
- Item 51: Design method signatures carefully(仔細設計方法簽名)
- Item 52: Use overloading judiciously(明智地使用重載)
- Item 53: Use varargs judiciously(明智地使用可變參數)
- Item 54: Return empty collections or arrays, not nulls(返回空集合或數組,而不是 null)
- Item 55: Return optionals judiciously(明智地的返回 Optional)
- Item 56: Write doc comments for all exposed API elements(為所有公開的 API 元素編寫文檔注釋)
- Chapter 9. General Programming(通用程序設計)
- Item 57: Minimize the scope of local variables(將局部變量的作用域最小化)
- Item 58: Prefer for-each loops to traditional for loops(for-each 循環優于傳統的 for 循環)
- Item 59: Know and use the libraries(了解并使用庫)
- Item 60: Avoid float and double if exact answers are required(若需要精確答案就應避免使用 float 和 double 類型)
- Item 61: Prefer primitive types to boxed primitives(基本數據類型優于包裝類)
- Item 62: Avoid strings where other types are more appropriate(其他類型更合適時應避免使用字符串)
- Item 63: Beware the performance of string concatenation(當心字符串連接引起的性能問題)
- Item 64: Refer to objects by their interfaces(通過接口引用對象)
- Item 65: Prefer interfaces to reflection(接口優于反射)
- Item 66: Use native methods judiciously(明智地使用本地方法)
- Item 67: Optimize judiciously(明智地進行優化)
- Item 68: Adhere to generally accepted naming conventions(遵守被廣泛認可的命名約定)
- Chapter 10. Exceptions(異常)
- Item 69: Use exceptions only for exceptional conditions(僅在確有異常條件下使用異常)
- Item 70: Use checked exceptions for recoverable conditions and runtime exceptions for programming errors(對可恢復情況使用 checked 異常,對編程錯誤使用運行時異常)
- Item 71: Avoid unnecessary use of checked exceptions(避免不必要地使用 checked 異常)
- Item 72: Favor the use of standard exceptions(鼓勵復用標準異常)
- Item 73: Throw exceptions appropriate to the abstraction(拋出能用抽象解釋的異常)
- Item 74: Document all exceptions thrown by each method(為每個方法記錄會拋出的所有異常)
- Item 75: Include failure capture information in detail messages(異常詳細消息中應包含捕獲失敗的信息)
- Item 76: Strive for failure atomicity(盡力保證故障原子性)
- Item 77: Don’t ignore exceptions(不要忽略異常)
- Chapter 11. Concurrency(并發)
- Item 78: Synchronize access to shared mutable data(對共享可變數據的同步訪問)
- Item 79: Avoid excessive synchronization(避免過度同步)
- Item 80: Prefer executors, tasks, and streams to threads(Executor、task、流優于直接使用線程)
- Item 81: Prefer concurrency utilities to wait and notify(并發實用工具優于 wait 和 notify)
- Item 82: Document thread safety(文檔應包含線程安全屬性)
- Item 83: Use lazy initialization judiciously(明智地使用延遲初始化)
- Item 84: Don’t depend on the thread scheduler(不要依賴線程調度器)
- Chapter 12. Serialization(序列化)
- Item 85: Prefer alternatives to Java serialization(優先選擇 Java 序列化的替代方案)
- Item 86: Implement Serializable with great caution(非常謹慎地實現 Serializable)
- Item 87: Consider using a custom serialized form(考慮使用自定義序列化形式)
- Item 88: Write readObject methods defensively(防御性地編寫 readObject 方法)
- Item 89: For instance control, prefer enum types to readResolve(對于實例控制,枚舉類型優于 readResolve)
- Item 90: Consider serialization proxies instead of serialized instances(考慮以序列化代理代替序列化實例)