JAVA基礎知識之IO——Java IO體系及常用類


Java IO體系

個人覺得可以用“字節流操作類和字符流操作類組成了Java IO體系”來高度概括Java IO體系。

借用幾張網絡圖片來說明(圖片來自 http://blog.csdn.net/zhangerqing/article/details/8466532 )

  •  基於字節的IO操作

 

 

 
  •  基於字符的IO操作

 

 

 

 

 從上圖可以看到,整個Java IO體系都是基於字符流(InputStream/OutputStream) 和 字節流(Reader/Writer)作為基類,根據不同的數據載體或功能派生出來的。

IO常用類

  • 文件流:FileInputStream/FileOutputStream, FileReader/FileWriter

這四個類是專門操作文件流的,用法高度相似,區別在於前面兩個是操作字節流,后面兩個是操作字符流。它們都會直接操作文件流,直接與OS底層交互。因此他們也被稱為節點流

注意使用這幾個流的對象之后,需要關閉流對象,因為java垃圾回收器不會主動回收。不過在Java7之后,可以在 try() 括號中打開流,最后程序會自動關閉流對象,不再需要顯示地close。

下面演示這四個流對象的基本用法,

 1 package io;
 2 
 3 import java.io.FileInputStream;
 4 import java.io.FileNotFoundException;
 5 import java.io.FileOutputStream;
 6 import java.io.FileReader;
 7 import java.io.FileWriter;
 8 import java.io.IOException;
 9 
10 public class TestIO {
11     public static void FileInputStreamTest() throws IOException {
12         FileInputStream fis = new FileInputStream("tmp2.txt");
13         byte[] buf = new byte[1024];
14         int hasRead = 0;
15         
16         //read()返回的是單個字節數據(字節數據可以直接專程int類型),但是read(buf)返回的是讀取到的字節數,真正的數據保存在buf中
17         while ((hasRead = fis.read(buf)) > 0) {
18             //每次最多將1024個字節轉換成字符串,這里tmp2.txt中的字符小於1024,所以一次就讀完了
19             //循環次數 = 文件字符數 除以 buf長度
20             System.out.println(new String(buf, 0 ,hasRead));
21             /*
22              * 將字節強制轉換成字符后逐個輸出,能實現和上面一樣的效果。但是如果源文件是中文的話可能會亂碼
23              
24             for (byte b : buf)    {
25                 char ch = (char)b;
26                 if (ch != '\r')
27                 System.out.print(ch);
28             }
29             */
30         }
31         //在finally塊里close更安全
32         fis.close();
33     }
34     
35     public static void FileReaderTest() throws IOException {
36         
37         try (
38                 // 在try() 中打開的文件, JVM會自動關閉
39                 FileReader fr = new FileReader("tmp2.txt")) {
40             char[] buf = new char[32];
41             int hasRead = 0;
42             // 每個char都占兩個字節,每個字符或者漢字都是占2個字節,因此無論buf長度為多少,總是能讀取中文字符長度的整數倍,不會亂碼
43             while ((hasRead = fr.read(buf)) > 0) {
44                 // 如果buf的長度大於文件每行的長度,就可以完整輸出每行,否則會斷行。
45                 // 循環次數 = 文件字符數 除以 buf長度
46                 System.out.println(new String(buf, 0, hasRead));
47                 // 跟上面效果一樣
48                 // System.out.println(buf);
49             }
50         } catch (IOException ex) {
51             ex.printStackTrace();
52         }
53     }
54     
55     public static void FileOutputStreamTest() throws FileNotFoundException, IOException {
56         try (    
57                 //在try()中打開文件會在結尾自動關閉
58                 FileInputStream fis = new FileInputStream("tmp2.txt");
59                 FileOutputStream fos = new FileOutputStream("tmp3.txt");
60                 ) {
61             byte[] buf = new byte[4];
62             int hasRead = 0;
63             while ((hasRead = fis.read(buf)) > 0) {
64                 //每讀取一次就寫一次,讀多少就寫多少
65                 fos.write(buf, 0, hasRead);
66             }
67             System.out.println("write success");
68         } catch (IOException e) {
69             e.printStackTrace();
70         }
71     }
72     
73     public static void FileWriterTest() throws IOException {
74         try (FileWriter fw = new FileWriter("tmp4.txt")) {
75             fw.write("天王蓋地虎\r\n");
76             fw.write("寶塔鎮河妖\r\n");
77         } catch (IOException e) {
78             e.printStackTrace();
79         }
80     }
81     public static void main(String[] args) throws IOException {
82         //FileInputStreamTest();
83         //FileReaderTest();
84         //FileOutputStreamTest();
85         FileWriterTest();
86     }
87 }

 

  • 包裝流:PrintStream/PrintWriter/Scanner

PrintStream可以封裝(包裝)直接與文件交互的節點流對象OutputStream, 使得編程人員可以忽略設備底層的差異,進行一致的IO操作。因此這種流也稱為處理流或者包裝流。

PrintWriter除了可以包裝字節流OutputStream之外,還能包裝字符流Writer

Scanner可以包裝鍵盤輸入,方便地將鍵盤輸入的內容轉換成我們想要的數據類型。

  • 字符串流:StringReader/StringWriter

這兩個操作的是專門操作String字符串的流,其中StringReader能從String中方便地讀取數據並保存到char數組,而StringWriter則將字符串類型的數據寫入到StringBuffer中(因為String不可寫)。

  • 轉換流:InputStreamReader/OutputStreamReader

這兩個類可以將字節流轉換成字符流,被稱為字節流與字符流之間的橋梁。我們經常在讀取鍵盤輸入(System.in)或網絡通信的時候,需要使用這兩個類

  • 緩沖流:BufferedReader/BufferedWriter , BufferedInputStream/BufferedOutputStream

Oracle官方的描述:

Most of the examples we've seen so far use unbuffered I/O. This means each read or write request is handled directly by the underlying OS. This can make a program much less efficient.

Buffered input streams read data from a memory area known as a buffer; the native input API is called only when the buffer is empty. Similarly, buffered output streams write data to a buffer, and the native output API is called only when the buffer is full.

即,

沒有經過Buffered處理的IO, 意味着每一次讀和寫的請求都會由OS底層直接處理,這會導致非常低效的問題。

經過Buffered處理過的輸入流將會從一個buffer內存區域讀取數據,本地API只會在buffer空了之后才會被調用(可能一次調用會填充很多數據進buffer)。

經過Buffered處理過的輸出流將會把數據寫入到buffer中,本地API只會在buffer滿了之后才會被調用。

 

BufferedReader/BufferedWriter可以將字符流(Reader)包裝成緩沖流,這是最常見用的做法。

另外,BufferedReader提供一個readLine()可以方便地讀取一行,而FileInputStream和FileReader只能讀取一個字節或者一個字符,

因此BufferedReader也被稱為行讀取器

 

下面演示上面提到的常見類,

  1 package io;
  2 
  3 import java.io.BufferedReader;
  4 import java.io.FileInputStream;
  5 import java.io.FileNotFoundException;
  6 import java.io.FileOutputStream;
  7 import java.io.FileReader;
  8 import java.io.IOException;
  9 import java.io.InputStreamReader;
 10 import java.io.PrintStream;
 11 import java.io.PushbackReader;
 12 import java.io.StringReader;
 13 import java.io.StringWriter;
 14 
 15 public class TestIO {
 16     public static void printStream() throws FileNotFoundException, IOException {
 17         try (
 18                 FileOutputStream fos = new FileOutputStream("tmp.txt");
 19                 PrintStream ps = new PrintStream(fos)) {
 20             ps.println("普通字符串\n");
 21             //輸出對象
 22             ps.println(new TestIO());
 23         } catch (IOException e) {
 24             e.printStackTrace();
 25         }
 26         System.out.println("輸出完成");
 27                 
 28     }
 29     public static void stringNode() throws IOException {
 30         String str = "天王蓋地虎\n"
 31                 + "寶塔鎮河妖\n";
 32         char[] buf = new char[32];
 33         int hasRead = 0;
 34         //StringReader將以String字符串為節點讀取數據
 35         try (StringReader sr = new StringReader(str)) {
 36             while ((hasRead = sr.read(buf)) > 0) {
 37                 System.out.print(new String(buf, 0, hasRead));
 38             }
 39         } catch (IOException e) {
 40             e.printStackTrace();
 41         }
 42         
 43         //由於String是一個不可變類,因此創建StringWriter時,實際上是以一個StringBuffer作為輸出節點
 44         try (StringWriter sw = new StringWriter()) {
 45             sw.write("黑夜給了我黑色的眼睛\n");
 46             sw.write("我卻用它尋找光明\n");
 47             //toString()返回sw節點內的數據
 48             System.out.println(sw.toString());
 49         } catch (IOException e) {
 50             e.printStackTrace();
 51         }
 52     }
 53     
 54     public static void keyIn() throws IOException {
 55         try (
 56                 //InputStreamReader是從byte轉成char的橋梁
 57                 InputStreamReader reader = new InputStreamReader(System.in);
 58                 //BufferedReader(Reader in)是char類型輸入的包裝類
 59                 BufferedReader br = new BufferedReader(reader);
 60                 ) {
 61             String line = null;
 62             while ((line = br.readLine()) != null) {
 63                 if (line.equals("exit")) {
 64                     //System.exit(1);
 65                     break;
 66                 }
 67                 System.out.println(line);
 68             }
 69         } catch (IOException e) {
 70             e.printStackTrace();
 71         }
 72     }
 73     
 74     public static void pushback() throws FileNotFoundException, IOException {
 75         try (PushbackReader pr = new PushbackReader(new FileReader("C:/PROJECT/JavaBasic/PROJECT_JavaBasic/src/io/TestIO.java"),64)) {
 76             char[] buf = new char[32];
 77             String lastContent = "";
 78             int hasRead = 0;
 79             while ((hasRead = pr.read(buf)) > 0) {
 80                 String content = new String(buf, 0, hasRead);
 81                 int targetIndex = 0;
 82                 if ((targetIndex = (lastContent + content).indexOf("targetIndex = (lastContent + content)")) > 0) {
 83                     pr.unread((lastContent + content).toCharArray());
 84                     if (targetIndex > 32) {
 85                         buf = new char[targetIndex];
 86                     }
 87                     pr.read(buf , 0 , targetIndex);
 88                     System.out.println(new String(buf, 0 , targetIndex));
 89                     System.exit(0);
 90                 } else {
 91                     System.out.println(lastContent);
 92                     lastContent = content;
 93                 }
 94             }
 95         } catch (IOException e) {
 96             e.printStackTrace();
 97         }
 98     }
 99     
100     public static void main(String[] args) throws IOException {
101         printStream();
102         //stringNode();
103         //keyIn();
104         //pushback();
105     }
106 }

 

 

總結上面幾種流的應用場景:

  • FileInputStream/FileOutputStream  需要逐個字節處理原始二進制流的時候使用,效率低下
  • FileReader/FileWriter 需要組個字符處理的時候使用
  • StringReader/StringWriter 需要處理字符串的時候,可以將字符串保存為字符數組
  • PrintStream/PrintWriter 用來包裝FileOutputStream 對象,方便直接將String字符串寫入文件 
  • Scanner 用來包裝System.in流,很方便地將輸入的String字符串轉換成需要的數據類型
  • InputStreamReader/OutputStreamReader ,  字節和字符的轉換橋梁,在網絡通信或者處理鍵盤輸入的時候用
  • BufferedReader/BufferedWriter , BufferedInputStream/BufferedOutputStream , 緩沖流用來包裝字節流后者字符流,提升IO性能,BufferedReader還可以方便地讀取一行,簡化編程。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM