java中的IO操作流程:
以文件操作爲例,步驟如下:
1)使用 File 類打開一個文件;
2)通過字節流或字符流的子類,指定輸出的位置;
3)進行讀 / 寫操作;
4)關閉輸入 / 輸出。
流:不同輸入/輸出設備(例如文件、網路、壓縮包等)之間的數據傳輸抽象。
根據傳輸的數據類型:
字節流(以 Stream 爲結尾的流)
字符流(以 Reader 和 Writer 結尾的流)
字符是指計算機中使用的文字和符號,比如1、2、3、A、B、C、~!·#¥%……—*()——+、等等。Java中的字符是 Unicode 編碼,英文字符和中文字符都佔雙字節。字節流是用來處理字節的,用來處理字符文本還要進行轉換,因此出現了字符流。
根據流的操作模式:
輸入流:打開一個從某數據源到程序的流,並從這個流中讀取數據;
輸出流:將程序中的數據傳輸到某個目的地,在傳輸過程中,將數據寫入這個流中。
目錄
? 字節輸入流 InputStream
public abstract class InputStream extends Object implements Closeable
// 此類是一個抽象類,如果想要使用此類的話,則首先必須通過子類實例化對象
// Closeable 表示可以關閉的操作,因爲程序運行到最後肯定要關閉
InputStream類常用的public方法 |
|
方法定義 |
功能說明 |
abstract int read() throws IOException |
讀一個字節並按int類型返回。 如果因爲已經到達流末尾而沒有可用的字節,則返回值爲 -1。 |
int read(byte[] b) throws IOException |
將數據讀入byte[], 返回實際讀取的字節數。 |
int read(byte[] b, int off, int len) throws IOException |
讀取最多 len 個字節到數組b中,返回實際讀取數量。 |
public long skip(long n) throws IOException |
跳過n個字節,返回實際跳過的字節數。 |
public void close() throws IOException |
關閉輸入流並釋放與該流有關聯的系統資源 |
InputStream 類中所有方法遇到錯誤時都會引發IOException異常,所以使用時一定要包含在 try … catch … 語句中。
以從文件中讀取的操作爲例:
/* 從mytext.txt文件讀出並顯示在屏幕上 */
import java.io.*;
public class FileIn {
public static void main(String args[]) {
try {
// 打開文件
FileInputStream rf = new FileInputStream("H:/java/temp/mytext.txt");
// 用read()方法逐個字節讀取
int b;
while((b = rf.read()) != -1)
{
System.out.print((char)b); //轉換成char並顯示
}
rf.close();
} catch(IOException ie) {
System.out.println(ie);
} catch(Exception e) {
System.out.println(e);
}
}
}
注意,上面代碼中 rf.read() 返回的是 int 字節值,要轉換成 char 類型才能顯示。
? 字節輸出流 OutputStream
OutputStream類常用的public方法 |
|
方法定義 |
功能說明 |
abstract void write(int b) throws IOException |
將一個字節 b 輸出,根據 java 規定,實際輸出的是參數 b 的低 8 位,其餘 24 個高位將被忽略。 例如,若 b = 825373492,即十六進制0x31323334,則只輸出低 8 位即 0x34,即最後輸出爲字符 '4' |
void write(byte[] b) throws IOException |
將數組 b 逐字節輸出 |
void write(byte[] b, int off, int len) throws IOException |
將數組 b 中從 off 開始的 len 個字節輸入 |
InputStream 類中所有方法遇到錯誤時都會引發IOException異常,所以使用時一定要包含在 try … catch … 語句中。
/* 以下示例用於說明如何利用FileOutputStream進行文件複製 */
import java.io.*;
public class TestFileCopy {
public static void main(String args[]) {
try {
// 複製的源文件 TestVector.java
FileInputStream rf = new FileInputStream("G:/java/TestVector.java");
//複製的目的文件 TV2.txt,若不存在,則會自動創建
FileOutputStream wf = new FileOutputStream("G:/java/TV2.txt");
byte b[] = new byte[512];
int count = -1;
//每次讀取512個字節,count 用於記錄實際讀取的字節數
while((count = rf.read(b, 0, 512)) != -1) {
wf.write(b, 0, count);
}
rf.close();
wf.close();
}
catch(IOException ie) {
System.out.println(ie.toString());
}
catch(Exception e) {
System.out.println(e.toString());
}
}
}
? 輸入字符流 Reader
Reader類常用的public方法 |
|
方法定義 |
功能說明 |
int read() throws IOException |
讀單個字符,以 int 類型返回 |
int read(char[] cbuf) throws IOException |
讀字符放入數組 cbuf 中 |
int read(char[] cbuf, int offset, int length) throws IOException |
讀字符放入數組的指定位置 |
boolean ready() throws IOException |
測試當前流是否準備好進行讀 |
void close( ) |
關閉流 |
long skip(long n) |
跳過n個字符 |
Reader 類中的方法與 InputStream 類中的方法類似。但 Reader 類中的 read() 方法的參數爲 char 類型的數組,InputStream 類中的 read() 方法的參數爲 byte 類型的數組。此外,它還提供了 ready()方法。
/*
讀取 G:/aillo.txt 文件的內容(一行一行讀),並將其內容寫入 G:/jacky.txt 中
知識點: java讀文件、寫文件---<以字符流方式>
*/
import java.io.*;
public class TestFileWR {
public static void main(String[] args) {
try {
// 創建 FileReader 對象,用來讀取字符流
FileReader fr = new FileReader("G:/aillo.txt");
// 緩衝指定文件的輸入
BufferedReader br = new BufferedReader(fr);
// 創建FileWriter對象,用來寫入字符流
FileWriter fw = new FileWriter("G:/jacky.txt");
// 緩衝對文件的輸出
BufferedWriter bw = new BufferedWriter(fw);
String strLine;//用來保存每次讀取的一行
while (br.ready()) {
strLine = br.readLine(); // 讀取一行
bw.write(strLine); // 寫入文件
bw.newLine();
System.out.println(strLine);//在屏幕上輸出
}
// 刷新該流的緩衝,即將該流輸出到目的
bw.flush();
bw.close();
br.close();
fw.close();
br.close();
fr.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
在這段代碼中,我們發現其中用到了字符緩衝流?:
字節流在操作的時候本身是不會用到緩衝區(內存)的,是與文件本身直接操作的,而字符流在操作的時候是使用到緩衝區的。
我們可以做一個實驗:字節流在操作文件時,即使不關閉資源(close方法),文件也能輸出,但是如果字符流不使用 close 方法的話,則不會輸出任何內容,說明字符流用的是緩衝區,並且可以使用 flush 方法強制進行刷新緩衝區,這時才能在不 close的情況下輸出內容。
? 輸出字符流 Writer
Writer 類常用的 public 方法 |
|
方法定義 |
功能說明 |
int write(int c) throws IOException |
輸出單個字符。要輸出的字符 c 包含在給定整數值的 16 個低位中,16 高位被忽略。例如,若b = 825360437,即十六進制0x31320035,則只輸出低 16 位即 0x0035 (爲字符 '5' 的ASCII碼),即最後輸出爲字符 '5' |
int write(char[] cbuf) throws IOException |
輸出字符數組 cbuf |
int write(char[] cbuf, int offset, int len) throws IOException |
將字符數組中從 offset 開始的 len 個字符輸出 |
int write(String str) throws IOException |
輸出字符串 str |
int write(String str, int offset, int length) throws IOException |
輸出字符串 str 中從 offset 開始的 len 個字符 |
abstract void close( ) |
關閉輸出字符流 |
abstract void flush( ) |
刷新該流的緩衝 —— 強行寫 |
/* 從鍵盤輸入一行文字(可輸入中文字符),寫入文件 TestFileOut.txt 中 */
import java.io.*;
public class TestFileOut {
public static void main(String args[]) {
char c[] = new char[512];
int n, i;
try {
FileWriter wf = new FileWriter("TestFileOut.txt");
//利用 InputStreamReader 正確讀取中文
InputStreamReader isr = new InputStreamReader(System.in);
n = isr.read(c,0,512); // 一次性讀取512個字符,n表示實際讀取的字符數
wf.write(c);
wf.close();
System.out.println("剛輸入的數據爲:" + System.valueOf(c,0,n));
}
catch(IOException e) {
System.out.println(e);
}
}
}
/* 以下代碼不能用來輸入中文字符 */
import java.io.*;
public class TestFileOut {
public static void main(String args[]) {
char c[] = new char[512];
byte b[] = new byte[512];
int n, i;
try {
FileWriter wf = new FileWriter("TestFileOut.txt");
// 從鍵盤讀入文字並存入字節數組 b 中
n = System.in.read(b);
for (i = 0; i < n; ++i)
{
c[i]=(char)b[i];
}
wf.write(c);
wf.close();
}
catch(IOException e) {
System.out.println(e);
}
}
}
/*
本程序首先在控制檯輸入字符(逐行輸入),程序將輸入的文字存儲至指定的文件中,如果要結束程序,輸入quit字符串即可。
*/
import java.util.*;
import java.io.*;
public class TestFileBRW {
public static void main(String[] args) {
try {
//緩衝 System.in 輸入流
//System.in是字節流,通過InputStreamReader將其轉換爲字符流
BufferedReader bufReader = new BufferedReader(new InputStreamReader(System.in));
//緩衝FileWriter
BufferedWriter bufWriter = new BufferedWriter(new FileWriter(args[0]));
String input = null;
//每讀一行進行一次寫入動作
while (!(input = bufReader.readLine()).equals("quit")) {
bufWriter.write(input);
// newLine()方法寫入與操作系統相依的換行字符,依執行環境當時的OS來決定該輸出那種換行字符
bufWriter.newLine();
}
bufReader.close();
bufWriter.close();
}
catch(ArrayIndexOutOfBoundsException e) {
System.out.println("沒有指定文件");
}
catch(IOException e) {
e.printStackTrace();
}
}
}
? 總結
字符流和字節流本質區別在於byte和char。字節流採用二進制直接傳輸,用字符流則牽涉到本地系統的編碼問題,在網絡通訊中,強烈建議使用byte字節流方式。
字節流與字符之間的轉化通過 InputStreamReader 和 OutputStreamWriter來關聯,實際上是通過 byte[] 和 String 來關聯。在實際開發中出現的漢字問題實際上都是在字符流和字節流之間轉化不統一而造成的。在從字節流轉化爲字符流時,也就是從byte[] 轉化爲 String 時,使用如下構造方法:
public String(byte bytes[], String charsetName)
這個方法中有一個關鍵的字符集編碼參數 charsetName,通常我們都省略了,那系統就用操作系統的默認的 language。而在字符流轉化爲字節流時,實際上是 String 轉化爲 byte[] 時,是使用如下方法進行轉化:
byte[] String.getBytes(String charsetName)
字符流和字節流是根據處理數據的不同來區分的。字節流按照8位傳輸,字符流按照16位傳輸由於字符流使用Unicode字符集,支持多國文字,因此若流要跨越多種平臺傳輸,應使用字符流。字符流的傳輸效率比字節流的高。