IO是Java及眾多程式語言很重要的一塊,同時很多程式的瓶頸和耗時操作也都在IO這塊。
一、簡介
IO操作面臨很多問題,資訊量的巨大,網路的環境等等,因為IO不僅僅是對本地檔案、目錄的操作,有時對二進位制流、還有一部分是網路方面的資源,所以多種原因直接造成IO操作無疑是耗時且複雜多變的。Java對IO的支援是個不斷的演變過程,經過了很多的優化,直到JDK1.4以後,才趨於穩定,在JDK1.4中,加入了nio類,解決了很多效能問題,雖然我們有足夠的理由不去了解關於Java IO以前的情況,但是為了學好現在的類,我們還是打算去研究下,通過掌握類的優化情況來徹底理解IO的機制!Java IO主要主要在java.io包下,分為四大塊近80個類:
1、基於位元組操作的I/O介面:InputStream和OutputStream
2、基於字元操作的I/O介面:Writer和Reader
3、基於磁碟操作的I/O介面:File
4、基於網路操作的I/O介面:Socket(不在java.io包下)
影響IO效能的無非就是兩大因素:資料的格式及儲存的方式,前兩類主要是資料格式方面的,後兩個類是儲存方式方面的:本地和網路。所以策劃好這兩個方面的活動,有助於我們合理使用IO。
二、基於位元組的I/O操作(InputStream和OutputStream)
我們先來看看類圖:
圖1
圖2
二者類似,我只詳細講解InputStream類,OutputStream留給大家自己去學習。InputStream類是個抽象類,裡面核心的方法就是read()、read(byte b[])、read(byte b[], int off, int len),這三個方法是用於讀取資料的底層的方法,他們可以用來讀取一下這些型別的資料:
A. 位元組陣列
B. String物件
C. 檔案
D. 管道,從一端進入,從另一端輸出
E. 流
F. internet資源
每一種資料來源都有相應的InputStream子類,因為InputStream是個處於頂層的類,用來處理各種資料來源的類都繼承了InputStream類,我們來看看這些類:
ByteArrayInputStream:處理位元組陣列的類,允許將記憶體的緩衝區當做InputStream使用。
StringBufferInputStream:將String轉換成InputStream,內部實現用的是StringBuffer。
FileInputStream:從檔案中讀取資料。
PipedInputStream:用於從管道中讀取資料。
SequenceInputStream:將多個流物件轉化成一個InputStream。
FilterInputStream:裝飾器類,為其它InputStream類提供功能。
做過關於IO操作的讀者知道,我們很少單獨使用哪個類來實現IO操作,平時都是幾個類合起來使用,這其實體現了一種裝飾器模式(詳見:http://blog.csdn.net/zhangerqing)的思想,在後面的分析中我們會詳細的分析。從上面的圖1中我們可以看出,FilterInputStream雖說是Inputstream的子類,但它依然是BufferedInputStream、DataInputStream、LineNumberInputStream、PushbackInputStream類的父類,這四個類分別提供了最貼近我們程式設計師使用的方法,如:readInt() 、readInt()、readInt()等等。對於IO操作,不管是磁碟還是網路,最終都是對位元組的操作,而我們平時寫的程式都是字元形式的,所以在傳輸的過程中需要進行轉換。在字元到位元組的轉換過程中,我們需要用到一個類:InputStreamReader。
三、基於字元的I/O操作(Writer和Reader)
圖3
圖4
Writer和Reader操作的目的就是操作字元和不是位元組,和InputStream和OutputStream配合增加IO效果。通過InputStreamReader和OutputStreamReader可以進行位元組和字元的轉換,設計Writer和Reader的目的是國際化,使IO操作支援16位的Unicode。我把它們單獨的畫出來,因為要是全畫的話,太大了放不下,有興趣的TX可以在rational rose中匯入其帶的JDK類圖看看,很過癮的!
四、基於磁碟的I/O操作(File)
五、基於網路的I/O操作(Socket)
六、NIO
四-六部分由於時間關係,還沒有整理完,後續會補出來!
七、經典IO操作
1、緩衝輸入檔案。
- import java.io.BufferedReader;
- import java.io.FileReader;
- public class InputStreamTest {
- public static String read(String filename) throws Exception {
- BufferedReader br = new BufferedReader(new FileReader(filename));
- String s;
- StringBuffer sb = new StringBuffer();
- while ((s = br.readLine()) != null) {
- sb.append(s + "\n");
- }
- br.close();
- return sb.toString();
- }
- public static void main(String[] args) throws Exception {
- System.out.println(read("src/InputStreamTest.java"));
- }
- }
這段程式碼是從磁碟讀入InputStreamTest.java檔案,然後轉換成字串。輸出就是將原始檔原樣輸出。
2、從記憶體中讀取。
- import java.io.StringReader;
- public class MemoryInput {
- public static void main(String[] args) throws Exception {
- StringReader in = new StringReader(
- InputStreamTest.read("src/MemoryInput.java"));
- int c;
- while ((c = in.read()) != -1)
- System.out.println((char) c);
- }
- }
read返回的是int型別的資料,所以在輸出語句中用char做了強型別轉換。該程式將一個一個的輸出字元。
3、基本的檔案輸出。
- import java.io.BufferedReader;
- import java.io.BufferedWriter;
- import java.io.FileWriter;
- import java.io.PrintWriter;
- import java.io.StringReader;
- public class BasicFileOutput {
- static String file = "basie.out";
- public static void main(String[] args) throws Exception {
- BufferedReader in = new BufferedReader(new StringReader(
- InputStreamTest.read("src/BasicFileOutput.java")));
- PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(
- file)));
- int lineCount = 1;
- String s;
- while ((s = in.readLine()) != null) {
- out.println(lineCount++ + ": " + s);
- }
- out.close();
- System.out.println(InputStreamTest.read(file));
- }
- }
輸出:
1: import java.io.BufferedReader;
2: import java.io.BufferedWriter;
3: import java.io.FileWriter;
…
4、RandomAccessFile
RandomAccessFile被我們稱為”自我獨立的類”,因為它獨立於我們前面說的IO類,與InputStream和OutputStream沒什麼關係,除了實現了DataOutput, DataInput兩個介面外。所有方法都是重新編寫,而且很多都是native方法,我們來看個例子,瞭解下這個類:
5、管道流
八、標準I/O
就是我們最原始的使用的從控制檯輸入或者輸出的那些類和方法,如System.in、System.out等。
- public class StandardIO {
- public static void main(String[] args) throws IOException {
- BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
- String s;
- while ((s = in.readLine()) != null && s.length() != 0)
- System.out.println(s);
- }
- }
System.in返回的是未經包裝的InputStream物件,所以需要進行裝飾,經InputStreamReader轉換為Reader物件,放入BufferedReader的構造方法中。除此之外,System.out和System.err都是直接的PriintStream物件,可直接使用。我們也可以使用java.util包下的Scanner類來代替上述程式:
- public class StandardIO {
- public static void main(String[] args) throws IOException {
- Scanner in = new Scanner(System.in);
- String s;
- while((s = in.next()) != null && s.length() != 0){
- System.out.println(s);
- }
- }
- }
九、效能分析及總結
1、一個檔案讀寫工具類。
- import java.io.BufferedReader;
- import java.io.File;
- import java.io.FileReader;
- import java.io.IOException;
- import java.io.PrintWriter;
- import java.util.ArrayList;
- import java.util.Arrays;
- /**
- * 一個非常實用的檔案操作類 . 2012-12-19
- *
- * @author Bruce Eckel , edited by erqing
- *
- */
- public class TextFile extends ArrayList<String> {
- private static final long serialVersionUID = -1942855619975438512L;
- // Read a file as a String
- public static String read(String filename) {
- StringBuilder sb = new StringBuilder();
- try {
- BufferedReader in = new BufferedReader(new FileReader(new File(
- filename).getAbsoluteFile()));
- String s;
- try {
- while ((s = in.readLine()) != null) {
- sb.append(s);
- sb.append("\n");
- }
- } finally {
- in.close();
- }
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- return sb.toString();
- }
- // Write a single file in one method call
- public static void write(String fileName, String text) {
- try {
- PrintWriter out = new PrintWriter(
- new File(fileName).getAbsoluteFile());
- try {
- out.print(text);
- } finally {
- out.close();
- }
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
- // Read a file,spilt by any regular expression
- public TextFile(String fileName, String splitter) {
- super(Arrays.asList(read(fileName).split(splitter)));
- if (get(0).equals(""))
- remove(0);
- }
- // Normally read by lines
- public TextFile(String fileName) {
- this(fileName, "\n");
- }
- public void write(String fileName) {
- try {
- PrintWriter out = new PrintWriter(
- new File(fileName).getAbsoluteFile());
- try {
- for (String item : this)
- out.println(item);
- } finally {
- out.close();
- }
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
- // test,I have generated a file named data.d at the root
- public static void main(String[] args) {
- /* read() test */
- System.out.println(read("data.d")); // testing is OK!
- /* write() test */
- write("out.d", "helloworld\negg"); // testing is OK!
- /* constractor test */
- TextFile tf = new TextFile("data.d"); // testing is OK!
- }
- }
2、讀取二進位制檔案。
- import java.io.BufferedInputStream;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.IOException;
- /**
- * to read the binary file
- *
- * @author erqing
- *
- */
- public class BinaryFile {
- /* the parametre is a file */
- public static byte[] read(File file) throws IOException {
- BufferedInputStream bf = new BufferedInputStream(new FileInputStream(
- file));
- try {
- byte[] data = new byte[bf.available()];
- bf.read(data);
- return data;
- } finally {
- bf.close();
- }
- }
- /* the param is the path of a file */
- public static byte[] read(String file) throws IOException {
- return read(new File(file).getAbsoluteFile());
- }
- }