程序經常需要訪問文件和目錄,讀取文件信息或寫入信息到文件,在Java語言中對文件的讀寫是通過I/O流技術實現的。
## 22.1 文件管理
Java語言使用File類對文件和目錄進行操作,查找文件時需要實現FilenameFilter或FileFilter接口。另外,讀寫文件內容可以通過FileInputStream、FileOutputStream、FileReader和FileWriter類實現,它們屬于I/O流。這些類和接口全部來源于java.io包。
### 22.1.1 File類
File類表示一個與平臺無關的文件或目錄。
File類中常用的方法如下。
1. 構造方法
* File(String path):如果path是實際存在的路徑,則該File對象表示的是目錄;如果path是文件名,則該File對象表示的是文件。
* File(String path, String name):path是路徑名,name是文件名。
* File(File dir, String name):dir是路徑對象,name是文件名。
2. 獲得文件名
* String getName( ):獲得文件的名稱,不包括路徑。
* String getPath( ):獲得文件的路徑。
* String getAbsolutePath( ):獲得文件的絕對路徑。
* String getParent( ):獲得文件的上一級目錄名。
3. 文件屬性測試
* boolean exists( ):測試當前File對象所表示的文件是否存在。
* boolean canWrite( ):測試當前文件是否可寫。
* boolean canRead( ):測試當前文件是否可讀。
* boolean isFile( ):測試當前文件是否是文件。
* boolean isDirectory( ):測試當前文件是否是目錄。
4. 文件操作
* long lastModified( ):獲得文件最近一次修改的時間。
* long length( ):獲得文件的長度,以字節為單位。
* boolean delete( ):刪除當前文件。成功返回 true,否則返回false。
* boolean renameTo(File dest):將重新命名當前File對象所表示的文件。成功返回 true,否則返回false。
5. 目錄操作
* boolean mkdir( ):創建當前File對象指定的目錄。
* String\[\] list():返回當前目錄下的文件和目錄,返回值是字符串數組。
* String\[\] list(FilenameFilter filter):返回當前目錄下滿足指定過濾器的文件和目錄,參數是實現FilenameFilter接口對象,返回值是字符串數組。
* File\[\] listFiles():返回當前目錄下的文件和目錄,返回值是File數組。
* File\[\] listFiles(FilenameFilter filter):返回當前目錄下滿足指定過濾器的文件和目錄,參數是實現FilenameFilter接口對象,返回值是File數組。
* File\[\] listFiles(FileFilter filter):返回當前目錄下滿足指定過濾器的文件和目錄,參數是實現FileFilter接口對象,返回值是File數組。
對目錄操作有兩個過濾器接口:FilenameFilter和FileFilter。它們都只有一個抽象方法accept
**FilenameFilter接口中的accept方法如下:文件**
* boolean accept(File dir, String name):測試指定dir目錄中是否包含文件名為name的文件。
**FileFilter接口中的accept方法如下:目錄**
* boolean accept(File pathname):測試指定路徑名pathname是否應該包含在某個路徑名列表中。
> **注意** 路徑中會用到路徑分隔符,路徑分隔符在不同平臺上是有區別的,UNIX、Linux和macOS中使用正斜杠“/”,而Windows下使用反斜杠“\\”。Java是支持兩種寫法,但是反斜杠“\\”屬于特殊字符,前面需要加轉義符。例如C:\\Users\\a.java在程序代碼中應該使用C:\\\\Users\\\\a.java表示,或表示為C:/Users/a.java也可以。
### 22.1.2 案例:文件過濾
```
~~~
//HelloWorld.java文件
package com.a51work6;
import java.io.File;
import java.io.FilenameFilter;
public class HelloWorld {
public static void main(String[] args) {
// 用File對象表示一個目錄,.表示當前目錄
File dir = new File("./TestDir"); ①
// 創建HTML文件過濾器
Filter filter = new Filter("html"); ②
System.out.println("HTML文件目錄:" + dir);
// 列出目錄TestDir下,文件后綴名為HTML的所有文件
String files[] = dir.list(filter); //dir.list();
// 遍歷文件列表
for (String fileName : files) {
// 為目錄TestDir下的文件或目錄創建File對象
File f = new File(dir, fileName);
// 如果該f對象是文件,則打印文件名
if (f.isFile()) {
System.out.println("文件名:" + f.getName());
System.out.println("文件絕對路徑:" + f.getAbsolutePath());
System.out.println("文件路徑:" + f.getPath());
} else {
System.out.println("子目錄:" + f);
}
}
}
}
// 自定義基于文件擴展名的文件過濾器
class Filter implements FilenameFilter { ③
// 文件擴展名
String extent;
// 構造方法
Filter(String extent) {
this.extent = extent;
}
@Override
public boolean accept(File dir, String name) { ④
// 測試文件擴展名是否為extent所指定的
return name.endsWith("." + extent);
}
}
~~~
```
上述代碼第①行創建TestDir目錄對象,"./TestDir"表示當前目錄下的TestDir目錄,還可以表示為".\\\\TestDir"和"TestDir"。
> **提示** 在編程時盡量使用相對路徑,盡量不要使用絕對路徑。"./TestDir"就是相對路徑,相對路徑中會用到點“.”,在目錄中一個點“.”表示當前目錄,兩個點表示“..”表示父目錄。
> **注意** 在Eclipse工具中運行的Java程序,那么當前目錄在哪里呢?例如"./TestDir"表示當前目錄下的TestDir子目錄,那么應該在哪里創建TestDir目錄呢?在Eclipse中當前目錄就是工程的根目錄,如圖22-1所示,當前目錄是Eclipse工程根目錄,子目錄TestDir位于工程根目錄下。

上述代碼第②行創建針對HTML文件過濾器Filter,Filter類要求實現FilenameFilter接口,見代碼第⑤行。FilenameFilter接口要求實現抽象方法accept,見代碼第④行,在該方法中通過判斷文件名是否指定的擴展名結尾則返回true,否則返回false。
## 22.2 I/O流概述
Java將數據的輸入輸出(I/O)操作當作“流”來處理,“流”是一組有序的數據序列。“流”分為兩種形式:輸入流和輸出流,從數據源中讀取數據是輸入流,將數據寫入到目的地是輸出流。
> **提示** 以CPU為中心,從外部設備讀取數據到內存,進而再讀入到CPU,這是輸入(Input,縮寫I)過程;將內存中的數據寫入到外部設備,這是輸出(Output,縮寫O)過程。所以輸入輸出簡稱為I/O。
### 22.2.1 Java流設計理念

所有的輸入形式都抽象為輸入流,所有的輸出形式都抽象為輸出流,它們與設備無關。
### 22.2.2 流類繼承層次
以字節為單位的流稱為字節流,以字符為單位的流稱為字符流。Java SE提供4個頂級抽象類,兩個字節流抽象類:InputStream和OutputStream;兩個字符流抽象類:Reader和Writer。
**字節輸入流**
字節輸入流根類是InputStream


**字節輸出流**
字節輸出流根類是OutputStream


**字符輸入流**
字符輸入流根類是Reader,這類流以16位的Unicode編碼表示的字符為基本處理單位。


**字符輸出流**
字符輸出流根類是Writer,這類流以16位的Unicode編碼表示的字符為基本處理單位。


## 22.3 字節流
上一節總體概述了Java中I/O流層次結構技術,本節詳細介紹一下字節流的API。掌握字節流的API先要熟悉它的兩個抽象類:InputStream 和OutputStream,了解它們有哪些主要的方法。
### 22.3.1 InputStream抽象類
InputStream是字節輸入流的根類,它定義了很多方法,影響著字節輸入流的行為。下面詳細介紹一下。
InputStream主要方法如下:
* int read():讀取一個字節,返回0到255范圍內的int字節值。如果已經到達流末尾,而且沒有可用的字節,則返回值-1。
* int read(byte b[] ):讀取多個字節,數據放到字節數組b中,返回值為實際讀取的字節的數量,如果已經到達流末尾,而且沒有可用的字節,則返回值-1。
* int read(byte b\[ \], int off, int len):最多讀取len個字節,數據放到以下標off開始字節數組b中,將讀取的第一個字節存儲在元素b\[off\]中,下一個存儲在b\[off+1\]中,依次類推。返回值為實際讀取的字節的數量,如果已經到達流末尾,而且沒有可用的字節,則返回值-1。
* void close():流操作完畢后必須關閉。
上述所有方法都可能會拋出IOException,因此使用時要注意處理異常。
### 22.3.2 OutputStream抽象類
OutputStream是字節輸出流的根類,它定義了很多方法,影響著字節輸出流的行為。下面詳細介紹一下。
OutputStream主要方法如下:
* void write(int b):將b寫入到輸出流,b是int類型占有32位,寫入過程是寫入b 的8個低位,b的24個高位將被忽略。
* void write(byte b\[ \]):將b.length個字節從指定字節數組b寫入到輸出流。
* void write(byte b\[ \], int off, int len):把字節數組b中從下標off開始,長度為len的字節寫入到輸出流。
* void flush():刷空輸出流,并輸出所有被緩存的字節。由于某些流支持緩存功能,該方法將把緩存中所有內容強制輸出到流中。
* void close( ):流操作完畢后必須關閉。
上述所有方法都聲明拋出IOException,因此使用時要注意處理異常。
> **注意** 流(包括輸入流和輸出流)所占用的資源,不能通過JVM的垃圾收集器回收,需要程序員自己釋放。一種方法是可以在finally代碼塊調用close()方法關閉流,釋放流所占用的資源。另一種方法通過自動資源管理技術管理這些流,流(包括輸入流和輸出流)都實現了AutoCloseable接口,可以使用自動資源管理技術,具體內容參考19.4.2節。
### 22.3.3 案例:文件復制
前面介紹了兩種字節流常用的方法,下面通過一個案例熟悉一下它們的使用,該案例實現了文件復制,數據源是文件,所以會用到文件輸入流FileInputStream,數據目的地也是文件,所以會用到文件輸出流FileOutputStream。
FileInputStream和FileOutputStream中主要方法都是繼承自InputStream和OutputStream前面兩個節已經詳細介紹了,這里不再贅述。下面介紹一下它們的構造方法,
**FileInputStream構造方法主要有:**
* FileInputStream(String name):創建FileInputStream對象,name是文件名。如果文件不存在則拋出FileNotFoundException異常。
* FileInputStream(File file):通過File對象創建FileInputStream對象。如果文件不存在則拋出FileNotFoundException異常。
**FileOutputStream構造方法主要有:**
* FileOutputStream(String name):通過指定name文件名創建FileOutputStream對象。如果name文件存在,但如果是一個目錄或文件無法打開則拋出FileNotFoundException異常。
* FileOutputStream(String name, boolean append):通過指定name文件名創建FileOutputStream對象,append參數如果為 true,則將字節寫入文件末尾處,而不是寫入文件開始處。如果name文件存在,但如果是一個目錄或文件無法打開則拋出FileNotFoundException異常。
* FileOutputStream(File file):通過File對象創建FileOutputStream對象。如果file文件存在,但如果是一個目錄或文件無法打開則拋出FileNotFoundException異常。
* FileOutputStream(File file, boolean append):通過File對象創建FileOutputStream對象,append參數如果為 true,則將字節寫入文件末尾處,而不是寫入文件開始處。如果file文件存在,但如果是一個目錄或文件無法打開則拋出FileNotFoundException異常。
*****
下面介紹如果將./TestDir/build.txt文件內容復制到./TestDir/subDir/build.txt。./TestDir/build.txt文件內容是AI-162.3764568,共14個字節:
```
~~~
//FileCopy.java文件
package com.a51work6;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileCopy {
public static void main(String[] args) {
//新建文件輸入輸出對象實例
try (FileInputStream in = new FileInputStream("./TestDir/build.txt");
FileOutputStream out = new FileOutputStream("./TestDir/subDir/build.txt")) { ①
// 準備一個緩沖區,一個字節數組
byte[] buffer = new byte[10]; ②
// 首先讀取一次,返回值為實際讀取的字節的數量
int len = in.read(buffer); ③
while (len != -1) { ④
// 開始寫入數據
out.write(buffer, 0, len); ⑥
// 再讀取一次
len = in.read(buffer); ⑦
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
~~~
```
代碼第③行是第一次從輸入流中讀取數據,數據保存到buffer中,len是實際讀取的字節數。代碼第⑦行也從輸入流中讀取數據。由于本例中緩沖區大小設置為10,因此這兩次讀取數據會把數據讀完,第一次讀了10個字節,第二次讀了4個字節。

上面的案例由于使用了字節輸入輸出流,所以不僅可以復制文本文件,還可以復制二進制文件。
### 22.3.4 使用字節緩沖流

**使用緩沖流,減少輸入流和輸出流的對數據源的IO操作次數。
緩沖流是裝飾流,擴展了其他流的功能
**
BufferedInputStream和BufferedOutputStream稱為字節緩沖流,使用字節緩沖流內置了一個緩沖區。
1. 第一次調用read方法時盡可能多地從數據源讀取數據到緩沖區
2. 后續再到用read方法時先看看緩沖區中是否有數據,如果有則先讀緩沖區中的數據,如果沒有再將數據源中的數據讀入到緩沖區
3. 通過輸出流調用write方法寫入數據時,也先將數據寫入到緩沖區。
4. 緩沖區滿了之后再寫入數據目的地
使用了緩沖字節流可以減少I/O操作次數,提高效率。
從圖22-3和圖22-4可見,
BufferedInputStream的父類是FilterInputStream,BufferedOutputStream的父類是FilterOutputStream
FilterInputStream和FilterOutputStream稱為**過濾流**。過濾流的作用是擴展其他流,增強其功能。那么BufferedInputStream和BufferedOutputStream增強了緩沖能力。
**BufferedInputStream構造方法主要有:**
* BufferedInputStream(InputStream in):通過一個底層輸入流in對象創建緩沖流對象,緩沖區大小是默認的,默認值8192。
* BufferedInputStream(InputStream in, int size):通過一個底層輸入流in對象創建緩沖流對象,size指定的緩沖區大小,緩沖區大小應該是2的n次冪,這樣可提供緩沖區的利用率。
**BufferedOutputStream構造方法主要有:**
* BufferedOutputStream(OutputStream out):通過一個底層輸出流out 對象創建緩沖流對象,緩沖區大小是默認的,默認值8192。
* BufferedOutputStream(OutputStream out, int size):通過一個底層輸出流out對象創建緩沖流對象,size指定的緩沖區大小,緩沖區大小應該是2的n次冪,這樣可提高緩沖區的利用率。
**下面將22.3.3節的文件復制的案例改造成緩沖流實現,代碼如下:**
```
~~~
//FileCopyWithBuffer.java文件
package com.a51work6;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileCopyWithBuffer {
public static void main(String[] args) {
try (
//文件流構造函數參數是數據源
FileInputStream fis = new FileInputStream("./TestDir/src.zip"); ①
//緩沖流構造函數參數是文件流
BufferedInputStream bis = new BufferedInputStream(fis); ②
FileOutputStream fos = new FileOutputStream("./TestDir/subDir/src.zip"); ③
BufferedOutputStream bos = new BufferedOutputStream(fos)) { ④
//開始時間
long startTime = System.nanoTime(); ⑤
// 準備一個緩沖區,這個緩沖區是輸入流和輸出流的中轉站
byte[] buffer = new byte[1024]; ⑥
// 首先讀取一次
int len = bis.read(buffer);
while (len != -1) {
// 開始寫入數據
bos.write(buffer, 0, len);
// 再讀取一次
len = bis.read(buffer);
}
//結束時間
long elapsedTime = System.nanoTime() - startTime; ⑦
//它的單位是納秒,需要除以10的6次方才是毫秒
System.out.println("耗時:" + (elapsedTime / 1000000.0) + " 毫秒");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
~~~
```
上述代碼第①行是創建文件輸入流,它是一個底層流,通過它構造緩沖輸入流,見代碼第②行。同理,代碼第④行是構造緩沖輸出流。
**提示** 在程序代碼第⑥行也指定了緩沖區buffer,這個緩沖區與緩沖流內置緩沖區不同,決定是否進行I/O操作次數的是緩沖流內置緩沖區,不是這個緩沖區。
## 22.4 字符流
掌握字符流的API先要熟悉它的兩個抽象類:Reader和Writer
### 22.4.1 Reader抽象類
Reader是字符輸入流的根類,它定義了很多方法,影響著字符輸入流的行為。下面詳細介紹一下。
**Reader主要方法如下:**
* int read():讀取一個字符,返回值范圍在0~65535(0x00~0xffff)之間。如果因為已經到達流末尾,則返回值-1。
* int read(char\[\] cbuf):將字符讀入到數組cbuf中,返回值為實際讀取的字符的數量,如果因為已經到達流末尾,則返回值-1。
* int read(char\[\] cbuf, int off, int len):最多讀取len個字符,數據放到以下標off開始字符數組cbuf中,將讀取的第一個字符存儲在元素cbuf\[off\]中,下一個存儲在cbuf\[off+1\]中,依次類推。返回值為實際讀取的字符的數量,如果因為已經到達流末尾,則返回值-1。
* void close():流操作完畢后必須關閉。
上述所有方法都聲明了拋出IOException,因此使用時要注意處理異常。
### 22.4.2 Writer抽象類
Writer是字符輸出流的根類,它定義了很多方法,影響著字符輸出流的行為。下面詳細介紹一下。
**Writer主要方法如下:**
* void write(int c):將整數值為c的字符寫入到輸出流,c是int類型占有32位,寫入過程是寫入c的16個低位,c的16個高位將被忽略。
* void write(char\[\] cbuf):將字符數組cbuf寫入到輸出流。
* void write(char\[\] cbuf, int off, int len):把字符數組cbuf中從下標off開始,長度為len的字符寫入到輸出流。
* void write(String str):將字符串str中的字符寫入輸出流。
* void write(String str,int off,int len):將字符串str 中從索引off開始處的len個字符寫入輸出流。
* void flush():刷空輸出流,并輸出所有被緩存的字符。由于某些流支持緩存功能,該方法將把緩存中所有內容強制輸出到流中。
* void close( ):流操作完畢后必須關閉。
上述所有方法都可以會拋出IOException,因此使用時要注意處理異常。
> **注意** Reader和Writer都實現了AutoCloseable接口,可以使用自動資源管理技術自動關閉它們。
### 22.4.3 案例:文件復制
前面兩節介紹了字符流常用的方法,下面通過一個案例熟悉一下它們的使用,該案例實現了文件復制,數據源是文件,所以會用到文件輸入流FileReader,數據目的地也是文件,所以會用到文件輸出流FileWriter。
FileReader和FileWriter中主要方法都是繼承自Reader和Writer,下面介紹一下它們的構造方法,FileReader構造方法主要有:
* FileReader(String fileName):創建FileReader對象,fileName是文件名。如果文件不存在則拋出FileNotFoundException異常。
* FileReader(File file):通過File對象創建FileReader對象。如果文件不存在則拋出FileNotFoundException異常。
**FileWriter構造方法主要有:**
* FileWriter(String fileName):通過指定fileName文件名創建FileWriter對象。如果fileName文件存在,但如果是一個目錄或文件無法打開則拋出FileNotFoundException異常。
* FileWriter(String fileName, boolean append):通過指定fileName文件名創建FileWriter對象,append參數如果為 true,則將字符寫入文件末尾處,而不是寫入文件開始處。如果fileName文件存在,但如果是一個目錄或文件無法打開則拋出FileNotFoundException異常。
* FileWriter(File file):通過File對象創建FileWriter對象。如果file文件存在,但如果是一個目錄或文件無法打開則拋出FileNotFoundException異常。
* FileWriter(File file, boolean append):通過File對象創建FileWriter對象,append參數如果為 true,則將字符寫入文件末尾處,而不是寫入文件開始處。如果file文件存在,但如果是一個目錄或文件無法打開則拋出FileNotFoundException異常。
> **注意** 字符文件流只能復制文本文件,不能是二進制文件。
下面采用文件字符流重新實現22.3.3節文件復制案例,代碼如下:
```
~~~
//FileCopy.java文件
package com.a51work6;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class FileCopy {
public static void main(String[] args) {
try (FileReader in = new FileReader("./TestDir/build.txt");
FileWriter out = new FileWriter("./TestDir/subDir/build.txt")) {
// 準備一個緩沖區
char[] buffer = new char[10];
// 首先讀取一次,從左到右(數據源,輸入流,中轉)
int len = in.read(buffer);
while (len != -1) {
String copyStr = new String(buffer);
// 打印復制的字符串
System.out.println(copyStr);
// 開始寫入數據,從左到右(數據源,輸出流,中轉)
out.write(buffer, 0, len);
// 再讀取一次
len = in.read(buffer);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
~~~
```
### 22.4.4 使用字符緩沖流
BufferedReader和BufferedWriter稱為字符緩沖流。
**BufferedReader特有方法和構造方法有:**
* String readLine():讀取一個文本行,如果因為已經到達流末尾,則返回值null。
* BufferedReader(Reader in):構造方法,通過一個底層輸入流in對象創建緩沖流對象,緩沖區大小是默認的,默認值8192。
* BufferedReader(Reader in, int size):構造方法,通過一個底層輸入流in對象創建緩沖流對象,size指定的緩沖區大小,緩沖區大小應該是2的n次冪,這樣可提高緩沖區的利用率。
**BufferedWriter特有方法和構造方法主要有:**
* void newLine():寫入一個換行符。
* BufferedWriter(Writer out):構造方法,通過一個底層輸出流out 對象創建緩沖流對象,緩沖區大小是默認的,默認值8192。
* BufferedWriter(Writer out, int size):構造方法,通過一個底層輸出流out對象創建緩沖流對象,size指定的緩沖區大小,緩沖區大小應該是2的n次冪,這樣可提高緩沖區的利用率。
**下面將22.4.3節的文件復制的案例改造成緩沖流實現,代碼如下:**
```
~~~
//FileCopyWithBuffer.java文件
package com.a51work6;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class FileCopyWithBuffer {
public static void main(String[] args) {
try (FileReader fis = new FileReader("./TestDir/JButton.html");
BufferedReader bis = new BufferedReader(fis);
FileWriter fos = new FileWriter("./TestDir/subDir/JButton.html");
BufferedWriter bos = new BufferedWriter(fos)) {
// 首先讀取一行文本(從數據源讀到中轉站line)
String line = bis.readLine(); ①
while (line != null) {
// 開始寫入數據,(代碼左到右,數據源 輸出流 中轉站line)
bos.write(line); ②
//寫一個換行符,輸入流的readLine方法會丟掉一個換行符或回車符
bos.newLine(); ③
// 再讀取一行文本
line = bis.readLine();
}
System.out.println("復制完成");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
~~~
```
* 上述代碼第①行是通過字節緩沖流readLine方法讀取一行文本,當讀取是文本為null時說明流已經讀完了。
* 代碼第②行是寫入文本到輸出流,由于在輸入流的readLine方法會丟掉一個換行符或回車符,為了保持復制結果完全一樣,因此需要在寫完一個文本后,調用輸出流的newLine方法寫入一個換行符。
### 22.4.5 字節流轉換字符流
有時需要將字節流轉換為字符流,InputStreamReader和OutputStreamWriter是為實現這種轉換而設計的。
**InputStreamReader構造方法如下:**
* InputStreamReader(InputStream in):將字節流in轉換為字符流對象,字符流使用默認字符集。
* InputStreamReader(InputStream in, String charsetName):將字節流in轉換為字符流對象,charsetName指定字符流的字符集,字符集主要有:US-ASCII、ISO-8859-1、UTF-8和UTF-16。如果指定的字符集不支持會拋出UnsupportedEncodingException異常。
**OutputStreamWriter構造方法如下:**
* OutputStreamWriter(OutputStream out):將字節流out轉換為字符流對象,字符流使用默認字符集。
* OutputStreamWriter(OutputStream out,String charsetName):將字節流out轉換為字符流對象,charsetName指定字符流的字符集,如果指定的字符集不支持會拋UnsupportedEncodingException異常。
**下面將22.4.3節的文件復制的案例改造成緩沖流實現,代碼如下:**
```
~~~
//FileCopyWithBuffer.java文件
package com.a51work6;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
public class FileCopyWithBuffer {
public static void main(String[] args) {
try ( // 創建字節文件輸入流對象
FileInputStream fis = new FileInputStream("./TestDir/JButton.html"); ①
// 創建轉換流對象
InputStreamReader isr = new InputStreamReader(fis);
// 字符緩沖輸入流裝飾輸入流
BufferedReader bis = new BufferedReader(isr);
// 創建字節文件輸出流對象
FileOutputStream fos = new FileOutputStream("./TestDir/subDir/JButton.html");
// 創建轉換流對象
OutputStreamWriter osw = new OutputStreamWriter(fos);
// 字符緩沖輸出流裝飾輸出流
BufferedWriter bos = new BufferedWriter(osw)) { ②
// 首先讀取一行文本
String line = bis.readLine();
while (line != null) {
// 開始寫入數據
bos.write(line);
// 寫一個換行符
bos.newLine();
// 再讀取一行文本
line = bis.readLine();
}
System.out.println("復制完成");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
~~~
```