流的概念
流是一個很形象的概念,當程序需要讀取數據的時候,就會開啟一個通向數據源的流,這個數據源可以使文件,內存,或是網絡連接。類似的,當程序需要寫入數據的時候,就會開啟一個通向目的地的流。這時候你就可以想象數據好像在這其中“流”動一樣。
流的分類
① 流按其流向分為“輸入流”和“輸出流”。
② 流按數據傳輸單位分為“字節流”和“字符流”。
a) “字節流”用來讀寫8位二進制的字節。
b) “字符流”用來讀寫16位二進制字符。
③ 流按功能分為“節點流”和“過濾流”。
a) “節點流”用於直接操作目標設備的流。例如:磁盤或一塊內存區域。
b) “過濾流”是對一個已存在的流的鏈接和封裝,通過對數據進行處理為程序提供功能強大、靈活的讀寫功能。
字節流
字節流類用於向字節流讀取8位二進制的字節。一般地,字節流主要用於讀寫諸如圖像或聲音等二進制數據。
字節流類以InputStream和OutputStream為頂層類。它們都是抽象類。
一、InputStream
① InputStream是定義了字節輸入流的抽象類。
② InputStream中定義的方法
a) public abstract int read()
b) public int read(byte[] b)
c) public int read(byte[] b,int off,int len)
d) public long skip(long n)
e) public int available()
f) public void close()
二、OutputStream
① OutputStream是定義了字節輸出流的抽象類。
② 該類所有方法返回void值,在出錯情況下拋IOException異常
③ OutputStream中定義的方法
g) public abstract void write(int b)
h) public void write(byte[] b)
i) public void write(byte[] b,int off,int len)
j) public void flush()
k) public void close()
每個抽象類都有多個具體的子類,這些子類對不同的外設進行處理,例如磁盤文件,網絡連接,甚至是內存緩沖區。
三、FileInputStream
① FileInputStream類表示能從文件讀取字節的InputStream類
② 構造方法
a) FileInputStream(String filePath)
b) FileInputStream(File fileObj)
四、FileOutputStream
① FileOutputStream表示能向文件寫入字節的OutputStream類
② 構造方法
a) FileOutputStream(String filePath)
b) FileOutputStream(File fileObj)
c) FileOutputStream(String filePath,boolean append)
示例:拷貝指定文件到指定目錄
1 class FileCopyUtil { 2 public static void copyFile(File src, File dst) throws IOException { 3 FileInputStream fis = new FileInputStream(src); 4 FileOutputStream fos = new FileOutputStream(dst); 5 long time1 = System.currentTimeMillis(); 6 int data = -1; 7 while ((data = fis.read()) != -1) { 8 fos.write(data); 9 } 10 fis.close(); 11 fos.close(); 12 long time2 = System.currentTimeMillis(); 13 System.out.println("復制完成,共花費:" + (time2 - time1) + "毫秒"); 14 } 15 }
主方法:
1 try { 2 FileCopyUtil.copyFile(new File("d:/zhangsan/tu.png"), new File("e:/fuzhi.png")); 3 } catch (IOException e) { 4 e.printStackTrace(); 5 }
輸出結果
復制完成,共花費:1545毫秒
單一張71K的圖片就花費了1545毫秒,由此可見效率是極低的,因為它是讀一個寫一個,我們可以換種思路,先全部讀到一個緩沖區,再將內容一次性寫入。這里就好比喝水時一滴一滴地喝還是接到杯子里再喝。
改造下方法,如下:
1 class FileCopyUtil { 2 public static void copyFile(File src, File dst) throws IOException { 3 FileInputStream fis = new FileInputStream(src); 4 FileOutputStream fos = new FileOutputStream(dst); 5 byte[] buff = new byte[1024 * 1024];// 創建一個1M大小的緩沖區,用來存放輸入流中的字節
6 int len = 0;// 用來保存實際讀到的字節數
7 long time1 = System.currentTimeMillis(); 8 while ((len = fis.read(buff)) != -1) { 9 fos.write(buff, 0, len); 10 } 11 fis.close(); 12 fos.close(); 13 long time2 = System.currentTimeMillis(); 14 System.out.println("復制完成,共花費:" + (time2 - time1) + "毫秒"); 15 } 16 }
同樣讀取那張圖片,輸出結果:
復制完成,共花費:3毫秒
再復制一個8M的mp3格式文件,輸出結果:
復制完成,共花費:39毫秒
五、ByteArrayInputStream
①ByteArrayInputStream是把字節數組當成源的輸入流。
②兩個構造方法,每個都需要一個字節數組提供數據源:
a) ByteArrayInputStream(byte array[])
b) ByteArrayInputStream(byte array[],int start,int numBytes)
1 String str="hello,HangZhou"; 2 ByteArrayInputStream bis=new ByteArrayInputStream(str.getBytes()); 3 int data=-1; 4 while((data=bis.read())!=-1) 5 { 6 System.out.println((char)data); 7 }
輸出結果:
h e l l o , H a n g Z h o u
六、ByteArrayOutputStream
①ByteArrayOutputStream是把字節數組當作目標的輸出流。
②兩個構造方法:
a) ByteArrayOutputStream()
創建一個新的byte數組輸出流
b) ByteArrayOutputStream(int numBytes)
創建一個新的byte數組輸出流,具有指定大小緩沖區(字節為單位)
1 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 2 bos.write(97); 3 bos.write(65); 4 try { 5 bos.write("hello,word".getBytes()); 6 } catch (IOException e) { 7 e.printStackTrace(); 8 } 9 byte[] buff = bos.toByteArray(); 10 for (byte b : buff) { 11 System.out.print((char)data+" "); 12 } 13
14 FileOutputStream fos=new FileOutputStream("d://aa.txt", true); 15 bos.writeTo(fos);//把ByteArrayOutputStream內部緩沖區的數據寫到對應的文件輸出流中
16 fos.close();
輸出結果:
aAhello,word
並會在d盤下創建一個aa.txt文件,里面內容為輸出結果的內容,再次執行將追加一次執行結果的內容。
過濾流
① 過濾流僅僅是為底層透明地提供擴展功能的輸入流(輸出流)的包裝。這些流一般由普通類的方法(即過濾流的一個父類)訪問。
② 過濾字節流FilterInputStream和FilterOutputStream。構造方法:
a) FilterInputStream(InputStream is)
b) FilterOutputStream(OutputStream os)
③ 這些類提供的方法和InputStream及OutputStream類的方法相同。
④ 常用的過濾流BufferedInputStream和BufferedOutputStream,DataInputStream和DataOutputStream
BufferedInputStream,BufferedOutputStream用法舉例
使用過濾流來改寫上面的復制方法
1 class FileUtil { 2 public static void copyFile(File src, File dst) throws IOException { 3 FileInputStream fis = new FileInputStream(src); 4 FileOutputStream fos = new FileOutputStream(dst); 5 BufferedInputStream bis=new BufferedInputStream(fis); 6 BufferedOutputStream bos=new BufferedOutputStream(fos); 7
8 int data = 0;// 用來保存實際讀到的字節數
9 long time1 = System.currentTimeMillis(); 10 while ((data = bis.read()) != -1) { 11 bos.write(data); 12 } 13 bis.close(); 14 bos.close(); 15 long time2 = System.currentTimeMillis(); 16 System.out.println("復制完成,共花費:" + (time2 - time1) + "毫秒"); 17 } 18 }
主方法:
1 try { 2 FileUtil.copyFile(new File("d:/zhangsan/Closer To Me.mp3"), new File( 3 "e:/fuzhi.mp3")); 4 } catch (IOException e) { 5 e.printStackTrace(); 6 } 7 }
復制同樣的一首歌,輸出結果:
復制完成,共花費:905毫秒
時間增加了很多,但也比文件直接讀寫快了不少。Buffered中自帶一個8k左右的緩沖區。
DataInputStream和DataOutputStream用法舉例:
寫入文件:
1 String name="zhangsan"; 2 int age=10; 3 boolean flag=true; 4 char sex='男'; 5 double money=123.45; 6
7 DataOutputStream dos=new DataOutputStream(new FileOutputStream("d:/b.txt")); 8 dos.writeUTF(name); 9 dos.writeInt(age); 10 dos.writeBoolean(flag); 11 dos.writeChar(sex); 12 dos.writeDouble(money); 13 dos.close();
打開b.txt后發現有亂碼,那是因為寫進去的是二進制
讀文件:
1 DataInputStream dis=new DataInputStream(new FileInputStream("d:/b.txt")); 2 //讀的順序必須與寫入的順序一致
3 System.out.println(dis.readUTF()); 4 System.out.println(dis.readInt()); 5 System.out.println(dis.readBoolean()); 6 System.out.println(dis.readChar()); 7 System.out.println(dis.readDouble()); 8 dis.close();
輸出結果:
zhangsan
10
true
男
123.45
過濾流總結:
BufferedInputStream與BufferedOutputStream
需要使用已經存在的節點流來構造,提供緩沖的讀寫,提高了讀寫的效率。
DataInputStream與DataOutputStream
數據輸入輸出流允許應用程序讀寫基本Java數據類型。應用程序可以使用數據輸出流寫入,稍后由數據輸入流讀取。讀寫順序要保持一致。