1. 程式人生 > >Java IO最詳解

Java IO最詳解

初學java,一直搞不懂java裡面的io關係,在網上找了很多大多都是給個結構圖草草描述也看的不是很懂。而且沒有結合到java7 的最新技術,所以自己來整理一下,有錯的話請指正,也希望大家提出寶貴意見。

首先看個圖:(如果你也是初學者,我相信你看了真個人都不好了,想想java設計者真是煞費苦心啊!)

這是java io 比較基本的一些處理流,除此之外我們還會提到一些比較深入的基於io的處理類,比如console類,SteamTokenzier,Externalizable介面,Serializable介面等等一些高階用法極其原理。

一、java io的開始:檔案

1. 我們主要講的是流,流的本質也是對檔案的處理,我們循序漸進一步一步從檔案將到流去。

2. java 處理檔案的類 File,java提供了十分詳細的檔案處理方法,舉了其中幾個例子,其餘的可以去

Java程式碼  收藏程式碼
  1. package com.hxw.io;  
  2. import java.io.*;  
  3. public class FileExample{  
  4.     public static void main(String[] args) {  
  5.         createFile();  
  6.     }  
  7.   /** 
  8.    * 檔案處理示例
     
  9.    */  
  10.   public static void createFile() {  
  11.      File f=new File("E:/電腦桌面/jar/files/create.txt");  
  12.         try{  
  13.             f.createNewFile();  //當且僅當不存在具有此抽象路徑名指定名稱的檔案時,不可分地建立一個新的空檔案。  
  14.             System.out.println("該分割槽大小"+f.getTotalSpace()/(1024*1024*1024)+"G"); //返回由此抽象路徑名錶示的檔案或目錄的名稱。  
  15.             f.mkdirs();  //建立此抽象路徑名指定的目錄,包括所有必需但不存在的父目錄。
      
  16. //            f.delete(); //  刪除此抽象路徑名錶示的檔案或目錄  
  17.            System.out.println("檔名  "+f.getName());  //  返回由此抽象路徑名錶示的檔案或目錄的名稱。  
  18.            System.out.println("檔案父目錄字串 "+f.getParent());// 返回此抽象路徑名父目錄的路徑名字串;如果此路徑名沒有指定父目錄,則返回 null。  
  19.         }catch (Exception e) {  
  20.             e.printStackTrace();  
  21.         }  
  22.   }  
  23. }  

二、位元組流:

1.位元組流有輸入和輸出流,我們首先看輸入流InputStream,我們首先解析一個例子(FileInputStream)。

Java程式碼  收藏程式碼
  1. package com.hxw.io;  
  2. import java.io.File;  
  3. import java.io.FileInputStream;  
  4. import java.io.IOException;  
  5. import java.io.InputStream;  
  6. public class FileCount {  
  7.    /** 
  8.     * 我們寫一個檢測檔案長度的小程式,別看這個程式挺長的,你忽略try catch塊後發現也就那麼幾行而已。 
  9.     */  
  10.    publicstatic void main(String[] args) {  
  11.       //TODO 自動生成的方法存根  
  12.              int count=0;  //統計檔案位元組長度  
  13.       InputStreamstreamReader = null;   //檔案輸入流  
  14.       try{  
  15.           streamReader=newFileInputStream(new File("D:/David/Java/java 高階進階/files/tiger.jpg"));  
  16.           /*1.new File()裡面的檔案地址也可以寫成D:\\David\\Java\\java 高階進階\\files\\tiger.jpg,前一個\是用來對後一個 
  17.            * 進行轉換的,FileInputStream是有緩衝區的,所以用完之後必須關閉,否則可能導致記憶體佔滿,資料丟失。 
  18.           */  
  19.           while(streamReader.read()!=-1) {  //讀取檔案位元組,並遞增指標到下一個位元組  
  20.              count++;  
  21.           }  
  22.           System.out.println("---長度是: "+count+" 位元組");  
  23.       }catch (final IOException e) {  
  24.           //TODO 自動生成的 catch 塊  
  25.           e.printStackTrace();  
  26.       }finally{  
  27.           try{  
  28.              streamReader.close();  
  29.           }catch (IOException e) {  
  30.              //TODO 自動生成的 catch 塊  
  31.              e.printStackTrace();  
  32.           }  
  33.       }  
  34.    }  
  35. }  

我們一步一步來,首先,上面的程式存在問題是,每讀取一個自己我都要去用到FileInputStream,我輸出的結果是“---長度是: 64982 位元組”,那麼進行了64982次操作!可能想象如果檔案十分龐大,這樣的操作肯定會出大問題,所以引出了緩衝區的概念。可以將streamReader.read()改成streamReader.read(byte[]b)此方法讀取的位元組數目等於位元組陣列的長度,讀取的資料被儲存在位元組陣列中,返回讀取的位元組數,InputStream還有其他方法mark,reset,markSupported方法,例如:

markSupported 判斷該輸入流能支援mark 和 reset 方法。

mark用於標記當前位置;在讀取一定數量的資料(小於readlimit的資料)後使用reset可以回到mark標記的位置。

FileInputStream不支援mark/reset操作;BufferedInputStream支援此操作;

mark(readlimit)的含義是在當前位置作一個標記,制定可以重新讀取的最大位元組數,也就是說你如果標記後讀取的位元組數大於readlimit,你就再也回不到回來的位置了。

通常InputStream的read()返回-1後,說明到達檔案尾,不能再讀取。除非使用了mark/reset。

2.FileOutputStream 循序漸進版, InputStream是所有位元組輸出流的父類,子類有ByteArrayOutputStream,FileOutputStream,ObjectOutputStreanm,這些我們在後面都會一一說到。先說FileOutputStream

我以一個檔案複製程式來說,順便演示一下快取區的使用。(Java I/O預設是不緩衝流的,所謂“緩衝”就是先把從流中得到的一塊位元組序列暫存在一個被稱為buffer的內部位元組數組裡,然後你可以一下子取到這一整塊的位元組資料,沒有緩衝的流只能一個位元組一個位元組讀,效率孰高孰低一目瞭然。有兩個特殊的輸入流實現了緩衝功能,一個是我們常用的BufferedInputStream.)

Java程式碼  收藏程式碼
  1. package com.hxw.io;  
  2. import java.io.*;  
  3. public class FileCopy {  
  4.   public static void main(String[] args) {  
  5.      // TODO自動生成的方法存根  
  6.      byte[] buffer=new byte[512];   //一次取出的位元組數大小,緩衝區大小  
  7.      int numberRead=0;  
  8.      FileInputStream input=null;  
  9.      FileOutputStream out =null;  
  10.      try {  
  11.         input=new FileInputStream("D:/David/Java/java 高階進階/files/tiger.jpg");  
  12.         out=new FileOutputStream("D:/David/Java/java 高階進階/files/tiger2.jpg"); //如果檔案不存在會自動建立  
  13.         while ((numberRead=input.read(buffer))!=-1) {  //numberRead的目的在於防止最後一次讀取的位元組小於buffer長度,  
  14.            out.write(buffer, 0, numberRead);       //否則會自動被填充0  
  15.         }  
  16.      } catch (final IOException e) {  
  17.         // TODO自動生成的 catch 塊  
  18.         e.printStackTrace();  
  19.      }finally{  
  20.         try {  
  21.            input.close();  
  22.            out.close();  
  23.         } catch (IOException e) {  
  24.            // TODO自動生成的 catch 塊  
  25.            e.printStackTrace();  
  26.         }  
  27.      }  
  28.   }  
  29. }  

3.讀寫物件:ObjectInputStream 和ObjectOutputStream ,該流允許讀取或寫入使用者自定義的類,但是要實現這種功能,被讀取和寫入的類必須實現Serializable介面,其實該介面並沒有什麼方法,可能相當於一個標記而已,但是確實不合缺少的。例項程式碼如下:

Java程式碼  收藏程式碼
  1. package com.hxw.io;  
  2. import java.io.*;  
  3. public class ObjetStream {  
  4.    /** 
  5.     * @param args 
  6.     */  
  7.    public static void main(String[] args) {  
  8.       // TODO自動生成的方法存根  
  9.       ObjectOutputStream objectwriter=null;  
  10.       ObjectInputStream objectreader=null;  
  11.       try {  
  12.          objectwriter=new ObjectOutputStream(new FileOutputStream("D:/David/Java/java 高階進階/files/student.txt"));  
  13.          objectwriter.writeObject(new Student("gg"22));  
  14.          objectwriter.writeObject(new Student("tt"18));  
  15.          objectwriter.writeObject(new Student("rr"17));  
  16.          objectreader=new ObjectInputStream(new FileInputStream("D:/David/Java/java 高階進階/files/student.txt"));  
  17.          for (int i = 0; i < 3; i++) {  
  18.             System.out.println(objectreader.readObject());  
  19.          }  
  20.       } catch (IOException | ClassNotFoundException e) {  
  21.          // TODO自動生成的 catch 塊  
  22.          e.printStackTrace();  
  23.       }finally{  
  24.          try {  
  25.             objectreader.close();  
  26.             objectwriter.close();  
  27.          } catch (IOException e) {  
  28.             // TODO自動生成的 catch 塊  
  29.             e.printStackTrace();  
  30.          }  
  31.       }  
  32.    }  
  33. }  
  34. class Student implements Serializable{  
  35.    private String name;  
  36.    private int age;  
  37.    public Student(String name, int age) {  
  38.       super();  
  39.       this.name = name;  
  40.       this.age = age;  
  41.    }  
  42.    @Override  
  43.    public String toString() {  
  44.       return "Student [name=" + name + ", age=" + age + "]";  
  45.    }  
  46. }  

執行後系統輸出:

Student [name=gg, age=22]

Student [name=tt, age=18]

Student [name=rr, age=17]

4.有時沒有必要儲存整個物件的資訊,而只是要儲存一個物件的成員資料,成員資料的型別假設都是Java的基本資料型別,這樣的需求不必使用到與Object輸入、輸出相關的流物件,可以使用DataInputStream、DataOutputStream來寫入或讀出資料。下面是一個例子:(DataInputStream的好處在於在從檔案讀出資料時,不用費心地自行判斷讀入字串時或讀入int型別時何時將停止,使用對應的readUTF()和readInt()方法就可以正確地讀入完整的型別資料。)