/*
@author StormWangxhu
@date 2017/11/1
*/
昨天我們總結了字元的輸入流,主要進行讀寫的功能。今天來看一下位元組流InputStream和OutputStream.
位元組流基本架構體系圖
整體體系:
InputStream:
基本概念
和字元流一樣,位元組流也有兩個抽象基類作為其他類的父類。一個是InputStream,一個是OutPutStream.其他的類都是這兩個類的拓展類。
(1)特點:
位元組流可以操作任何資料。
(2)注意:
位元組流使用的是位元組數組 byte[]
字元流使用得是字元數組 char[]
-
inputStream
(1)、inputStream是所有位元組輸入流的父類,是一個抽象類。
(2)ByteArrayInputStream、StringBufferInputStream、FileInputStream 是三種基本的媒體流,它們分别從Byte 數組、StringBuffer、和本地檔案中讀取資料。
(3)ObjectInputStream 和所有FilterInputStream 的子類都是裝飾流(裝飾器模式的主角)。
-
outputStream
(1)、OutputStream 是所有的輸出位元組流的父類,它是一個抽象類。
(2)、B
yteArrayOutputStream、FileOutputStream 是兩種基本的媒體流,它們分别向Byte 數組、和本地檔案中寫入資料。PipedOutputStream 是向與其它線程共用的管道中寫入資料。
(3)、ObjectOutputStream 和所有FilterOutputStream 的子類都是裝飾流。
- 其他
(1)、LineNumberInputStream 主要完成從流中讀取資料時,會得到相應的行号,至于什麼時候分行、在哪裡分行是由改類主動确定的,并不是在原始中有這樣一個行号。在輸出部分沒有對應的部 分,我們完全可以自己建立一個LineNumberOutputStream,在最初寫入時會有一個基準的行号,以後每次遇到換行時會在下一行添加一個行 号,看起來也是可以的。好像更不入流了。
(2)、PushbackInputStream 的功能是檢視最後一個位元組,不滿意就放入緩沖區。主要用在編譯器的文法、詞法分析部分。輸出部分的BufferedOutputStream 幾乎實作相近的功能。
StringBufferInputStream 已經被Deprecated,本身就不應該出現在InputStream 部分,主要因為String 應該屬于字元流的範圍。已經被廢棄了,當然輸出部分也沒有必要需要它了!還允許它存在隻是為了保持版本的向下相容而已。
(3)、SequenceInputStream 可以認為是一個工具類,将兩個或者多個輸入流當成一個輸入流依次讀取。完全可以從IO 包中去除,還完全不影響IO 包的結構,卻讓其更“純潔”――純潔的Decorator 模式。
(4)、PrintStream 也可以認為是一個輔助工具。主要可以向其他輸出流,或者FileInputStream 寫入資料,本身内部實作還是帶緩沖的。本質上是對其它流的綜合運用的一個工具而已。一樣可以踢出IO 包!System.out 和System.out 就是PrintStream 的執行個體!
下面我們通過案例來分别講解一下這些位元組流的使用。
首先講解一下FileInputStream:
案例一:利用位元組流讀取或寫入文本内容到控制台或者文本檔案中
package com.StormWang.InputStream;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
/**
* @author StormWangxhu
* @version 建立時間:2017年11月1日 上午11:19:03
*
*/
class FileInStream{
//讀檔案,一個一個的讀,讀一個存一個
public static void readFile_1(){
//先寫一個檔案
FileWriter fileWriter = null ;
FileInputStream fileInputStream = null ;
try {
fileWriter = new FileWriter("input.txt");
fileWriter.write("FileInputStream,success.");
fileWriter.flush();
fileInputStream = new FileInputStream("input.txt");
int ch = ;
while ((ch= fileInputStream.read())!=-) {
System.out.print((char)ch);
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}finally {
if (fileInputStream != null) {
try {
fileInputStream.close();
} catch (Exception e2) {
// TODO: handle exception
e2.printStackTrace();
}
}
if (fileWriter != null) {
try {
fileWriter.close();
} catch (Exception e2) {
// TODO: handle exception
e2.printStackTrace();
}
}
}
}
/*
//讀方法二(讀完之後,一起存起來。)
public static void readFile_2() {
FileInputStream fileInputStream = null ;
try {
fileInputStream = new FileInputStream("E:\\eclipse-workspace\\day1101\\input.txt");
byte[] buf = new byte[1024];
int len = 0 ;
while ((len = fileInputStream.read()) != -1) {
System.out.println("len是:"+len);
//System.out.print(new String(buf,0,len));
System.out.println(buf);
}
System.out.println("buf數組内容:"+buf);
} catch (IOException e) {
e.printStackTrace();
}finally {
if (fileInputStream != null) {
try {
fileInputStream.close();
} catch (Exception e2) {
// TODO: handle exception
System.out.println("close:"+e2.toString());
}
}
}
}*/
}
public class FileInputStreamDemo {
public static void main(String[] args) {
FileInStream fileInStream = new FileInStream() ;
fileInStream.readFile_1();
}
}
我們來看一下結果:
再來看一個執行個體:實作對一個圖檔檔案的複制
案例二:
package com.StormWang.InputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* @author StormWangxhu
* @version 建立時間:2017年11月1日 下午4:53:32
*
*/
public class CopyPicture {
/*
* 實作功能: 實作圖檔檔案的複制
* 思路:
* 1、位元組輸入流和圖檔檔案相關聯
* 2、再用位元組輸入流将圖檔檔案資料寫入到緩沖區中,即位元組數組中
* 3、通過循環讀寫,完成資料的存儲
* 4、關閉流資源。
* */
public static void main(String[] args) {
// TODO Auto-generated method stub
FileInputStream fileInputStream = null;
FileOutputStream fileOutputStream =null ;
try {
fileInputStream = new FileInputStream("F:\\2016\\1.jpg");
//測試代碼
System.out.println("已過!");
fileOutputStream = new FileOutputStream("F:\\2.JPG");
byte[] buff = new byte[];
int ch = ;
//此處的read()方法可能會抛出IOException異常,我們catch一下。
while ((ch= fileInputStream.read(buff)) !=-) {
/*
*write方法說明:
*write(byte[] b, int off, int len)
* 将指定 byte 數組中從偏移量 off 開始的 len 個位元組寫入此檔案輸出流。
*
* */
fileOutputStream.write(buff, , ch);
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
if (fileInputStream != null) {
try {
fileInputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (fileOutputStream != null) {
try {
fileOutputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
問題:在輸入流和圖檔檔案關聯時,系統找不到指定檔案。
F盤檔案如下所示,不知道為什麼會找不到檔案呢?
待解決:。。。。。。。
自定義緩沖位元組流
package com.StormWang.InputStream;
/**
* @author StormWangxhu
* @version 建立時間:2017年11月1日 下午6:44:40
*
*/
import java.io.IOException;
import java.io.InputStream;
public class MyBuffereedInputStream {
private InputStream iStream ;
private byte[] buff = new byte[];
private int pos = ,count = ;
public MyBuffereedInputStream(InputStream iStream) {
// TODO Auto-generated constructor stub
this.iStream = iStream ;
}
public int myRead() throws IOException{
//通過iStream讀取硬碟中的資料,存儲到buff
//如果count=0,說明byte數組是空的,是以開始讀取資料
if (count == ) {
count = iStream.read(buff);
//标記位
pos = ;
//通過下标找到數組
byte bt = buff[pos];
//byte數組個數遞減
count--;
//移動标記位
pos++;
return bt&;
}
//如果位元組數組中還有資料,則繼續往出取
else if (count>) {
byte bt = buff[pos];
count--;
pos++;
return bt & ;
}
return - ;
}
//關閉流方法
public void myClose() {
try {
iStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
這樣就完成了自定義位元組流的定義。下面我們來應用一下:
package com.StormWang.InputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
/**
* @author StormWangxhu
* @version 建立時間:2017年11月1日 下午7:08:36
*
*/
public class myBufferedInputStreamTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
FileInputStream fInStream = null;
MyBuffereedInputStream myBuffereedInputStream = null ;
BufferedOutputStream bufferedOutputStream = null;
try {
fInStream = new FileInputStream("E:\\eclipse-workspace\\day1101\\src\\com\\StormWang\\InputStream\\CopyPicture.java");
myBuffereedInputStream = new MyBuffereedInputStream(fInStream);
bufferedOutputStream = new BufferedOutputStream(new FileOutputStream("myBuff.txt"));
int ch = ;
while ((ch=myBuffereedInputStream.myRead())!= -) {
bufferedOutputStream.write(ch);
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
if (myBuffereedInputStream!= null) {
myBuffereedInputStream.myClose();
}
if (bufferedOutputStream != null) {
try {
bufferedOutputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (fInStream != null) {
try {
fInStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
我們來看一下結果:
讀寫成功!
(待回頭再思考。。。。。。)
**注意:**
位元組流的read()方法讀取一個位元組。為什麼傳回的不是byte類型的,而是int 類型的呢?
因為read方法讀到末尾時傳回的是-.而在所操作的資料中的很容易出現連續多個的情況,而連續讀到個,就是-.導緻讀取會提前停止。
是以将讀到的一個位元組給提升為一個int類型的數值,但是隻保留原位元組,并在剩餘二進制位補.
具體操作是:byte& or byte&
對于write方法,可以一次寫入一個位元組,但接收的是一個int類型數值。隻寫入該int類型的數值的最低一個位元組(位)。即對于write()方法實際上進行了強制轉化的動作。
簡單說:read方法對讀到的資料進行提升。write對操作的資料進行轉換。**
轉換流
-
特點:
1,是位元組流和字元流之間的橋梁。
2,該流對象中可以對讀取到的位元組資料進行指定編碼表的編碼轉換。
-
什麼時候使用呢?
1,當位元組和字元之間有轉換動作時。
2,流操作的資料需要進行編碼表的指定時。
具體的對象展現:這兩個流對象是字元流體系中的成員。
1,InputStreamReader:位元組到字元的橋梁。
2,OutputStreamWriter:字元到位元組的橋梁。
這兩個流對象是字元流體系中的成員。那麼它們有轉換作用,而本身又是字元流。是以在構造的時候,需要傳入位元組流對象進來。
構造函數:
InputStreamReader(InputStream):通過該構造函數初始化,使用的是本系統預設的編碼表GBK。
InputStreamReader(InputStream,StringcharSet):通過該構造函數初始化,可以指定編碼表。
OutputStreamWriter(OutputStream):通過該構造函數初始化,使用的是本系統預設的編碼表GBK。
OutputStreamWriter(OutputStream,StringcharSet):通過該構造函數初始化,可以指定編碼表。
我們來看看API是怎麼解釋的:
我們通過代碼來示範一下:
小技巧:
* 源:鍵盤錄入
* 目的:控制台
* @author StormWangxhu
* 改變需求:想把鍵盤錄入的資料存儲到一個檔案中
* 源:鍵盤
* 目的:檔案
*
* 改變需求:想要将一個檔案資料列印到控制台上
* 源:檔案
* 目的:控制台
*
* ######################流操作的基本規律:##############
* 最痛苦的就是流對象有很多,不知道該用哪一個?
* 通過三個明确來完成:
* 明确源和目的
* 源:輸入流. InputStream Reader
* 目的:輸出流. OutputStream Writer
* 明确操作的資料是否是是純文字
* 是純文字:用字元流
* 不是純文字:用位元組流
* 當體系明确後,再明确要使用哪個具體的對象
* 通過裝置來進行區分:
* 源裝置:記憶體、硬碟、鍵盤
* 目的裝置:記憶體、硬碟(檔案)、控制台
* ----------------------個需求之一-----------------------
* 将一個文本檔案中的資料存儲到另一個檔案中 ,也就是複制檔案
* 源:因為是源,是以使用讀取流:InputStream Reader
* a.是不是操作文本檔案?
* 是:這就可以選擇Reader
* 不是:可以選擇InputStream
* 這樣下來,所屬體系就明确了。
*
* b.接下來就要明确要使用體系中的哪個對象?
* 明确裝置:硬碟上的一個檔案
* Reader體系中可以操作檔案的對象是FileReader
* c.是否需要提高效率?
* 需要:加入Reader體系的緩區:BufferedReader
*
* 接下來就是:
* ) FileReader fr =new FileReader("a.txt");
) BufferedReader br = new BufferedReader(fr);
* 目的:檔案 OutputStream Writer
* a.目的是否是純文字的?
* 是:Writer
* 不是:OutputStream
* b.明确裝置:硬碟上的一個檔案
* Writer體系中可以操作一個檔案的對象是FileWriter
*
* c.是否需要提高效率?
* 需要:加入Writer體系的緩沖區:BufferedWriter
*
* 接下來就是:
* ) FileWriter fw = new FileWriter("b.txt");
) BufferedWriter bw = new BufferedWriter(fw);
*
* 練習: 将一個圖檔檔案中的資料存儲到另一個圖檔中 ,即為copy圖檔,要按照以上的格式去寫
* 源(是圖檔):因為是源, 是以使用讀取流:InputStream Reader
* a.是不是操作的純文字檔案?
* 不是:這就可以選擇InputStream
* 是:這就可以選擇 Reader
* b.接下來就要明确要使用體系中的哪個對象?
* 明确裝置:硬碟上的一個圖檔
* InputStream體系中可以操作圖檔的對象時FileInputStream
* c.是否需要提高效率?
*
* 需要:加入InputStream體系的緩沖區:BufferedInputStream
*
* 接下來就是:
* FileInputStream fis = new FileInputStream("1.jpg"); BufferedInputStream bis = new BufferedInputStream(fis);
* 目的:檔案 OutputStream Writer
* a.是否是純文字的檔案?
* 不是:OutputStream
* 是:writer
* b.明确裝置:硬碟上的一個圖檔
*
* OutputStream體系中可以操作一個檔案的對FileOutputStream
* c.是否需要提高效率?
*
* 需要:加入OutputStream體系的緩沖區:BufferedOutputStream
*
FileOutputStream fos = new FileOutputStream("2.jpg");
*
* BufferedOutputStream bos = BufferedOutputStream(fos);
*
我們來實作一個功能,把一個 檔案的内容 輸出到 控制台 上
package com.StormWang.InputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
/**
* @author StormWangxhu
* @version 建立時間:2017年11月1日 下午8:04:29
*
*/
/*
* 實作将一個檔案中的内容輸出到控制台上
* */
public class FileStreamDemo_2 {
public static void main(String[] args) {
// TODO Auto-generated method stub
//建立讀取和寫入對象
BufferedReader bReader = null ;
BufferedWriter bWriter = null ;
try {
/**
* FileinputStream fs = new FileInutStream("E:\\eclipse-workspace\\day1101\\myBuff.txt");
* InputStreamReader isr = new InputStreamReader(fs);
* BufferedReader br = new BufferedReader(isr);
* 以上三句合并為一句如下:
*/
bReader = new BufferedReader(
new InputStreamReader(//将位元組流轉換為字元流。
new FileInputStream("E:\\eclipse-workspace\\day1101\\myBuff.txt")));
bWriter = new BufferedWriter(
//将字元流轉化為位元組流
//并輸出到控制台上
new OutputStreamWriter(System.out));
//循環讀取
String lines = null ;
/**
* readLine()方法的原理:
* 其實緩沖區中的readLine()方法,用的還是與緩沖區關聯的流對象的read()方法 。
* 隻不過,每一次讀到一個字元,先不進行具體的操作,先進行臨時存儲。
* 當讀到回車标記時,将臨時容器中存儲的資料一次性的傳回。
*
* */
while ((lines = bReader.readLine())!= null) {
//讀一行,寫一行,把每一行都寫到輸出流中
bWriter.write(lines);
//寫一行後換一行
//newLine()方法為跨平台換行。。
bWriter.newLine();
//寫一行重新整理一行
bWriter.flush();
}
} catch (FileNotFoundException e) {
System.out.println("找不到檔案了!");
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
if (bReader!= null) {
try {
bReader.close();
}catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (bWriter!= null) {
try {
bWriter.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
}
看一下結果:
輸出成功!
好了,就先總結到這兒吧。如有錯誤,還望指正!