### 基本用法
這是一個帶有靜態方法**work()**的類,它對該類的對象執行某些工作:
```java
// concurrent/Machina.java
import onjava.Nap;
public class Machina {
public enum State {
START, ONE, TWO, THREE, END;
State step() {
if(equals(END))
return END;
return values()[ordinal() + 1];
}
}
private State state = State.START;
private final int id;
public Machina(int id) {
this.id = id;
}
public static Machina work(Machina m) {
if(!m.state.equals(State.END)){
new Nap(0.1);
m.state = m.state.step();
}
System.out.println(m);
return m;
}
@Override
public String toString() {
return"Machina" + id + ": " + (state.equals(State.END)? "complete" : state);
}
}
```
這是一個有限狀態機,一個微不足道的機器,因為它沒有分支......它只是從頭到尾遍歷一條路徑。**work()**方法將機器從一個狀態移動到下一個狀態,并且需要100毫秒才能完成“工作”。
**CompletableFuture**可以被用來做的一件事是, 使用**completedFuture()**將它感興趣的對象進行包裝。
```java
// concurrent/CompletedMachina.java
import java.util.concurrent.*;
public class CompletedMachina {
public static void main(String[] args) {
CompletableFuture<Machina> cf =
CompletableFuture.completedFuture(
new Machina(0));
try {
Machina m = cf.get(); // Doesn't block
} catch(InterruptedException |
ExecutionException e) {
throw new RuntimeException(e);
}
}
}
```
**completedFuture()**創建一個“已經完成”的**CompletableFuture**。對這樣一個未來做的唯一有用的事情是**get()**里面的對象,所以這看起來似乎沒有用。注意**CompletableFuture**被輸入到它包含的對象。這個很重要。
通常,**get()**在等待結果時阻塞調用線程。此塊可以通過**InterruptedException**或**ExecutionException**中斷。在這種情況下,阻止永遠不會發生,因為CompletableFutureis已經完成,所以答案立即可用。
當我們將**handle()**包裝在**CompletableFuture**中時,發現我們可以在**CompletableFuture**上添加操作來處理所包含的對象,使得事情變得更加有趣:
```java
// concurrent/CompletableApply.java
import java.util.concurrent.*;
public class CompletableApply {
public static void main(String[] args) {
CompletableFuture<Machina> cf =
CompletableFuture.completedFuture(
new Machina(0));
CompletableFuture<Machina> cf2 =
cf.thenApply(Machina::work);
CompletableFuture<Machina> cf3 =
cf2.thenApply(Machina::work);
CompletableFuture<Machina> cf4 =
cf3.thenApply(Machina::work);
CompletableFuture<Machina> cf5 =
cf4.thenApply(Machina::work);
}
}
```
**輸出結果**:
```
Machina0: ONE
Machina0: TWO
Machina0: THREE
Machina0: complete
```
`thenApply()` 應用一個接收輸入并產生輸出的函數。在本例中,`work()` 函數產生的類型與它所接收的類型相同 (`Machina`),因此每個 `CompletableFuture`添加的操作的返回類型都為 `Machina`,但是(類似于流中的 `map()` )函數也可以返回不同的類型,這將體現在返回類型上。
你可以在此處看到有關**CompletableFutures**的重要信息:它們會在你執行操作時自動解包并重新包裝它們所攜帶的對象。這使得編寫和理解代碼變得更加簡單, 而不會在陷入在麻煩的細節中。
我們可以消除中間變量并將操作鏈接在一起,就像我們使用Streams一樣:
```java
// concurrent/CompletableApplyChained.javaimport java.util.concurrent.*;
import onjava.Timer;
public class CompletableApplyChained {
public static void main(String[] args) {
Timer timer = new Timer();
CompletableFuture<Machina> cf =
CompletableFuture.completedFuture(
new Machina(0))
.thenApply(Machina::work)
.thenApply(Machina::work)
.thenApply(Machina::work)
.thenApply(Machina::work);
System.out.println(timer.duration());
}
}
```
輸出結果:
```
Machina0: ONE
Machina0: TWO
Machina0: THREE
Machina0: complete
514
```
這里我們還添加了一個 `Timer`,它的功能在每一步都顯性地增加 100ms 等待時間之外,還將 `CompletableFuture` 內部 `thenApply` 帶來的額外開銷給體現出來了。
**CompletableFutures** 的一個重要好處是它們鼓勵使用私有子類原則(不共享任何東西)。默認情況下,使用 **thenApply()** 來應用一個不對外通信的函數 - 它只需要一個參數并返回一個結果。這是函數式編程的基礎,并且它在并發特性方面非常有效[^5]。并行流和 `ComplempleFutures` 旨在支持這些原則。只要你不決定共享數據(共享非常容易導致意外發生)你就可以編寫出相對安全的并發程序。
回調 `thenApply()` 一旦開始一個操作,在完成所有任務之前,不會完成 **CompletableFuture** 的構建。雖然這有時很有用,但是開始所有任務通常更有價值,這樣就可以運行繼續前進并執行其他操作。我們可通過`thenApplyAsync()` 來實現此目的:
```java
// concurrent/CompletableApplyAsync.java
import java.util.concurrent.*;
import onjava.*;
public class CompletableApplyAsync {
public static void main(String[] args) {
Timer timer = new Timer();
CompletableFuture<Machina> cf =
CompletableFuture.completedFuture(
new Machina(0))
.thenApplyAsync(Machina::work)
.thenApplyAsync(Machina::work)
.thenApplyAsync(Machina::work)
.thenApplyAsync(Machina::work);
System.out.println(timer.duration());
System.out.println(cf.join());
System.out.println(timer.duration());
}
}
```
輸出結果:
```
116
Machina0: ONE
Machina0: TWO
Machina0:THREE
Machina0: complete
Machina0: complete
552
```
同步調用(我們通常使用的那種)意味著:“當你完成工作時,才返回”,而異步調用以意味著: “立刻返回并繼續后續工作”。 正如你所看到的,`cf` 的創建現在發生的更快。每次調用 `thenApplyAsync()` 都會立刻返回,因此可以進行下一次調用,整個調用鏈路完成速度比以前快得多。
事實上,如果沒有回調 `cf.join()` 方法,程序會在完成其工作之前退出。而 `cf.join()` 直到cf操作完成之前,阻止 `main()` 進程結束。我們還可以看出本示例大部分時間消耗在 `cf.join()` 這。
這種“立即返回”的異步能力需要 `CompletableFuture` 庫進行一些秘密(`client` 無感)工作。特別是,它將你需要的操作鏈存儲為一組回調。當操作的第一個鏈路(后臺操作)完成并返回時,第二個鏈路(后臺操作)必須獲取生成的 `Machina` 并開始工作,以此類推! 但這種異步機制沒有我們可以通過程序調用棧控制的普通函數調用序列,它的調用鏈路順序會丟失,因此它使用一個函數地址來存儲的回調來解決這個問題。
幸運的是,這就是你需要了解的有關回調的全部信息。程序員將這種人為制造的混亂稱為 callback hell(回調地獄)。通過異步調用,`CompletableFuture` 幫你管理所有回調。 除非你知道系統的一些具體的變化,否則你更想使用異步調用來實現程序。
- 其他操作
當你查看`CompletableFuture`的 `Javadoc` 時,你會看到它有很多方法,但這個方法的大部分來自不同操作的變體。例如,有 `thenApply()`,`thenApplyAsync()` 和第二種形式的 `thenApplyAsync()`,它們使用 `Executor` 來運行任務(在本書中,我們忽略了 `Executor` 選項)。
下面的示例展示了所有"基本"操作,這些操作既不涉及組合兩個 `CompletableFuture`,也不涉及異常(我們將在后面介紹)。首先,為了提供簡潔性和方便性,我們應該重用以下兩個實用程序:
```java
package onjava;
import java.util.concurrent.*;
public class CompletableUtilities {
// Get and show value stored in a CF:
public static void showr(CompletableFuture<?> c) {
try {
System.out.println(c.get());
} catch(InterruptedException
| ExecutionException e) {
throw new RuntimeException(e);
}
}
// For CF operations that have no value:
public static void voidr(CompletableFuture<Void> c) {
try {
c.get(); // Returns void
} catch(InterruptedException
| ExecutionException e) {
throw new RuntimeException(e);
}
}
}
```
`showr()` 在 `CompletableFuture<Integer>` 上調用 `get()`,并顯示結果,`try/catch` 兩個可能會出現的異常。
`voidr()` 是 `CompletableFuture<Void>` 的 `showr()` 版本,也就是說,`CompletableFutures` 只為任務完成或失敗時顯示信息。
為簡單起見,下面的 `CompletableFutures` 只包裝整數。`cfi()` 是一個便利的方法,它把一個整數包裝在一個完整的 `CompletableFuture<Integer>` :
```java
// concurrent/CompletableOperations.java
import java.util.concurrent.*;
import static onjava.CompletableUtilities.*;
public class CompletableOperations {
static CompletableFuture<Integer> cfi(int i) {
return
CompletableFuture.completedFuture(
Integer.valueOf(i));
}
public static void main(String[] args) {
showr(cfi(1)); // Basic test
voidr(cfi(2).runAsync(() ->
System.out.println("runAsync")));
voidr(cfi(3).thenRunAsync(() ->
System.out.println("thenRunAsync")));
voidr(CompletableFuture.runAsync(() ->
System.out.println("runAsync is static")));
showr(CompletableFuture.supplyAsync(() -> 99));
voidr(cfi(4).thenAcceptAsync(i ->
System.out.println("thenAcceptAsync: " + i)));
showr(cfi(5).thenApplyAsync(i -> i + 42));
showr(cfi(6).thenComposeAsync(i -> cfi(i + 99)));
CompletableFuture<Integer> c = cfi(7);
c.obtrudeValue(111);
showr(c);
showr(cfi(8).toCompletableFuture());
c = new CompletableFuture<>();
c.complete(9);
showr(c);
c = new CompletableFuture<>();
c.cancel(true);
System.out.println("cancelled: " +
c.isCancelled());
System.out.println("completed exceptionally: " +
c.isCompletedExceptionally());
System.out.println("done: " + c.isDone());
System.out.println(c);
c = new CompletableFuture<>();
System.out.println(c.getNow(777));
c = new CompletableFuture<>();
c.thenApplyAsync(i -> i + 42)
.thenApplyAsync(i -> i * 12);
System.out.println("dependents: " +
c.getNumberOfDependents());
c.thenApplyAsync(i -> i / 2);
System.out.println("dependents: " +
c.getNumberOfDependents());
}
}
```
**輸出結果** :
```
1
runAsync
thenRunAsync
runAsync is static
99
thenAcceptAsync: 4
47
105
111
8
9
cancelled: true
completed exceptionally: true
done: true
java.util.concurrent.CompletableFuture@6d311334[Complet ed exceptionally]
777
dependents: 1
dependents: 2
```
- `main()` 包含一系列可由其 `int` 值引用的測試。
- `cfi(1)` 演示了 `showr()` 正常工作。
- `cfi(2)` 是調用 `runAsync()` 的示例。由于 `Runnable` 不產生返回值,因此使用了返回 `CompletableFuture <Void>` 的`voidr()` 方法。
- 注意使用 `cfi(3)`,`thenRunAsync()` 效果似乎與 上例 `cfi(2)` 使用的 `runAsync()`相同,差異在后續的測試中體現:
- `runAsync()` 是一個 `static` 方法,所以你通常不會像`cfi(2)`一樣調用它。相反你可以在 `QuittingCompletable.java` 中使用它。
- 后續測試中表明 `supplyAsync()` 也是靜態方法,區別在于它需要一個 `Supplier` 而不是`Runnable`, 并產生一個`CompletableFuture<Integer>` 而不是 `CompletableFuture<Void>`。
- `then` 系列方法將對現有的 `CompletableFuture<Integer>` 進一步操作。
- 與 `thenRunAsync()` 不同,`cfi(4)`,`cfi(5)` 和`cfi(6)` "then" 方法的參數是未包裝的 `Integer`。
- 通過使用 `voidr()`方法可以看到:
- `AcceptAsync()`接收了一個 `Consumer`,因此不會產生結果。
- `thenApplyAsync()` 接收一個`Function`, 并生成一個結果(該結果的類型可以不同于其輸入類型)。
- `thenComposeAsync()` 與 `thenApplyAsync()`非常相似,唯一區別在于其 `Function` 必須產生已經包裝在`CompletableFuture`中的結果。
- `cfi(7)` 示例演示了 `obtrudeValue()`,它強制將值作為結果。
- `cfi(8)` 使用 `toCompletableFuture()` 從 `CompletionStage` 生成一個`CompletableFuture`。
- `c.complete(9)` 顯示了如何通過給它一個結果來完成一個`task`(`future`)(與 `obtrudeValue()` 相對,后者可能會迫使其結果替換該結果)。
- 如果你調用 `CompletableFuture`中的 `cancel()`方法,如果已經完成此任務,則正常結束。 如果尚未完成,則使用 `CancellationException` 完成此 `CompletableFuture`。
- 如果任務(`future`)完成,則**getNow()**方法返回`CompletableFuture`的完成值,否則返回`getNow()`的替換參數。
- 最后,我們看一下依賴(`dependents`)的概念。如果我們將兩個`thenApplyAsync()`調用鏈路到`CompletableFuture`上,則依賴項的數量不會增加,保持為1。但是,如果我們另外將另一個`thenApplyAsync()`直接附加到`c`,則現在有兩個依賴項:兩個一起的鏈路和另一個單獨附加的鏈路。
- 這表明你可以使用一個`CompletionStage`,當它完成時,可以根據其結果派生多個新任務。
- 譯者的話
- 前言
- 簡介
- 第一章 對象的概念
- 抽象
- 接口
- 服務提供
- 封裝
- 復用
- 繼承
- "是一個"與"像是一個"的關系
- 多態
- 單繼承結構
- 集合
- 對象創建與生命周期
- 異常處理
- 本章小結
- 第二章 安裝Java和本書用例
- 編輯器
- Shell
- Java安裝
- 校驗安裝
- 安裝和運行代碼示例
- 第三章 萬物皆對象
- 對象操縱
- 對象創建
- 數據存儲
- 基本類型的存儲
- 高精度數值
- 數組的存儲
- 代碼注釋
- 對象清理
- 作用域
- 對象作用域
- 類的創建
- 類型
- 字段
- 基本類型默認值
- 方法使用
- 返回類型
- 參數列表
- 程序編寫
- 命名可見性
- 使用其他組件
- static關鍵字
- 小試牛刀
- 編譯和運行
- 編碼風格
- 本章小結
- 第四章 運算符
- 開始使用
- 優先級
- 賦值
- 方法調用中的別名現象
- 算術運算符
- 一元加減運算符
- 遞增和遞減
- 關系運算符
- 測試對象等價
- 邏輯運算符
- 短路
- 字面值常量
- 下劃線
- 指數計數法
- 位運算符
- 移位運算符
- 三元運算符
- 字符串運算符
- 常見陷阱
- 類型轉換
- 截斷和舍入
- 類型提升
- Java沒有sizeof
- 運算符總結
- 本章小結
- 第五章 控制流
- true和false
- if-else
- 迭代語句
- while
- do-while
- for
- 逗號操作符
- for-in 語法
- return
- break 和 continue
- 臭名昭著的 goto
- switch
- switch 字符串
- 本章小結
- 第六章 初始化和清理
- 利用構造器保證初始化
- 方法重載
- 區分重載方法
- 重載與基本類型
- 返回值的重載
- 無參構造器
- this關鍵字
- 在構造器中調用構造器
- static 的含義
- 垃圾回收器
- finalize()的用途
- 你必須實施清理
- 終結條件
- 垃圾回收器如何工作
- 成員初始化
- 指定初始化
- 構造器初始化
- 初始化的順序
- 靜態數據的初始化
- 顯式的靜態初始化
- 非靜態實例初始化
- 數組初始化
- 動態數組創建
- 可變參數列表
- 枚舉類型
- 本章小結
- 第七章 封裝
- 包的概念
- 代碼組織
- 創建獨一無二的包名
- 沖突
- 定制工具庫
- 使用 import 改變行為
- 使用包的忠告
- 訪問權限修飾符
- 包訪問權限
- public: 接口訪問權限
- 默認包
- private: 你無法訪問
- protected: 繼承訪問權限
- 包訪問權限 Vs Public 構造器
- 接口和實現
- 類訪問權限
- 本章小結
- 第八章 復用
- 組合語法
- 繼承語法
- 初始化基類
- 帶參數的構造函數
- 委托
- 結合組合與繼承
- 保證適當的清理
- 名稱隱藏
- 組合與繼承的選擇
- protected
- 向上轉型
- 再論組合和繼承
- final關鍵字
- final 數據
- 空白 final
- final 參數
- final 方法
- final 和 private
- final 類
- final 忠告
- 類初始化和加載
- 繼承和初始化
- 本章小結
- 第九章 多態
- 向上轉型回顧
- 忘掉對象類型
- 轉機
- 方法調用綁定
- 產生正確的行為
- 可擴展性
- 陷阱:“重寫”私有方法
- 陷阱:屬性與靜態方法
- 構造器和多態
- 構造器調用順序
- 繼承和清理
- 構造器內部多態方法的行為
- 協變返回類型
- 使用繼承設計
- 替代 vs 擴展
- 向下轉型與運行時類型信息
- 本章小結
- 第十章 接口
- 抽象類和方法
- 接口創建
- 默認方法
- 多繼承
- 接口中的靜態方法
- Instrument 作為接口
- 抽象類和接口
- 完全解耦
- 多接口結合
- 使用繼承擴展接口
- 結合接口時的命名沖突
- 接口適配
- 接口字段
- 初始化接口中的字段
- 接口嵌套
- 接口和工廠方法模式
- 本章小結
- 第十一章 內部類
- 創建內部類
- 鏈接外部類
- 使用 .this 和 .new
- 內部類與向上轉型
- 內部類方法和作用域
- 匿名內部類
- 嵌套類
- 接口內部的類
- 從多層嵌套類中訪問外部類的成員
- 為什么需要內部類
- 閉包與回調
- 內部類與控制框架
- 繼承內部類
- 內部類可以被覆蓋么?
- 局部內部類
- 內部類標識符
- 本章小結
- 第十二章 集合
- 泛型和類型安全的集合
- 基本概念
- 添加元素組
- 集合的打印
- 迭代器Iterators
- ListIterator
- 鏈表LinkedList
- 堆棧Stack
- 集合Set
- 映射Map
- 隊列Queue
- 優先級隊列PriorityQueue
- 集合與迭代器
- for-in和迭代器
- 適配器方法慣用法
- 本章小結
- 簡單集合分類
- 第十三章 函數式編程
- 新舊對比
- Lambda表達式
- 遞歸
- 方法引用
- Runnable接口
- 未綁定的方法引用
- 構造函數引用
- 函數式接口
- 多參數函數式接口
- 缺少基本類型的函數
- 高階函數
- 閉包
- 作為閉包的內部類
- 函數組合
- 柯里化和部分求值
- 純函數式編程
- 本章小結
- 第十四章 流式編程
- 流支持
- 流創建
- 隨機數流
- int 類型的范圍
- generate()
- iterate()
- 流的建造者模式
- Arrays
- 正則表達式
- 中間操作
- 跟蹤和調試
- 流元素排序
- 移除元素
- 應用函數到元素
- 在map()中組合流
- Optional類
- 便利函數
- 創建 Optional
- Optional 對象操作
- Optional 流
- 終端操作
- 數組
- 集合
- 組合
- 匹配
- 查找
- 信息
- 數字流信息
- 本章小結
- 第十五章 異常
- 異常概念
- 基本異常
- 異常參數
- 異常捕獲
- try 語句塊
- 異常處理程序
- 終止與恢復
- 自定義異常
- 異常與記錄日志
- 異常聲明
- 捕獲所有異常
- 多重捕獲
- 棧軌跡
- 重新拋出異常
- 精準的重新拋出異常
- 異常鏈
- Java 標準異常
- 特例:RuntimeException
- 使用 finally 進行清理
- finally 用來做什么?
- 在 return 中使用 finally
- 缺憾:異常丟失
- 異常限制
- 構造器
- Try-With-Resources 用法
- 揭示細節
- 異常匹配
- 其他可選方式
- 歷史
- 觀點
- 把異常傳遞給控制臺
- 把“被檢查的異常”轉換為“不檢查的異常”
- 異常指南
- 本章小結
- 后記:Exception Bizarro World
- 第十六章 代碼校驗
- 測試
- 如果沒有測試過,它就是不能工作的
- 單元測試
- JUnit
- 測試覆蓋率的幻覺
- 前置條件
- 斷言(Assertions)
- Java 斷言語法
- Guava斷言
- 使用斷言進行契約式設計
- 檢查指令
- 前置條件
- 后置條件
- 不變性
- 放松 DbC 檢查或非嚴格的 DbC
- DbC + 單元測試
- 使用Guava前置條件
- 測試驅動開發
- 測試驅動 vs. 測試優先
- 日志
- 日志會給出正在運行的程序的各種信息
- 日志等級
- 調試
- 使用 JDB 調試
- 圖形化調試器
- 基準測試
- 微基準測試
- JMH 的引入
- 剖析和優化
- 優化準則
- 風格檢測
- 靜態錯誤分析
- 代碼重審
- 結對編程
- 重構
- 重構基石
- 持續集成
- 本章小結
- 第十七章 文件
- 文件和目錄路徑
- 選取路徑部分片段
- 路徑分析
- Paths的增減修改
- 目錄
- 文件系統
- 路徑監聽
- 文件查找
- 文件讀寫
- 本章小結
- 第十八章 字符串
- 字符串的不可變
- +的重載與StringBuilder
- 意外遞歸
- 字符串操作
- 格式化輸出
- printf()
- System.out.format()
- Formatter類
- 格式化修飾符
- Formatter轉換
- String.format()
- 一個十六進制轉儲(dump)工具
- 正則表達式
- 基礎
- 創建正則表達式
- 量詞
- CharSequence
- Pattern和Matcher
- find()
- 組(Groups)
- start()和end()
- Pattern標記
- split()
- 替換操作
- 正則表達式與 Java I/O
- 掃描輸入
- Scanner分隔符
- 用正則表達式掃描
- StringTokenizer類
- 本章小結
- 第十九章 類型信息
- 為什么需要 RTTI
- Class對象
- 類字面常量
- 泛化的Class引用
- cast()方法
- 類型轉換檢測
- 使用類字面量
- 遞歸計數
- 一個動態instanceof函數
- 注冊工廠
- 類的等價比較
- 反射:運行時類信息
- 類方法提取器
- 動態代理
- Optional類
- 標記接口
- Mock 對象和樁
- 接口和類型
- 本章小結
- 第二十章 泛型
- 簡單泛型
- 泛型接口
- 泛型方法
- 復雜模型構建
- 泛型擦除
- 補償擦除
- 邊界
- 通配符
- 問題
- 自限定的類型
- 動態類型安全
- 泛型異常
- 混型
- 潛在類型機制
- 對缺乏潛在類型機制的補償
- Java8 中的輔助潛在類型
- 總結:類型轉換真的如此之糟嗎?
- 進階閱讀
- 第二十一章 數組
- 數組特性
- 一等對象
- 返回數組
- 多維數組
- 泛型數組
- Arrays的fill方法
- Arrays的setAll方法
- 增量生成
- 隨機生成
- 泛型和基本數組
- 數組元素修改
- 數組并行
- Arrays工具類
- 數組比較
- 數組拷貝
- 流和數組
- 數組排序
- Arrays.sort()的使用
- 并行排序
- binarySearch二分查找
- parallelPrefix并行前綴
- 本章小結
- 第二十二章 枚舉
- 基本 enum 特性
- 將靜態類型導入用于 enum
- 方法添加
- 覆蓋 enum 的方法
- switch 語句中的 enum
- values 方法的神秘之處
- 實現而非繼承
- 隨機選擇
- 使用接口組織枚舉
- 使用 EnumSet 替代 Flags
- 使用 EnumMap
- 常量特定方法
- 使用 enum 的職責鏈
- 使用 enum 的狀態機
- 多路分發
- 使用 enum 分發
- 使用常量相關的方法
- 使用 EnumMap 進行分發
- 使用二維數組
- 本章小結
- 第二十三章 注解
- 基本語法
- 定義注解
- 元注解
- 編寫注解處理器
- 注解元素
- 默認值限制
- 替代方案
- 注解不支持繼承
- 實現處理器
- 使用javac處理注解
- 最簡單的處理器
- 更復雜的處理器
- 基于注解的單元測試
- 在 @Unit 中使用泛型
- 實現 @Unit
- 本章小結
- 第二十四章 并發編程
- 術語問題
- 并發的新定義
- 并發的超能力
- 并發為速度而生
- 四句格言
- 1.不要這樣做
- 2.沒有什么是真的,一切可能都有問題
- 3.它起作用,并不意味著它沒有問題
- 4.你必須仍然理解
- 殘酷的真相
- 本章其余部分
- 并行流
- 創建和運行任務
- 終止耗時任務
- CompletableFuture類
- 基本用法
- 結合 CompletableFuture
- 模擬
- 異常
- 流異常(Stream Exception)
- 檢查性異常
- 死鎖
- 構造方法非線程安全
- 復雜性和代價
- 本章小結
- 缺點
- 共享內存陷阱
- This Albatross is Big
- 其他類庫
- 考慮為并發設計的語言
- 拓展閱讀
- 第二十五章 設計模式
- 概念
- 單例模式
- 模式分類
- 構建應用程序框架
- 面向實現
- 工廠模式
- 動態工廠
- 多態工廠
- 抽象工廠
- 函數對象
- 命令模式
- 策略模式
- 責任鏈模式
- 改變接口
- 適配器模式(Adapter)
- 外觀模式(Fa?ade)
- 包(Package)作為外觀模式的變體
- 解釋器:運行時的彈性
- 回調
- 多次調度
- 模式重構
- 抽象用法
- 多次派遣
- 訪問者模式
- RTTI的優劣
- 本章小結
- 附錄:補充
- 附錄:編程指南
- 附錄:文檔注釋
- 附錄:對象傳遞和返回
- 附錄:流式IO
- 輸入流類型
- 輸出流類型
- 添加屬性和有用的接口
- 通過FilterInputStream 從 InputStream 讀取
- 通過 FilterOutputStream 向 OutputStream 寫入
- Reader和Writer
- 數據的來源和去處
- 更改流的行為
- 未發生改變的類
- RandomAccessFile類
- IO流典型用途
- 緩沖輸入文件
- 從內存輸入
- 格式化內存輸入
- 基本文件的輸出
- 文本文件輸出快捷方式
- 存儲和恢復數據
- 讀寫隨機訪問文件
- 本章小結
- 附錄:標準IO
- 附錄:新IO
- ByteBuffer
- 數據轉換
- 基本類型獲取
- 視圖緩沖區
- 字節存儲次序
- 緩沖區數據操作
- 緩沖區細節
- 內存映射文件
- 性能
- 文件鎖定
- 映射文件的部分鎖定
- 附錄:理解equals和hashCode方法
- 附錄:集合主題
- 附錄:并發底層原理
- 附錄:數據壓縮
- 附錄:對象序列化
- 附錄:靜態語言類型檢查
- 附錄:C++和Java的優良傳統
- 附錄:成為一名程序員