【轉載】J2SE知識點歸納筆記(七)---Java IO Part 3:基本字節流


J2SE知識點歸納筆記(七)---Java IO Part 3:基本字節流




注:本文轉載自coder-pig

原文鏈接:http://blog.csdn.net/coder_pig/article/details/45311943


聽說小豬去談戀愛了,很少有空寫博客了,祝福他。。。偷笑




本節引言:


在上一節中我們學習了控制檯輸入數據的方法以及對Java IO流體系圖進行了初步的瞭解,

在本節中我們針對性地學習一些基本字節流與字符流的使用~開始本節內容:




1.InputStream與OutputStream的相關方法:

首先要說一點的是:這兩個類是抽象類,通過子類來實現各種功能;

數據單位爲字節(1 byte = 8bit);下面是相關的方法:


1)InputStream的相關方法:

①public abstract int read( ):讀取一個byte的數據,返回值是高位補0的int類型值;

                                         若返回值=-1說明沒有讀取到任何字節讀取工作結束。

②public int read(byte b[ ]):讀取b.length個字節的數據放到b數組中。返回值是讀取的字節數。
                                 如果流位於文件末尾不在有數據可用,返回-1;等同於read(b,0,b.length)

③public int read(byte b[ ], int off, int len):從輸入流中最多讀取len個字節的數據,存放到偏移量爲off的b數組中,

                                 如果流位於文件末尾不在有數據可用,返回-1;

④public int available( ):返回輸入流中可以讀取的字節數

                                   ps:若輸入阻塞,當前線程將被掛起,如果InputStream對象調用這個方法的話,它只會返回0,

                                   這個方法必須由繼承InputStream類的子類對象調用纔有用

⑤public int close( ):使用完畢後,關閉流

⑥public long skip(long n):忽略輸入流中的n個字節,返回值是實際忽略的字節數,跳過一些字節來讀取

⑦public void mark(int readlimit):在此輸入流中標記當前位置,如果後續調用reset方法會在最後標記的位置重新定位

                                  此流,以便後續讀取重新讀取相同的字節,另外參數:readlimit代表:標記失效錢允許讀取的字節數

                                  ps:jdk 1.6後貌似沒限制多少個字節~

⑧public void reset( ):將流重新定位到最後一次調用mark方法的位置!

⑨public boolean markSupported( ):判斷該輸入流是否支持mark和reset方法


ps:這裏的mark和reset方法,你們可能不是很明白,後面學習BufferedInputStream的時候,

會給大家寫個例子,到時你就知道這兩個玩意的作用了...生氣

還有,雖然read有三種不同的構造方法,但是建議使用後面兩個,第一個少用!!!



2)OutputStream的相關方法:

①public void close( ):關閉此輸出流並釋放與此流有關的所有系統資源

②public void flush( ):刷新此輸出流並強制寫出所有緩衝的輸出字節

③public abstract void write(int b):將指定的字節寫入此輸出流

public void write(byte[] b):將 b.length個字節從指定的byte數組寫入此輸出流

⑤public void write(byte[] b,int off,int len):將指定 byte 數組中從偏移量 off 開始的 len 個字節寫入此輸出流



ps:其實這些方法,直接查文檔都可以查出來的,筆者也是貼的api上的解析而已~

    所以遇到不會用,沒見過的方法,先查文檔~




2.FileInputStream與FileOutputStream類的使用:

先來介紹字節流的第一個子類:ileInputStream與FileOutputStream類吧!

見名知意:就是以字節的形式對文件進行讀寫操作;

這裏就不貼相關方法了,自行查閱API文檔吧,直接上實例:


代碼實例:實現文件複製

  1. /* 
  2.      * 該方法用於複製文件,是最簡單的,沒有加入緩衝機制,所以很垃圾~只是方便讀者理解 
  3.      * 參數以此是:被複制文件的完整路徑,以及複製後生成的文件的完整路徑 
  4.      * */  
  5.     public static void copyFile1(String file1,String file2) throws IOException  
  6.     {  
  7.         File file = new File(file1);  
  8.         File outfile = new File(file2);  
  9.         FileInputStream inputStream = new FileInputStream(file);  
  10.         FileOutputStream outputStream = new FileOutputStream(outfile);  
  11.         int i = 0;  
  12.         while(i != -1) {  
  13.             i = inputStream.read();  
  14.             outputStream.write(i);  
  15.         }  
  16.         //記得關閉流  
  17.         inputStream.close();  
  18.         outputStream.close();  
  19.     }  

運行截圖:



好了,上述的代碼,如果複製的是大文件,就呵呵了,等死你...所以我們需要加入緩衝區,

以便提高文件的複製速度,就是添加一個字節緩衝數組而已~

代碼也很簡單:

  1. /* 
  2.      * 該方法在1的基礎上,添加了byte[]數組緩衝機制,複製大文件的時候,他的優勢就顯現出來了~ 
  3.      */  
  4.     public static void copyFile2(String file1, String file2) throws IOException {  
  5.         File file = new File(file1);  
  6.         File outfile = new File(file2);  
  7.         FileInputStream inputStream = new FileInputStream(file);  
  8.         FileOutputStream outputStream = new FileOutputStream(outfile);  
  9.         int i = 0;  
  10.         //設置緩衝的字節數組爲緩衝512 byte  
  11.         byte[] buffer = new byte[512];  
  12.         while (true) {  
  13.             if (inputStream.available() < 512) {  
  14.                 while (i != -1) {  
  15.                     i = inputStream.read();  
  16.                     outputStream.write(i);  
  17.                 }  
  18.                 break;  
  19.             } else {  
  20.                 // 當文件的大小大於512 byte時  
  21.                 inputStream.read(buffer);  
  22.                 outputStream.write(buffer);  
  23.             }  
  24.         }  
  25.         // 記得關閉流  
  26.         inputStream.close();  
  27.         outputStream.close();  
  28.     }  


代碼解析:

就是添加一個512byte的字符數組,但是速度卻明顯提高了,不信麼?

你可以找個大文件試試,比如筆者這裏用的美圖秀秀,差不多30M,用1中的

方法,過了差不多1分多鐘才複製完畢,而用加了緩衝的幾秒鐘就複製好了~




3)ByteArrayInputStream與ByteArrayOutputStream類的使用:

見名知意:字節數組的字節流,創建實例是程序內部會創建一個byte型數組的緩衝區,

在網絡傳輸的時候用的比較多,這裏給獲得某個頁面的網頁源代碼~

ByteArrayOutputStream用得比較多,另外toByteArray( )方法很常用

你也可以自行擴展,比如,訪問的url是圖片,然後把圖片保存到本地~


代碼實例:

  1. /* 
  2.      * 獲得某個網頁源代碼: 
  3.      * 參數是一個網頁的鏈接: 
  4.      * */  
  5.     public static String getHtml(String path)throws Exception  
  6.     {  
  7.         URL url = new URL(path);  
  8.         HttpURLConnection conn = (HttpURLConnection) url.openConnection();  
  9.         conn.setConnectTimeout(5000);  
  10.         conn.setRequestMethod("GET");  
  11.         if(conn.getResponseCode() == 200)  
  12.         {  
  13.             InputStream in = conn.getInputStream();  
  14.             ByteArrayOutputStream out = new ByteArrayOutputStream();  
  15.             byte[] buffer = new byte[1024];  
  16.             int len = 0;  
  17.             while((len = in.read(buffer)) != -1)  
  18.             {  
  19.                 out.write(buffer,0,len);  
  20.             }  
  21.             in.close();  
  22.             byte[] data = out.toByteArray();  
  23.             String html = new String(data,"UTF-8");  
  24.             return html;  
  25.         }  
  26.         return null;  
  27.     }  


運行截圖:


好了,這樣就讀取到一個網頁的源代碼了~





4)ObjectInputStream與ObjectOutputStream類 

見名知意,對象字節流,就是可以直接把對象轉換爲流,另外,轉換的對象對應的類,

需要實現Serializable接口,另外對象中的transient和static類型成員變量不會被讀取和寫入

下面我們來寫個簡單的代碼示例:


step 1:定義一個業務Bean:

  1. import java.io.Serializable;  
  2.   
  3. public class Book implements Serializable {  
  4.     private String bName;  
  5.     private String bReporter;  
  6.   
  7.     public Book(String bName, String bReporter) {  
  8.         super();  
  9.         this.bName = bName;  
  10.         this.bReporter = bReporter;  
  11.     }  
  12.   
  13.     public String getbName() {  
  14.         return bName;  
  15.     }  
  16.   
  17.     public void setbName(String bName) {  
  18.         this.bName = bName;  
  19.     }  
  20.   
  21.     public String getbReporter() {  
  22.         return bReporter;  
  23.     }  
  24.   
  25.     public void setbReporter(String bReporter) {  
  26.         this.bReporter = bReporter;  
  27.     }  
  28.   
  29. }  


step 2:先構造低級流FileOutputStream和FileInputStream,然後才能使用

ObjectOutputStream與ObjectInputStream:


  1. public static void main(String[] args) throws Exception {  
  2.         Book b1 = new Book("《如何與傻B相處》""豬小豬");  
  3.         Book b2 = new Book("《逗比的自我修養》""貓小貓");  
  4.         Book b3,b4;  
  5.         //注意:使用高級流需要先構造低級流  
  6.         //①把對象直接寫入到文件中  
  7.         FileOutputStream fout = new FileOutputStream("D:/books.txt");  
  8.         ObjectOutputStream out = new ObjectOutputStream(fout);  
  9.         out.writeObject(b1);  
  10.         out.writeObject(b2);  
  11.         //關閉輸出流  
  12.         out.close();  
  13.         //②讀取文件中的對象:  
  14.         FileInputStream fin=new FileInputStream("D:/books.txt");  
  15.         ObjectInputStream in=new ObjectInputStream(fin);  
  16.         b3=(Book) in.readObject();  
  17.         b4=(Book) in.readObject();  
  18.         //關閉輸入流  
  19.         in.close();  
  20.         System.out.println(b3.getbName() + " 作者: "+ b3.getbReporter());  
  21.         System.out.println(b4.getbName() + " 作者: "+ b4.getbReporter());  
  22.     }  


運行截圖:



同時,我們可以在D盤下看到這個book.txt的文件,但是打開後是亂碼,

這是因爲,我們寫入的是字節流~中文就會出現亂碼,如果想解決這種情況,

可以使用後面會說的FileWriter和FileReader~






5)PipedOutputStream與PipedInputStream類

這個是管道流,相比起前面的介紹的幾個流,這個就稍微複雜點了,

通過這兩個流可以讓多線程通過管道進行線程間的通訊,兩者需要搭配使用哦!

不同於Linux和Unix系統中的管道,他們是不同地址空間空間的兩個進程可以通過管道通信,

Java中則是運行在同一進程中的不同線程!

原理:PipedInputStream內部有一個Buffer,使用InputStream的方法讀取其Buffer中的字節;

而Buffer中的字節是PipedOutputStream調用PipedInputStream的方法放入的;

另外,不建議把這兩個流對應的對象寫在同一線程中,不然很容易引起死鎖,

使用connect( )方法可以將這個管道搭建起來,再進行數據傳輸~


這裏找了個簡單的小例子給大家體會下吧~


1)SendThread.java:(輸出流)

  1. import java.io.IOException;  
  2. import java.io.PipedOutputStream;  
  3.   
  4.   
  5. public class SendThread extends Thread{  
  6.     private PipedOutputStream out = new PipedOutputStream();     
  7.     public PipedOutputStream getOutputStream(){     
  8.         return out;     
  9.     }     
  10.     public void run(){     
  11.             String strInfo = "我是PipedOutputStream寫入的東西" ;     
  12.             try {     
  13.                 out.write(strInfo.getBytes());     
  14.                 out.close();     
  15.             } catch (IOException e) {     
  16.                 e.printStackTrace();     
  17.             }     
  18.         }     
  19. }  

2)ReceiverThread.java:(輸入流)


  1. import java.io.IOException;  
  2. import java.io.PipedInputStream;  
  3.   
  4. public class ReceiverThread extends Thread {  
  5.     private PipedInputStream in = new PipedInputStream();  
  6.   
  7.     public PipedInputStream getInputStream() {  
  8.         return in;  
  9.     }  
  10.   
  11.     public void run() {  
  12.         byte[] buf = new byte[1024];  
  13.         try {  
  14.             int len = in.read(buf);  
  15.             System.out.println(new String(buf, 0, len));  
  16.             in.close();  
  17.         } catch (IOException e) {  
  18.             e.printStackTrace();  
  19.         }  
  20.     }  
  21. }  


測試類:Test5.java:

  1. import java.io.IOException;  
  2. import java.io.PipedInputStream;  
  3. import java.io.PipedOutputStream;  
  4.   
  5. public class Test5 {  
  6.     public static void main(String[] args) {  
  7.         ReceiverThread rThread = new ReceiverThread();  
  8.         SendThread sThread = new SendThread();  
  9.         // 獲取對應流:  
  10.         PipedOutputStream out = sThread.getOutputStream();  
  11.         PipedInputStream in = rThread.getInputStream();  
  12.         try {  
  13.             // 管道鏈接  
  14.             out.connect(in);  
  15.             sThread.start();  
  16.             rThread.start();  
  17.         } catch (IOException e) {  
  18.             e.printStackTrace();  
  19.         }  
  20.     }  
  21. }  


運行截圖:





6)SequenceInputStream合併流:

見名知意,就是合併多個輸入流.即當我們需要從多個輸入流中向程序讀入數據的時候可以考慮使用合併流,

SequenceInputStream會將與之相連接的流集組合成一個輸入流並從第一個輸入流開始讀取,

直到到達文件末尾,接着從第二個輸入流讀取,依次類推,直到到達包含的最後一個輸入流的文件末尾爲止;

合併流的作用是將多個源合併合一個源!


要注意的地方:(構造方法)

SequenceInputStream爲我們提供了兩個不同的構造方法:

1~SequenceInputStream(Enumeration<? extends InputStream> e) 

2~SequenceInputStream(InputStream s1, InputStream s2)


PS:這個Enumertaion是一個接口,Enumeration接口定義了從一個數據結構得到連續數據的手段

該接口提供的兩個方法:

boolean hasMoreElements():測試此枚舉是否包含更多的元素。 

nextElement( ):如果此枚舉對象至少還有一個可提供的元素,則返回此枚舉的下一個元素.

這裏並不詳細介紹Enumertaion了,想了解的自行查閱文檔~




那麼接下來我們來寫兩個簡單的小程序方便我們理解:

①使用兩個流作爲參數的構造方法:

  1. public static void main(String[] args) throws IOException {    
  2.        InputStream s1 = new ByteArrayInputStream("ABC".getBytes());    
  3.        InputStream s2 = new ByteArrayInputStream("abc".getBytes());    
  4.        InputStream in = new SequenceInputStream(s1, s2);    
  5.        int data;    
  6.        while ((data = in.read()) != -1) {    
  7.            System.out.println(data);    
  8.        }    
  9.        in.close();    
  10.    }    

運行結果:(對應ABCabc的ASCII碼值,別問我爲什麼不用漢字,因爲本節講的是字節流,一個漢字兩個字節~)




②使用Enumeration作爲參數的那個構造方法:

  1. public static void main(String[] args) throws IOException {   
  2.          //創建合併流  
  3.         SequenceInputStream sin = null;    
  4.         //創建輸出流    
  5.         BufferedOutputStream bout = null;    
  6.         try {    
  7.             //構建流集合    
  8.             Vector<InputStream> vector = new Vector<InputStream>();    
  9.             vector.addElement(new FileInputStream("D:\\test1.txt"));    
  10.             vector.addElement(new FileInputStream("D:\\test2.txt"));    
  11.             vector.addElement(new FileInputStream("D:\\test3.txt"));    
  12.             vector.addElement(new FileInputStream("D:\\test4.txt"));    
  13.             Enumeration<InputStream> e = vector.elements();    
  14.                 
  15.             sin = new SequenceInputStream(e);    
  16.             bout = new BufferedOutputStream(    
  17.                     new FileOutputStream("D:\\textUnion.txt"));    
  18.               
  19.             //讀寫數據    
  20.             byte[] buf = new byte[1024];    
  21.             int len = 0;    
  22.             while ((len = sin.read(buf)) != -1) {    
  23.                 bout.write(buf, 0, len);    
  24.                 bout.flush();    
  25.             }    
  26.         } catch (FileNotFoundException e1) {    
  27.             e1.printStackTrace();    
  28.         } catch (IOException e1) {    
  29.             e1.printStackTrace();    
  30.         } finally {    
  31.             try {    
  32.                 if (sin != null)    
  33.                     sin.close();    
  34.             } catch (IOException e) {    
  35.                 e.printStackTrace();    
  36.             }    
  37.             try {    
  38.                 if (bout != null)    
  39.                     bout.close();    
  40.             } catch (IOException e) {    
  41.                 e.printStackTrace();    
  42.             }    
  43.         }    
  44.     }    

接着我們在D盤目錄下創建:test1,test2,test3,test4四個txt文件,內容依次爲:

逗A,逗B,逗C,逗D

運行截圖:

會在D盤下生產testUnion.txt的文件,打開文件後可以看到:









最後說兩句:

好的,關於Java IO中的基本字節流就講到這裏了,現在我們來回顧下本節講解的東東:

①InputStream和OutputStream兩個父類的相關方法

②FileInputStream與FileOutputStream類文件讀寫類的使用,加入了緩衝數組提高了讀寫效率~

③ByteArrayInputStream與ByteArrayOutputStream類字節數組類,我們寫了個獲取網頁源碼的例子:

④ObjectInputStream與ObjectOutputStream類對象讀寫類,我們通過這兩個類可以很方便地完成

從對象到流,流到對象的轉化,另外要注意一點,這個對象所屬的類要繼承Serializable接口!

⑤PipedOutputStream與PipedInputStream類管道輸入輸出類,這兩個是用於線程間進行通信的,

一般避免把這兩個寫到同一個線程中,不然很容易引起死鎖,本節只是給出一個簡單的應用例子,後續有

需要再進行深入研究~

⑥SequenceInputStream合併流,就是可以將多個輸入流合併成一個輸入流,有兩種構造形式:

分別是兩個輸入流,或者一個Enumeration參數,也很簡單,大家看懂例子即可~



那麼,關於本節的基本字節流就到這裏,謝謝你的閱讀~大笑

如果你覺得本文可以的話,可花1s點個贊,讓更多的朋友可以看到這篇文章~萬分感謝得意~


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