天天看點

Java IO工作機制分析1 IO類庫的基本結構2 同步和異步、阻塞和非阻塞3 序列化

  Java的IO類都在java.io包下,這些類大緻可分為以下4種:

  1. 基于位元組操作的 I/O 接口:InputStream 和 OutputStream
  2. 基于字元操作的 I/O 接口:Writer 和 Reader
  3. 基于磁盤操作的 I/O 接口:File
  4. 基于網絡操作的 I/O 接口:Socket

1 IO類庫的基本結構

1.1 基于位元組操作的IO接口

  基于位元組操作的IO接口分别是InputStream和OutputStream,InputStream的類結構圖如下所示:

Java IO工作機制分析1 IO類庫的基本結構2 同步和異步、阻塞和非阻塞3 序列化

  同InputStream類似,OutputStream類也有着相同的類結構圖。

Java IO工作機制分析1 IO類庫的基本結構2 同步和異步、阻塞和非阻塞3 序列化

   關于各個子類的使用可以參考JDK 的 API 說明文檔,這裡我們需要注意的是:操作資料的方式是可以組合的,如下所示:

InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream("h:\\haha.txt"), "utf-8");      

  從上面代碼可以看出,InputStreamReader可以從FileInputStream中讀取資料,從源碼中可以看出來,其實不僅是InputStreamReader,所有的IO類都可以以這種方式來組合使用。

Java IO工作機制分析1 IO類庫的基本結構2 同步和異步、阻塞和非阻塞3 序列化

  還有一點需要注意的是必須制定流最終寫入到什麼地方,是磁盤還是網絡,從OutputStream類圖中可以看出,寫入網絡中實際也是寫檔案操作,隻不過底層是通過網絡傳輸了。

1.2 基于字元的IO操作接口

   不管是磁盤還是網絡資料傳輸,都是以位元組為機關的,但是程式中一般常見的資料操作都是以字元為機關的(Java中char占用2位元組,C/C++中 char占用1位元組),這就需要我們有一個操作字元的IO接口,來處理字元與位元組見的編碼轉換問題,也就是Write和Reader接口及其實作類,他們 二者的類接口圖如下:

Java IO工作機制分析1 IO類庫的基本結構2 同步和異步、阻塞和非阻塞3 序列化
Java IO工作機制分析1 IO類庫的基本結構2 同步和異步、阻塞和非阻塞3 序列化

  讀字元接口Reader的最主要操作方法為read(),其讀取字元并傳回讀取的字元數,不管是 Writer 還是 Reader 類它們都隻定義了讀取或寫入的資料字元的方式,也就是怎麼寫或讀,但是并沒有規定資料要寫到哪去(比如磁盤或者網絡)。

1.3 位元組與字元的轉化接口

  有時資料持久化和網絡傳輸是以位元組進行的,所有需要位元組和字元之間的互相轉換。

Java IO工作機制分析1 IO類庫的基本結構2 同步和異步、阻塞和非阻塞3 序列化
Java IO工作機制分析1 IO類庫的基本結構2 同步和異步、阻塞和非阻塞3 序列化
/**
 * 使用FileReader進行讀取檔案
 */
@Test
public void testFileReader() throws IOException {
    FileReader fileReader = new FileReader("h:\\haha.txt");
    char[] buff = new char[512];
    StringBuffer stringBuffer = new StringBuffer();

    while (fileReader.read(buff) > 0) {
        stringBuffer.append(buff);
    }
    fileReader.close();

    System.out.print(stringBuffer.toString());
}

/**
 * 使用FileReader進行讀取檔案,然後FileWriter寫入另一個檔案
 */
@Test
public void testFileReaderAndFileWriter() throws IOException {
    FileReader fileReader = new FileReader("h:\\haha.txt");
    char[] buff = new char[512];
    StringBuffer stringBuffer = new StringBuffer();

    while (fileReader.read(buff) > 0) {
        stringBuffer.append(buff);
    }
    System.out.println(stringBuffer.toString());

    FileWriter fileWriter = new FileWriter("h:\\haha2.txt");
    fileWriter.write(stringBuffer.toString().trim());

    fileWriter.close();
    System.out.println("寫入檔案成功");
}
/**
 * 使用InputStreamReader進行讀取檔案
 */
@Test
public void testInputStreamReader() throws IOException {
    InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream("h:\\haha.txt"), "utf-8");
    char[] buff = new char[512];
    StringBuffer stringBuffer = new StringBuffer();

    while (inputStreamReader.read(buff) > 0) {
        stringBuffer.append(buff);
    }
    System.out.println(stringBuffer.toString());
}

@Test
public void testIntputStream2() throws IOException {
    InputStreamReader inputStreamReader = new InputStreamReader(new StringBufferInputStream("hello world"));
    char[] buff = new char[512];

    int n = inputStreamReader.read(buff);
    System.out.println(n);
    System.out.println(buff);
}

/**
 * 使用inputStreamReader進行讀取檔案,然後OutputStreamWriter寫入另一個檔案
 */
@Test
public void testOutputStreamWriter() throws IOException {
    InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream("h:\\haha.txt"), "utf-8");
    char[] buff = new char[512];
    StringBuffer stringBuffer = new StringBuffer();

    while (inputStreamReader.read(buff) > 0) {
        stringBuffer.append(buff);
    }
    System.out.println(stringBuffer.toString());

    OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream("h:\\haha2.txt"), "utf-8");
    outputStreamWriter.write(stringBuffer.toString().trim());
    outputStreamWriter.close();
}      

  注意:FileReader類繼承了InputStreamReader,FileReader讀取檔案流,通過StreamDecoder解碼成char,其解碼字元集使用的是預設字元集。在Java中,我們應該使用File對象來判斷某個檔案是否存在,如果我們用FileOutputStream或者FileWriter打開,那麼它肯定會被覆寫。

2 同步和異步、阻塞和非阻塞

  同步和異步是針對IO來說的。所謂同步就是一個任務的完成需要依賴另外一個任務時,隻有等待被依賴的任務完成後,依賴的任務才能算完成,這是一種可靠的任務序列。要麼成功都成功,失敗都失敗,兩個任務的狀态可以保持一緻。而異步是不需要等待被依賴的任務完成,隻是通知被依賴的任務要完成什麼工作,依賴的任務也立即執行,隻要自己完成了整個任務就算完成了。至于被依賴的任務最終是否真正完成,依賴它的任務無法确定,是以它是不可靠的任務序列。我們可以用打電話和發短信來很好的比喻同步與異步操作。

  阻塞和非阻塞是針對CPU來說的。阻塞與非阻塞主要是從 CPU 的消耗上來說的,阻塞就是 CPU 停下來等待一個慢的操作完成 CPU 才接着完成其它的事。非阻塞就是在這個慢的操作在執行時 CPU 去幹其它别的事,等這個慢的操作完成時,CPU 再接着完成後續的操作。雖然表面上看非阻塞的方式可以明顯的提高 CPU 的使用率,但是也帶了另外一種後果就是系統的線程切換增加。增加的 CPU 使用時間能不能補償系統的切換成本需要好好評估。

3 序列化

  Java的對象序列化将那些實作了Serializable接口的對象轉換成一個位元組序列,并能夠在以後将這個位元組序列完全恢複為原來的對象。這一過程可通過網絡進行,這樣序列化機制能夠自動彌補不同作業系統之間的差異。對應序列化的聰明之處在于它不僅儲存了對象的“全景圖”,而且能夠追蹤到對象自所包含的引用,并儲存這些對象;接着又能夠對對象内包含的每個這樣的引用進行最終;以此類推。

  要執行個體化一個對象,首先建立某些OutputStream對象,然後将其封裝在一個ObjectOutputStream對象内,這是,隻需要調用writeObject()即可将對象序列化,并将其發送到OutputStream(對象序列化基于位元組,是以使用InputStream和OutputStream繼承類層次結構)。反序列化和序列化過程正好相反,需要将一個InputStream封裝在ObjectInputStream内,然後調用readObject()擷取一個引用,它指向一個向上轉型的Object,是以必須向下轉型才能直接設定它們。下面是序列化和反序列化示例代碼:

@Test
public void testWriteSerialization() {
    try {
        FileOutputStream fileOutputStream = new FileOutputStream("h:\\serialize.txt");
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);

        objectOutputStream.writeObject("start");
        objectOutputStream.writeObject(new Person("luoxn28", 23));
        objectOutputStream.writeObject(12);
        objectOutputStream.close();

    } catch (IOException e) {
        e.printStackTrace();
    }

    System.out.println("end...");
}

@Test
public void testReadSerialization() {
    try {
        FileInputStream fileInputStream = new FileInputStream("h:\\serialize.txt");
        ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
        String info1 = (String) objectInputStream.readObject();
        Person person = (Person) objectInputStream.readObject();
        int num = (int) objectInputStream.readObject();

        System.out.println(info1);
        System.out.println(person);
        System.out.println(num);

    } catch (IOException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
}      

參考資料:

  1、深入分析 Java I/O 的工作機制

  2、Java IO系統