Java中的IO

     輸入和輸出是所有應用中所必需的組成部分,通過IO可以讀取輸入數據以及存儲數據到外部設備上。Java中的輸入和輸出是通過java.io來支持的。下面是本人在學習中的歸納和體會。


1. File類和文件過濾器


    顧名思義,File類中是有關文件的操作。這裏必須明確,文件還包括目錄。通過文件或目錄路徑的字符串作爲參數,根據傳入的是目錄路徑還是文件名路徑可以分別初始化不同的File對象。在File對象中封裝了對文件的操作,例如新建文件,刪除文件和重命名等方法。但是它並不能訪問文件本身的內容,這需要輸入輸出流來完成。下面是有關File類的代碼。

 
 

  1. public class FileTest  
  2. {  
  3.     public static void main(String[] args) throws IOException  
  4.     {  
  5.         //以當前路徑來創建一個File對象  
  6.         File file = new File(".");   
  7.         //直接獲取文件名,輸出一點  
  8.         System.out.println(file.getName());  
  9.         //獲取相對路徑的父路徑可能出錯,下面代碼輸出null  
  10.         System.out.println(file.getParent());  
  11.         //獲取絕對路徑  
  12.         System.out.println(file.getAbsoluteFile());  
  13.         //獲取上一級路徑  
  14.         System.out.println(file.getAbsoluteFile().getParent());  
  15.         //在當前路徑下創建一個臨時文件  
  16.         File tmpFile = File.createTempFile("aaa"".txt", file);  
  17.         //指定當JVM退出時刪除該文件  
  18.         tmpFile.deleteOnExit();  
  19.         //以系統當前時間作爲新文件名來創建新文件  
  20.         File newFile = new File(System.currentTimeMillis() + "");  
  21.         System.out.println("newFile對象是否存在:" + newFile.exists());  
  22.         //以指定newFile對象來創建一個文件  
  23.         newFile.createNewFile();  
  24.         //以newFile對象來創建一個目錄,因爲newFile已經存在,  
  25.         //所以下面方法返回false,即無法創建該目錄  
  26.         newFile.mkdir();  
  27.         //使用list方法來列出當前路徑下的所有文件和路徑  
  28.         String[] fileList = file.list();  
  29.         System.out.println("======當前路徑下所有文件和路徑如下=====");  
  30.         for (String fileName : fileList)  
  31.         {  
  32.             System.out.println(fileName);  
  33.         }  
  34.       
  35.         //listRoots靜態方法列出所有的磁盤根路徑。  
  36.         File[] roots = File.listRoots();  
  37.         System.out.println("======系統所有根路徑如下=====");  
  38.         for (File root : roots)  
  39.         {  
  40.             System.out.println(root);  
  41.         }  
  42.     }  
  43. }  

    在上面的代碼中,使用list()方法可以返回目錄下所有文件的路徑和名稱。使用文件過濾器,可以根據自己的需要選擇返回的文件類型。通過一個實現了FilenameFilter接口的類對象作爲list()方法的參數,就可以實現文件過濾的作用。過濾的規則在重寫FilenameFilter中的accept()方法中定義。代碼如下:

 
 

  1. public class FilenameFilterTest  
  2. {  
  3.     public static void main(String[] args)   
  4.     {  
  5.         File file = new File(".");  
  6.         String[] nameList = file.list(new MyFilenameFilter());  
  7.         for (String name : nameList)  
  8.         {  
  9.             System.out.println(name);  
  10.         }  
  11.     }  
  12. }  
  13. //實現自己的FilenameFilter實現類  
  14. class MyFilenameFilter implements FilenameFilter  
  15. {  
  16.     public boolean accept(File dir, String name)  
  17.     {  
  18.         //如果文件名以.java結尾,或者文件對應一個路徑,返回true  
  19.         return name.endsWith(".java")  
  20.             || new File(name).isDirectory();  
  21.     }  
  22. }  

2. 輸入輸出流


    在Java中,從不同角度定義了輸入輸出流的分類。以內存作爲流的方向基點,可將其分爲輸入流和輸出流,流向內存的成爲輸入流,流出內存的成爲輸出流。從數據操作的最小單元來分類,可將其分爲字節流和字符流。根據抽象程度分類,可將其分爲節點流和處理流,處理流是連接到實際物理節點的節點流的封裝,這樣既可以不必去關注節點的來源(文件或者數組),用統一的方法去操作,又可以使用更加方面的方法來實現操作。


    在學習中,本人對於輸入輸出流的總結歸納:


    A. 字節流和字符流其實並沒有本質的區別,無非是它們二者的操作單元不同。字節流的基類爲InputStream和OutputStream,字符流的基類爲Reader和Writer。它們所提供的讀寫方法一樣,區別在於參數,需要對應流類型。


    B. 輸入流中的read系列方法是從輸入流中讀取數據的操作。空參數將返回所讀取的字節(字符)。read方法中可以傳入數組參數,數組的類型必須和流的數據類型相匹配,相當於一個竹筒,一次可讀取最多等於數組容量的數據,並將所讀數據裝進數組中。


    C. 輸出流中的write系列方法是向輸出流中寫入數據的操作。方法中的參數可以是整型或字符型變量,也可以傳入數組參數,數組的類型必須和流的數據類型相匹配,相當於一個竹筒,一次可寫入最多等於數組容量的數據。特別地,字符型輸出流的write方法可以傳入字符串。


    D. InputStream和OutputStream,Reader和Writer這四個類作爲Java中輸入輸出流的抽象基類,並不能直接被初始化使用。Java中提供了一些繼承了它們的類,用於實現具體的某種流操作。如:

    a.File前綴系列的類(如FileInputStream),用於對文件類進行流操作,使用文件名作爲構造函數的參數;示例代碼如下:

輸出類:

 
 

  1. public class FileOutputStreamTest  
  2. {  
  3.     public static void main(String[] args) throws IOException  
  4.     {  
  5.         FileInputStream fis = null;  
  6.         FileOutputStream fos = null;  
  7.         try 
  8.         {  
  9.             //創建字節輸入流  
  10.             fis = new FileInputStream("FileOutputStreamTest.java");  
  11.             //創建字節輸入流  
  12.             fos = new FileOutputStream("newFile.txt");  
  13.             byte[] bbuf = new byte[32];  
  14.             int hasRead = 0;  
  15.             //循環從輸入流中取出數據  
  16.             while ((hasRead = fis.read(bbuf)) > 0 )  
  17.             {  
  18.                 //每讀取一次,即寫入文件輸出流,讀了多少,就寫多少。  
  19.                 fos.write(bbuf , 0 , hasRead);  
  20.             }  
  21.         }  
  22.         catch (IOException ioe)  
  23.         {  
  24.             ioe.printStackTrace();  
  25.         }  
  26.         finally 
  27.         {  
  28.             //使用finally塊來關閉文件輸入流  
  29.             if (fis != null)  
  30.             {  
  31.                 fis.close();  
  32.             }  
  33.             //使用finally塊來關閉文件輸出流  
  34.             if (fos != null)  
  35.             {  
  36.                 fos.close();  
  37.             }  
  38.         }  
  39.     }  
  40. }  

輸入類:

 
 

  1. public class FileInputStreamTest  
  2. {  
  3.     public static void main(String[] args) throws IOException  
  4.     {  
  5.         //創建字節輸入流  
  6.         FileInputStream fis = new FileInputStream("FileInputStreamTest.java");  
  7.         //創建一個長度爲1024的“竹筒”  
  8.         byte[] bbuf = new byte[1024];  
  9.         //用於保存實際讀取的字節數  
  10.         int hasRead = 0;  
  11.         //使用循環來重複“取水”過程  
  12.         while ((hasRead = fis.read(bbuf)) > 0 )  
  13.         {  
  14.             //取出“竹筒”中水滴(字節),將字節數組轉換成字符串輸入!  
  15.             System.out.print(new String(bbuf , 0 , hasRead ));  
  16.         }  
  17.         fis.close();  
  18.     }  
  19. }  

    b. 數組前綴系列類(如ByteArrayInputSteam),用於對數組進行流操作,使用數組對象作爲構造函數的參數;

    c. 轉換流InputStreamReader將字節輸入流轉換爲字符輸入流;InputStreamReader將字節輸出流轉換爲字符輸出流,代碼如下:

 
 

  1. public class KeyinTest  
  2. {  
  3.     public static void main(String[] args)   
  4.     {  
  5.         BufferedReader br = null;  
  6.         try 
  7.         {  
  8.             //將Sytem.in對象轉換成Reader對象  
  9.             InputStreamReader reader = new InputStreamReader(System.in);  
  10.             //將普通Reader包裝成BufferedReader  
  11.             br = new BufferedReader(reader);  
  12.             String buffer = null;  
  13.             //採用循環方式來一行一行的讀取  
  14.             while ((buffer = br.readLine()) != null)  
  15.             {  
  16.                 //如果讀取的字符串爲"exit",程序退出  
  17.                 if (buffer.equals("exit"))  
  18.                 {  
  19.                     System.exit(1);  
  20.                 }  
  21.                 //打印讀取的內容  
  22.                 System.out.println("輸入內容爲:" + buffer);  
  23.             }  
  24.         }  
  25.         catch (IOException ioe)  
  26.         {  
  27.             ioe.printStackTrace();  
  28.         }  
  29.         //關閉輸入流  
  30.         finally 
  31.         {  
  32.             try 
  33.             {  
  34.                 br.close();               
  35.             }  
  36.             catch (IOException ioe)  
  37.             {  
  38.                 ioe.printStackTrace();  
  39.             }  
  40.         }  
  41.     }  
  42. }  

    d. 字符流的子類有String前綴(StringReader和StringWriter)系列類可以對字符串進行操作,代碼如下:

 
 

  1. public class StringNodeTest  
  2. {  
  3.     public static void main(String[] args)   
  4.     {  
  5.         String src = "從明天起,做一個幸福的人\n" 
  6.             + "餵馬,劈柴,周遊世界\n" 
  7.             + "從明天起,關心糧食和蔬菜\n" 
  8.             + "我有一所房子,面朝大海,春暖花開\n" 
  9.             + "從明天起,和每一個親人通信\n" 
  10.             + "告訴他們我的幸福\n";  
  11.         StringReader sr = new StringReader(src);  
  12.         char[] buffer = new char[32];  
  13.         int hasRead = 0;  
  14.         try 
  15.         {  
  16.             //採用循環讀取的訪問讀取字符串  
  17.             while((hasRead = sr.read(buffer)) > 0)  
  18.             {  
  19.                 System.out.print(new String(buffer ,0 , hasRead));  
  20.             }  
  21.         }  
  22.         catch (IOException ioe)  
  23.         {  
  24.             ioe.printStackTrace();  
  25.         }  
  26.         finally 
  27.         {  
  28.             sr.close();  
  29.         }  
  30.         //創建StringWriter時,實際上以一個StringBuffer作爲輸出節點  
  31.         //下面指定的20就是StringBuffer的初始長度  
  32.         StringWriter sw = new StringWriter(20);   
  33.         //調用StringWriter的方法執行輸出  
  34.         sw.write("我遠離了大海,\n");  
  35.         sw.write("看不到春暖花開,\n");  
  36.         sw.write("我只有一隻小龜,\n");  
  37.         sw.write("一樣可以聞到馥郁花香\n");  
  38.         System.out.println("------下面是sw的字符串節點裏的內容------:");  
  39.         //使用toString方法返回StringWriter的字符串節點的內容  
  40.         System.out.println(sw.toString());  
  41.     }  
  42. }  

    d.推回輸入流PushbackInputStream和PushbackReader帶有一個推回緩衝區,使用unread(數組)方法可以將一個字節/字符數組推回到推回緩衝區中。推回輸入流也有read方法,使用read方法讀取輸入流時,首先從輸入緩衝區中讀取,讀完之後才從輸入流中讀。代碼如下:

 
 

  1. public class PushbackTest  
  2. {  
  3.     public static void main(String[] args)   
  4.     {  
  5.         PushbackReader pr = null;  
  6.         try 
  7.         {  
  8.             //創建一個PushbackReader對象,指定推回緩衝區的長度爲64  
  9.             pr = new PushbackReader(new FileReader("PushbackTest.java") , 64);  
  10.             char[] buf = new char[32];  
  11.             //用以保存上次讀取的字符串內容  
  12.             String lastContent = "";  
  13.             int hasRead = 0;  
  14.             //循環讀取文件內容  
  15.             while ((hasRead = pr.read(buf)) > 0)  
  16.             {  
  17.                 //將讀取的內容轉換成字符串  
  18.                 String content = new String(buf , 0 , hasRead);  
  19.                 int targetIndex = 0;  
  20.                 //將上次讀取的字符串和本次讀取的字符串拼起來,查看是否包含目標字符串  
  21.                 //如果包含目標字符串  
  22.                 if ((targetIndex = (lastContent + content).indexOf("new PushbackReader")) > 0)  
  23.                 {  
  24.                     //將本次內容和上次內容一起推回緩衝區  
  25.                     pr.unread((lastContent + content).toCharArray());  
  26.                     //再次讀取指定長度的內容(就是目標字符串之前的內容)  
  27.                     pr.read(buf , 0 , targetIndex);  
  28.                     //打印讀取的內容  
  29.                     System.out.print(new String(buf , 0 ,targetIndex));  
  30.                     System.exit(0);  
  31.                 }  
  32.                 else 
  33.                 {  
  34.                     //打印上次讀取的內容  
  35.                     System.out.print(lastContent);  
  36.                     //將本次內容設爲上次讀取的內容  
  37.                     lastContent = content;  
  38.                 }  
  39.             }  
  40.         }  
  41.         catch (IOException ioe)  
  42.         {  
  43.             ioe.printStackTrace();  
  44.         }  
  45.         finally 
  46.         {  
  47.             try 
  48.             {  
  49.                 if (pr != null)  
  50.                     pr.close();  
  51.             }  
  52.             catch (IOException ioe)  
  53.             {  
  54.                 ioe.printStackTrace();  
  55.             }  
  56.         }  
  57.     }  
  58. }  

    E. 在Java虛擬機中可以通過exec方法執行其他的應用程序,並返回Proess對象。利用該對象的getErrorStream、getInputStream和getOutputStream方法可以分別獲得子進程的錯誤流,輸出流和輸入流(方向是以程序角度爲基點)。

    F. RandomAccessFile類提供對文件的隨機訪問,程序可以直接跳轉到文件的任何地方來讀取數據。它內部提供了與字節流同法相同的讀寫方法,另外又加入了自由定位文件記錄指針的方法seek。在讀寫操作時以字節爲單位。在構造函數中還需加入一個mode參數由於制定讀寫權限。


    G. 需要注意Scanner對象的使用。Scanner對象用於捕獲輸入,並將輸入內容轉換爲字符串。

 
 

  1. //使用System.in創建Scanner對象,用於獲取標準輸入  
  2.         Scanner sc = new Scanner(System.in);  
  3.         PrintStream ps = new PrintStream(  
  4.             new FileOutputStream("out.txt"));  
  5.         //增加下面一行將只把回車作爲分隔符  
  6.         sc.useDelimiter("\n");  
  7.         //判斷是否還有下一個輸入項  
  8.         while(sc.hasNext())  
  9.         {  
  10.             //輸出輸入項  
  11.             ps.println("鍵盤輸入的內容是:" + sc.next());  
  12.         }  
  13.         ps.close(); 

    對於初學者來說一個容易忽略的地方是,需要區別節點流和處理流構造方法中參數的意義。節點流中,構造方法參數是要讀取或者輸出地物理節點,是“目的地”或者“始發地”,而處理流的構造函數參數是所要包裝的處理流對象,包裝之後的操作實際上是間接操作節點流,並未對被包裝的節點流本身屬性做修改。採用處理流包裝後,可以不用管節點流的數據類型,而根據處理流的性質傳遞在節點流中不能傳遞的內容,例如Obj;同時還可以增加一些便於操作的方法,比如緩衝區。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章