# Java I/O
>Java 將 I/O (input/output),亦即輸入/輸出的概念,以 stream (水流或串流)的抽象概念表達
```graphviz
digraph dfd{
rankdir=LR;
node[shape=record];
subgraph hd {
source [label="資料來源" shape=box];
target [label="資料目的" shape=box];
}
subgraph cluster_memory {
label="Java";
input [label="輸入流"];
output [label="輸出流"];
method [label="程式" shape=Mrecord];
}
source -> input
input -> method
method -> output
output -> target
}
```
### Stream 如水流具有方向性:
1. 若方向是流入 Java 程式,則稱為「輸入(input)流」或「來源(source)」
2. 若方向是流出 Java 程式,則稱為「輸出(output)流」或「目的(sink)」
### 流動的內容也分兩類:
1. 位元(byte)
2. 字元(character)
## 處理串流的類別
根據串流內的資料分類,加上流動方向,處理類別可以區分成四大類,這些都是抽象類別:
|方向|位元(byte)| 字元(character) |
|-|-|-|
|輸入流|InputStream|Reader|
|輸出流|OutputStream|Writer|
## InputStream 類別
### 基本方法
* int read()
每次讀取1個 byte。
* int read(byte[] buffer)
每次讀取1個 byte[] 至 buffer 中,回傳讀取長度。
* int read(byte[] buffer, int offset, int length)
每次讀取1個 byte[],指定偏移量(offset)和讀取長度(length)。
* void close()
關閉 stream 。
* int available()
有多少 bytes 可供讀取。
* long skip(long n)
讀取時掠過 n 個 bytes。
### 其他方法
* boolean markSupported()
* void mark(int readlimit)
* void reset()
push-back 操作,合併用於改變檔案中的讀取位置,特別是回到過去某個指定的讀取位置。
## OutputStream 類別
### 基本方法
* void write(int b)
將 b 寫入 OutputStream。
* void write(byte[] buffer)
將 buffer 寫入 OutputStream。
* void write(byte[] buffer, int offset, int length)
將 buffer 寫入 OutputStream,且指定偏移量(offset)和長度(length)。
* void close()
關閉 stream 。
* int flush()
強制將 OutputStream 中的資料寫入目的。
## 位元串流範例
### 範例程式
```java=
public class CopyByteStream {
public static void main(String[] args) {
String source = "source.txt";
String target = "target.txt";
byte[] bytes = new byte[128];
int bLen = bytes.length;
try (InputStream fis = new FileInputStream(source);
OutputStream fos = new FileOutputStream(target)) {
System.out.println("Will copy bytes:" + fis.available());
int read;
while ((read = fis.read(bytes)) != -1) {
if (read < bLen) {
fos.write(bytes, 0, read);
} else {
fos.write(bytes);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
```
### 範例程式說明
|行號|內容|
|-|-|
|10|宣告每次讀取長度 read|
|11|每次都讀一個 byte[128],回傳 -1 表示讀完|
|12|讀入長度不滿 byte[128] 時|
|13|寫入 bytes[0:read]|
## Reader 類別
### 基本方法
* int read()
每次讀取1個 char。
* int read(char[] buffer)
每次讀取1個 char[] 至 buffer 中,回傳讀取長度。
* int read(byte[] buffer, int offset, int length)
每次讀取1個 char[],指定偏移量(offset)和讀取長度(length)。
* void close()
關閉 stream 。
* boolean ready()
確認 stream 是否已準備好進行資料讀取。
* long skip(long n)
讀取時掠過 n 個 chars。
### 其他方法
* boolean markSupported()
* void mark(int readAheadLimit)
* void reset()
push-back 操作,合併用於改變檔案中的讀取位置,特別是回到過去某個指定的讀取位置。
## Writer 類別
### 基本方法
* void write(int c)
將 c 寫入 Writer。
* void write(char[] buffer)
將 buffer 寫入 Writer。
* void write(byte[] buffer, int offset, int length)
將 buffer 寫入 Writer,且指定偏移量(offset)和長度(length)。
* void close()
關閉 stream 。
* int flush()
強制將 Writer 中的資料寫入目的。
## 字元串流範例
### 範例程式
```java=
public class CopyCharStream {
public static void main(String[] args) {
String source = "source.txt";
String target = "target.txt";
char[] chars = new char[128];
int cLen = chars.length;
try (Reader fr = new FileReader(source);
Writer fw = new FileWriter(target)) {
int read;
while ((read = fr.read(chars)) != -1) {
if (read < cLen) {
fw.write(chars, 0, read);
} else {
fw.write(chars);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
```
### 範例程式說明
|行號|內容|
|-|-|
|9|宣告每次讀取長度 read|
|10|每次都讀一個 char[128],回傳 -1 表示讀完|
|11|讀入長度不滿 char[128] 時|
|12|寫入 chars[0:read]|
###### tags: `Java`, `Java I/O`