---
# System prepended metadata

title: Java 串接 I/O 串流
tags: [Java Channel, Java, Java I/O]

---

# Java 串接 I/O 串流
>處理 I/O 的程式，很少使用單一串流物件完成；經常將多個串流物件組成「串接(chain)」，藉由不同的串流物件提供不同的功能，一起完成。

### 常用的串接類別
| 功能 | 位元串流 (Byte Stream) | 字元串流 (Character Stream) |
| - | - | - |
| Buffering (緩衝) | BufferedInputStream / BufferedOutputStream| BufferedReader / BufferedWriter |
| Filtering (過濾) | FilterInputStream / FilterOutputStream | FilterReader / FilterWriter |
| Conversion (位元轉字元) || InputStreamReader / OutputStreamWriter |
| Object serialization (物件序列化) | ObjectInputStream / ObjectOutputStream ||
| Data conversion (資料型態轉換) | DataInputStream / DataOutputStream ||
| Counting (計算行數) | LineNubmerInputStream | LineNumberReader |
| Printing (列印) | PrintStream | PrintWriter |

## 串流串接範例
### 兩個串流類別
>使用 FileInputStream 將資料讀出，然後加入 BufferedInputStream 增加緩衝功能(避免 I/O 忙碌)再進入程式。
```graphviz
digraph dfd{
    rankdir=LR;
    node[shape=record];
    subgraph hd {
        source [label="資料來源" shape=box];
    }
    subgraph cluster_memory {
        label="Java";
        input1 [label="FileInputStream"];
        input2 [label="BufferedInputStream"]
        method [label="程式" shape=Mrecord];
    }
    source -> input1
    input1 -> input2
    input2 -> method
}
```

### 三個串流類別
>若再加入 DataInputStream，則資料可以被轉換成不同型態(int, double...)，再進入程式。
```graphviz
digraph dfd2{
    rankdir=LR;
    node[shape=record];
    subgraph hd {
        source [label="資料來源" shape=box];
    }
    subgraph cluster_memory {
        label="Java";
        input1 [label="FileInputStream"];
        input2 [label="BufferedInputStream"];
        input3 [label="DataInputStream"];
        method [label="程式" shape=Mrecord];
    }
    source -> input1
    input1 -> input2
    input2 -> input3
    input3 -> method
}
```
### 範例程式
```java=
public class CopyBufferedStream {
    public static void main(String[] args) {
        String source = "source.txt";
        String target = "target.txt";
        try (Reader fr = new FileReader(source);
             BufferedReader br = new BufferedReader(fr);
             Writer fw = new FileWriter(target);
             BufferedWriter bw = new BufferedWriter(fw)
        ) {
            String line = null;
            while ((line = br.readLine()) != null) {
                bw.write(line);
                bw.newLine();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
```
### 範例程式說明
| 行號 | 內容 |
| - | - |
| 6 | 宣告 BufferedReader 裝飾 FileReader |
| 8 | 宣告 BufferedWriter 裝飾 FileWriter |
| 10 | BufferedReader 每次讀一行(內容+換行符號)，**內容**指派給變數 line |
| 11 | BufferedReader 逐行寫出資料|
| 12 | line 之中無換行符號，所以呼叫 newLine()方法 |

## Channel I/O
> JDK 1.4 導入 Channel，Channel 有通道、頻道的意思，可以指兩個設備之間傳送資訊所經過的通路或連接。
> 屬於 java.nio 套件。功能是不需要以迴圈讀寫內容，可以讀大量讀寫位元和字元。

### 範例程式
```java=
public class CopyByteChannel {
    public static void main(String[] args) {
        String source = "source.txt";
        String target = "target.txt";
        try (FileInputStream fis = new FileInputStream(source);
             FileChannel fisc = fis.getChannel();
             FileOutputStream fos = new FileOutputStream(target);
             FileChannel fosc = fos.getChannel()
        ) {
            ByteBuffer buff = ByteBuffer.allocate((int) fisc.size());
            fisc.read(buff);
            buff.position(0);
            fosc.write(buff);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
```

### 範例程式說明
| 行號 | 內容 |
| - | - |
| 9 | 建立 source 檔大小的 ByteBuffer 物件 |
| 10 | 將實體檔案一次全數讀入 ByteBuffer 物件 |
| 11 | 將 ByteBuffer 裡的標示位置移到最前面 |
| 12 | FileChannel 接收 ByteBuffer 裡的資料，直接輸出檔案 |

#### 備註
> FileChannel 的 read() 和 write() 方法都是透過 ByteBuffer 物件一次完成，所以不用迴圈分批處理。


###### tags: `Java`, `Java I/O`, `Java Channel`

