一、java.io包概覽

Java IO包主要可以分為如下4類:

基於字節操作的I/O接口:InputStream和OutputStream。

基於字符操作的I/O接口:Writer和Reader

基於磁盤操作的I/O接口:File。

基於網絡操作的I/O接口:Socket(沒在IO包下)。

前2種區分I/O操作中數據的格式,後2種主要是數據傳輸的方式。

二、基於字節的I/O操作

1、 InputStream介紹

InputStream是所有基於字節格式處理讀數據的父類,其類層次結構如及大致介紹下:

java處理utf8_java處理utf8

 

1.1 ByteArrayInputStream

包含一個內部緩衝區,該緩衝區包含從流中讀取的字節。內部計數器跟蹤 read 方法要提供的下一個字節。 關閉 ByteArrayInputStream 無效。此類中的方法在關閉此流後仍可被調用,而不會產生任何 IOException。實例代碼如下:

java處理utf8_Code_02

java處理utf8_java處理utf8_03

1 public static void main(String[] args) throws IOException {
 2 
 3 // TODO Auto-generated method stub
 4 
 5         byte [] buf = {0x3A,0x22,0x33};
 6 
 7        InputStream is = new ByteArrayInputStream(buf);
 8 
 9        int c = is.read();
10 
11        while(c != -1){
12 
13        System.out.println(c);
14 
15        c = is.read();
16 
17        }
18 
19        is.close();
20 
21        is.reset();
22 
23        System.out.println(is.read());
24 
25        
26 
27 }

View Code

 1.2 FileInputStram

利用此類可以以字節方式讀取文件內容,一般用於讀取二進制文件,若讀取文本文件,考慮使用FileReader,示例代碼如下:

java處理utf8_Code_02

java處理utf8_java處理utf8_03

File file = new File("test.txt");
        InputStream inputStream = new FileInputStream(file);
        byte [] content = new byte[10];
        StringBuffer sb = new StringBuffer();
        while(inputStream.read(content) != -1){
            sb.append(new String(content));
            content = new byte[10];
        }     
        inputStream.close();
        System.out.println(sb.toString());

View Code

 

 1.3 FilterInputStream

封裝其它的輸入流,併為它們提供額外的功能,它的常用的子類有BufferedInputStream和DataInputStream。BufferedInputStream的作用就是為“輸入流提供緩衝功能,以及mark()和reset()功能”。
DataInputStream 是用來裝飾其它輸入流,它“允許應用程序以與機器無關方式從底層輸入流中讀取基本 Java 數據類型”。應用程序可以使用DataOutputStream(數據輸出流)寫入由DataInputStream(數據輸入流)讀取的數據。示例代碼如下:

java處理utf8_Code_02

java處理utf8_java處理utf8_03

BufferedInputStream in = new BufferedInputStream(new FileInputStream("test.txt"));
        byte [] data = new byte[10];
        StringBuffer sb  = new StringBuffer();
        while(in.read(data) != -1){
            sb.append(new String(data));
            data = new byte[10];
        }
        in.close();
        System.out.println(sb.toString());

View Code

 

 

2. OutputStream介紹

OutputStream是所有基於字節格式處理寫數據的父類,其類層次結構如及大致介紹下:

java處理utf8_數據_08

 

2.1 ByteArrayOutputStream

此類實現了一個輸出流,其中的數據被寫入一個 byte 數組。緩衝區會隨着數據的不斷寫入而自動增長。可使用 toByteArray()和 toString()獲取數據,關閉後仍可使用。由於這個原因,ByteArrayOutputStream常用於存儲數據以用於一次寫入。

java處理utf8_Code_02

java處理utf8_java處理utf8_03

1     public static void main(String[] args) throws IOException {
 2         // TODO Auto-generated method stub
 3         ByteArrayOutputStream os = new ByteArrayOutputStream();
 4         Random random = new Random();
 5         for(int i=0;i<5;i++){
 6             int a = random.nextInt(999);
 7             os.write(a);
 8             System.out.println(a);
 9         }
10         System.out.println(os.toByteArray().length); //length:5
11         for(int i=0;i<os.toByteArray().length;i++){
12             System.out.println(os.toByteArray()[i]);
13         }
14     }

View Code

2.2 FileOutputStream

A file output stream is an output stream for writing data to a File or to a FileDescriptor. Whether or not a file is available or may be created depends upon the underlying platform. Some platforms, in particular, allow a file to be opened for writing by only one FileOutputStream (or other file-writing object) at a time. In such situations the constructors in this class will fail if the file involved is already open. 示例代碼如下:

java處理utf8_Code_02

java處理utf8_java處理utf8_03

String content = "\n今天沒吃藥\n明天再吃藥";
        OutputStream os = new FileOutputStream("test_write.txt",true);
        os.write(content.getBytes());
        os.close();
        System.out.println("dddd");

View Code

 

2.3 FilterOutputStream

This class is the superclass of all classes that filter output streams. These streams sit on top of an already existing output stream (the underlying output stream) which it uses as its basic sink of data, but possibly transforming the data along the way or providing additional functionality.
The class FilterOutputStream itself simply overrides all methods of OutputStream with versions that pass all requests to the underlying output stream. Subclasses of FilterOutputStream may further override some of these methods as well as provide additional methods and fields.

與FilterInputStream用法類似,示例代碼如下:

java處理utf8_Code_02

java處理utf8_java處理utf8_03

String data = "新年好\n紅包呢";
        File file = new File("filter_output_stream.txt");
        BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(file, true));
        os.write(data.getBytes("UTF-8"));
        os.close();
        System.out.println("write over!!!");

View Code

三、基於字符的I/O操作

不管是磁盤還是網絡操作,最小的存儲單元都是字節, 而不是字符,所以I/O操作都是字節,而不是字符。但由於程序中通常操作的都是字符,為了方便使用,java提供了字符接口。

但字節到字符必須經過轉碼,而編碼非常耗時,而且還會經常出現亂碼。

1. Reader介紹

Reader類是所有字符操作的父類,Abstract class for reading character streams. The only methods that a subclass must implement are read(char[], int, int) and close(). Most subclasses, however, will override some of the methods defined here in order to provide higher efficiency, additional functionality, or both.

java處理utf8_java處理utf8_15

StringBuffer sb = new StringBuffer();
        char [] buf = new char[10];
        Reader reader = new BufferedReader(new InputStreamReader(new FileInputStream("test.txt"),"UTF-8"));//一般會包一層BufferedReader以提升性能
        while(reader.read(buf) != -1){
            sb.append(buf);
            
        }
        reader.close();
        System.out.println(sb.toString());

 

2. Writer介紹

Abstract class for writing to character streams. The only methods that a subclass must implement are write(char[], int, int), flush(), and close(). Most subclasses, however, will override some of the methods defined here in order to provide higher efficiency, additional functionality, or both.

java處理utf8_java處理utf8_16

java處理utf8_Code_02

java處理utf8_java處理utf8_03

String data = "你好周杰倫\n快來快來\n頂頂頂頂";
        Writer writer = 
           new BufferedWriter(new OutputStreamWriter(
               new FileOutputStream("output_stream_writer.txt",true),"UTF-8"));
        writer.write(data);
        System.out.println("write over");
        writer.close();

View Code

 

3. 字節與字符的轉化接口

數據持久化或網絡傳輸都是以字節進行的,所以必須有從字符到字節或字節到字符的轉化接口,從字符到字節需要轉化,其過程為:

java處理utf8_System_19

InputStreamReader類是從字節到字符的轉化的橋樑,從InputStream到Reader要制定編碼字符集,否則會用系統某人字符集,很可能出現亂碼問題。StreamDecoder是完成從字節到字符解碼功能實現類。

java處理utf8_數據_20

FileReader類就是按上述方式讀取文件內容的,FileReader繼承自InputStreamReader類,實際上是讀取文件,然後通過StreamDecoder解碼成char,此處用的默認字符編碼集。

寫入過程類似,

java處理utf8_System_21

通過OutputStreamWriter類  完成從字符到字節的編碼過程,有StreamEncoder完成編碼過程。

四、磁盤I/O機制分析

讀取和寫入文件需調用操作系統接口,分別對應read和write2個系統調用,而只要是系統調用,就可能存在內核地址空間和用户地址空間轉換的問題,因為操作系統需保護自身運行的安全性。

但內核調用時存在數據從內核複製到用户空間的問題,若遇到非常耗時的I/O操作,性能會很差。

因此,操作系統在內核使用緩存機制,以減少I/O相應時間。下面是常見的文件訪問方式。

1. 標準文件訪問方式

read時,OS檢查內核緩存是否有,有則返回緩存,無則從磁盤讀取,並緩存。

write時,OS將用户空間數據複製到內核地址緩存,這是對用户程序來説,write已經完成,但寫入磁盤的時機由OS決定,除非顯示調用sync命令。

2. 直接I/O方式

直接I/O方式指應用程序直接訪問磁盤數據,而不經過OS內核緩衝區,目的是為了減少從內核緩衝區到用户空間數據複製的時間,此種方式常用於數據庫。

因為數據庫明確知道應該緩存哪些數據,且可對熱點數據提前預加載到內存,負面影響是若數據不在應用緩存中,則會直接去磁盤操作,此時耗時較多。

因此,直接I/O通常會跟異步I/O結合使用。

3. 同步訪問文件的方式

數據寫入和讀取都是同步的,只有數據成功寫到磁盤時才返回寫成功,性能較差,用於對數據安全性要求較高的場合,且硬件通常是定製的。

4. 異步訪問方式

發出訪問數據請求後,線程會接着處理其他事情,而不是堵塞等待,請求數據返回後才處理下面的操作。

此方式明顯提高應用程序的效率,但不會改變訪問文件的效率。

5. 內存映射方式

將內存和硬盤映射,當訪問內存時,轉化到訪問磁盤,目的是減少內核到用户空間的數據複製,因為此時這2個空間的數據是共享的。

五、 File和FileDescriptor類

1. File類

Java中的File類並不代表一個真實存在的文件對象,當創建一個File對象時,返回代表這個路徑的虛擬對象

該路徑本身可能是個文件或文件夾,不會檢查改路徑是否存在。

2. FileDescriptor類

當真正訪問文件時,會創建一個關聯真實存在的磁盤文件的文件描述符FileDescriptor,通過這個對象可以直接控制磁盤文件。

File對象可以通過getFD方法獲取FileDescriptor對象,FileDescriptor.sync()方法將OS緩存數據強制刷新到磁盤中。

java處理utf8_java處理utf8_22

六、Java序列化技術

一般會用通用技術存儲,如JSON或XML,且儘量存儲通用的數據結構。