Java IO詳解(轉)

weixin_34321977發表於2016-03-10

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、緩衝輸入檔案。

[java] view plain copy
 
  1. import java.io.BufferedReader;  
  2. import java.io.FileReader;  
  3.   
  4. public class InputStreamTest {  
  5.   
  6.     public static String read(String filename) throws Exception {  
  7.         BufferedReader br = new BufferedReader(new FileReader(filename));  
  8.         String s;  
  9.         StringBuffer sb = new StringBuffer();  
  10.         while ((s = br.readLine()) != null) {  
  11.             sb.append(s + "\n");  
  12.         }  
  13.         br.close();  
  14.         return sb.toString();  
  15.     }  
  16.   
  17.     public static void main(String[] args) throws Exception {  
  18.         System.out.println(read("src/InputStreamTest.java"));  
  19.     }  
  20.  }  

這段程式碼是從磁碟讀入InputStreamTest.java檔案,然後轉換成字串。輸出就是將原始檔原樣輸出。

2、從記憶體中讀取。

[java] view plain copy
 
  1. import java.io.StringReader;  
  2.   
  3. public class MemoryInput {  
  4.   
  5.     public static void main(String[] args) throws Exception {  
  6.         StringReader in = new StringReader(  
  7.                 InputStreamTest.read("src/MemoryInput.java"));  
  8.         int c;  
  9.         while ((c = in.read()) != -1)  
  10.             System.out.println((char) c);  
  11.     }  
  12.   
  13. }  

read返回的是int型別的資料,所以在輸出語句中用char做了強型別轉換。該程式將一個一個的輸出字元。

3、基本的檔案輸出。

[java] view plain copy
 
  1. import java.io.BufferedReader;  
  2. import java.io.BufferedWriter;  
  3. import java.io.FileWriter;  
  4. import java.io.PrintWriter;  
  5. import java.io.StringReader;  
  6.   
  7. public class BasicFileOutput {  
  8.   
  9.     static String file = "basie.out";  
  10.   
  11.     public static void main(String[] args) throws Exception {  
  12.         BufferedReader in = new BufferedReader(new StringReader(  
  13.                 InputStreamTest.read("src/BasicFileOutput.java")));  
  14.         PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(  
  15.                 file)));  
  16.         int lineCount = 1;  
  17.         String s;  
  18.         while ((s = in.readLine()) != null) {  
  19.             out.println(lineCount++ + ": " + s);  
  20.         }  
  21.         out.close();  
  22.         System.out.println(InputStreamTest.read(file));  
  23.     }  
  24. }  

輸出:

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等。

[java] view plain copy
 
  1. public class StandardIO {  
  2.   
  3.     public static void main(String[] args) throws IOException {  
  4.         BufferedReader in = new BufferedReader(new InputStreamReader(System.in));  
  5.         String s;  
  6.         while ((s = in.readLine()) != null && s.length() != 0)  
  7.             System.out.println(s);  
  8.     }  
  9. }  

System.in返回的是未經包裝的InputStream物件,所以需要進行裝飾,經InputStreamReader轉換為Reader物件,放入BufferedReader的構造方法中。除此之外,System.out和System.err都是直接的PriintStream物件,可直接使用。我們也可以使用java.util包下的Scanner類來代替上述程式:

[java] view plain copy
 
  1. public class StandardIO {  
  2.   
  3.     public static void main(String[] args) throws IOException {  
  4.         Scanner in = new Scanner(System.in);  
  5.         String s;  
  6.         while((s = in.next()) != null && s.length() != 0){  
  7.             System.out.println(s);  
  8.         }  
  9.     }  
  10. }  

九、效能分析及總結

1、一個檔案讀寫工具類。

[java] view plain copy
 
  1. import java.io.BufferedReader;  
  2. import java.io.File;  
  3. import java.io.FileReader;  
  4. import java.io.IOException;  
  5. import java.io.PrintWriter;  
  6. import java.util.ArrayList;  
  7. import java.util.Arrays;  
  8.   
  9. /** 
  10.  * 一個非常實用的檔案操作類 . 2012-12-19 
  11.  *  
  12.  * @author Bruce Eckel , edited by erqing 
  13.  *  
  14.  */  
  15. public class TextFile extends ArrayList<String> {  
  16.   
  17.     private static final long serialVersionUID = -1942855619975438512L;  
  18.   
  19.     // Read a file as a String  
  20.     public static String read(String filename) {  
  21.         StringBuilder sb = new StringBuilder();  
  22.         try {  
  23.             BufferedReader in = new BufferedReader(new FileReader(new File(  
  24.                     filename).getAbsoluteFile()));  
  25.             String s;  
  26.             try {  
  27.                 while ((s = in.readLine()) != null) {  
  28.                     sb.append(s);  
  29.                     sb.append("\n");  
  30.                 }  
  31.             } finally {  
  32.                 in.close();  
  33.             }  
  34.   
  35.         } catch (IOException e) {  
  36.             throw new RuntimeException(e);  
  37.         }  
  38.         return sb.toString();  
  39.     }  
  40.   
  41.     // Write a single file in one method call  
  42.     public static void write(String fileName, String text) {  
  43.         try {  
  44.             PrintWriter out = new PrintWriter(  
  45.                     new File(fileName).getAbsoluteFile());  
  46.             try {  
  47.                 out.print(text);  
  48.             } finally {  
  49.                 out.close();  
  50.             }  
  51.         } catch (IOException e) {  
  52.             throw new RuntimeException(e);  
  53.         }  
  54.     }  
  55.   
  56.     // Read a file,spilt by any regular expression  
  57.     public TextFile(String fileName, String splitter) {  
  58.         super(Arrays.asList(read(fileName).split(splitter)));  
  59.         if (get(0).equals(""))  
  60.             remove(0);  
  61.     }  
  62.   
  63.     // Normally read by lines  
  64.     public TextFile(String fileName) {  
  65.         this(fileName, "\n");  
  66.     }  
  67.   
  68.     public void write(String fileName) {  
  69.         try {  
  70.             PrintWriter out = new PrintWriter(  
  71.                     new File(fileName).getAbsoluteFile());  
  72.             try {  
  73.                 for (String item : this)  
  74.                     out.println(item);  
  75.             } finally {  
  76.                 out.close();  
  77.             }  
  78.   
  79.         } catch (IOException e) {  
  80.             throw new RuntimeException(e);  
  81.         }  
  82.   
  83.     }  
  84.   
  85.     // test,I have generated a file named data.d at the root  
  86.     public static void main(String[] args) {  
  87.   
  88.         /* read() test */  
  89.         System.out.println(read("data.d")); // testing is OK!  
  90.   
  91.         /* write() test */  
  92.         write("out.d", "helloworld\negg"); // testing is OK!  
  93.   
  94.         /* constractor test */  
  95.         TextFile tf = new TextFile("data.d"); // testing is OK!  
  96.   
  97.     }  
  98.   
  99. }  

2、讀取二進位制檔案。

[java] view plain copy
 
  1. import java.io.BufferedInputStream;  
  2. import java.io.File;  
  3. import java.io.FileInputStream;  
  4. import java.io.IOException;  
  5.   
  6. /** 
  7.  * to read the binary file 
  8.  *  
  9.  * @author erqing 
  10.  *  
  11.  */  
  12. public class BinaryFile {  
  13.   
  14.     /* the parametre is a file */  
  15.     public static byte[] read(File file) throws IOException {  
  16.         BufferedInputStream bf = new BufferedInputStream(new FileInputStream(  
  17.                 file));  
  18.         try {  
  19.             byte[] data = new byte[bf.available()];  
  20.             bf.read(data);  
  21.             return data;  
  22.         } finally {  
  23.             bf.close();  
  24.         }  
  25.     }  
  26.   
  27.     /* the param is the path of a file */  
  28.     public static byte[] read(String file) throws IOException {  
  29.         return read(new File(file).getAbsoluteFile());  
  30.     }  
  31. }  

相關文章