Java 字節(jié)流操作

?????在java中我們使用輸入流來向一個(gè)字節(jié)序列對(duì)象中寫入仑撞,使用輸出流來向輸出其內(nèi)容。C語言中只使用一個(gè)File包處理一切文件操作妖滔,而在java中卻有著60多種流類型隧哮,構(gòu)成了整個(gè)流家族∽幔看似龐大的體系結(jié)構(gòu)沮翔,其實(shí)只要使用適合的方法將其分門別類,就顯得清晰明了了曲秉。而我準(zhǔn)備將其按照處理文件類型的不同采蚀,分為字節(jié)流類型和字符流類型。共兩篇文章承二,本篇從字節(jié)流開始榆鼠。主要包含以下內(nèi)容:

  • InputStream/OutPutStream - - -字節(jié)流基類
  • FileInputStream/FileOutputStream - - - - -處理文件類型
  • ByteArrayInputStream/ByteArrayOutputStream - - - -字節(jié)數(shù)組類型
  • DataInputStream/DataOutputStream - - - -裝飾類
  • BufferedInputStream/BufferedOutputStream - - - -緩沖流
    一、基類流
    ?????其實(shí)始終有人搞不清楚到底InputStream是讀還是OutputStream是讀亥鸠。其實(shí)很簡單就可以記住妆够,你把你自己想象為是一個(gè)程序,InputStream對(duì)你來說是輸入负蚊,也就是你要從某個(gè)地方讀到自己這來神妹,而OutputStream對(duì)你來說就是輸出,也就是說你需要寫到某個(gè)地方盖桥。這樣就可以簡單的區(qū)分輸入輸出流灾螃。下面看看InputStream的成員方法:
public abstract int read() throws IOException;

public int read(byte b[]) throws IOException
 
public int read(byte b[], int off, int len)

public long skip(long n) throws IOException

public int available() throws IOException

public void close() throws IOException

public synchronized void mark(int readlimit)
public synchronized void reset() throws IOException
public boolean markSupported()

?????InputStream是一個(gè)輸入流,也就是用來讀取文件的流揩徊,抽象方法read讀取下一個(gè)字節(jié)腰鬼,當(dāng)讀取到文件的末尾時(shí)候返回 -1。如果流中沒有數(shù)據(jù)read就會(huì)阻塞直至數(shù)據(jù)到來或者異常出現(xiàn)或者流關(guān)閉塑荒。這是一個(gè)受查異常熄赡,具體的調(diào)用者必須處理異常。除了一次讀取一個(gè)字節(jié)齿税,InputStream中還提供了read(byte[])彼硫,讀取多個(gè)字節(jié)。read(byte[])其實(shí)默認(rèn)調(diào)用的還是read(byte b[], int off, int len)方法凌箕,表示每讀取一個(gè)字節(jié)就放在b[off++]中拧篮,總共讀取len個(gè)字節(jié),但是往往會(huì)出現(xiàn)流中字節(jié)數(shù)小于len牵舱,所以返回的是實(shí)際讀取到的字節(jié)數(shù)串绩。
?????接下來是一些高級(jí)的用法,skip方法表示跳過指定的字節(jié)數(shù)芜壁,來讀取礁凡。調(diào)用這種方法需要知道,一旦跳過就不能返回到原來的位置慧妄。當(dāng)然顷牌,我們可以看到還有剩下的三種方法,他們一起合作實(shí)現(xiàn)了可重復(fù)讀的操作塞淹。mark方法在指定的位置打上標(biāo)記窟蓝,reset方法可以重新回到之前的標(biāo)記索引處。但是我們可以想到饱普,它一定是在打下mark標(biāo)記的地方运挫,使用字節(jié)數(shù)組記錄下接下來的路徑上的所有字節(jié)數(shù)據(jù),直到你使用了reset方法费彼,取出字節(jié)數(shù)組中的數(shù)據(jù)供你讀然(實(shí)際上也不是一種能夠重復(fù)讀,只是用字節(jié)數(shù)組記錄下這一路上的數(shù)據(jù)而已箍铲,等到你想要回去的時(shí)候?qū)⒆止?jié)數(shù)組給你重新讀裙途怼)。
?????OutputStream是一種輸出流颠猴,具體的方法和InputStream差不多关划,只是,一個(gè)讀一個(gè)寫翘瓮。但是贮折,他們都是抽象類,想要實(shí)現(xiàn)具體的功能還是需要依賴他們的子類來實(shí)現(xiàn)资盅,例如:FileInputStream/FileOutputStream等调榄。
二踊赠、文件字節(jié)流
?????FileInputStream繼承與InputStream,主要有以下兩個(gè)構(gòu)造方法:

 public FileInputStream(String name)
 
 public FileInputStream(File file)

?????第一種構(gòu)造方法傳的是一個(gè)字符串每庆,實(shí)際上是一個(gè)確定文件的路徑筐带,內(nèi)部將此路徑封裝成File類型,調(diào)用第二種構(gòu)造方法缤灵。第二中構(gòu)造方法伦籍,直接綁定的是一個(gè)具體的文件。
?????FileInputStream 的內(nèi)部方法其實(shí)和父類InputStream中定義的方法差不多腮出,我們通過一個(gè)讀文件的實(shí)例來演示用法帖鸦。

public class Test_InputOrOutput {
    public static void main(String[] args) throws IOException{
            FileInputStream fin = new FileInputStream("hello.txt");
            byte[] buffer = new byte[1024];
            int x = fin.read(buffer,0,buffer.length);
            String str = new String(buffer);
            System.out.println(str);
            System.out.println(x);
            fin.close();
    }
}
輸出結(jié)果:
hello world
13

?????結(jié)果意料之中,調(diào)用了read方法將hello.txt中的內(nèi)容讀到字節(jié)數(shù)組buffer中胚嘲,然后通過String類構(gòu)造方法將字節(jié)數(shù)組轉(zhuǎn)換成字符串作儿。返回實(shí)際上讀取到的字節(jié)數(shù)13。(10個(gè)字母+兩個(gè)空格+一個(gè)字符串結(jié)束符)
?????FileOutputStream繼承父類OutputStream慢逾,主要方法代碼如下:

private final boolean append;

public FileOutputStream(String name)
public FileOutputStream(String name, boolean append)
public FileOutputStream(File file)
public FileOutputStream(File file, boolean append)

private native void writeBytes(byte b[], int off, int len, boolean append)
public void write(byte b[]) throws IOException

?????FileOutputStream的一些基本的操作和FileInputStream類似立倍,只是一個(gè)是讀一個(gè)是寫。我們主要要知道侣滩,append屬性是指定對(duì)于文件的操作是覆蓋方式(false)口注,還是追加方式(true)。下面通過一個(gè)實(shí)例演示其用法:

public class Test_InputOrOutput {
    public static void main(String[] args) throws IOException{
        FileOutputStream fou = new FileOutputStream("hello.txt");
        String str = "Walker_YAM";
        byte[] buffer = str.getBytes("UTF-8");
        fou.write(buffer,0 ,buffer.length);
        fou.close();
    }
}

?????如我們所料君珠,字符串"Walker_YAM"將會(huì)被寫入hello.txt寝志,由于沒有指定append,所以將會(huì)覆蓋hello.txt中的所有內(nèi)容策添。
三材部、動(dòng)態(tài)字節(jié)數(shù)組流
?????在我們上述的文件讀取流中,我們定義 byte[] buffer = new byte[1024];唯竹,buffer數(shù)組為1024乐导,如果我們將要讀取的文件中的內(nèi)容有1025個(gè)字節(jié),buffer是不是裝不下浸颓?當(dāng)然我們也可以定義更大的數(shù)組容量物臂,但是從內(nèi)存的使用效率上,這是低效的产上。我們可以使用動(dòng)態(tài)的字節(jié)數(shù)組流來提高效率棵磷。
?????ByteArrayInputStream的內(nèi)部使用了類似于ArrayList的動(dòng)態(tài)數(shù)組擴(kuò)容的思想。

protected byte buf[];
protected int count;

public ByteArrayInputStream(byte buf[])
public ByteArrayInputStream(byte buf[], int offset, int length)
public synchronized int read()
public synchronized int read(byte b[], int off, int len)

?????ByteArrayInputStream內(nèi)部定義了一個(gè)buf數(shù)組和記錄數(shù)組中實(shí)際的字節(jié)數(shù)晋涣,read方法也很簡單仪媒,讀取下一個(gè)字節(jié),read(byte b[], int off, int len) 將內(nèi)置字節(jié)數(shù)組讀入目標(biāo)數(shù)組谢鹊。實(shí)際上算吩,整個(gè)ByteArrayInputStream也就是將一個(gè)字節(jié)數(shù)組封裝在其內(nèi)部留凭。為什么這么做?主要還是為了方便參與整個(gè)InputStream的體系赌莺,復(fù)用代碼冰抢。
?????ByteArrayOutputStream的作用要比ByteArrayInputStream更加的實(shí)際一點(diǎn):

protected byte buf[];
protected int count;

public ByteArrayOutputStream() {
        this(32);
    }
public ByteArrayOutputStream(int size)
private void ensureCapacity(int minCapacity)
public synchronized void write(byte b[], int off, int len)
public synchronized void writeTo(OutputStream out)
public synchronized byte toByteArray()[]
public synchronized String toString()

?????和ByteArrayInputStream一樣松嘶,內(nèi)部依然封裝了字節(jié)數(shù)組buf和實(shí)際容量count艘狭,通過構(gòu)造方法可以指定內(nèi)置字節(jié)數(shù)組的長度。主要的是write方法翠订,將外部傳入的字節(jié)數(shù)組寫到內(nèi)置數(shù)組中巢音,writeTo方法可以理解為將自己內(nèi)置的數(shù)組交給OutputStream 的其他子類使用。toByteArray和toString則會(huì)將內(nèi)置數(shù)組轉(zhuǎn)換成指定類型返回尽超。
?????下面我們利用他們來解決剛開始說的效率問題官撼。

public class Test_InputOrOutput {
    public static void main(String[] args) throws IOException{
        FileInputStream fin = new FileInputStream("hello.txt");
        ByteArrayOutputStream bou = new ByteArrayOutputStream();
        int x = 0;
        while((x = fin.read()) !=-1){
                bou.write(x);
        }
        System.out.println(bou.toString());
    }
}

?????從hello文件中每讀取一個(gè)字節(jié)寫入ByteArrayOutputStream 中,我們不用擔(dān)心hello文件太大而需要設(shè)置較大的數(shù)組似谁,使用ByteArrayOutputStream 動(dòng)態(tài)增加容量傲绣,如果添加字節(jié)即將超過容量上限,進(jìn)行擴(kuò)充(往往是指數(shù)級(jí)擴(kuò)充)
四巩踏、裝飾者字節(jié)流
?????上述的流都是直接通過操作字節(jié)數(shù)組來實(shí)現(xiàn)輸入輸出的秃诵,那如果我們想要輸入一個(gè)字符串類型或者int型或者double類型,那還需要調(diào)用各自的轉(zhuǎn)字節(jié)數(shù)組的方法塞琼,然后將字節(jié)數(shù)組輸入到流中菠净。我們可以使用裝飾流,幫我們完成轉(zhuǎn)換的操作彪杉。我們先看DataOutputStream毅往。

 public DataOutputStream(OutputStream out)
 public synchronized void write(byte b[], int off, int len)
 public final void writeBoolean(boolean v)
 public final void writeByte(int v)
 public final void writeShort(int v)
 public final void writeInt(int v) 
 public final void writeDouble(double v)

?????簡單的列舉了一些方法,可以看到派近,DataOutputStream只有一個(gè)構(gòu)造方法耍攘,必須傳入一個(gè)OutputStream類型參數(shù)。(其實(shí)它的內(nèi)部還是圍繞著OutputStream尸折,只是在它的基礎(chǔ)上做了些封裝)熬芜。我們看到,有writeBoolean曙强、writeByte残拐、writeShort、writeDouble等方法碟嘴。他們內(nèi)部都是將傳入的 boolean溪食,Byte,short娜扇,double類型變量轉(zhuǎn)換為了字節(jié)數(shù)組错沃,然后調(diào)用從構(gòu)造方法中接入的OutputStream參數(shù)的write方法栅组。

//這是writeInt的具體實(shí)現(xiàn)
    public final void writeInt(int v) throws IOException {
        out.write((v >>> 24) & 0xFF);
        out.write((v >>> 16) & 0xFF);
        out.write((v >>>  8) & 0xFF);
        out.write((v >>>  0) & 0xFF);
        incCount(4);
    }

?????將一個(gè)四個(gè)字節(jié)的int類型,分開寫入枢析,先寫入高八位玉掸。總共寫四次醒叁,第一次將高八位移動(dòng)到低八位與上0xFF獲得整個(gè)int的低八位司浪,這樣就完成了將原高八位寫入的操作,后續(xù)操作類似把沼。

public class Test_InputOrOutput {
    public static void main(String[] args) throws IOException{
        DataOutputStream da = new DataOutputStream(new FileOutputStream("hello.txt"));
        da.writeInt(11);
        da.close();
    }
}
這里寫圖片描述

????? 使用UltraEditor打開hello文件啊易,可以看到11這個(gè)int型數(shù)值被存入文件中。DataInputStream完成的就是讀取的操作饮睬,基本和DataOutputStream 的操作是類似的租谈,是一個(gè)逆操作。
五捆愁、緩沖流
?????在這之前割去,我們讀取一個(gè)字節(jié)就要將它寫會(huì)磁盤,這樣來回開銷很大昼丑,我們可以使用緩沖區(qū)來提高效率呻逆,在緩沖區(qū)滿的時(shí)候,或者流關(guān)閉時(shí)候矾克,將緩沖區(qū)中所有的內(nèi)容全部寫會(huì)磁盤页慷。BufferedInputStream和BufferedOutputStream也是一對(duì)裝飾流,我們先看看BufferedInputStream:

private static int DEFAULT_BUFFER_SIZE = 8192;
protected volatile byte buf[];
protected int pos;
protected int count;
public BufferedInputStream(InputStream in)
public BufferedInputStream(InputStream in, int size)
public synchronized int read()
public synchronized void mark(int readlimit)
public synchronized void reset()

?????一樣也是裝飾類流胁附,第一種構(gòu)造方法要求必須傳入InputStream類型參數(shù)酒繁,DEFAULT_BUFFER_SIZE 指定了默認(rèn)的緩沖區(qū)的大小,當(dāng)然還可以使用第二種構(gòu)造方法指定緩沖區(qū)的大锌仄蕖(當(dāng)然不能超過上界)州袒,read方法讀取的時(shí)候會(huì)將數(shù)據(jù)讀入內(nèi)部的緩沖區(qū)中,當(dāng)然緩沖區(qū)也是可以動(dòng)態(tài)擴(kuò)容的弓候。

public class Test_InputOrOutput {
    public static void main(String[] args) throws IOException{
        BufferedInputStream bi = new BufferedInputStream(new FileInputStream("hello.txt"));
        bi.read();
        bi.read();
        bi.read();
        bi.read();
        System.out.println(bi.available());
    }
}

?????BufferedOutputStream和它是逆操作郎哭,不在贅述。這種緩沖字節(jié)流可以很大程度上提高我們的程序執(zhí)行的效率菇存,所以一般在使用別的流的時(shí)候都會(huì)包裝上這層緩沖流夸研。
?????最后,本文如有錯(cuò)誤指出依鸥,望大家指出亥至!下一篇會(huì)寫字符流。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市姐扮,隨后出現(xiàn)的幾起案子絮供,更是在濱河造成了極大的恐慌,老刑警劉巖茶敏,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件壤靶,死亡現(xiàn)場離奇詭異,居然都是意外死亡惊搏,警方通過查閱死者的電腦和手機(jī)贮乳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來胀屿,“玉大人塘揣,你說我怎么就攤上這事∷拚福” “怎么了?”我有些...
    開封第一講書人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵才写,是天一觀的道長葡兑。 經(jīng)常有香客問我,道長赞草,這世上最難降的妖魔是什么讹堤? 我笑而不...
    開封第一講書人閱讀 58,893評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮厨疙,結(jié)果婚禮上洲守,老公的妹妹穿的比我還像新娘。我一直安慰自己沾凄,他們只是感情好梗醇,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著撒蟀,像睡著了一般叙谨。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上保屯,一...
    開封第一講書人閱讀 51,708評(píng)論 1 305
  • 那天手负,我揣著相機(jī)與錄音,去河邊找鬼姑尺。 笑死竟终,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的切蟋。 我是一名探鬼主播统捶,決...
    沈念sama閱讀 40,430評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了瘾境?” 一聲冷哼從身側(cè)響起歧杏,我...
    開封第一講書人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎迷守,沒想到半個(gè)月后犬绒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡兑凿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評(píng)論 3 337
  • 正文 我和宋清朗相戀三年凯力,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片礼华。...
    茶點(diǎn)故事閱讀 40,115評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡咐鹤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出圣絮,到底是詐尸還是另有隱情祈惶,我是刑警寧澤,帶...
    沈念sama閱讀 35,804評(píng)論 5 346
  • 正文 年R本政府宣布扮匠,位于F島的核電站捧请,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏棒搜。R本人自食惡果不足惜疹蛉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望力麸。 院中可真熱鬧可款,春花似錦、人聲如沸克蚂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽陨舱。三九已至翠拣,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間游盲,已是汗流浹背误墓。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留益缎,地道東北人谜慌。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像莺奔,于是被迫代替她去往敵國和親欣范。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容