Java-IO詳解

IO概覽

圖片1.png

字節(jié)流與字符流的區(qū)別
字節(jié)流在操作時本身不會用到緩沖區(qū)(內(nèi)存),是文件本身直接操作的聪建,而字符流在操作時使用了緩沖區(qū)钙畔,通過緩沖區(qū)再操作文件。

圖片1.png

使用字節(jié)流示例

public static void main(String[] args) {
     File f = new File("d:" + File.separator + "test.txt");
     OutputStream out = new FileOutputStream(f);
     //準(zhǔn)備一個字符串   
     String str = "Hello World!!!"; 
     //字符串轉(zhuǎn)byte數(shù)組   
     byte b[] = str.getBytes();       
     //將內(nèi)容輸出      
     out.write(b);                       
     //關(guān)閉輸出流金麸,此時沒有關(guān)閉 
     //out.close();                     
}    

程序運(yùn)行結(jié)果:

18070962@CNHQ-18070962N /cygdrive/d
$ cat text.txt
Hello World!!!

使用字符流示例

public static void main(String[] args) {
     File f = new File("d:" + File.separator + "test.txt");
     Writer out = new FileWriter(f);
     //準(zhǔn)備一個字符串   
     String str = "Hello World!!!"; 
     //將內(nèi)容輸出      
     out.write(b);                       
     //關(guān)閉輸出流擎析,此時沒有關(guān)閉 
     //out.close();                     
}    

程序運(yùn)行結(jié)果:

18070962@CNHQ-18070962N /cygdrive/d
$ cat text.txt

使用OutputStream的強(qiáng)制刷新,將結(jié)果強(qiáng)制輸出

public static void main(String[] args) {
     File f = new File("d:" + File.separator + "test.txt");
     Writer out = new FileWriter(f);
     //準(zhǔn)備一個字符串   
     String str = "Hello World!!!"; 
     //將內(nèi)容輸出      
     out.write(b);         
     //強(qiáng)制刷新挥下,輸出
     out.flush();              
     //關(guān)閉輸出流揍魂,此時沒有關(guān)閉 
     //out.close();                       
}    

程序運(yùn)行結(jié)果:

18070962@CNHQ-18070962N /cygdrive/d
$ cat text.txt
Hello World!!!

IO流常用基類----字符流

Reader

  • BufferedReader:從字符輸入流中讀取文本,緩沖各個字符棚瘟,從而實現(xiàn)字符现斋、數(shù)組和行的高效讀取。

    • LineNumberReader:跟蹤行號的緩沖字符輸入流解取。
  • InputStreamReader:轉(zhuǎn)換流步责,字節(jié)通向字符的橋梁。

    • FileReader:專門用于處理文件的字符讀取流對象禀苦。

Writer

  • BufferedWriter:將文本寫入字符輸出流蔓肯,緩沖各個字符,從而提供單個字符振乏、數(shù)組和字符串的高效寫入蔗包。

  • OutputStreamWriter:轉(zhuǎn)換流,字符通向字節(jié)的橋梁

    • FileWriter:專門用于處理文件的字符寫入流對象慧邮。

字符流中的對象融合了編碼表调限。使用的是默認(rèn)的編碼,即當(dāng)前系統(tǒng)的編碼误澳。

1耻矮、Writer抽象類 寫入字符流的抽象類

(1)、Writer抽象類常用方法

//寫入單個字符
void  write(String str);//寫入 字符串
void  write(String str,int off,int len);//寫入字符串的某一部分
void  write(char[] cbhf);//寫入字符數(shù)組
abstract void   write(char[] cbuf,int off,int len);//寫入字符數(shù)組的某一部分
abstract void   close();//關(guān)閉流忆谓,但是會先刷新它
abstract void   flush();//刷新該流的緩沖區(qū)

(2)裆装、FileWriter類(Writer抽象類的子類)

因為Writer是抽象的,無法創(chuàng)建對象倡缠。所以我們找到一個專門用于操作文件的Writer子類對象:FileWriter哨免。后綴名是父類名,前綴名是該流對象的功能昙沦。該對象可以直接操作文件琢唾。該對象中沒有特有方法,所以可以直接用Writer中的方法操作文件盾饮。但是這個對象可以創(chuàng)建對象采桃,我們用它的構(gòu)造函數(shù)懒熙。

1)、FileWriter構(gòu)造方法

//根據(jù)給定的File對象構(gòu)造一個 FileWriter 對象
new  FileWriter(File file,boolean append);//根據(jù)給定的File對象構(gòu)造一個 FileWriter 對象,而且不會覆蓋
new  FileWriter(String fileName);//根據(jù)給定的文件名創(chuàng)建FileWriter 對象
new  FileWriter(String fileName,boolean append);//對已有文件的數(shù)據(jù)續(xù)寫

2)普办、寫入字符流步驟

1.創(chuàng)建一個FileWriter對象煌珊。該對象一被初始化就必須明確被操作的文件,而且該文件會被創(chuàng)建到指定目錄下泌豆。如果該目錄下已有同名文件定庵,會將其覆蓋掉。當(dāng)然可以用new FileWriter(String fileName,true)構(gòu)造方法則將數(shù)據(jù)寫入文件末尾處踪危,這樣就不會覆蓋原來的文件了蔬浙。其實這步就是在明確詩句要存放的目的地。

2.調(diào)用write方法贞远,將字符串寫入到輸出流中畴博。

3.調(diào)用flush方法。刷新流對象中的緩沖區(qū)數(shù)據(jù)蓝仲,將數(shù)據(jù)刷到指定目的地中俱病。

4.最后一定要記得關(guān)閉流資源。但是在關(guān)閉之前會刷新一次內(nèi)部的緩沖區(qū)中的數(shù)據(jù)袱结。和flush區(qū)別:flush刷新后亮隙,流可以繼續(xù)使用,close刷新后垢夹,會將流關(guān)閉溢吻。

注:
1. close方法只能用一次。
2. 流關(guān)閉以后不能果元,不能再調(diào)用write方法促王,否則會報異常錯誤:Stream closed IO異常。
3.由于在創(chuàng)建對象時而晒,需要指定創(chuàng)建文件位置蝇狼,如果指定的位置不存在,就會發(fā)生IOException異常倡怎。所以要進(jìn)行異常處理迅耘。

需求:在硬盤上,創(chuàng)建一個文件并寫入一些文字?jǐn)?shù)據(jù)诈胜。

import java.io.*;

//標(biāo)準(zhǔn)的IO異常處理方法
class FileWriterDemo {
    public static void main(String[] args) {
        FileWriter fw = null;
        try {
            //創(chuàng)建一個可以往文件中寫入字符數(shù)據(jù)的字符輸出流對象
            //既然是往一個文件中寫入文字?jǐn)?shù)據(jù)豹障,那么在創(chuàng)建對象時冯事,就必須明確該文件(用于存儲數(shù)據(jù)的目的地)
            //如果文件不存在焦匈,則會自動創(chuàng)建,如果文件存在,則會被覆蓋
            fw = new FileWriter("Demo.txt");
            //傳遞一個true參數(shù)昵仅,代表不覆蓋已有的文件。并在已有文件的末尾處進(jìn)行數(shù)據(jù)續(xù)寫弄捕。
            //fw=new FileWriter("Demo.txt",true);
            //調(diào)用Writer對象中的write(string)方法屎飘,寫入數(shù)據(jù)。其實該數(shù)據(jù)寫到臨時緩沖區(qū)中
            fw.write("我愛黑馬程序員");

            //刷新緩沖區(qū)垦写,將數(shù)據(jù)從緩沖區(qū)寫入到目的地中
            fw.flush();
        } catch (IOException e) {
            System.out.println(e);
        } finally {
            if (fw != null)//首先判斷流對象時候創(chuàng)建成功
                try {
                    //close方法一定要關(guān)閉流資源,一定會執(zhí)行的代碼放在finally塊中彰触,
                    //close方法在調(diào)用的時候也會拋出io異常梯投,所以要單獨(dú)的進(jìn)行try catch處理
                    fw.close();
                } catch (IOException e) {
                    System.out.println("輸出流關(guān)閉失敗况毅!");
                }
        }
    }

2分蓖、Reader抽象類 讀取字符流的抽象類

使用Reader體系,讀取一個文本文件中的數(shù)據(jù)尔许。返回 -1 么鹤,標(biāo)志讀到結(jié)尾。

(1)味廊、Reader抽象類中的常用方法

int  read();//讀取一個字符
int  read(char[] cbuf);//將字符讀入數(shù)組
int  read(char[] cbuf,int off,int len);//將字符讀入數(shù)組的某一部分
void  close();//關(guān)閉該流并釋放與之關(guān)聯(lián)的所有資源蒸甜。
void mark(int readAheadLimit);//標(biāo)記流中的當(dāng)前位置
long  skip(long n);//跳過字符
void  rest();//重置該流

注意:flush方法只有Writer類中有,Reader中沒有余佛。

(2)柠新、FileReader類(Reader抽象類的子類)

因為Reader是抽象的,無法創(chuàng)建對象辉巡。所以我們找到一個專門用于操作文件的Reader子類對象:FileReader登颓。后綴名是父類名,前綴名是該流對象的功能红氯。該對象可以直接操作文件框咙。該對象中沒有特有方法,所以可以直接用Reader中的方法操作文件痢甘。但是這個對象可以創(chuàng)建對象喇嘱,我們用它的構(gòu)造函數(shù)。

1)塞栅、FileReader構(gòu)造方法

new FileReader();//給定讀取數(shù)據(jù)的文件名創(chuàng)建讀取流對象
new FileReader();//給定讀取數(shù)據(jù)的File創(chuàng)建讀取流對象

2)者铜、讀取字符流步驟

1.創(chuàng)建一個文件讀取流對象,和指定名稱的文件相關(guān)聯(lián)放椰。要保證該文件是已經(jīng)存在的作烟,如果不存在,會發(fā)生異常FileNotFoundException砾医。

2.調(diào)用讀取流對象的read方法拿撩。read():一次讀一個字符,而且會自動往下讀。如果讀到流的末尾處就返回-1如蚜;使用read(char[])方法讀取文本文件數(shù)據(jù)压恒。通過字符數(shù)組進(jìn)行讀取影暴。這種方法比較高效建議用這種方法。

3.調(diào)用close方法關(guān)閉流資源探赫。

需求:讀取一個文文件型宙,將讀取到的字符打印到控制臺(FileReader)

第一種讀取方式:使用read()方法單個讀取文本字符數(shù)據(jù)。

import java.io.*;

class FileReaderDemo {
    public static void main(String[] args) {
        FileReader fr = null;
        try {
            //創(chuàng)建可以讀取文本文件的流對象伦吠,F(xiàn)ileReader讓創(chuàng)建好的流對象和指定的文件相關(guān)聯(lián)
            fr = new FileReader("Demo.txt");
            //第一種讀取方式:read方法妆兑,一次讀單個字符
            int ch = 0;
            //read():一次讀一個字符,而且會自動往下讀。如果讀到流的末尾處就返回-1
            while ((ch = fr.read()) != -1)//如果不等于-1就循環(huán)的獲取字符
            {
                System.out.print((char) ch);//因為read返回的是int毛仪,所以要強(qiáng)轉(zhuǎn)成char類型的
            }
        } catch (IOException e) {
            System.out.println(e);
        } finally {
            if (fr != null)
                try {
                    fr.close();
                } catch (IOException e) {
                    System.out.println("讀取流關(guān)閉失敗");
                }
        }
    }
}

第二種讀取方式:使用read(char[])方法讀取文本文件數(shù)據(jù)箭跳。通過字符數(shù)組進(jìn)行讀取。這種方法比較高效建議用這種方法潭千。

import java.io.*;

class FileReaderDemo {
    public static void main(String[] args) {
        FileReader fr = null;
        try {
            //創(chuàng)建讀取流對象和指定文件關(guān)聯(lián)谱姓。
            fr = new FileReader("Demo.txt");
            //定義一個變量記錄read的返回值
            int len = 0;
            //定義一個字符數(shù)組,用于存儲讀到字符。一般數(shù)組的長度都是1024的整數(shù)倍刨晴。
            char[] buf = new char[1024];
            //該read(char[])返回的是讀到字符個數(shù)屉来。
            while ((len = fr.read(buf)) != -1) {
                //把char數(shù)組中的數(shù)據(jù)變成字符串打印
                System.out.println(new String(buf, 0, len));
            }
        } catch (IOException e) {
            System.out.println(e);
        } finally {
            if (fr != null)
                try {
                    fr.close();
                } catch (IOException e) {
                    System.out.println("讀取流關(guān)閉失敗");
                }
        }
    }
}

練習(xí):將D盤的一個文本文件復(fù)制到E盤。

復(fù)制的原理:其實就是將D盤下的文件數(shù)據(jù)存儲到E盤的一個文件中狈癞。

步驟:

1茄靠,在E盤創(chuàng)建一個文件。用于存儲D盤文件中的數(shù)據(jù)蝶桶。

2慨绳,定義讀取流和D盤文件關(guān)聯(lián)。

3真竖,通過不斷的讀寫完成數(shù)據(jù)存儲脐雪。

4,關(guān)閉資源恢共。

第一種方法:單個字符不短的讀與寫战秋。

import java.io.*;

class CopyDemo {
    public static void main(String[] args) {
        FileReader fr = null;
        FileWriter fw = null;
        try {
            //1、讀取一個已有的文本文件讨韭,使用字符讀取流和文件相關(guān)聯(lián)
            fr = new FileReader("D:\\Demo.java");
            //2脂信、創(chuàng)建一個目的,用于存儲讀到數(shù)據(jù)透硝。
            fw = new FileWriter("E:\\Demo.java");
            //第一種方法:單個字符不短的讀與寫
            //3狰闪、頻繁的讀寫操作。從D盤讀一個字符濒生,就往E盤寫一個字符埋泵。
            int ch = 0;
            while ((ch = fr.read()) != -1) {
                fw.write(ch);
                fw.flush();
            }

        } catch (IOException e) {
            System.out.println(e);
        } finally {
            if (fr != null)
                try {
                    //4、關(guān)閉流資源
                    fr.close();
                } catch (IOException e) {
                    System.out.println("讀取流關(guān)閉失敗");
                }
            if (fw != null)
                try {
                    //4甜攀、關(guān)閉流資源
                    fw.close();
                } catch (IOException e) {
                    System.out.println("輸出流關(guān)閉失敗");
                }
        }
    }
}

第二種方法:建立一個字符數(shù)組緩沖區(qū)秋泄。使用read(char[])讀取文本文件數(shù)據(jù)。

import java.io.*;

class CopyDemo {
    public static void main(String[] args) {
        FileReader fr = null;
        FileWriter fw = null;
        try {
            fr = new FileReader("D:\\Demo.java");
            fw = new FileWriter("E:\\Demo.java");
            //創(chuàng)建一個臨時容器规阀,用于緩存讀取到的字符
            char[] buf = new char[1024];
            //定義一個變量記錄讀取到的字符數(shù)(其實就是數(shù)組里裝的字符個數(shù))
            int len = 0;
            while ((len = fr.read(buf)) != -1) {
                fw.write(buf, 0, len);
            }
        } catch (IOException e) {
            System.out.println(e);
        } finally {
            if (fr != null)
                try {
                    //關(guān)閉流資源
                    fr.close();
                } catch (IOException e) {
                    System.out.println("讀取流關(guān)閉失敗");
                }
            if (fw != null)
                try {
                    //關(guān)閉流資源
                    fw.close();
                } catch (IOException e) {
                    System.out.println("輸出流關(guān)閉失敗");
                }
        }
    }
}

3恒序、字符流的緩沖區(qū)(BufferedReader、BufferedWriter)

字符流的緩沖區(qū)的出現(xiàn)提高了對數(shù)據(jù)的讀寫效率谁撼。

對應(yīng)的類:BufferedReader和BufferedWriter歧胁。

緩沖區(qū)要結(jié)合流才可以使用。在緩沖區(qū)創(chuàng)建前厉碟,要先創(chuàng)建流對象喊巍。即先將流對象初始化到構(gòu)造函數(shù)中

作用:在流的基礎(chǔ)上對流的功能進(jìn)行了增強(qiáng)。一般對字符操作都要加上緩沖區(qū)提高效率箍鼓。

new  BufferedReader(Reader in)//創(chuàng)建一個使用輸入緩沖區(qū)的緩沖字符輸入流崭参。
new  BufferedWriter(Writer out)// 創(chuàng)建一個使用輸出緩沖區(qū)的緩沖字符輸出流。

BufferedReader中特有的行讀取方法:String readLine()款咖,此方法可以實現(xiàn)對行的高效讀取何暮。如果讀取到達(dá)流末尾,則返回 null铐殃。readLine方法返回的時候只返回回車符之前的數(shù)據(jù)內(nèi)容海洼,并不返回回車符。其實readLine方法底層還是調(diào)用了read方法富腊。

BufferedWriter中特有的換行方法:void newline(),此方法可以跨平臺實現(xiàn)寫入一個行分隔符坏逢。可以在不同操作系統(tǒng)上調(diào)用赘被,用作數(shù)據(jù)換行

總結(jié):
字符流緩沖區(qū):寫入換行使用BufferedWriter類中的newLine()方法是整。讀取一行數(shù)據(jù)使用BufferedReader類中的readLine()方法。
只要用到緩沖區(qū)民假,就一定要記得刷新贰盗。要不然數(shù)據(jù)還在緩沖區(qū)內(nèi),沒有寫到目的地中去阳欲。(關(guān)閉流同樣會刷新舵盈,在循環(huán)的往目的地寫數(shù)據(jù)的時候,建議寫入一次就刷新一次)球化。

需求秽晚;通過緩沖區(qū)復(fù)制一個.java文件。

import java.io.*;

class CopyJavaByBuf {
    public static void main(String[] args) {
        BufferedReader bfr = null;
        BufferedWriter bfw = null;
        try {
            //為了提高寫入的效率筒愚,使用了字符流的緩沖區(qū)
            //只要將需要被提高效率的流對象作為參數(shù)傳遞給緩沖區(qū)的構(gòu)造函數(shù)即可赴蝇。
            bfr = new BufferedReader(new FileReader("D:\\Demo.java"));
            bfw = new BufferedWriter(new FileWriter("E:\\Demo.java"));
            String line = null;
            //讀取整行數(shù)據(jù),如果已到達(dá)流末尾巢掺,則返回 null
            while ((line = bfr.readLine()) != null) {
                bfw.write(line);
                //寫入內(nèi)容換行方法:newLine();
                bfw.newLine();
                bfw.flush();
            }
        } catch (IOException e) {
            System.out.println(e);
        } finally {
            if (bfr != null)
                try {
                    bfr.close();
                } catch (IOException e) {
                    System.out.println("讀取流關(guān)閉失敗");
                }
            if (bfw != null)
                try {
                    bfw.close();
                } catch (IOException e) {
                    System.out.println("輸出流關(guān)閉失敗");
                }
        }
    }
}

需求:模擬一下BufferedReader句伶。自定義一個類中包含一個功能和readLine一致的方法劲蜻。

import java.io.*;

class MyBufferedReader {
    private Reader r;

    MyBufferedReader(Reader r) {
        this.r = r;
    }

    //自定義一個可以一次讀一行數(shù)據(jù)的方法。
    public String myReadLine() throws IOException {
        //定義一個臨時容器考余。原BufferReader封裝的是字符數(shù)組先嬉。
        //為了演示方便。定義一個StringBuilder容器楚堤。因為最終還是要將數(shù)據(jù)變成字符串
        StringBuilder sb = new StringBuilder();
        int ch = 0;
        while ((ch = r.read()) != -1) {
            if (ch == '\r')
                continue;
            if (ch == '\n')
                return sb.toString();
            else
                sb.append((char) ch);
        }
        if (sb.length() != 0)//如果sb中還有數(shù)據(jù)的話返回數(shù)據(jù)
            return sb.toString();
        return null;//如果已經(jīng)讀到流的末尾了返回null疫蔓。BufferedReader中的readLine()返回的也是null.
    }

    //定義一個自己的關(guān)閉流資源的方法
    public void myClose() throws IOException {
        r.close();//字符流緩沖區(qū)的close方法其實就是調(diào)用了Reader方法中的close的方法,
        //所以只需要關(guān)閉字符流緩沖區(qū)的流對象即可身冬。
    }

}

class MyBufferedReaderDemo {
    public static void main(String[] args) throws IOException {
        MyBufferedReader bfr = new MyBufferedReader(new FileReader("D:\\Demo.java"));
        String line = null;
        while ((line = bfr.myReadLine()) != null) {
            System.out.println(line);
        }
        bfr.myClose();
    }
}

4衅胀、LineNumberReader

跟蹤行號的緩沖字符輸入流。此類定義了方法 setLineNumber(int) 和 getLineNumber()酥筝,它們可分別用于設(shè)置和獲取當(dāng)前行號滚躯。

(1)、LineNumberReader 常用方法

new LineNumberReader(Reader in);//使用默認(rèn)輸入緩沖區(qū)的大小創(chuàng)建新的行編號 reader嘿歌。
int  getLineNumber();//獲取行號
void  setLineNumber();//設(shè)置行號
String  readLine();//讀取整行數(shù)據(jù)

既然LineNumberReader繼承了BufferedReader為何還要復(fù)寫readLine()呢哀九?

因為LineNumberReader的readLine()方法中加入了行號的操作的,所以要復(fù)寫B(tài)ufferedReader中的readLine方法搅幅。

(2)阅束、需求:模擬一個帶行號的緩沖區(qū)對象,就是自定義一個LineNumberReader茄唐。

import java.io.*;

class MyLineNumberReader {
    private Reader r;
    private int lineNumber;

    MyLineNumberReader(Reader r) {
        this.r = r;
    }

    public void setLineNumber(int lineNumber) {//設(shè)置行號
        this.lineNumber = lineNumber;
    }

    public int getLineNumber() {//獲取行號
        return lineNumber;
    }

    public String myReadLine() throws IOException {
        lineNumber++;//每調(diào)用一次myReadLine行號都自增1息裸。
        StringBuilder sb = new StringBuilder();
        int ch = 0;
        while ((ch = r.read()) != -1) {
            if (ch == '\r')
                continue;
            if (ch == '\n')
                return sb.toString();
            sb.append((char) ch);
        }
        if (sb.length() != 0)
            return sb.toString();
        return null;
    }

    public void myClose() throws IOException {
        r.close();
    }
}

class MyLineNumberReaderDemo {
    public static void main(String[] args) throws IOException {
        MyLineNumberReader mlnr = new MyLineNumberReader(new FileReader("D:\\Demo.java"));
        mlnr.setLineNumber(100);
        String line = null;
        while ((line = mlnr.myReadLine()) != null) {
            System.out.println(mlnr.getLineNumber() + ":" + line);
        }
        mlnr.myClose();
    }
}

5、裝飾設(shè)計模式

MyReader//專門用于讀取數(shù)據(jù)的類沪编。

  • MyTextReader

    • MyBufferTextReader
  • MyMediaReader

    • MyBufferMediaReader
  • MyDataReader

    • MyBufferDataReader
class MyBufferReader {
     MyBufferReader(MyTextReader text) {}    
     MyBufferReader(MyMediaReader media){}     
}

上面這個類擴(kuò)展性很差呼盆。

找到其參數(shù)的共同類型。通過多態(tài)的形式蚁廓》闷裕可以提高擴(kuò)展性。

class MyBufferReader extends MyReader{
     private MyReader r;
     MyBufferReader(MyReader r)  {}   
}

MyReader//專門用于讀取數(shù)據(jù)的類相嵌。

  • MyTextReader

    • MyMediaReader
  • MyDataReader

  • MyBufferReader

以前是通過繼承將每一個子類都具備緩沖功能腿时。那么繼承體系會復(fù)雜,并不利于擴(kuò)展》贡觯現(xiàn)在優(yōu)化思想批糟。單獨(dú)描述一下緩沖內(nèi)容。將需要被緩沖的對象看铆。傳遞進(jìn)來徽鼎。也就是,誰需要被緩沖,誰就作為參數(shù)傳遞給緩沖區(qū)否淤。這樣繼承體系就變得很簡單悄但。優(yōu)化了體系結(jié)構(gòu)。

裝飾模式比繼承要靈活石抡。避免了繼承體系臃腫檐嚣。而且降低了類于類之間的關(guān)系。裝飾類因為增強(qiáng)已有對象汁雷,具備的功能和已有的是相同的净嘀,只不過提供了更強(qiáng)功能报咳。所以裝飾類和被裝飾類通常是都屬于一個體系中的侠讯。裝飾和繼承都能實現(xiàn)一樣的特點(diǎn):進(jìn)行功能的擴(kuò)展增強(qiáng)。有什么區(qū)別呢暑刃?
Writer

  • TextWriter:用于操作文本
    • BufferTextWriter:加入了緩沖技術(shù)的操作文本的對象
  • MediaWriter:用于操作媒體
    • BufferMediaWriter:加入了緩沖技術(shù)的操作媒體的對象 以上方式并不理想厢漩,如果這個體系需要再進(jìn)行功能擴(kuò)展,又多了更多流對象岩臣。這樣就會發(fā)現(xiàn)只為提高功能溜嗜,導(dǎo)致繼承體系越來越臃腫,不夠靈活架谎。

重新思考問題:既然加入的都是同一種技術(shù)--緩沖炸宵。前一種是讓緩沖和自己的流對象相結(jié)合」瓤郏可不可以將緩沖進(jìn)行單獨(dú)的封裝土全,哪個對象需要緩沖就將哪個對象和緩沖關(guān)聯(lián)。

class Buffer {
    Buffer(TextWriter w){}
    Buffer(MediaWriter w){}
}

簡化為:

class BufferedWriter extends Writer{ 
    BufferedWriter(Writer w){}
 }

Writer

  • TextWriter:用于操作文本

  • MediaWriter:用于操作媒體

  • BufferedWriter:用于提高效率

可見:裝飾比繼承靈活会涎。

特點(diǎn):裝飾類和被裝飾類都必須所屬同一個接口或者父類裹匙。

三、IO流常用基類----字節(jié)流

1末秃、基本操作與字符流類相同概页。但它不僅可以操作字符,還可以操作其他媒體文件练慕。

2惰匙、由于媒體文件數(shù)據(jù)中都是以字節(jié)存儲的,所以铃将,字節(jié)流對象可直接對媒體文件的數(shù)據(jù)寫入到文件中徽曲,而可以不用再進(jìn)行刷流動作。

1麸塞、OutputStream 抽象類

此抽象類是表示輸出字節(jié)流的所有類的超類秃臣。輸出流接受輸出字節(jié)并將這些字節(jié)發(fā)送到某個接收器。

(1)、OutputStream常用方法

void  close();//關(guān)閉輸出流
void  flush();//刷新輸出流并強(qiáng)制寫出所有緩沖的輸出字節(jié)
void  write(byte[] buf);//寫出一個byte型數(shù)組
void  write(byte[] buf,int off,int len);//將指定 byte 數(shù)組從偏移量 off 開始奥此, len 個字節(jié)寫入此輸出流
void  write(int b);//寫出一個字節(jié)

(2)弧哎、FileOutputStream類

因為OutputStream是抽象的,無法創(chuàng)建對象稚虎。所以我們找到一個專門用于操作文件的OutputStream子類對象:FileOutputStream撤嫩。后綴名是父類名,前綴名是該流對象的功能蠢终。該對象可以直接操作文件序攘。所以可以直接用OutputStream中的方法操作文件。但是這個對象可以創(chuàng)建對象寻拂,我們用它的構(gòu)造函數(shù)程奠。

new  FileOutputStream(String name);//創(chuàng)建一個向具有指定名稱的文件中寫入數(shù)據(jù)的輸出文件流。
new  FileOutputStream(String name,boolean append);//創(chuàng)建一個向具有指定 name 的文件中寫入數(shù)據(jù)的輸出文件流祭钉。該構(gòu)造函數(shù)具有續(xù)寫的功能瞄沙。
new  FileOutputStream(File flie);//創(chuàng)建一個向指定 File 對象表示的文件中寫入數(shù)據(jù)的文件輸出流。
new  FileOutputStream(File file,boolean append);//創(chuàng)建一個向指定 File 對象表示的文件中寫入數(shù)據(jù)的文件輸出流慌核。

代碼演示:

import java.io.*;
class FileOutputStreamDemo 3 {
     public static void main(String[] args)throws IOException 5     {
         //1距境、創(chuàng)建字節(jié)輸出流對象,用于操作文件
         FileOutputStream fos=new FileOutputStream("黑馬.txt");
         //2垮卓、寫數(shù)據(jù)垫桂,直接寫入到了目的地中
         fos.write("我愛黑馬".getBytes()); 
         //OutputStream 可以不用flush。因為字節(jié)流操作的是字節(jié)粟按,是數(shù)據(jù)的最小單位诬滩。可以直接輸出而不用刷新钾怔。 
        //3碱呼、關(guān)閉資源動作要完成
         fos.close(); 
    } 
 }

2、InputStream 抽象類

此抽象類是表示字節(jié)輸入流的所有類的超類宗侦。

(1)愚臀、 InputStream中的常用方法

int available();//返回輸入流讀取的字節(jié)數(shù)
void close();//關(guān)閉此輸入流
abstract int   read();//一個一個字符的讀取數(shù)據(jù)
int  read(byte[] b);//從輸入流中讀取一定數(shù)量的字,并將其存儲在緩沖區(qū)數(shù)組 b 中矾利。
int  read(byte[] b,int off,int len);//將輸入流中最多 len 個數(shù)據(jù)字節(jié)讀入 byte 數(shù)組姑裂。

(2)、FileInputStream類

因為InputStream是抽象的男旗,無法創(chuàng)建對象舶斧。所以我們找到一個專門用于操作文件的InputStream子類對象:FileInputStream。后綴名是父類名察皇,前綴名是該流對象的功能茴厉。該對象可以直接操作文件泽台。所以可以直接用InputStream中的方法操作文件。但是這個對象可以創(chuàng)建對象矾缓,我們用它的構(gòu)造函數(shù)怀酷。

new InputStream(String name);//創(chuàng)建一個向具有指定名稱的文件中寫入數(shù)據(jù)的讀取文件流。
new InputStream(File file);//創(chuàng)建一個向指定 File 對象表示的文件中寫入數(shù)據(jù)的文件讀取流嗜闻。

代碼演示:

import java.io.*;

class FileInputStreamDemo {
    public static void main(String[] args) throws IOException {

//        read_1();
//        read_2();
        read_3();
    }

    //單個字節(jié)的讀取
    public static void read_1() throws IOException {
        //創(chuàng)建一個讀取流對象蜕依,和指定文件關(guān)聯(lián)
        FileInputStream fis = new FileInputStream("Demo.txt");
        int ch = 0;
        while ((ch = fis.read()) != -1) {
            System.out.print((char) ch);
        }
        fis.close();
    }

    //創(chuàng)建緩沖區(qū),提高讀取效率
    public static void read_2() throws IOException {
        FileInputStream fis = new FileInputStream("Demo.txt");
        int len = 0;
        //建立數(shù)組把數(shù)據(jù)存儲到數(shù)組緩沖區(qū)中
        byte[] buf = new byte[1024];
        while ((len = fis.read(buf)) != -1) {
            System.out.print(new String(buf, 0, len));
        }
        fis.close();
    }

    //創(chuàng)建一個剛剛好的數(shù)組
    public static void read_3() throws IOException {
        FileInputStream fis = new FileInputStream("Demo.txt");
        //打印字符字節(jié)大小琉雳,文件太大样眠,可能內(nèi)存溢出。不建議用
        byte[] buf = new byte[fis.available()];
        int len = fis.read(buf);
        System.out.println(new String(buf, 0, len));
    fis.close();
    }
}

需求:復(fù)制一個圖片

思路:

1翠肘,用字節(jié)讀取流對象和圖片關(guān)聯(lián)檐束。

2,用字節(jié)寫入流對象創(chuàng)建一個圖片文件锯茄。用于存儲獲取到的圖片數(shù)據(jù)厢塘。

3茶没,通過循環(huán)讀寫肌幽,完成數(shù)據(jù)的存儲。

4抓半,關(guān)閉資源喂急。

import java.io.*;

class CopyPicDemo {
    public static void main(String[] args) throws IOException {
        copy_1();
        copy_2();
    }

    //一個字節(jié)一個字節(jié)的讀寫
    public static void copy_1() throws IOException {
        FileInputStream fis = new FileInputStream("D:\\1.jpg");
        FileOutputStream fos = new FileOutputStream("E:\\1.jpg");
        int ch = 0;
        while ((ch = fis.read()) != -1) {
            fos.write(ch);
        }
        fis.close();
        fos.close();
    }

    //定義數(shù)組緩沖區(qū)高效讀寫
    public static void copy_2() throws IOException {
        FileInputStream fis = new FileInputStream("D:\\1.jpg");
        FileOutputStream fos = new FileOutputStream("E:\\1.jpg");
        byte[] buf = new byte[1024];
        int len = 0;
        while ((len = fis.read(buf)) != -1) {
            fos.write(buf, 0, len);
        }
        fos.close();
        fis.close();
    }
}

需求:自定義一個字節(jié)流緩沖區(qū)復(fù)制mp3文件。

import java.io.*;
class MyBufferedInputStream
{
    private InputStream in;
    private byte[] buf=new byte[1024];
    private int count=0,pos=0;//初始化個數(shù)笛求,和數(shù)組指針
    MyBufferedInputStream(InputStream in){
        this.in=in;
    }
    public int myRead()throws IOException{
        //如果緩沖區(qū)中沒有數(shù)據(jù)count==0廊移,那么就從硬盤上取一批數(shù)據(jù)到數(shù)組緩沖區(qū)中,
        //用count記錄住數(shù)組中的數(shù)據(jù)個數(shù)探入,每次獲取數(shù)據(jù)到緩沖區(qū)后狡孔,角標(biāo)歸零
        if(count==0){
            count=in.read(buf);
            pos=0;
        }
        //如果count<0的時候表示數(shù)組中沒有數(shù)據(jù)了,返回-1
        if(count<0){
            return -1;
        }
        else{//表示數(shù)組中有數(shù)據(jù)蜂嗽,就從數(shù)組中一個一個的取數(shù)據(jù)
            byte by=buf[pos];
            pos++;
            count--;
            return by&255;//返回的byte類型提升為int類型苗膝,字節(jié)數(shù)增加,且高24位被補(bǔ)1植旧,原字節(jié)數(shù)據(jù)改變辱揭。
                          //通過與上255,主動將byte類型提升為int類型病附,將高24位補(bǔ)0问窃,原字節(jié)數(shù)據(jù)不變。
                          //而在輸出字節(jié)流寫入數(shù)據(jù)時完沪,只寫該int類型數(shù)據(jù)的最低8位域庇。
        }
    }
    public void myClose()throws IOException{
        in.close();
    }
}
class CopyMp3Demo
{
    public static void main(String[] args) throws IOException
    {
        long start=System.currentTimeMillis();
        runCode();
        long end=System.currentTimeMillis();
        System.out.println("毫秒:"+(end-start));
    }
    public static void runCode()throws IOException{
        MyBufferedInputStream fis=new MyBufferedInputStream(new FileInputStream("D:\\年輪.mp3"));
        FileOutputStream fos=new FileOutputStream("E:\\年輪.mp3");
        int ch=0;
        while ((ch=fis.myRead())!=-1)
        {
            fos.write(ch);
        }
        fis.myClose();
        fos.close();
    }
}

結(jié)論:

字節(jié)流的讀一個字節(jié)的read方法為什么返回值類型不是byte,而是int?

因為有可能會讀到連續(xù)8個二進(jìn)制1的情況听皿,8個二進(jìn)制1對應(yīng)的十進(jìn)制是-1咕别。那么就會數(shù)據(jù)還沒有讀完,就結(jié)束的情況写穴。因為我們判斷讀取結(jié)束是通過結(jié)尾標(biāo)記-1來確定的惰拱。所以,為了避免這種情況將讀到的字節(jié)進(jìn)行int類型的提升啊送。并在保留原字節(jié)數(shù)據(jù)的情況前面了補(bǔ)了24個0偿短,變成了int類型的數(shù)值。而在寫入數(shù)據(jù)時馋没,只寫該int類型數(shù)據(jù)的最低8位昔逗。所以:read方法在提升,而write方法在降低篷朵。這樣就不會導(dǎo)致數(shù)據(jù)的不一致勾怒。

四、轉(zhuǎn)換流----InputStreamReader声旺、OutputStreamWriter

繼承體系

Reader

|--InputStreamReader

|--FileReader

Writer

|--OuputStreamWriter

|--FileWriter

轉(zhuǎn)換流最強(qiáng)大的地方就是可以指定編碼表笔链。

(1)、InputStreamReader 字節(jié)流通向字符流的橋梁腮猖。解碼鉴扫。

它使用指定的 charset 讀取字節(jié)并將其解碼為字符。它使用的字符集可以由名稱指定或顯式給定澈缺,或者可以接受平臺默認(rèn)的字符集坪创。

構(gòu)造方法:

new InputStreamReader(InputStream in);//創(chuàng)建一個使用默認(rèn)字符集的 InputStreamReader
new InputStreamReader(InputStream in,Charset cs);//創(chuàng)建使用給定字符集的 InputStreamReader
new InputStreamReader(InputStream in,CharsetDecoder dec);//創(chuàng)建使用給定字符集解碼器的 InputStreamReader
new InputStreamReader(InputStream in,String charsetName);//創(chuàng)建使用指定字符集的 InputStreamReader

(2)、OutputStreamWriter 字符流通向字節(jié)流的橋梁姐赡。編碼莱预。

可使用指定的 charset 將要寫入流中的字符編碼成字節(jié)。它使用的字符集可以由名稱指定或顯式給定项滑,否則將接受平臺默認(rèn)的字符集依沮。

new OutputStreamWriter(OutputStream out);//創(chuàng)建使用默認(rèn)字符編碼的 OutputStreamWriter
new OutputStreamWriter(OutputStream out,Charset cs);//創(chuàng)建使用給定字符集的 OutputStreamWriter 
new OutputStreamWriter(OutputStream out, CharsetEncoder enc);//創(chuàng)建使用給定字符集編碼器的 OutputStreamWriter
new OutputStreamWriter(OutputStream out, String charsetName));//創(chuàng)建使用指定字符集的 OutputStreamWriter

使用字節(jié)流讀取一個中文字符需要讀取兩次,因為一個中文字符由兩個字節(jié)組成杖们,而使用字符流只需讀取一次悉抵。System.out的類型是PrintStream,屬于OutputStream類別摘完。

什么時候使用轉(zhuǎn)換流呢姥饰?
1、源或者目的對應(yīng)的設(shè)備是字節(jié)流孝治,但是操作的卻是文本數(shù)據(jù)列粪,可以使用轉(zhuǎn)換作為橋梁审磁,提高對文本操作的便捷∑褡  
2态蒂、一旦操作文本涉及到具體的指定編碼表時,必須使用轉(zhuǎn)換流费什。

(3)钾恢、讀取鍵盤錄入

鍵盤本身就是一個標(biāo)準(zhǔn)的輸入設(shè)備。對于java而言鸳址,對于這種輸入設(shè)備都有對應(yīng)的對象瘩蚪。默認(rèn)的輸入和輸出系統(tǒng)不需要關(guān),它會隨著系統(tǒng)的結(jié)束而消失稿黍。

System.in:對應(yīng)的標(biāo)準(zhǔn)輸入設(shè)備疹瘦,鍵盤。

System.out:對應(yīng)的是標(biāo)準(zhǔn)的輸出設(shè)備巡球,控制臺言沐。

System.in的類型是InputStream。

System.out的類型是PrintStream是OutputStream的子類酣栈。

需求:獲取用戶鍵盤錄入的數(shù)據(jù)并將數(shù)據(jù)變成大寫顯示在控制臺上险胰,如果用戶輸入的是over,結(jié)束鍵盤錄入钉嘹。

思路:
1. 因為鍵盤錄入只讀取一個字節(jié)鸯乃,要判斷是否是over鲸阻,需要將讀取到的字節(jié)拼成字符串跋涣。
2. 那就需要一個容器:StringBuilder。
3. 在用戶回車之前將錄入的數(shù)據(jù)變成字符串判斷即可鸟悴。

import java.io.*;

class ReadKey {
    public static void main(String[] args) throws IOException {
        //獲取鍵盤讀取流,System.in 是InputStream的子類
        InputStream in = System.in;
        //創(chuàng)建容器
        StringBuilder sb = new StringBuilder();
        //定義變量記錄讀取到的字節(jié)陈辱,并循環(huán)獲取
        int ch = 0;
        while ((ch = in.read()) != -1) {
            //如果是\r,繼續(xù)循環(huán)
            if (ch == '\r')
                continue;
            //如果是\n,把緩沖區(qū)的數(shù)據(jù)變成字符串
            if (ch == '\n') {
                String s = sb.toString();
                if ("over".equals(s))//如果變成的字符串正好是over细诸,就停止循環(huán)
                    break;
                System.out.println(s.toUpperCase());
                sb.delete(0, sb.length());//清空緩沖區(qū)的數(shù)據(jù)
            } else
                sb.append((char) ch);//如果以上的情況都不是沛贪,則將讀取到的字節(jié)存儲到StringBuilder中

        }
    }
}

通過剛才的鍵盤錄入一行數(shù)據(jù)并打印其大寫,發(fā)現(xiàn)其實就是讀一行數(shù)據(jù)的原理震贵。也就是readLine方法利赋。能不能直接使用readLine方法來完成鍵盤錄入的一行數(shù)據(jù)的讀取呢?

readLine方法是字符流BufferedReader類中的方法猩系。而鍵盤錄入的read方法是字節(jié)流InputStream的方法媚送。那么能不能將字節(jié)流轉(zhuǎn)成字符流在使用字符流緩沖去的readLine方法呢?

這時候我們查看API發(fā)現(xiàn)Reader類中有一個字節(jié)通向字符的橋梁:InputStreamReader,而Writer類中有一個字符通向字節(jié)的橋梁:OutputStreamWriter寇甸。

這兩個轉(zhuǎn)換流可以將字節(jié)流轉(zhuǎn)換成字符流塘偎,這樣我們就可以用字節(jié)流緩沖區(qū)的readLine方法一行一行的讀取數(shù)據(jù)比較方便疗涉。

import java.io.*;
class ReadIn{
    public static void main(String[] args)throws IOException {
        //獲取鍵盤錄入對象
        InputStream in=System.in;

        //將字節(jié)流轉(zhuǎn)成字符流,用轉(zhuǎn)換流InputStreamReader
        InputStreamReader isr=new InputStreamReader(in);

        //為了提高效率,將字符串進(jìn)行緩沖區(qū)技術(shù)高效操作吟秩。
        BufferedReader bfr=new BufferedReader(isr);

        //字節(jié)輸出流
        OutputStream out=System.out;
        //將字符轉(zhuǎn)成字節(jié)的橋梁
       OutputStreamWriter osw=new OutputStreamWriter(out);

        //對字符流進(jìn)行高效裝飾咱扣,緩沖區(qū)
        BufferedWriter bfw=new BufferedWriter(osw);
        String line=null;
        while((line=bfr.readLine)!=null){
            if(“over”.equals(line))
                break;
            bfw.write(line.toUpperCase());
            bfw.newLine();
            bfw.flush();
        }
}

上面的代碼可以簡寫成下面的代碼:

import java.io.*;
class ReadKey {
    public static void main(String[] args) throws IOException {
        //讀取鍵盤錄入最常見的方法
        BufferedReader in=new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter out=new BufferedWriter(new OutputStreamWriter(System.out));
        String line=null;
        while ((line=in.readLine())!=null) {
            if("over".equals(line))
                break;
            out.write(line.toUpperCase());
            out.newLine();
            out.flush();
        }
        in.close();
        out.close();
    }
}

獲取鍵盤錄入數(shù)據(jù),然后將數(shù)據(jù)流向顯示器涵防,那么顯示器就是目的地闹伪。 通過System類的setIn(InputStream in),setOut(PrintStream out)方法可以對默認(rèn)設(shè)備進(jìn)行改變壮池。

System.setIn(new FileInputStream(“1.txt”));//將源改成文件1.txt祭往。

System.setOut(new PrintStream(“2.txt”));//將目的改成文件2.txt

因為是字節(jié)流處理的是文本數(shù)據(jù),可以轉(zhuǎn)換成字符流火窒,操作更方便硼补。

讀取鍵盤錄入最常見的方法:BufferedReader in=new BufferedReader(new InputStreamReader(System.in));

擴(kuò)展:鍵盤錄入常見的三種操作

方法一:從控制臺接收一個字符,然后將其打印出來

import java.io.*; 
public static void main(String[] args)throws IOException{ 
     System.out.print(“請輸入一個字符”); 
     char ch=(char)System.in.read(); 
     System.out.println(“你輸入的字符是:”+ch); 
}

雖然上面的方式實現(xiàn)了從鍵盤獲取輸入的字符熏矿,但是System.out.read()只能針對一個字符的獲取已骇,同時,獲取進(jìn)來的變量的類型只能是char,當(dāng)我們輸入一個數(shù)字票编,希望得到的也是一個整型變量的時候褪储,我們還得修改其中的變量類型,這樣就顯得比較麻煩慧域。

方法二:從控制臺接收一個字符串鲤竹,然后將其打印出來。用到BufferedReader類和InputStreamReader類昔榴。

import java.io.*; 
public static void main(String[] args)throws IOException{ 
     BufferedReder bfr=new BufferedReader(new             
     InputStreamReader(System.in)); 
     String str=null; 
     System.out.println(“輸入你的數(shù)據(jù):”); 
     str.bfr.readLine(); 
     System.out.println(“輸入的值是:”+str); 
}

這種方法可以獲取我們輸入的字符串?dāng)?shù)據(jù)辛藻。

//第三種方法:這種方法我認(rèn)為是最簡單,最強(qiáng)大的互订,就是用Scanner類吱肌。
import java.util.Scanner;
public static void main(String[] args){
    Scanner sc=new Scanner(System.in);
    Systm.out.println(“請輸入你的名字”);
    String name=sc.nextLine();
    System.out.println(“請輸入你的年齡”);
    int age=sc.nextInt();
    System.out.println(“請輸入你的工資”);
    double salary=sc.nextDouble();
    System.out.println(“你的信息如下:”);
    System.out.println("姓名:"+name+"\n"+"年齡:"+age+"\n"+"工資:"+salary);
}

這段代碼已經(jīng)表明,Scanner類不管是對于字符串還是整型數(shù)據(jù)或者float類型的變量仰禽,只需做一點(diǎn)小小的改變氮墨,就能夠?qū)崿F(xiàn)功能!無疑他是最強(qiáng)大的吐葵!

五规揪、流操作的規(guī)律

之所以要弄清楚這個規(guī)律竿滨,是因為流對象太多音诫,開發(fā)時不知道用哪個對象合適。想要知道對象的開發(fā)時用到哪些對象耿焊,只要通過四個明確即可诚镰。

1奕坟、流操作規(guī)律

(1)祥款、明確源和目的
源:輸入流。InputStream Reader
目的:輸出流月杉。OutputStream Writer

(2)刃跛、操作的數(shù)據(jù)是否是純文本
源:是純文本:字符流Reader
不是:字節(jié)流 InputStream
目的:是純文本:字符流Writer
不是:字節(jié)流: OutputStream

(3)、當(dāng)體系明確后苛萎,在明確要使用哪個具體的對象桨昙。
通過設(shè)備可以來進(jìn)行區(qū)分:
源設(shè)備:硬盤(File)、鍵盤(System.in)腌歉、內(nèi)存(數(shù)組流)蛙酪、網(wǎng)絡(luò)(Socket流)。
目的設(shè)備:硬盤(File)翘盖、控制臺(System.out)桂塞、內(nèi)存(數(shù)組流)、網(wǎng)絡(luò)(Socket流)馍驯。

(4)阁危、是否西藥其他額外的功能
是否需要高效(緩沖區(qū)):是,就加上buffered汰瘫。

需求1:復(fù)制一個文本文件

(1)狂打、明確源和目的

源:InputStream Reader

目的:OutputStream Writer

(2)、是否是純文本

是:源:Reader

目的:Writer

(3)混弥、明確操作的設(shè)備

源:硬盤:File

目的:硬盤:File

FileReader fr=new FileReader(“a.txt”); 
FileWriter fw=new FileWriter(“b.txt”);

(4)趴乡、是否需要高效

BufferedReader bfr=new BufferedReader(fr);
BufferedWriter bfw=new BufferedWriter(fw);

需求2:讀取鍵盤錄入信息,并寫入到一個文件中

(1)蝗拿、明確源和目的
源:InputStream Reader
目的:OutputStream Writer
(2)晾捏、明確是否是純文本
是:源:Reader
目的:Writer
(3)、明確具體設(shè)備
源:鍵盤:System.in
目的:文件:File

System.in對應(yīng)的不是字節(jié)流嗎蛹磺?為了操作鍵盤的文本數(shù)據(jù)方便粟瞬。轉(zhuǎn)成字符流按照字符串操作是最方便的。所以既然明確了Reader萤捆,那么就將System.in轉(zhuǎn)換成Reader。

用了Reader體系中轉(zhuǎn)換流,InputStreamReader俗批。
InputStream is=(new InputStreamReader(System.in);
FileWriter fw=new FileWriter(“c.txt”);

(4)俗或、需要高效嗎?
BufferedReader bfr=new BufferedReader(is);
BufferedWriter bfw=new BufferedWriter(fw);

需求3:將一個文本文件數(shù)據(jù)顯示在控制臺上

(1)岁忘、明確源和目的

源:InputStream Reader

目的:OutputStream Writer

(2)辛慰、是否是純文本

是:源:Reader

目的:Writer

(3)、明確設(shè)備

源:文件:File

目的:控制臺:System.out

FileReader fr=new FileReader(“a.txt”);
OutputStreamWriter osw=new OutputStreamWriter(System.out);

(4)干像、是否需要高效
BufferedReader bfr=new BufferedReader(fr);
BufferedWriter bfw=new BufferedWiter(osw);

需求4:讀取鍵盤錄入數(shù)據(jù)帅腌,顯示在控制臺上

(1)驰弄、明確源和目的

源:InputStream Reader

目的:OutputStream Writer

(2)、是否是純文本

是:源:Reader

目的:Writer

(3)速客、明確具體設(shè)備

源:鍵盤:System.in

目的:控制臺:System.out

InputStream is=System.in;
OutputStream os=System.out;</pre>

(4)戚篙、是否需要轉(zhuǎn)換

需要轉(zhuǎn)換。因為都是字節(jié)流溺职,但是操作的卻是文本數(shù)據(jù)岔擂。所以使用字符流操作起來更為方便。
InputStreamReader isr=new InputStreamReader(is);
OutputStreamWriter osw=new OutputStreamWriter(os);

(5)浪耘、是否需要高效

需要

BufferedReader bfr=new BufferedReader(isr);
3 BufferWriter bfw=new BufferedWriter(osw);

練習(xí):將一個中文字符串?dāng)?shù)據(jù)按照指定的編碼表寫入到一個文本文件中乱灵。

(1)、目的:OutputStream Writer

(2)七冲、是否純文本:是:Writer

(3)痛倚、具體設(shè)備:硬盤:File

import java.io.*;
 class IODemo  {
     public static void main(String[] args) throws IOException 5     {
         //FileWriter其實就是轉(zhuǎn)換流指定了本機(jī)默認(rèn)碼表的體現(xiàn),而且這個轉(zhuǎn)換流的子類對象澜躺,可以方便操作文本文件状原。
         //簡單說:操作文件的字節(jié)流+本機(jī)默認(rèn)的編碼表(GBK)。
         //這是按照默認(rèn)碼表來操作文件的便捷類
        FileWriter fw=new FileWriter("D:\\1.txt"); 
         fw.write("你好"); 11         
         //用轉(zhuǎn)換流,指定編碼格式苗踪,這句話和上面的代碼一樣都是GBK編碼
         OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("D:\\2.txt"),"GBK"); 
         osw.write("你好"); 15 
         //用轉(zhuǎn)換流,指定UTF-8編碼格式
         OutputStreamWriter out=new OutputStreamWriter(new FileOutputStream("D:\\3.txt"),"UTF-8"); 
         out.write("你好"); 
         fw.close(); 
         osw.close();
         out.close(); 
      }
}

雖然上面的三種寫入方式颠区,記事本打開都能顯示"你好",但是仔細(xì)看了一下字節(jié)數(shù)發(fā)現(xiàn):FileWriter使用的是默認(rèn)編碼和指定GBK編碼格式的字節(jié)數(shù)一樣都是4個字節(jié)通铲,而UTF-8寫入的數(shù)據(jù)是6個字節(jié)毕莱。可以得到FileWriter的默認(rèn)編碼格式就是本機(jī)的編碼(GBK)颅夺,而UTF-8編碼朋截,一個中文三個字節(jié),GBK編碼吧黄,一個中文二個字節(jié)部服。可以用System類中的getProperties方法獲取系統(tǒng)的屬性信息拗慨。

image

需求:打印3.txt文件中的內(nèi)容至控制臺顯示廓八。

import java.io.*;
class IODemo {
public static void main(String[] args) throws IOException {
BufferedReader bfr=new BufferedReader(new
FileReader("D:\3.txt"));
String line=bfr.readLine();
System.out.println(line);
bfr.close();
}
}

運(yùn)行結(jié)果;

image

原因分析:因為3.txt文件中的數(shù)據(jù)是用UTF-8指定編碼進(jìn)行寫入的,一個中文是3個字節(jié)赵抢,而讀取的時候用GBK編碼進(jìn)行讀取的時候剧蹂,GBK一個中文是2個字節(jié)。由于GBK編碼的字節(jié)使用UTF-8沒有對應(yīng)的字符烦却,所以讀取的時候2個字節(jié)2個字節(jié)的讀取宠叼,導(dǎo)致了讀取成了3個亂碼:浣、犲其爵、ソ冒冬。

需求:按照指定編碼寫字符到指定文件中伸蚯。

如果需求中已經(jīng)明確指定了編碼表。那就不可以使用FileWriter简烤,因為FileWriter內(nèi)部使用的默認(rèn)的本地碼表剂邮。只能使用其父類,OutputStreamWriter乐埠。

OutputStreamWriter接收一個字節(jié)輸出流對象抗斤,既然是操作文件,那么該對象應(yīng)該是FileOutputStream丈咐。

<pre style="margin: 0px; padding: 0px;">1 OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("a.txt"),charsetName);</pre>

并且需要高效:
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("a.txt"),charsetName));

2瑞眼、System類中的setIn和setOut方法

System類中的setIn和setOut方法可以改變標(biāo)準(zhǔn)的輸入和輸出設(shè)備(鍵盤和控制臺)。

InputStream    System.in //“標(biāo)準(zhǔn)”輸入流棵逊∩烁恚“標(biāo)準(zhǔn)”輸入流。
PrintStream     System.out //“標(biāo)準(zhǔn)”輸出流辆影。
void  setIn(InputStream in);//重新分配標(biāo)準(zhǔn)輸入
void  setOut(PrintStream  out);//重新分配標(biāo)準(zhǔn)輸出流

java.io.OutputStream

 |--java.io.FilterOutputStream

      |-- java.io.PrintStream

PrintStream是非抽象的徒像,可以直接創(chuàng)建對象。所以我們可以用其構(gòu)造函數(shù)創(chuàng)建對象蛙讥。

new  PrintStream(String fileName);//創(chuàng)建具有指定文件名稱且不帶自動行刷新的新打印流锯蛀。
new PrintStream(OutputStream out);//創(chuàng)建新的打印流。
new  PrintStream(OutputStream  out,boolean autFlush);//創(chuàng)建新的打印流次慢。當(dāng)boolean值為true的時候旁涤,能自動刷新

代碼演示:

import java.io.*;

class PrintStreamDemo {
    public static void main(String[] args) throws IOException {
        //改變標(biāo)準(zhǔn)的輸入流設(shè)備
        System.setIn(new FileInputStream("D:\\Demo.java"));

        //改變標(biāo)準(zhǔn)的輸出流設(shè)備
        System.setOut(new PrintStream("E:\\Demo.java"));
    //如果同時改變標(biāo)準(zhǔn)的輸入和輸出設(shè)備的話,可以達(dá)到類似于文件復(fù)制的效果
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

        PrintStream out = new PrintStream(new FileOutputStream("E:\\Demo.java"), true);

        String line = null;
        while ((line = br.readLine()) != null) {
            if ("over".equals(line))
                break;
            out.println(line);
//            bw.write(line.toUpperCase());
//            bw.newLine();
//            bw.flush();
        }
        br.close();
        br.close();
    }
}

3迫像、printStackTrace(PrintStream s)異常和IO相結(jié)合的方法

以前我們只學(xué)了打印異常堆棧信息的printStackTrace()的方法劈愚,查閱API可以發(fā)現(xiàn)其有重載方法printStackTrace(PrintStream s)和printStackTrace(PrintWriter s)。這兩個方法可以將異常信息存放到指定的地方闻妓,而不是僅僅顯示在控制臺上菌羽。

import java.io.*;
import java.text.*;
import java.util.*;
class PrintStackTrace {
    public static void main(String[] args)throws Exception {
        try {
            //制造數(shù)組角標(biāo)越界異常
            int[] arr=new int[4];
            System.out.println(arr[4]);
        } catch (ArrayIndexOutOfBoundsException e) {
            //格式化日期對象
            Date d=new Date();
            SimpleDateFormat sdf=new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
            String date=sdf.format(d);
            //把異常信息打印到指定的文件中
            PrintWriter pw=new PrintWriter(new FileOutputStream("E:\\exception.log"),true);
            pw.println(date);
            e.printStackTrace(pw);
        }
    }
}

4、獲取系統(tǒng)屬性信息由缆。

System類中的getProperties()可以獲取當(dāng)前系統(tǒng)屬性信息注祖,該方法返回值類型是:Properties類,該類中有個可以將屬性列表輸出到指定的輸出流的方法:list(PrintStream out)犁功。

import java.io.*;
import java.util.*;
class PropertiesDemo {
    public static void main(String[] args)throws IOException {
        //獲取Properties對象氓轰,Properties是Hashtable的子類,里面存放的鍵值對
        Properties pro=System.getProperties();
        PrintWriter out=new PrintWriter(new     
        FileOutputStream("E:\\propetties.ini"),true); 
        pro.list(System.out);//打印到控制臺上
        pro.list(out);
     }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末浸卦,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子案糙,更是在濱河造成了極大的恐慌限嫌,老刑警劉巖靴庆,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異怒医,居然都是意外死亡炉抒,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進(jìn)店門稚叹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來焰薄,“玉大人,你說我怎么就攤上這事扒袖∪” “怎么了?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵季率,是天一觀的道長野瘦。 經(jīng)常有香客問我,道長飒泻,這世上最難降的妖魔是什么鞭光? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮泞遗,結(jié)果婚禮上惰许,老公的妹妹穿的比我還像新娘。我一直安慰自己史辙,他們只是感情好汹买,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著髓霞,像睡著了一般卦睹。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上方库,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天结序,我揣著相機(jī)與錄音,去河邊找鬼纵潦。 笑死徐鹤,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的邀层。 我是一名探鬼主播返敬,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼寥院!你這毒婦竟也來了劲赠?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎凛澎,沒想到半個月后霹肝,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡塑煎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年沫换,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片最铁。...
    茶點(diǎn)故事閱讀 40,680評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡讯赏,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出冷尉,到底是詐尸還是另有隱情漱挎,我是刑警寧澤,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布网严,位于F島的核電站识樱,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏震束。R本人自食惡果不足惜怜庸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望垢村。 院中可真熱鬧割疾,春花似錦、人聲如沸嘉栓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽侵佃。三九已至麻昼,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間馋辈,已是汗流浹背抚芦。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留迈螟,地道東北人叉抡。 一個月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像答毫,于是被迫代替她去往敵國和親褥民。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評論 2 361

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