Java-io學(xué)習(xí)總結(jié):輸出

Output輸出

在程序或者內(nèi)存中處理完的數(shù)據(jù)足丢,大多時候都要輸出橡娄,或者重定向輸出到其他地方,可以是另外一個線程輸入厘唾,緩沖池,文件等等鹃操。大多數(shù)情況下程序或者業(yè)務(wù)都有這樣的需求韭寸,輸出同樣也是占用計算機(jī)資源的一種處理過程。那么就看看java中對字符和字節(jié)的輸出是如何進(jìn)行處理的呢荆隘?其實(shí)多數(shù)輸出類都可以與上面描述的輸入類進(jìn)行類比恩伺,非常相似,只是將輸入變成輸出而已椰拒。

OutputStream字節(jié)輸入流

就如同InptuStream是流向程序的字節(jié)流般晶渠,OutputStream則是從程序或者內(nèi)存中流向外部資源文件,網(wǎng)絡(luò)燃观,數(shù)據(jù)庫等等的字節(jié)流褒脯。通過IPO這個流程,就能將一端輸入源中的字節(jié)流動到程序中進(jìn)行處理缆毁,然后再將處理后的字節(jié)流又寫入其他輸出源番川。數(shù)據(jù)從哪里來,到哪里去脊框,這個需要清楚知道颁督。下面通過字節(jié)輸出流的類圖來大致看看有那些子類和那些應(yīng)用場景:


outputStream.png

從圖中的基類OutputStream可以看到,作為一個字節(jié)輸出流浇雹,具有那些基本的功能特性:

public abstract void write(int b) ;//將一個字節(jié)寫入到輸出流中
public void write(byte b[]) 沉御;//將b字節(jié)數(shù)組中數(shù)據(jù)寫入到字節(jié)輸出流中
public void write(byte b[], int off, int len);//將b中指定長度字節(jié)寫入到輸出流中
public void flush();//刷新輸出緩沖區(qū)
public void close();//關(guān)閉輸出流

可以看到昭灵,作為字節(jié)輸出流吠裆,我們可以通過write方法將需要的字節(jié)數(shù)據(jù),寫入到字節(jié)輸出流中烂完,那么當(dāng)我們調(diào)用close或者強(qiáng)制使用flush刷新緩沖區(qū)的時候试疙,多數(shù)情況下,就可以將字節(jié)輸出流輸出到磁盤源資源文件中窜护。在輸出流中,我們需要注意的flush機(jī)制非春,這個是與輸入流很大的不同柱徙。而像輸入流中的針對文件,線程奇昙,使用緩沖區(qū)等使用場景都有對應(yīng)的操作類护侮,而flush是針對輸出流的概念。

看看文件字節(jié)輸出流的基本使用:使用FileOutputStream連接輸出文件資源句柄储耐,然后將字節(jié)數(shù)據(jù)寫入buf字節(jié)輸出羊初,再將這個字節(jié)數(shù)組中數(shù)據(jù)寫入文件輸出流中,當(dāng)調(diào)用write方法后,就會將字節(jié)輸出流輸出到文件中长赞。當(dāng)雙擊打開文件晦攒,會對這些字節(jié)進(jìn)行解碼,得到寫入的原始字符串得哆。

    @Test
    public void outputStreamTest(){
        FileOutputStream fos;
        try {
            //設(shè)置字節(jié)流輸出源文件,false代表不追加寫入文件脯颜,全部重新寫入
            fos = new FileOutputStream("C:/Users/XianSky/Desktop/output.txt",false);
            String str = "hello";
            byte[] buf = str.getBytes();//要寫入文件的字節(jié)數(shù)據(jù)
            System.out.println(System.getProperty("file.encoding"));//UTF-8
            System.out.println(Charset.defaultCharset().name());//UTF-8
            System.out.println("寫入字節(jié)數(shù):>>"+buf.length);//5個字節(jié),每個英文對應(yīng)一個字節(jié)
            fos.write(buf, 0, buf.length);//字節(jié)流根據(jù)系統(tǒng)編碼后贩据,觸發(fā)真正的磁盤寫入
            System.out.println();
//          fos.flush();
            fos.close();
            
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

通過源代碼栋操,大致看看FileOutputStream是如何將字節(jié)數(shù)據(jù)輸出到文件中的:

//FileOutputStream.java
 /*
 * 通過該文件字節(jié)輸出流構(gòu)造器中的初始化配置可以知道,
 * 將文件描述句柄FileDescriptor初始化完成饱亮,并檢查文件數(shù)據(jù)寫入方式矾芙,是否是追加寫入。
 * 然后對文件資源的多線程加上鎖近上,防止多個線程同時對該文件進(jìn)行讀寫操作剔宪。
 * 最后,通過native本地方法open來進(jìn)行與操作系統(tǒng)交互戈锻,打開指定的文件資源歼跟,觸發(fā)真正的磁盤寫入。
 */   
 public FileOutputStream(File file, boolean append)
        throws FileNotFoundException
    {
        String name = (file != null ? file.getPath() : null);
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkWrite(name);
        }
        if (name == null) {
            throw new NullPointerException();
        }
        if (file.isInvalid()) {
            throw new FileNotFoundException("Invalid file path");
        }
        this.fd = new FileDescriptor();
        this.append = append;
        this.path = name;
        fd.incrementAndGetUseCount();
        open(name, append);//native方法
    }

    /*
    * 在上面通過open與操作系統(tǒng)交互連接文件操作句柄后格遭,
    * 就能通過內(nèi)地方法writeBytes將b字節(jié)數(shù)組中的字節(jié)數(shù)據(jù)
    * 寫入到文件中哈街。
    */
   public void write(byte b[], int off, int len) throws IOException {
        Object traceContext = IoTrace.fileWriteBegin(path);
        int bytesWritten = 0;
        try {
            writeBytes(b, off, len, append); //native方法
            bytesWritten = len;
        } finally {
            IoTrace.fileWriteEnd(traceContext, bytesWritten);
        }
    }
    //底層將字節(jié)寫入文件的方法,觸發(fā)真正的磁盤寫入實(shí)現(xiàn)
    private native void writeBytes(byte b[], int off, int len, boolean append)
        throws IOException;

可以看到拒迅,對文件的輸出操作骚秦,會涉及到資源的加鎖,與操作系統(tǒng)的交互璧微,因?yàn)榇疟P上文件是操作系統(tǒng)進(jìn)行管理的作箍,可以在native本地方法中,調(diào)用操作系統(tǒng)對文件的操作api,這樣我們就能將程序與輸出文件連通前硫,就能往這個通道中寫入字節(jié)數(shù)據(jù)胞得,最后輸出到文件端中,完成對文件觸發(fā)真正的磁盤寫入 屹电。阶剑。。

當(dāng)程序中處理的數(shù)據(jù)比較大和多的時候危号,利用字節(jié)緩沖區(qū)來對文件的寫入效率會更快牧愁,節(jié)省每次寫入對操作系統(tǒng)資源的占用與協(xié)同花費(fèi)的時間與資源。

這樣外莲,可以只是通過一次操作系統(tǒng)調(diào)用猪半,打開輸出文件,完成對文件真正的磁盤寫入。就像河上的一條橋一樣磨确,固定住這個連通通道沽甥,我們就能將大量的輸出,每次按照固定長度俐填,從一端運(yùn)輸?shù)搅硗庖欢税步樱?dāng)所有的數(shù)據(jù)都運(yùn)輸完成之后,就可以關(guān)閉這座橋英融,而不是每次運(yùn)輸一次數(shù)據(jù)就打開關(guān)閉這座橋一次盏檐。

    @Test
    public void bufferedOutputStreamTest() throws Exception{
        //每次寫入,是追加到文件最后
        OutputStream os = new FileOutputStream("C:/Users/XianSky/Desktop/output.txt",true);
        BufferedOutputStream bos = new BufferedOutputStream(os);
        String[] src = {"heihei","haha","xixi"};//通過這個字符數(shù)組模擬大量數(shù)據(jù)驶悟,每次只是將一個字符字節(jié)寫入到緩沖區(qū)
        byte[] buf = new byte[1024];
        /*
         * 注意胡野,這里每次調(diào)用write方法后,都是將src中的每個字符串
         * 的字節(jié)數(shù)據(jù)寫入到bos內(nèi)部的緩沖區(qū)中痕鳍,并沒有直接寫入到文件
         * bos緩沖區(qū)的默認(rèn)大小是8192個字節(jié)=8k硫豆。
         */
        for(String s : src){    //每次最多運(yùn)輸1024個字節(jié)到緩沖區(qū)
            buf = s.getBytes();
            bos.write(buf, 0, buf.length);
            buf = null;
        }
        /*
         * 當(dāng)所有要輸出的字節(jié)數(shù)據(jù),都成功寫入到bos的字節(jié)緩沖區(qū)后笼呆,
         * 通過顯示調(diào)用flush或者close方法熊响,都一次性將bos中
         * 緩沖區(qū)的字節(jié)數(shù)據(jù)輸出寫入到文件中。
         */
        bos.flush();    
        bos.close();
    }

當(dāng)在flush方法處打斷點(diǎn)測試時候诗赌,上面循環(huán)將字符數(shù)組數(shù)據(jù)轉(zhuǎn)換成字節(jié)寫入到BufferedOutputStream的緩沖區(qū)中時候汗茄,人為打開輸出文件,發(fā)現(xiàn)其實(shí)還并沒有數(shù)據(jù)在文件中铭若。當(dāng)調(diào)用flush或者close后洪碳,再次打開文件就發(fā)現(xiàn)數(shù)據(jù)已經(jīng)被寫入了。緩沖區(qū)默認(rèn)大小是8192個字節(jié)叼屠,下面通過源代碼看看這個緩沖輸出類是如何寫入字節(jié)數(shù)據(jù)的:

public
class BufferedOutputStream extends FilterOutputStream {
    protected byte buf[];//緩沖區(qū)
    protected int count;//緩沖區(qū)有效字節(jié)個數(shù)
    public BufferedOutputStream(OutputStream out) {
        this(out, 8192);//默認(rèn)8k字節(jié)緩沖區(qū)大小
    }
...
    //帶有緩沖區(qū)的字節(jié)輸出流的寫入方法
    public synchronized void write(byte b[], int off, int len) throws IOException {
        if (len >= buf.length) {
            /*
            * 若是要寫入字節(jié)緩沖的字節(jié)數(shù)量大于緩沖區(qū)數(shù)量瞳腌,
            * 則先刷新緩沖區(qū),然后通過內(nèi)部關(guān)聯(lián)的無緩沖區(qū)功能的OutputStream的write方法镜雨,
            * 一次性的將字節(jié)數(shù)據(jù)寫入文件嫂侍,觸發(fā)真正的磁盤寫入。
            * 這樣就清空了緩沖區(qū)數(shù)據(jù)荚坞,并且將大量數(shù)據(jù)寫入到文件中挑宠,在不破環(huán)緩沖區(qū)的情況下。
            */
            flushBuffer();
            //用過基礎(chǔ)字節(jié)輸出流OutputStream寫入西剥,觸發(fā)真正的磁盤寫入痹栖,或者會占用IO資源
            out.write(b, off, len);
            return;
        }
        if (len > buf.length - count) {
            flushBuffer();//若是要寫入的字節(jié)數(shù)大于緩沖區(qū)空白空間亿汞,刷新緩沖區(qū)
        }
        System.arraycopy(b, off, buf, count, len);//拷貝方式將輸出參數(shù)b中的字符寫入到緩沖區(qū)中
        count += len;
    }

根據(jù)源代碼可以看到當(dāng)緩沖區(qū)的空間不足或者要將緩沖區(qū)數(shù)據(jù)寫入文件中時候瞭空,就會調(diào)用flush或者flushBuffer方法,下面看看這兩個方法:

    public synchronized void flush() throws IOException {
        flushBuffer();
        out.flush();
    }
    
    /** Flush the internal buffer */
    private void flushBuffer() throws IOException {
        if (count > 0) {//緩沖區(qū)內(nèi)有字節(jié)數(shù)據(jù)
            out.write(buf, 0, count);//通過原始基礎(chǔ)字節(jié)流一次性將字節(jié)數(shù)據(jù)寫入到文件中,觸發(fā)真正的磁盤寫入
            count = 0;//重置清空緩沖區(qū)
        }
    }

所以咆畏,通常flush是依賴與帶有緩沖輸出字節(jié)或者字符流的南捂,每次調(diào)用flush,實(shí)質(zhì)都是調(diào)用out.write()將緩沖區(qū)中數(shù)據(jù)旧找,對磁盤文件進(jìn)行真正的寫入溺健。
那么為什么要調(diào)用flush?
通過上面的源代碼也知道,當(dāng)一次讀取大量的字節(jié)數(shù)據(jù)時候钮蛛,當(dāng)緩沖區(qū)內(nèi)容量不足時候(默認(rèn)緩沖區(qū)為8192個字節(jié))鞭缭,就會進(jìn)行flush啦,先將緩沖區(qū)中數(shù)據(jù)對磁盤進(jìn)行寫入魏颓,或者是不用緩沖區(qū)數(shù)據(jù)岭辣,直接通過基礎(chǔ)字節(jié)流寫入,最后在清空處理緩沖區(qū)甸饱,為下一次讀取數(shù)據(jù)做準(zhǔn)備沦童。
用下面的圖進(jìn)行簡單描述這個過程:

bufferoutput.png

若是僅僅像處理程序中的字節(jié)數(shù)組,或者不進(jìn)行文件輸出的話叹话,我們有時候也會使用ByteArrayOutputStream偷遗,給字節(jié)數(shù)組添加緩沖功能⊥蘸可以輸出處理好的字節(jié)數(shù)組到內(nèi)存中氏豌,或者到文件中。

    @Test
    public void byteArrayOutputStreamTest() throws Exception{
        FileOutputStream fos = new FileOutputStream("C:/Users/XianSky/Desktop/output.txt",true);
        byte[] src = "bytearrayoutputstream".getBytes();//要處理的原始字節(jié)數(shù)組
        //默認(rèn)構(gòu)造器緩沖區(qū)32個字節(jié),可以通過構(gòu)造器自定義大小,若是緩沖區(qū)大小不夠辅柴,則會對當(dāng)前緩沖區(qū)大小擴(kuò)容一倍
        ByteArrayOutputStream baos = new ByteArrayOutputStream(src.length);
        baos.write(src, 0, src.length);//將src字節(jié)數(shù)組數(shù)據(jù)拷貝到內(nèi)部緩沖區(qū)中
        baos.write(104);//寫入字母h到緩沖區(qū)末尾中
        System.out.println("緩沖區(qū)中字節(jié)數(shù):>>"+baos.size());
        byte[] buff = baos.toByteArray();//將處理后的緩沖區(qū)字節(jié)轉(zhuǎn)換成字節(jié)數(shù)組
        System.out.println(new String(buff));
        baos.close();
        fos.write(buff);//將處理后的字節(jié)數(shù)組一次性寫入到文件中
        baos.close();
        fos.close();
    }

ByteArrayOutputStream還有一個特別方法就是:public synchronized void writeTo(OutputStream out),即可以將當(dāng)前緩沖區(qū)中的字節(jié)數(shù)據(jù)寫入到指定輸出字節(jié)流中箩溃。

還有一個打印流:PrintStream,這個類對于字節(jié)碌嘀,字符輸出都能完成涣旨。想寫入字節(jié)就使用write方法;想寫入字符串股冗,整型等基本數(shù)據(jù)類型就使用print方法霹陡。因?yàn)镻rintStream這個打印流內(nèi)部有兩個私有的Writer類,結(jié)合本身字節(jié)的基礎(chǔ)字節(jié)流止状,所以功能很強(qiáng)大烹棉。

public class PrintStream extends FilterOutputStream
    implements Appendable, Closeable
{
    ...
    private BufferedWriter textOut; //緩沖字符輸出流
    private OutputStreamWriter charOut;//字符字節(jié)轉(zhuǎn)換流
...
}

打印流內(nèi)部通過聚合一個字符輸出流與一個字符字節(jié)輸出轉(zhuǎn)換流,那么就能實(shí)現(xiàn)字節(jié)字符的切換輸出了怯疤,來看看基本使用方法浆洗,這里輸出源是文件,也可以不是文件集峦,像我們經(jīng)常使用的jsp文件中也可以使用這個打印流輸出信息到瀏覽器中:

    @Test
    public void PrintStreamTest() throws Exception{
        FileOutputStream fos = new FileOutputStream("C:/Users/XianSky/Desktop/output.txt",true);
        PrintStream ps = new PrintStream(fos);//在文件字節(jié)輸出流上添加打印功能
        ps.print("aa");//寫入字符串
        ps.print(104);//寫入int數(shù)值
        ps.println();//換行
        ps.write(104);//寫入字節(jié)伏社,對應(yīng)字母h
        
        ...
        ps.close()
        fos.close();
    }

從使用上抠刺,操作文件也方便,可以輸出任意基本數(shù)據(jù)類型和字符摘昌,字節(jié)等等數(shù)據(jù)速妖。
其他的子類可以對照相應(yīng)的輸入流,可知道這些流的具體適合使用的場景聪黎。

Writer字符輸出流

與OutputStream字節(jié)輸出流相對罕容,這個Writer則是字符輸出流。相比而言稿饰,通常我們無論是程序輸出到磁盤文件锦秒,和在網(wǎng)絡(luò)輸出,瀏覽器html輸出喉镰,字符輸出流相較而言使用比較多脂崔。下面先從整體類圖看看這個字符輸出流有什么子類以及通用方法特性。


writer.png

從抽象基類Writer看看梧喷,對于字符輸出流有那些基本的操作方法:

public void write(int c);//寫入一個2字節(jié)字符
public void write(char cbuf[]);//將cbuf字符數(shù)組數(shù)據(jù)寫入緩沖區(qū)
public void write(String str)砌左;//寫入字符串到輸出流中
public void write(String str, int off, int len);
public Writer append(CharSequence csq);//添加字符序列到輸出流中

從這些方法總大致可以看出,這個Writer字符輸出流系列铺敌,功能很強(qiáng)大汇歹,無論是單個,多個字符偿凭,字符串都能寫入到字符輸出流中产弹。下面從一些常用的字符輸出流的使用例子來說明內(nèi)部是如何運(yùn)行的?哎弯囊,其實(shí)與Reader字符輸入流般痰哨,Writer除了與其方向相反,可以flush刷新緩沖區(qū)外匾嘱,其他的也都產(chǎn)不多斤斧,你輸入解碼,我輸出編碼霎烙。

    @Test
    public void fileWriterTest() throws Exception{
        FileWriter fw = new FileWriter("C:/Users/XianSky/Desktop/output.txt",true);
        char[] cbuf = {'w','t'};
        fw.write("hhh");//寫入字符串
        fw.write('A');//寫入單個字符
        fw.append("like");//在字符流添加字符串
        fw.write(cbuf);//寫入字符數(shù)組
        
        fw.flush();
        fw.close();
    }

因?yàn)镕ileWirter是繼承自O(shè)utputStreamWriter撬讽,其內(nèi)部實(shí)現(xiàn)就如同F(xiàn)ileReader類似,F(xiàn)ileWriter寫入數(shù)據(jù)都是通過:FileWriter --> OutputStreamWriter --> StreamEncoder這個流程實(shí)現(xiàn)的悬垃。最終還是StreamEncoder這個類實(shí)現(xiàn)將字節(jié)數(shù)據(jù)編碼成字符數(shù)據(jù)游昼,進(jìn)行磁盤文件寫入。

public class FileWriter extends OutputStreamWriter {
...
    public FileWriter(String fileName) throws IOException {
        super(new FileOutputStream(fileName));
    }
}

同理尝蠕,若是添加輸出緩沖烘豌,則使用BufferedWriter或者CharArrayWirter來實(shí)現(xiàn),這都是處理流看彼,需要在基礎(chǔ)流上封裝處理廊佩,添加新功能昧捷。但是到讀取字節(jié)數(shù)據(jù)底層還是由字節(jié)輸出流來支持實(shí)現(xiàn)的。字符數(shù)據(jù)要進(jìn)行輸出罐寨,都要通過OutputStreamWriter類將這些字符輸出流轉(zhuǎn)換成字節(jié)輸出流,最后統(tǒng)一磁盤輸出到文件中序矩。鸯绿。。

在web開發(fā)中最常用的字符輸出流簸淀,也就是PrintWriter了瓶蝴,通過得到response的輸出通道流,往緩沖區(qū)里面寫入字符租幕,字符串等等數(shù)據(jù)舷手,response刷新緩沖區(qū)后,就能將數(shù)據(jù)輸出到客戶端的瀏覽器頁面進(jìn)行顯示了劲绪。下面看看jsp頁面中或者servlet中字符輸出流PrintWriter的基本使用:

//*.jsp
<%      
    //得到response相應(yīng)輸出流
    PrintWriter pw = response.getWriter();
    //往輸出流中寫入數(shù)據(jù)
    pw.write(new char[]{'r','e','s'});//輸出流中寫入字符數(shù)組
    pw.write("response output");//輸出流中寫入字符串
    pw.append("append string");//在輸出流后添加字符串
    pw.flush(); //將response緩沖區(qū)中數(shù)據(jù)輸出返回到客戶端
        
    %>

對于其他的使用場景男窟,由于沒有實(shí)際需求,所以就大致將這些基本的使用場景中的基本使用方式羅列出來了贾富∏妇欤基礎(chǔ)掌握好了,高階的也不是問題了颤枪。

輸出字節(jié)流與字符流的轉(zhuǎn)換

對于字符輸出流來說汗捡,要想輸出到文件或者其他遠(yuǎn)程網(wǎng)絡(luò)資源中,實(shí)質(zhì)上還是要轉(zhuǎn)換成字節(jié)進(jìn)行傳輸?shù)奈犯佟W止?jié)數(shù)據(jù)才能直接寫入外部資源中扇住,磁盤中存儲的也是這些輸出字符對應(yīng)的字節(jié)。所以盗胀,字節(jié)輸出是字符輸出的根本艘蹋。那么這個字符轉(zhuǎn)換成字節(jié)的操作類OutputStreamWriter也至關(guān)重要了。
轉(zhuǎn)換流過程可以參照如下圖:


微信截圖_20171119115949.png

就如同InputStreamReader中使用StreamDecoder將字節(jié)解碼成字符一樣票灰,OutputStreamWriter則是逆向過程簿训,通過StreamEncoder將字符轉(zhuǎn)換字節(jié),最后輸出到文件中進(jìn)行保存米间。

public class OutputStreamWriter extends Writer {

    private final StreamEncoder se;

...
    /*
    * 當(dāng)調(diào)用write將字符數(shù)組寫入到字符輸出流中强品,底層是調(diào)用StreamEncoder se對象,
    * 將字符轉(zhuǎn)換成字節(jié)進(jìn)行輸出屈糊。
    */
    public void write(char cbuf[], int off, int len) throws IOException {
        se.write(cbuf, off, len);
    }
}

繼續(xù)進(jìn)入StreamEncoder中的write方法:

//StreamEncoder.java
  public void write(char cbuf[], int off, int len) throws IOException {
        synchronized (lock) {
            ensureOpen();
            if ((off < 0) || (off > cbuf.length) || (len < 0) ||
                ((off + len) > cbuf.length) || ((off + len) < 0)) {
                throw new IndexOutOfBoundsException();
            } else if (len == 0) {
                return;
            }
            implWrite(cbuf, off, len);
        }
    }


    void implWrite(char cbuf[], int off, int len)
        throws IOException
    {
        CharBuffer cb = CharBuffer.wrap(cbuf, off, len);

        if (haveLeftoverChar)
        flushLeftoverChar(cb, false);

        while (cb.hasRemaining()) {
        //CharsetEncoder對CharBuffer中字符進(jìn)行編碼成字節(jié)
        CoderResult cr = encoder.encode(cb, bb, false);
        if (cr.isUnderflow()) {
           assert (cb.remaining() <= 1) : cb.remaining();
           if (cb.remaining() == 1) {
                haveLeftoverChar = true;
                leftoverChar = cb.get();
            }
            break;
        }
        if (cr.isOverflow()) {
            assert bb.position() > 0;
            writeBytes();   //實(shí)際字節(jié)輸出方法
            continue;
        }
        cr.throwException();
        }
    }

   private void writeBytes() throws IOException {
        bb.flip();
        int lim = bb.limit();
        int pos = bb.position();
        assert (pos <= lim);
        int rem = (pos <= lim ? lim - pos : 0);

            if (rem > 0) {
        if (ch != null) {// WritableByteChannel不為空
            if (ch.write(bb) != rem)//通過WritableByteChannel寫入ByteBuffer bb中字節(jié)數(shù)據(jù)的榛。
                assert false : rem;
        } else {
            //WritableByteChannel不可通過通道方式寫字節(jié)數(shù)據(jù),就用OutputStream的
            //底層的write方法逻锐,內(nèi)部通過native的writeBytes()方法將字節(jié)寫入文件中夫晌。
            out.write(bb.array(), bb.arrayOffset() + pos, rem);
        }
        }
        bb.clear();
        }

字節(jié)-->字符(InputStreamReader):解碼雕薪,通過StreamDecoder將讀取到字節(jié),通過CharBuffer,ByteBuffer,ReadableByteChannel等nio包中類晓淀,將字節(jié)轉(zhuǎn)換成字符到程序中進(jìn)行處理所袁。

字符-->字節(jié)(OutputStreamWriter):編碼,通過StreamEncoder將程序內(nèi)存中的字符數(shù)組等數(shù)據(jù)凶掰,通過CharBuffer,ByteBuffer,WritableByteChannel等nio包中類燥爷,將字符編碼成字節(jié)后,在進(jìn)行輸出懦窘。

總結(jié)

經(jīng)過自己對java io的理解前翎,通過類圖對字節(jié)流,字符流的輸入輸出進(jìn)行大致了解畅涂。然后通過分別對一些常用的子類的使用場景進(jìn)行說明港华,舉例。并通過底層源碼進(jìn)行分析午衰,當(dāng)然了立宜,自己對java io的理解也肯定有限,也有可能自己的理解錯誤了臊岸,有偏差赘理,希望自己在日后學(xué)習(xí)中不斷更深入了解,在回過頭能發(fā)現(xiàn)問題并進(jìn)行更正扇单。

java中還有nio的一些知識也是非常有意思的商模,想上面的基礎(chǔ)io中內(nèi)部實(shí)現(xiàn)也有不少依賴nio中的通道概念類。希望自己也可以再下一章節(jié)寫寫對這些nio的使用和理解蜘澜。再java io設(shè)計中施流,也有想適配器模式,裝飾器模式的應(yīng)用鄙信,也希望自己能對它們進(jìn)行總結(jié)瞪醋,更深入的理解。再找時間來總結(jié)和分析装诡,堅持堅持银受,在寫總結(jié)的過程中,自己對基礎(chǔ)知識的理解也確實(shí)加深不少鸦采,這些時間花費(fèi)還是挺值得的吧.....

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末宾巍,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子渔伯,更是在濱河造成了極大的恐慌顶霞,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件锣吼,死亡現(xiàn)場離奇詭異选浑,居然都是意外死亡蓝厌,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進(jìn)店門古徒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拓提,“玉大人,你說我怎么就攤上這事隧膘〈” “怎么了?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵舀寓,是天一觀的道長。 經(jīng)常有香客問我肌蜻,道長互墓,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任蒋搜,我火速辦了婚禮篡撵,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘豆挽。我一直安慰自己育谬,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布帮哈。 她就那樣靜靜地躺著膛檀,像睡著了一般。 火紅的嫁衣襯著肌膚如雪娘侍。 梳的紋絲不亂的頭發(fā)上咖刃,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天,我揣著相機(jī)與錄音憾筏,去河邊找鬼嚎杨。 笑死,一個胖子當(dāng)著我的面吹牛氧腰,可吹牛的內(nèi)容都是我干的枫浙。 我是一名探鬼主播,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼古拴,長吁一口氣:“原來是場噩夢啊……” “哼箩帚!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起黄痪,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤膏潮,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后满力,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體焕参,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡轻纪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了叠纷。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片刻帚。...
    茶點(diǎn)故事閱讀 39,688評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖涩嚣,靈堂內(nèi)的尸體忽然破棺而出崇众,到底是詐尸還是另有隱情,我是刑警寧澤航厚,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布顷歌,位于F島的核電站,受9級特大地震影響幔睬,放射性物質(zhì)發(fā)生泄漏眯漩。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一麻顶、第九天 我趴在偏房一處隱蔽的房頂上張望赦抖。 院中可真熱鬧,春花似錦辅肾、人聲如沸队萤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽要尔。三九已至,卻和暖如春新娜,著一層夾襖步出監(jiān)牢的瞬間盈电,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工杯活, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留匆帚,地道東北人。 一個月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓旁钧,卻偏偏與公主長得像吸重,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子歪今,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評論 2 353

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