計算機I/O
理解IO先要知道計算機對數據的輸入輸出是怎么處理的,下面一張圖可以大致理解:
可以看出所謂輸入是外部數據向CPU輸入,而輸出是CPU將數據輸出到我們可見的地方,例如文件、屏幕等。而計算機通常是通過流來傳遞數據。
Java I/O
Java中的IO包中的類可以處理不同類型的流,例如:字節流(byte[])、字符流(character)、文件流、對象流等。
java.io中的抽象類:
- 處理字節流的抽象類:InputStream和OutputStream
- 處理過濾流的抽象類:FilterOutputStream和FilterInputStream
- 處理字符流的抽象類:Reader和Writer
- 處理壓縮流的抽象類:InflaterInputStream和DeflaterOutputStream
它們之間的關系如下:
字符流
輸入流Reader
基本的字符輸入流的類結構如下,每個類的作用從類名大致可以猜出:
這里想強調的是出現的設計模式————裝飾器模式:
例如其中BufferedReader是對Reader接口的其他子類的一個裝飾器,封裝了其他Reader接口實現類,提供更方便的方法調用。
BufferedReader的構造器如下,初始化時需要傳入一個對Reader接口的實現類的對象:
public BufferedReader(Reader in){}
實例代碼,讀取指定路徑的文件內容並輸出:
public class ReaderTest {
public static String read(String path) throws IOException {
//裝飾器模式,BufferedReader包裝了FileReader
BufferedReader reader = new BufferedReader(new FileReader(path));
String s;
StringBuilder sb = new StringBuilder();
while ((s = reader.readLine()) != null) {
sb.append(s + "\n");
}
reader.close();
return sb.toString();
}
public static void main(String[] args) throws IOException {
System.out.println(read("D:/test.txt"));
}
}
可以看出BufferedReader封裝了FileReader來提供更方便的文件輸入功能。
輸出流Writer
類比輸出流,輸出流很容易理解,類關系如下:
同樣也用到了裝飾器模式,下面直接用代碼來說明問題,這段代碼目的是將in.txt的內容寫到out.txt中:
public class WriterTest {
public static void main(String[] args) throws IOException{
String inFile = "D:/in.txt";
String outFile = "D:/out.txt";
BufferedReader in = new BufferedReader(new StringReader(ReaderTest.read(inFile)));
PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(outFile)));
String s;
int line = 1;
while ((s = in.readLine()) != null) {
out.println(line++ + ":" + s);
}
in.close();
out.close();
System.out.println(ReaderTest.read(outFile));
}
}
我們可以看出BufferedReader封裝了StringReader,BufferedWriter封裝了FileWriter。
字符流就說這么多吧,具體的類用的時候再去了解也不遲。
字節流
輸入流InputStream
InputStream是以字節流來讀取文件的,如果文件是圖片或二進制等格式,則不能以字符流來讀,需要InputStream來讀取。
InputStream的子類關系如下圖:
值得注意的是,字節流也可以轉換成字節流,利用的是InputStreamReader類,該類封裝了InputStream。下面的實例代碼通過兩種不同的方式來讀取文件內容。其中readByStream是通過字節流,而readByReader是字符流。
public class InputStreamTest {
public static void readByStream(String path) {
File f = new File(path);
try {
InputStream in = new FileInputStream(f);
int b;
while ((b=in.read()) != -1) {
char c = (char)b;
System.out.printf("%c", c);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void readByReader(String path) {
File f = new File(path);
try {
InputStream in = new FileInputStream(f);
BufferedReader reader = new BufferedReader(new InputStreamReader(in, "utf-8"));
String string;
StringBuilder sb = new StringBuilder();
while ((string = reader.readLine()) != null) {
sb.append(string);
}
System.out.println(sb.toString());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
String path = "D:/test.txt";
readByReader(path);
readByStream(path);
}
}
另外,值得注意的是,如果要讀取中文,建議使用字符流,因為一個漢字占兩個字節,而InputStream每次只會讀取一個字節,因此會亂碼。而InputStreamReader有一個構造函數可以設置文件編碼,如上述代碼中的new InputStreamReader(in, "utf-8")指定了文件編碼問utf-8,因此可以很好的解決中文亂碼問題。
輸出流OutputStream
輸出流OutputStream的作用類比於Writer,也非常容易理解,即通過字節流來把數據輸出到文件、磁盤等設備中。關系如下圖。
同InputStream一樣,也有一個OutputStreamWriter包裝了OutputStream,代碼很簡單我就不舉例了。
RandomAccessFile 隨機存取
上面這兩大類IO,雖然結構清晰,但是多少還是復雜了點,於是乎,Java封裝了一個文件隨機存取類————RandomAccessFile。
RandomAccessFile實現了兩個接口,DataInput和DataOutput,因此它同時擁有讀和寫兩個功能,方便了我們的使用。使用時需要指定打開文件的模式,可以是“r”只讀,“rw”讀寫等。下面是實例代碼:
public class RandomAccessTest {
public static void main(String[] args) {
String filePath = "D:" + File.separator + "test.txt";
File f = new File(filePath);
try {
RandomAccessFile write = new RandomAccessFile(f, "rw");
String str = "abc, hello, randomAccess!";
//把byte[]中的字節寫入文件
write.write(str.getBytes());
System.out.println("寫入文件完成...");
RandomAccessFile read = new RandomAccessFile(f, "rw");
String strRead;
StringBuilder sb = new StringBuilder();
while ((strRead = read.readLine()) != null) {
sb.append(strRead);
}
System.out.println("讀取文件內容:" + sb.toString());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}