Java基礎進階 IO流之字節(jié)流

1、IO流

1.1绿饵、概述

之前學習的File類它只能操作文件或文件夾欠肾,并不能去操作文件中的數(shù)據(jù)。真正保存數(shù)據(jù)的是文件拟赊,數(shù)據(jù)是在文件中刺桃。而File類它只是去對文件本身做操作,不能對文件中的數(shù)據(jù)進行操作吸祟。
如果要操作文件中的數(shù)據(jù)瑟慈,這時必須使用Java中提供的IO流技術完成桃移。
IO可以實現(xiàn)數(shù)據(jù)的傳輸,把數(shù)據(jù)比作是像水一樣在流動葛碧。
IO:input/output
IO流:它的功能就是專門操作文件中的數(shù)據(jù)借杰。

2.2、IO流分類

1)按流向分:
輸入流:讀取數(shù)據(jù)进泼,把持久設備的數(shù)據(jù)讀取到內(nèi)存中蔗衡。
輸出流:寫出數(shù)據(jù),把內(nèi)存的數(shù)據(jù)寫出到持久設備乳绕。
2)按數(shù)據(jù)類型分:
計算機中一切數(shù)據(jù)都是:字節(jié)數(shù)據(jù)绞惦。
字符數(shù)據(jù):底層還是字節(jié)數(shù)據(jù),但是可以根據(jù)某些規(guī)則洋措,把字節(jié)變成人們認識的文字济蝉、符號等等。
字節(jié)流:數(shù)據(jù)在持久設備上都是以二進制形式保存的菠发。二進制就是字節(jié)數(shù)據(jù)王滤。Java就給出了字節(jié)流可以直接操作字節(jié)數(shù)據(jù)。

字節(jié)輸入流:InputStream
兒子:XxxInputStream
字節(jié)輸出流:OutputStream
兒子:XxxOutputStream

字符流:讀取字符數(shù)據(jù)滓鸠。數(shù)據(jù)在設備上是以二進制形式表示淑仆,但是有些二進制合并在一起可以表示一些字符數(shù)據(jù)。

字符輸入流:Reader
兒子:XxxReader

字符輸出流:Writer
兒子:XxxWriter

IO流的分類如下圖所示:


IO流的分類

說明:
1)字節(jié)流可以對任意類型的文件按照字節(jié)進行讀和寫的操作哥力;
例如:圖片、視頻墩弯、文本文件吩跋、word文檔、mp3等渔工。
2)字符流只能對文本類型的文件進行操作锌钮;
問題1:文本類型的文件是什么文件?
只要可以使用記事本打開并看得懂的文件就是文本文件引矩。
例如:.java文件梁丘、.txt等文件。
而字符流只能操作文本類型的文件旺韭,也就是說如果一個文件可以使用記事本打開并能夠看懂氛谜,那么這個文件就可以使用字符流來操作,否則其他的文件都得使用字節(jié)流進行操作区端。

注意:字節(jié)流也可以操作文本文件值漫。

2 字節(jié)流

2.1、字節(jié)流介紹

字節(jié)流:它是以字節(jié)為單位织盼,讀寫數(shù)據(jù)的杨何。

讀寫:
讀是從持久設備上給程序讀取數(shù)據(jù)酱塔。(硬盤-------》內(nèi)存)
寫是把程序中的數(shù)據(jù)寫到持久設備上。(內(nèi)存-------》硬盤)
不管是字節(jié)流還是字符流危虱,他們都有讀和寫的操作羊娃。
字節(jié)流:它分成字節(jié)輸入流和字節(jié)輸出流。
字節(jié)輸入流:從持久設備上把數(shù)據(jù)讀取到程序中埃跷。
字節(jié)輸出流:把程序中的數(shù)據(jù)寫到持久設備上蕊玷。
所有的數(shù)據(jù)都可以使用字節(jié)流操作。
常見的數(shù)據(jù)保存形式:記事本捌蚊、word文檔集畅、圖片、音頻缅糟、視頻挺智、壓縮文件等

2.2、字節(jié)輸出流(掌握)

之前我們在學習其他類的時候窗宦,例如異常赦颇、集合都有頂層父類或者頂層接口。那么在字節(jié)流中也有頂層父類赴涵,規(guī)定了字節(jié)輸入流或字節(jié)輸出流的基本操作行為媒怯。

字節(jié)流:
字節(jié)輸出:Output 字節(jié)輸出流:OutputStream
字節(jié)輸入:Input 字節(jié)輸入流:InputStream


1.png

OutputStream:它是字節(jié)輸出流的頂層父類。它可以把字節(jié)數(shù)據(jù)寫給JVM髓窜,JVM在交給操作系統(tǒng)扇苞,操作系統(tǒng)把數(shù)據(jù)寫到持久設備上。

OutputStream類中的函數(shù)如下所示:


2.png

注意: 學習IO流寄纵,我們是在使用Java代碼操作Java以外的其他設備鳖敷。不管操作中是否有問題,最后都要斷開Java程序和這些設備之間的連接程拭。

   close方法是關閉Java和其他設備之間的連接定踱。
   write方法是把數(shù)據(jù)寫到Java關聯(lián)的設備中
   write(byte[] b ) 把這個b字節(jié)數(shù)組中的所有數(shù)據(jù)寫到關聯(lián)的設備中(設備包括文件、網(wǎng)絡或者其他任何地方)恃鞋。
   write(byte[] b , int off , int len ) 把b字節(jié)中的數(shù)據(jù)從下標off位置開始往出寫崖媚,共計寫len個
   write(int b ) 把這個b數(shù)據(jù)寫到關聯(lián)的設備中。 

OutputStream:它是抽象類恤浪,不能創(chuàng)建對象畅哑,這里我們需要使用OutputStream類的子類FileOutputStream的對象把數(shù)據(jù)寫到文件中。


3.png

構造方法如下所示:


4.png

FileOutputStream(Filefile)
創(chuàng)建一個向指定 File 對象表示的文件中寫入數(shù)據(jù)的文件輸出流水由。

FileOutputStream(String name)
創(chuàng)建一個向具有指定名稱的文件中寫入數(shù)據(jù)的輸出文件流敢课。

需求1:將一個字節(jié)byte數(shù)組{97,98,65,66}中的數(shù)據(jù)寫到D:\test1\1.txt 的文件中。

分析:
構造函數(shù):
FileOutputStream(File file)
創(chuàng)建一個向指定 File 對象表示的文件中寫入數(shù)據(jù)的文件輸出流。


5.png

FileOutputStream(String name)
創(chuàng)建一個向具有指定名稱的文件中寫入數(shù)據(jù)的輸出文件流直秆。

步驟:
1)創(chuàng)建一個測試類FileOutputStreamDemo濒募;
2)在這個類中定義一個method_1函數(shù),在這個函數(shù)中創(chuàng)建FileOutputStream類的對象out圾结,分別使用上述兩種構造函數(shù)創(chuàng)建瑰剃,并在構造函數(shù)的參數(shù)中指定目錄路徑D:\test1\1.txt;
3)定義一個字節(jié)byte數(shù)組b筝野,并存入值晌姚;
4)使用字節(jié)輸出流對象out調(diào)用write()函數(shù),將字節(jié)數(shù)組中的數(shù)據(jù)寫到指定的文件中歇竟;
5)使用對象out調(diào)用close()函數(shù)關閉流資源挥唠;

//需求1:將一個字節(jié)byte數(shù)組中的數(shù)據(jù)寫到D:\\test1\\1.txt 的文件中。
    public static void method_1() throws IOException {
        // 創(chuàng)建字節(jié)輸出流對象 FileOutputStream(String name) 
        /*
         *  public FileOutputStream(String name) throws FileNotFoundException 
         *  {
                this(name != null ? new File(name) : null, false);
            }
         */
        //經(jīng)常用 底層幫我們將字符串封裝成了File類的對象
        OutputStream out = new FileOutputStream("D:\\test1\\1.txt");
        //FileOutputStream(File file) 
        //創(chuàng)建File類的對象 
    /*  File file=new File("D:\\test1\\1.txt");
        //創(chuàng)建輸出流對象  不經(jīng)常使用
        FileOutputStream out = new FileOutputStream(file);*/
        //創(chuàng)建一個字節(jié)數(shù)組
        byte[] b={97,98,65,66};
        //將字節(jié)數(shù)組中的數(shù)據(jù)寫到指定的文件中
        out.write(b);
        //關閉輸出流
        out.close();
}

注意:
問題1:構造函數(shù)執(zhí)行的時候焕议,做了什么宝磨?
A:判斷目標文件所在的目錄路徑在硬盤上是否存在,如果路徑不存在盅安,那么就會報系統(tǒng)找不到指定的路徑的異常FileNotFoundException唤锉。


6.png

B:如果目標文件所在的路徑存在了,也就是說D:\test1别瞭,那么接下來就會判斷目標文件1.txt在指定的目錄中D:\test1中是否存在:
如果不存在窿祥,替我們創(chuàng)建1.txt文件出來,并將數(shù)據(jù)寫到1.txt文件中蝙寨;
如果這個文件1.txt已經(jīng)存在晒衩,這時用新的數(shù)據(jù)覆蓋掉原來文件中的數(shù)據(jù);
C:調(diào)用系統(tǒng)資源墙歪,關聯(lián)目標文件听系;

舉例:其實我們平常使用記事本或者其他軟件打開.txt文件的時候都是在調(diào)用系統(tǒng)資源,進行關聯(lián)目標文件箱亿。

問題2:為什么要close?
A:流關閉后弃秆,就會釋放系統(tǒng)資源届惋;
B:流關閉,就變成了垃圾數(shù)據(jù)菠赚,這樣可以被垃圾回收器回收脑豹,免得沒有用的資源浪費空間;

問題3:關于FileOutputStream輸出流的另一個構造函數(shù)


7.png

FileOutputStream(File file)如何使用衡查?
其實對于FileOutputStream(String name) 這個構造函數(shù)在底層幫我們將字符串封裝成了File類的對象瘩欺,作為程序員我們不用再創(chuàng)建File類的對象了,所以開發(fā)中建議使用FileOutputStream(Stringname)這個構造函數(shù)。

需求2:使用字節(jié)輸出流把字符串數(shù)據(jù)”hello俱饿,編程”寫到硬盤D:\test1\2.txt上歌粥;
1)在上述測試類中在創(chuàng)建一個method_2()函數(shù);
2)在這個函數(shù)中創(chuàng)建FileOutputStream類的對象out拍埠,并在構造函數(shù)的參數(shù)中指定目錄路徑D:\test1\2.txt失驶;
3)定義一個字符串s=”hello,狗哥”枣购;
4)使用字符串對象s調(diào)用String類中的函數(shù)將字符串轉(zhuǎn)成字節(jié)數(shù)組嬉探;
5)使用字節(jié)輸出流對象out調(diào)用write()函數(shù),將字節(jié)數(shù)組中的數(shù)據(jù)寫到指定的文件中棉圈;
6)使用對象out調(diào)用close()函數(shù)關閉流資源涩堤;

//需求2:使用字節(jié)輸出流把字符串數(shù)據(jù)”hello,狗哥”寫到硬盤上分瘾;
    public static void method_2() throws IOException {
        //創(chuàng)建輸出流對象
        OutputStream out = new FileOutputStream("D:\\test1\\2.txt");
        //定義一個字符串
        String s="hello胎围,狗哥";
        /*
         * 使用輸出流對象調(diào)用write函數(shù)將數(shù)據(jù)寫到硬盤上指定的文件中
         * 將字符串s轉(zhuǎn)換為字節(jié)數(shù)組
         */
//      out.write("hello 編程語言".getBytes());
        out.write(s.getBytes());
        //關閉資源
        out.close();
    }

需求3:演示write(int b) 了解使用謹慎
1)在上述測試類中在創(chuàng)建一個method_3()函數(shù);
2)在這個函數(shù)中創(chuàng)建FileOutputStream類的對象out芹敌,并在構造函數(shù)的參數(shù)中指定目錄路徑D:\test1\3.txt痊远;
3)使用字節(jié)輸出流對象out調(diào)用write()函數(shù),將整數(shù)97,353寫到指定的文件中氏捞;
4)使用對象out調(diào)用close()函數(shù)關閉流資源碧聪;

//演示write(int b)函數(shù)
    public static void method_3() throws IOException {
        // 創(chuàng)建輸出流對象
        OutputStream out=new FileOutputStream("D:\\test1\\3.txt");
//      OutputStream out1=new FileOutputStream("D:\\test1\\3.txt");
        //使用輸出流對象調(diào)用write函數(shù)寫出整數(shù)
        /*
         * 字節(jié)輸出流中的write方法每調(diào)用一次,只能寫出一個字節(jié)數(shù)據(jù)液茎。
         * 如果指定的數(shù)據(jù)較大逞姿,這個時候它只會把這個數(shù)據(jù)中最低位上的1個字節(jié)數(shù)據(jù)寫到文件中
         * 97  :00000000 00000000 00000000 01100001 
         * 353 :00000000 00000000 00000001 01100001
         */
//      out.write(353);//a
//      out.write(97);//a
        /*
         * public void write(byte[] b,int off,int len)
         * b表示字節(jié)數(shù)組
         * off表示從下標為off開始
         * len表示寫到文件中的字節(jié)個數(shù)
         */
        byte[] b={97,98,65,66};
        out.write(b, 0, 2);//寫出結果是:ab
        //關閉資源
        out.close();
//      out1.write(b);
//      out.close();
//      out.write(97);//a
//      System.out.println(Integer.toBinaryString(353));
}

說明:
1)字節(jié)輸出流中的write方法每調(diào)用一次,只能寫出一個字節(jié)數(shù)據(jù)捆等。
如果指定的數(shù)據(jù)較大滞造,這個時候它只會把這個數(shù)據(jù)中最低位上的1個字節(jié)數(shù)據(jù)寫到文件中

例如:353輸出的結果是97.
           0000 0000 0000 0000 0000 0000 0110 0001  97
           0000 0000 0000 0000 0000 0001 0110 0001  353

2)public void write(byte[] b,intoff,int len)
b表示字節(jié)數(shù)組
off表示從下標為off開始
len表示寫到文件中的字節(jié)個數(shù)
3)如果使用完一個輸出流,關閉之后栋烤,那么不能繼續(xù)使用這個輸出流向該文件中繼續(xù)寫入數(shù)據(jù)谒养,否則會報異常。只能重新再創(chuàng)建一個新的輸出流明郭,繼續(xù)向該文件中寫數(shù)據(jù)买窟,這樣后寫入的數(shù)據(jù)會覆蓋之前書寫的數(shù)據(jù);

2.3薯定、追加數(shù)據(jù)和換行

數(shù)據(jù)追加問題:
通過以上演示我們發(fā)現(xiàn)一個問題始绍,就是流一旦關閉,再次寫數(shù)據(jù)话侄,會覆蓋文件中原來的數(shù)據(jù)亏推。流不關閉学赛,一次性寫多次,也會追加吞杭。

如何解決盏浇?
使用FileOutputStream類中的其余構造函數(shù):
FileOutputStream(File file, booleanappend) 創(chuàng)建一個向指定 File 對象表示的文件中寫入數(shù)據(jù)的文件輸出流乌逐。
FileOutputStream(String name, boolean append) 創(chuàng)建一個向具有指定name 的文件中寫入數(shù)據(jù)的文件輸出流氨菇。

說明:
A:上述構造函數(shù)中如果第二個參數(shù)append為true设预,那么就在已經(jīng)存在的文件末位追加數(shù)據(jù)睁蕾,如果為false杠览,那么就不會文件中已經(jīng)存在的數(shù)據(jù)末尾追加數(shù)據(jù)被饿,而是將原來的數(shù)據(jù)給覆蓋容达;
B:如果指定的文件在硬盤上不存在衙猪,也會創(chuàng)建這個文件柔昼;
需求:使用字節(jié)輸出流把字符串數(shù)據(jù)”你好嗎”寫到硬盤上哑芹,要求不能覆蓋文件中原有的數(shù)據(jù);

分析和步驟:
1)使用new關鍵字調(diào)用FileOutputStream類的構造函數(shù)創(chuàng)建輸出流對象fos捕透;
2)使用對象fos調(diào)用write函數(shù)向指定的文件添加數(shù)據(jù)聪姿;
3)關閉資源;

//演示向文件末尾追加數(shù)據(jù) 
    //需求:使用字節(jié)輸出流把字符串數(shù)據(jù)”你好嗎”寫到硬盤上乙嘀,要求不能覆蓋文件中原有的數(shù)據(jù)末购;
    public static void method_1() throws IOException {
        //創(chuàng)建輸出流對象 
        FileOutputStream fos = new FileOutputStream("D:\\test1\\4.txt", true);
        /*
         * 對于以上構造函數(shù)進行說明:
         * 如果第二個參數(shù)為true,那么就會在已經(jīng)存在的文件中的末尾處追加數(shù)據(jù)虎谢,如果這個文件4.txt不存在
         * 那么就會創(chuàng)建這個文件
         * 如果第二個參數(shù)為false盟榴,那么向文件添加數(shù)據(jù)的時候就會覆蓋原來的數(shù)據(jù)
         */
        //向文件中添加數(shù)據(jù)
        fos.write("你好嗎".getBytes());
        fos.write("我叫狗哥".getBytes());
        //關閉資源
        fos.close();
    }

數(shù)據(jù)換行問題:
我們?nèi)绻霌Q行,可以在數(shù)據(jù)的末尾加:\r\n
但是:\r\n是windows系統(tǒng)識別的換行符婴噩。不同的操作系統(tǒng)擎场,換行符可能會不相同的。我們的代碼擴展性就變差了几莽。

解決方案:
如果我能根據(jù)系統(tǒng)來獲取對應的換行符迅办,就可以跨平臺。如何從系統(tǒng)獲取換行符呢章蚣?

System類中的方法:
public staticProperties getProperties() 獲取系統(tǒng)中所有的屬性這個函數(shù)的返回值是 Properties 類站欺,這個類實現(xiàn)了Map接口,所以getProperties()這個函數(shù)獲得系統(tǒng)中所有的屬性并以鍵值對形式存在纤垂。在眾多系統(tǒng)屬性中我們想要的是行分隔符矾策,就是類似\r\n,那么行分隔符的鍵是line.separator洒忧,也就是說通過這個鍵就可以獲得系統(tǒng)對應的行分隔符蝴韭。

這里還得需要借助一個函數(shù)通過以上的鍵line.separator獲得行分隔符的值够颠,這個函數(shù)就是getProperty()
static String getProperty(String key) 獲取指定鍵指示的系統(tǒng)屬性熙侍。
代碼如下所示:

/*
     * 換行演示
     * 我們?nèi)绻霌Q行可以在添加數(shù)據(jù)的末尾書寫\r\n 就可以實現(xiàn)換行
     * 但是\r\n屬于Windows系統(tǒng)中特有的方法,不能在其他系統(tǒng)中使用,也就是說不能
     * 跨平臺蛉抓,這樣代碼的擴展性就變差了
     * 解決辦法:
     *  如果我們能夠根據(jù)不同的系統(tǒng)獲取系統(tǒng)對應的行分隔符庆尘,那么這樣代碼就可以跨平臺了,
     * 那么如何獲得系統(tǒng)的的屬性巷送,行分隔符呢驶忌?
     * 通過System類,調(diào)用這個類中的函數(shù)getProperties()
     */
    public static void method_2() throws IOException {
        //創(chuàng)建輸出流對象
        FileOutputStream fos = new FileOutputStream("D:\\test1\\5.txt", true);
        //向文件中寫入數(shù)據(jù)
//      fos.write("hello 上海傳智\r\n".getBytes());
        //表示獲得系統(tǒng)中所有的屬性
        /*Properties properties = System.getProperties();
        System.out.println(properties);*/
        //獲得系統(tǒng)中的行分隔符
        String separator = System.getProperty("line.separator");
        //向文件中寫出數(shù)據(jù)
        fos.write(("狗哥真帥哈哈"+separator).getBytes());
        //關閉資源
        fos.close();
    }

2.4笑跛、字節(jié)輸入流(掌握)

之前學習的是輸出流對象付魔,是用來從內(nèi)存中向文件(硬盤)中寫入數(shù)據(jù)。如果想要從文件(硬盤)中向內(nèi)存中讀取數(shù)據(jù)飞蹂,需要使用輸入流對象:InputStream几苍。

2.4.1、InputSteam介紹

字節(jié)輸入流:InputStream:


8.png
java.lang.Object
   |----java.io.InputStream:屬于抽象類陈哑。是IO中所有的字節(jié)輸入流的父類妻坝。
                    該類中定義了所有字節(jié)輸入流的共性功能 

InputStream類中的共性功能:

9.png

說明:close(): 關閉字節(jié)輸入流對象。

10.png

說明:
1)read():調(diào)用一次read惊窖,就可以從關聯(lián)的文件中讀取一個字節(jié)數(shù)據(jù)刽宪,并返回這個字節(jié)數(shù)據(jù)。
2)read():方法可以從關聯(lián)的文件中讀取數(shù)據(jù)界酒。所有read方法如果讀取到文件的末尾圣拄,都會返回-1。遇到-1就代表文件中的數(shù)據(jù)已經(jīng)被讀取完畢盾计。
3)read(byte[] b) :調(diào)用一次售担,讀取多個字節(jié)數(shù)據(jù),把讀到的字節(jié)數(shù)據(jù)保存在傳遞的b字節(jié)數(shù)組中署辉。返回字節(jié)數(shù)組中讀取的字節(jié)個數(shù)族铆。注意啦:這個返回值不是數(shù)組長度。

由于InputStream類是抽象類哭尝,不能創(chuàng)建這個類的對象哥攘,但是如果想使用這個類中的函數(shù),那必須得創(chuàng)建這個類的對象材鹦,如果想要創(chuàng)建對象逝淹,那么只能創(chuàng)建InputStream類的子類。

由于我們這里是操作文件的桶唐,所以我們需要創(chuàng)建FileInputStream類的對象栅葡。

2.4.2、FileInputStream介紹


11.png

2.4.3尤泽、每次讀單個字節(jié)
演示:字節(jié)輸入流讀取數(shù)據(jù)欣簇,一次讀一個字節(jié)规脸。
構造函數(shù):
FileInputStream(File file)
FileInputStream(String name)
讀取功能:int read()
先使用輸入流對象,從文件中讀取數(shù)據(jù)熊咽,每調(diào)用一次read()方法莫鸭,可以從硬盤文件中讀取一個字節(jié)數(shù)據(jù),把這個字節(jié)數(shù)據(jù)保存在一個int類型的變量中横殴。然后判斷讀取到的這個數(shù)據(jù)也就是這個int類型的變量是否是-1被因,如果不是-1,說明當前沒有讀取到文件的末尾衫仑。如果是-1梨与,說明當前已經(jīng)讀取到文件的末尾。

int類型的變量中就保存著當前讀取到的那個字節(jié)數(shù)據(jù)文狱,后續(xù)步驟可以對這個數(shù)據(jù)進行相關處理蛋欣。

輸入流的使用步驟:
A:創(chuàng)建輸入流,關聯(lián)源文件如贷;
B:讀取數(shù)據(jù)陷虎;
C:釋放資源;

注意:
1)字節(jié)輸入流杠袱,構造函數(shù)執(zhí)行時尚猿,如果源文件不存在,那么拋出異常i垢弧T涞唷;
2)由于輸入流讀取的是文件中的字節(jié)數(shù)據(jù)纹蝴,所以要求輸入流指定的一定是文件庄萎,不能是文件夾,否則會報異常塘安;

分析和步驟:
1)使用new關鍵字調(diào)用FileInputStream類的構造函數(shù)創(chuàng)建指定路徑D:\test1\1.txt的輸入流對象in糠涛;
2)使用輸入流對象in調(diào)用read()函數(shù)開始讀取文件,返回一個int類型的整數(shù)兼犯,并輸出最后返回值忍捡;
3)多次調(diào)用read()函數(shù)引起代碼重復,我們可以考慮使用循環(huán)來實現(xiàn)切黔;
4)循環(huán)條件是返回值是-1砸脊;

/*
 * 演示:字節(jié)輸入流讀取數(shù)據(jù),一次讀一個字節(jié)纬霞。
 * 注意:由于輸入流讀取的是文件中的字節(jié)數(shù)據(jù)凌埂,所以要求輸入流指定的一定是文件,否則會報異常
 * FileNotFoundException
 * 讀取功能:
 * int read():表示讀取下一個字節(jié)并返回
 * 輸入流的使用步驟:
 * 1)創(chuàng)建輸入流诗芜;
 * 2)讀取數(shù)據(jù)瞳抓;
 * 3)關閉輸入流秒紧;
 */
public class FileInputStreamDemo {
    public static void main(String[] args) throws IOException {
        // 創(chuàng)建輸入流的對象 java.io.FileNotFoundException: D:\test1 (拒絕訪問。) 系統(tǒng)找不到指定的文件
        FileInputStream fis = new FileInputStream("D:\\test1\\1.txt");
        //使用輸入流對象調(diào)用read()函數(shù)一次讀一個字節(jié)數(shù)據(jù)
        //第一次讀取
        int i = fis.read();//i表示讀取的字節(jié)數(shù)據(jù)挨下,如果是-1說明文件的結尾
        //輸出讀取的字節(jié)數(shù)據(jù)
        System.out.println((char)i);
        //第二次讀取
        int i2 = fis.read();
        System.out.println((char)i2);
        //第三次讀取
        int i3 = fis.read();
        System.out.println((char)i3);
        //第四次讀取
        int i4 = fis.read();
        System.out.println((char)i4);
        //第五次讀取
        int i5 = fis.read();
        System.out.println(i5);//-1
        //第六次讀取
        int i6 = fis.read();
        System.out.println(i6);//-1
        //關閉資源
        fis.close();
    }
}

通過上述代碼發(fā)現(xiàn),在使用輸入流對象fis調(diào)用read()函數(shù)的時候脐湾,出現(xiàn)多次調(diào)用的情況臭笆,這樣也會導致代碼重復,在開發(fā)中盡量簡化代碼的書寫秤掌,所以對上述代碼還得進一步優(yōu)化:
終極版代碼單個讀取數(shù)據(jù)的代碼模板如下所示:

   /*
         * 通過書寫代碼發(fā)現(xiàn)上述代碼重復太多愁铺,我們可以考慮使用循環(huán)來解決上述代碼重復性的問題
         * 問題:循環(huán)的循環(huán)條件是什么?
         * 讀取到文件結尾闻鉴,即-1則結束茵乱,所以可以讓讀取的結果是-1結束讀取文件
         */
        //讀取數(shù)據(jù)
        int i = fis.read();
        //循環(huán)控制讀取文件數(shù)據(jù)的次數(shù)
        while(i!=-1)
        {
            //說明文件中還有數(shù)據(jù),可以繼續(xù)讀取,輸出讀取的數(shù)據(jù)
            System.out.println((char)i);
            //修改循環(huán)條件 可以理解為一個光標孟岛,讀取一次瓶竭,i的值改變一次
            i=fis.read();
          }

2.4.4、復制文件方式1練習
需求:每次讀1個字節(jié)來完成復制練習1:
復制D:\test\1.txt里面的數(shù)據(jù)到F:\2.txt文件中

分析:
數(shù)據(jù)源:一個文本文件渠羞,這里使用字節(jié)流斤贰,讀取,所以FileInputStream
目的地:一個文本文件次询,這里使用字節(jié)流荧恍,寫出,所以FileOutputStream

思路:
A:創(chuàng)建一個輸入流屯吊,關聯(lián)源文件
B:創(chuàng)建一個輸出流送巡,關聯(lián)目標文件
C:讀取數(shù)據(jù),讀取硬盤中指定文件中的數(shù)據(jù)內(nèi)容,直到讀取的返回值是-1
D:寫出數(shù)據(jù)盒卸,將上述每次讀取的數(shù)據(jù)內(nèi)容都寫入到目標文件F:\2.txt中
E:釋放資源

package cn.xuexi.inputstream.demo;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/*
 * 復制D:\\test\\1.txt里面的數(shù)據(jù)到F:\\2.txt文件中
 * 分析:
 * 數(shù)據(jù)源:D:\\test\\1.txt骗爆,使用字節(jié)流,讀入蔽介,F(xiàn)ileInputStream
 * 目的地:F:\\2.txt淮腾,使用字節(jié)流,寫出,FileOutputStream
 * 思路:
 *  1)創(chuàng)建輸入流對象屉佳,關聯(lián)數(shù)據(jù)源文件谷朝;
 *  2)創(chuàng)建輸出流對象,關聯(lián)目的地文件武花;
 *  3)讀取數(shù)據(jù)圆凰,讀取硬盤中指定文件中的數(shù)據(jù),直到讀取文件的末尾處体箕;
 *  4)寫出數(shù)據(jù)专钉,將上述每次讀取的文件中的內(nèi)容寫到目標文件中挑童;
 *  5)關閉資源;
 */
public class FileInputStreamTest {
    public static void main(String[] args) throws IOException {
        //1)創(chuàng)建輸入流對象跃须,關聯(lián)數(shù)據(jù)源文件站叼;
        FileInputStream fis = new FileInputStream("D:\\test\\1.txt");
        //2)創(chuàng)建輸出流對象,關聯(lián)目的地文件
        FileOutputStream fos = new FileOutputStream("F:\\2.txt");
        //3)讀取數(shù)據(jù)菇民,讀取硬盤中指定文件中的數(shù)據(jù)尽楔,直到讀取文件的末尾處;
        int b=0;
        while((b=fis.read())!=-1)
        {
            //說明數(shù)據(jù)源文件中還有數(shù)據(jù)第练,將每次讀取的數(shù)據(jù)寫到目的地文件中
            fos.write(b);
        }
        //關閉資源
        fis.close();
        fos.close();
    }
}

2.4.5阔馋、每次讀字節(jié)數(shù)組
演示:字節(jié)輸入流,一次讀一個字節(jié)數(shù)組娇掏。
讀取功能:
1)int read(byte[] buf) 讀取數(shù)據(jù)到數(shù)組:buf中呕寝,返回的是讀取到的字節(jié)的個數(shù)len.
定義的字節(jié)數(shù)組是用來存儲從底層文件中讀取到的多個字節(jié)數(shù)據(jù);
2)在把讀取的字節(jié)個數(shù)保存在len中。len中保存的是真正給字節(jié)數(shù)組中讀取的字節(jié)個數(shù)婴梧,如果讀取到文件末尾下梢,也會返回-1;
一般這個數(shù)組的長度都會定義成1024的整數(shù)倍塞蹭。
使用循環(huán)重復的從文件中讀取數(shù)據(jù)怔球,每次最多可以從文件中讀取1024個字節(jié)數(shù)據(jù)
需求:在D:\test\1.txt這個目錄的1.txt文件中書寫幾個字符串,如下所示:
hello
world
Java
字樣浮还,然后使用字節(jié)輸入流一次讀一個字節(jié)數(shù)組來讀取上述路徑中的1.txt文件中的數(shù)據(jù)竟坛,將每次讀取的數(shù)據(jù)輸出打印到控制臺。

分析和步驟:
1)創(chuàng)建一個輸入流對象钧舌,和D:\test\1.txt文件進行關聯(lián)担汤;
2)定義的字節(jié)byte數(shù)組b,這個字節(jié)數(shù)組的長度是5,主要是用來存儲從底層文件中讀取到的多個字節(jié)數(shù)據(jù)洼冻;
3)用來記錄當前給byte數(shù)組中讀取的字節(jié)個數(shù)的變量崭歧,int len = 0;
4)先執(zhí)行fis.read()函數(shù)從底層讀取數(shù)據(jù),然后會把數(shù)據(jù)保存在我們傳遞的參數(shù)b數(shù)組中撞牢。返回值定義一個int類型的變量len記錄著讀取到字節(jié)數(shù)組中的字節(jié)數(shù)率碾;
5)輸出記錄的讀取到字節(jié)數(shù)len和將字節(jié)數(shù)組轉(zhuǎn)換后的字符串數(shù)據(jù),將字節(jié)轉(zhuǎn)換為字符串可以使用Arrays.toString(b)或者String類的構造函數(shù)屋彪;
6)由于字節(jié)數(shù)組長度是5所宰,所以需要多次讀取,按照上述操作多次讀取1.txt文件中剩余的數(shù)據(jù)畜挥,將結果輸出到控制臺上面仔粥;

/*
 * 演示:字節(jié)輸入流,一次讀一個字節(jié)數(shù)組。
 * int read(byte[] b) 表示定義一個byte數(shù)組躯泰,每次讀取的字節(jié)都存儲到這個數(shù)組中
 */
public class FileInputStreamDemo1 {
    public static void main(String[] args) throws IOException {
        // 創(chuàng)建輸入流對象谭羔,關聯(lián)源文件
        FileInputStream fis = new FileInputStream("D:\\test\\1.txt");
        //第一次讀取
        //定義一個數(shù)組保存字節(jié)數(shù)據(jù)
        byte[] b=new byte[5];
        //讀取數(shù)據(jù) 將數(shù)據(jù)保存到字節(jié)數(shù)組中
        int len = fis.read(b);
        //輸出len
        System.out.println("len="+len);
        //輸出字節(jié)數(shù)組中的內(nèi)容
//      System.out.println(Arrays.toString(b));
        System.out.println(new String(b));//hello
         //第一次讀取結果:
            /* 
              len=5
            hello
            */
          //第二次讀取
         len = fis.read(b);
        //輸出len
        System.out.println("len="+len);
        //輸出字節(jié)數(shù)組中的內(nèi)容
        System.out.println(new String(b));
         //第二次讀取結果:
        /*
            len=5
            \r\n
            wor
         */

        //第三次讀取
        len = fis.read(b);
        //輸出len
        System.out.println("len="+len);
        //輸出字節(jié)數(shù)組中的內(nèi)容
        System.out.println(new String(b));
         //第三次讀取結果:
        /*
         *  len=5
            ld\r\n
            J
         */
        //第四次讀取
        len = fis.read(b);
        //輸出len
        System.out.println("len="+len);
        //輸出字節(jié)數(shù)組中的內(nèi)容
        System.out.println(new String(b));
         //第四次讀取結果:
        /*
         在1.txt文件中,如果最后的數(shù)據(jù)Java有回車換行麦向,那么會輸出如下所示數(shù)據(jù):
            len=5
            ava\r\n
         在1.txt文件中瘟裸,如果最后的數(shù)據(jù)Java沒有回車換行,那么會輸出如下所示數(shù)據(jù):
             len=3
            ava
            J 
         */
    }
}

說明:
1)通過上述代碼發(fā)現(xiàn)返回值len表示讀取到的字節(jié)數(shù)诵竭,而不是字節(jié)數(shù)組的長度话告。如果讀取為5個字節(jié)數(shù),那么返回5秀撇,即,len等于5向族。如果讀取到的字節(jié)數(shù)是3呵燕,那么返回3,即len等于3件相。如果文件中沒有要讀取的數(shù)據(jù)再扭,則返回-1。
2)上述代碼中當?shù)谒拇巫x取的時候有問題夜矗,如果文件中最后一個數(shù)據(jù)后面沒有回車換行泛范,那么應該只打印ava,為什么會打印:
ava
J
呢紊撕?
原因如下圖所示:


13.png

為了解決上述代碼出現(xiàn)的問題罢荡,我們更希望看到當我們讀取幾個字節(jié)數(shù)據(jù),我們就輸出幾個字節(jié)數(shù)據(jù)对扶,所以這里我們不能在使用new String(b)構造函數(shù)区赵,我們應該使用new String(b,int offset,int length)構造函數(shù)浪南,這樣做就不會出現(xiàn)上述問題笼才。

說明:new String(b,0,len):
       創(chuàng)建一個字符串對象,把b數(shù)組中的數(shù)據(jù)轉(zhuǎn)成字符串络凿,從0位置開始骡送,共計轉(zhuǎn)len個。
       從0位置開始絮记,因為每次調(diào)用輸入流的read方法的時候摔踱,把數(shù)據(jù)給byte數(shù)組中保存
       這時真正是從byte數(shù)組的0位置開始存儲讀取的每個字節(jié)數(shù)據(jù)。
       len是byte數(shù)組中的保存的真正的讀取的字節(jié)個數(shù)怨愤。

這樣做就可以做到我們讀取幾個字節(jié)數(shù)據(jù)昌渤,我們就輸出幾個字節(jié)數(shù)據(jù)的目的。

代碼如下所示:

public class FileInputStreamDemo1 {
    public static void main(String[] args) throws IOException {
        // 創(chuàng)建輸入流對象憔四,關聯(lián)源文件
        FileInputStream fis = new FileInputStream("D:\\test\\1.txt");
        //第一次讀取
        //定義一個數(shù)組保存字節(jié)數(shù)據(jù)
        byte[] b=new byte[5];
        //讀取數(shù)據(jù) 將數(shù)據(jù)保存到字節(jié)數(shù)組中
        int len = fis.read(b);
//      System.out.println(new String(b,0,len));//hello
        System.out.print(new String(b,0,len));//hello
        //第二次讀取
         len = fis.read(b);
        //輸出len
//      System.out.println("len="+len);
        //輸出字節(jié)數(shù)組中的內(nèi)容
//      System.out.println(new String(b,0,len));
        System.out.print(new String(b,0,len));
        //第三次讀取
        len = fis.read(b);
        //輸出len
//      System.out.println("len="+len);
        //輸出字節(jié)數(shù)組中的內(nèi)容
//      System.out.println(new String(b,0,len));
        System.out.print(new String(b,0,len));
        //第四次讀取
        len = fis.read(b);
        //輸出len
//      System.out.println("len="+len);
        //輸出字節(jié)數(shù)組中的內(nèi)容
//      System.out.println(new String(b,0,len));
        System.out.print(new String(b,0,len));
         //關閉資源
        fis.close();
      }
}

上面的代碼重復太多了膀息,考慮用循環(huán)般眉。

問題來了:循環(huán)的接收條件是什么呢?
結束條件:末尾返回-1
終極版代碼如下所示:

//定義一個數(shù)組
//      byte[] b=new byte[5];
        //終極版代碼模板
        byte[] b=new byte[1024];//數(shù)組長度一般是1024的整數(shù)倍
        //定義一個變量保存讀取字節(jié)的個數(shù)
        int len=0;
        //fis.read(b)表示讀取的數(shù)據(jù)都存放到byte數(shù)組中了潜支,len表示讀取字節(jié)數(shù)
        while((len=fis.read(b))!=-1)//一定要傳遞參數(shù)數(shù)組b
        {
            System.out.print(new String(b,0,len));
        }
         //關閉資源
        fis.close();

2.4.6甸赃、復制文件方式2練習:
需求:每次讀字節(jié)數(shù)組來完成復制練習2:
復制D:\test\1.txt中的數(shù)據(jù)到F:\2.txt文件中。

分析和步驟:
1)創(chuàng)建輸入流冗酿,關聯(lián)源文件D:\test\1.txt埠对;
2)創(chuàng)建輸出流,關聯(lián)目標文件F:\2.txt裁替;
3)讀寫數(shù)據(jù)项玛;
4)關閉資源;

/*
 * 需求:每次讀字節(jié)數(shù)組來完成復制練習2:
    復制D:\\test\\1.txt到F:\\2.txt
 */
public class FileInputStreamTest1 {
    public static void main(String[] args) throws IOException {
        // 創(chuàng)建輸入流對象弱判,關聯(lián)源文件
        FileInputStream fis = new FileInputStream("D:\\test\\1.txt");
        //創(chuàng)建輸出流對象襟沮,關聯(lián)目標文件
        FileOutputStream fos = new FileOutputStream("F:\\2.txt");
        //讀取數(shù)據(jù)
        byte[] b=new byte[1024];
        int len=0;
        while((len=fis.read(b))!=-1)
        {
            //寫數(shù)據(jù)
            fos.write(b, 0, len);
        }
        //關閉資源
        fis.close();
        fos.close();
    }
}

注意:
1)使用InputStream類中的read()函數(shù)表示每次讀單個字節(jié),然后使用OutputStream類中的write(int b)函數(shù)把讀出來的字節(jié)寫入到新的文件中昌腰;
2)使用InputStream類中的read(byte[] b)函數(shù)表示每次讀字節(jié)數(shù)組开伏,然后使用OutputStream類中的write(byte[] b,int off,int len)函數(shù)把讀出來的字節(jié)數(shù)組中的數(shù)據(jù)寫入到新的文件中;
上述兩種辦法雖然都可以實現(xiàn)遭商,但是是有區(qū)別的固灵,第二種字節(jié)數(shù)組效率要高,開發(fā)中優(yōu)先考慮第二種劫流,兩種讀寫文件方式的區(qū)別如下圖所示:


14.png

復制文件的時候注意:
1巫玻、 需要定義中間的變量(容器),讓字節(jié)輸入流和輸出流可以聯(lián)系起來祠汇。然后輸入流讀取數(shù)據(jù)大审,輸出流把讀取到的數(shù)據(jù)寫到文件中。
2座哩、 在讀取數(shù)據(jù)的時候徒扶,調(diào)用read() 和read( 字節(jié)數(shù)組 )有嚴格的區(qū)別。
3根穷、 在寫出數(shù)據(jù)的時候姜骡,write(字節(jié)數(shù)組) 和 write( 字節(jié)數(shù)組,0 屿良,長度 )有區(qū)別圈澈。

2.5、復制文件綜合練習

需求:
演示復制文件:把D盤下的1.mp3 拷貝到F盤下的1.mp3尘惧。

分析和步驟: 每次讀1個字節(jié)來完成復制
1)創(chuàng)建測試類CopyFile康栈,在這個類中創(chuàng)建兩個函數(shù)method_1和method_2;
2)在method_1()函數(shù)中演示復制文件一次讀寫一個,在這個函數(shù)中創(chuàng)建輸入流對象fis和輸出流對象fos啥么,分別指定源文件位置D:\1.mp3和目標文件位置F:\1.mp3登舞;
3)獲得開始復制的系統(tǒng)時間的毫秒值start;
4)使用while循環(huán)控制讀文件的次數(shù)悬荣,使用輸入流對象fis調(diào)用read()函數(shù)一次一次讀菠秒,使用輸出流對象fos調(diào)用write()函數(shù)一次一次寫;
5)獲取復制結束的時間end氯迂;
6)結束時間end減去開始復制的時間start践叠,關閉輸入和輸出流;

//演示一次讀取一個字節(jié)
    public static void method_1() throws IOException {
        //創(chuàng)建輸入流對象 關聯(lián)源文件
        FileInputStream fis = new FileInputStream("D:\\1.mp3");
        //創(chuàng)建輸出流對象 關聯(lián)目標文件
        FileOutputStream fos=new FileOutputStream("F:\\1.mp3");
        //獲取開始復制的時間
        long start = System.currentTimeMillis();
        //定義變量保存字節(jié)
        int ch=0;
        while((ch=fis.read())!=-1)
        {
            //寫數(shù)據(jù)
            fos.write(ch);
        }
        //獲取結束的時間
        long end = System.currentTimeMillis();
        System.out.println("復制時間是:"+(end-start));//復制時間是:38406
        //關閉資源
        fis.close();
        fos.close();
    }

需求:
演示復制文件:把D盤下的1.mp3 拷貝到F盤下嚼蚀。

分析和步驟:每次讀字節(jié)數(shù)組來完成復制
1)步驟和上述步驟大致相同禁灼,只是需要定義一個整數(shù)變量len來記錄當前給byte數(shù)組中讀取的字節(jié)個數(shù)的;
2)定義的字節(jié)數(shù)組buf是用來存儲從底層文件中讀取到的多個字節(jié)數(shù)據(jù)轿曙;
3)使用read(buf)函數(shù)讀取文件的時候需要傳入一個數(shù)組參數(shù)buf弄捕;
4)每讀取一次使用輸出流對象調(diào)用write()函數(shù)向文件中寫數(shù)據(jù);

//演示一次讀取一個字節(jié)數(shù)組
    public static void method_2() throws IOException {
        // 創(chuàng)建輸入流對象 關聯(lián)源文件
        FileInputStream fis = new FileInputStream("D:\\1.mp3");
        //創(chuàng)建輸出流對象 關聯(lián)目標文件
        FileOutputStream fos = new FileOutputStream("F:\\1.mp3");
        //獲取開始復制的時間
        long start = System.currentTimeMillis();
        //定義數(shù)組  讀數(shù)據(jù)
//      byte[] b=new byte[8192];
        byte[] b=new byte[1024];
        int len=0;
        while((len=fis.read(b))!=-1)
        {
            //寫數(shù)據(jù)
            fos.write(b, 0, len);
        }
        //獲取結束復制的時間
        long end = System.currentTimeMillis();
        System.out.println("復制時間是:"+(end-start));//復制時間是:15
        //關閉資源
        fis.close();
        fos.close();
        
    }

2.6拳芙、總結輸入流讀取文件的模版代碼(掌握)

1察藐、一次讀取一個字節(jié)的模版代碼
          1)皮璧、創(chuàng)建輸入流對象
                 FileInputStream  fis = new FileInputStream( “文件” );
注意:
1)FileInputStream類的構造函數(shù)的參數(shù)一定要是個文件路徑舟扎,不能是文件夾路徑,因為FileInputStream類是用來操作文件的悴务,是讀取文件中的內(nèi)容睹限;
2)在構造函數(shù)中指定的文件在硬盤上一定要存在,否則會拋異常讯檐;
          2)羡疗、定義變量,記錄每次讀取到的字節(jié)數(shù)據(jù)
                 int b= 0;
          3)别洪、使用循環(huán)讀取數(shù)據(jù)
                 while(  ( b = fis.read() ) !=-1  ){
       //處理讀取到的數(shù)據(jù)叨恨,數(shù)據(jù)在b中。
     }
          4)挖垛、關閉流對象
                 fis.close(); 
2痒钝、一次讀取多個字節(jié)數(shù)據(jù)的模版代碼(掌握)
          1)、創(chuàng)建輸入流對象
                 FileInputStream  fis = new FileInputStream( “文件” ); 
          2)痢毒、定義變量送矩,記錄讀取到的字節(jié)個數(shù)
                 int len = 0;         
          3)、定義數(shù)組保存讀取到的多個字節(jié)數(shù)據(jù)
                 byte[] buf = newbyte[1024*n];            
          4)哪替、使用循環(huán)讀取數(shù)據(jù)
                     while(  (  len= fis.read( buf ) ) !=-1 ){
             //處理讀取到的數(shù)據(jù)栋荸,數(shù)據(jù)在buf中,buf中共計有效的數(shù)據(jù)是len個
           }
          5)、關閉流對象
                 fis.close(); 

2.7晌块、IO流中的異常處理模版(了解)

我們使用Java程序爱沟,操作的是Java程序以外的其他設備上的數(shù)據(jù),都有可能發(fā)生異常問題摸袁。
我們在書寫的Java程序讀寫其他設備上的數(shù)據(jù)時钥顽,都要考慮異常問題。這些異常一般開發(fā)中都要開發(fā)者自己處理掉靠汁,不能直接往出拋蜂大。

說明:
1)對于輸入流FileInputStream來說,硬盤上一定要有文件蝶怔,否則會報找不到文件異常奶浦;對于輸入流read()函數(shù)來說,假設有文件踢星,但是文件沒有可讀的權限澳叉,那么對于輸入流對象調(diào)用read()函數(shù)也會報異常;對于關閉流的函數(shù)close()來說沐悦,也同樣有異常成洗;
2)對于輸出流FileOutputStream來說,指定的路徑一定是硬盤上的文件不能是文件夾藏否,否則會報異常瓶殃;對于輸入流write()函數(shù)來說,假設有文件副签,但是文件沒有可寫的權限遥椿,那么對于輸出流對象調(diào)用write()函數(shù)也會報異常;對于關閉流的函數(shù)close()來說淆储,也同樣有異常冠场;
3)流的關閉動作,必須被執(zhí)行本砰。但是如果在流關閉之前碴裙,已經(jīng)出異常,那么流的關閉動作無法執(zhí)行点额。
必須被執(zhí)行的動作舔株,放到finally中;把關閉動作咖楣,放到finally代碼塊中后督笆,流的定義也必須放到try塊的外面,否則看不到;在finally中關閉流的時候要先判斷流是否為null,如果不判斷那么流對象調(diào)用close()函數(shù)有可能報空指針異常。

IO流讀寫數(shù)據(jù)的模版代碼:
分析和步驟:
1)定義一個測試類IOExceptionDemo 在這個類中定義兩個函數(shù)method_1()和method_2()分別書寫讀數(shù)據(jù)和寫數(shù)據(jù)的模版處理代碼泥畅;
2)在method_1()函數(shù)中定義一個FileInputStream 類的對象fis=null;
3)在try-catch-finally代碼塊中創(chuàng)建FileInputStream 類的對象并指定讀數(shù)據(jù)的文件D:\1.txt;
4)使用一次性讀取一個字節(jié)數(shù)據(jù)來完成讀數(shù)據(jù)料扰;
5)在finally代碼塊中在關閉流之前要先判斷流對象是否存在凭豪;
6)在method_2()函數(shù)中定義一個FileOutputStream 類的對象fos=null;
7)在try-catch-finally代碼塊中創(chuàng)建FileOutputStream 類的對象并指定讀數(shù)據(jù)的文件D:\2.txt;
8)使用輸出流對象fos調(diào)用write()函數(shù)向指定的文件中寫數(shù)據(jù);
9)在finally代碼塊中在關閉流之前要先判斷流對象是否存在晒杈;

/*
 * 字節(jié)流處理異常代碼模板
 */
public class IOExceptionDemo {
    public static void main(String[] args) {
        method_2();
    }
    //讀數(shù)據(jù)的處理異常的模板代碼
    public static void method_1() {
        FileInputStream fis=null;
        try {
            //創(chuàng)建輸入流對象
            //這里的fis對象只能在try的大括號里面使用嫂伞,不能在其他地方使用
            fis = new FileInputStream("D:\\test\\1.txt");
            //定義數(shù)組
            byte[] b=new byte[1024];
            //定義變量
            int len=0;
            while((len=fis.read(b))!=-1)
            {
                System.out.println(new String(b,0,len));
            }
        } catch (IOException e) {
            System.out.println("讀數(shù)據(jù)異常了");
            //后期在寫項目的時候,抓到異常了拯钻,這里需要寫日志文檔
        }finally
        {
            if(fis!=null)
            {
                try {
                    //不管程序是否有異常都得需要關閉資源
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    //寫數(shù)據(jù)的處理異常的模板代碼
    public static void method_2() {
        FileOutputStream fos=null;
        try {
            // 創(chuàng)建輸出流對象
            fos = new FileOutputStream("D:\\test\\1.txt");
            fos.write(97);
        } catch (IOException e) {
            System.out.println("寫數(shù)據(jù)異常了");
        }finally
        {
            if(fos!=null)
            {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

2.8帖努、字節(jié)流小節(jié)

1、字節(jié)輸出流:
     A:使用輸出流給文件中寫數(shù)據(jù)的時候粪般,如果文件不存在拼余,會創(chuàng)建這個文件,如果文件存在亩歹,會覆蓋掉文件中原有的數(shù)據(jù)匙监。
     B:如果不想覆蓋掉原來文件中的數(shù)據(jù),在創(chuàng)建輸出流的時候小作,可以指定布爾值為true亭姥,這樣就會在文件的末尾追加數(shù)據(jù)。
可以使用構造函數(shù)FileOutputStream(String name, boolean append)來實現(xiàn)顾稀。
    C:write( int b ) 它只能寫出這個int數(shù)據(jù)中最低位的1個字節(jié)數(shù)據(jù)达罗。
     D:換行
    //獲得系統(tǒng)中的行分隔符
    Stringseparator = System.getProperty("line.separator");
     //向文件中寫出數(shù)據(jù)
     fos.write(("黑旋風真帥哈哈"+separator).getBytes()); 

2、字節(jié)輸入流:
   它不會創(chuàng)建文件础拨,同時要求輸入流關聯(lián)的一定是文件氮块,不能是文件夾绍载。
   字節(jié)輸入流有自己的讀取數(shù)據(jù)的模版代碼诡宗。
   一次讀取一個字節(jié)數(shù)據(jù),或一次讀取多個字節(jié)數(shù)據(jù)击儡。
 3塔沃、流的操作:
   不管是輸入流還是輸出流,在操作完之后都要關閉流阳谍。 

2.9蛀柴、字節(jié)流緩沖區(qū)介紹(了解)

通過以上演示發(fā)現(xiàn),一次讀很多字節(jié)比一次讀一個字節(jié)快矫夯。Java的設計者肯定知道這一點鸽疾。
Java專門提供了一類流,可以實現(xiàn)高效的讀取训貌。這種流制肮,就是在內(nèi)部維護了一個緩沖區(qū)數(shù)組冒窍。
字節(jié)流緩沖區(qū):包括字節(jié)輸入流緩沖區(qū)和字節(jié)輸出流緩沖區(qū)。
字節(jié)輸入流緩沖區(qū):
BufferedInputStream:

15.png

說明:
BufferedInputStream:它就是一個緩沖區(qū)豺鼻,它內(nèi)部維護一個數(shù)組综液,可以從底層讀取多個數(shù)據(jù)。

16.png

構造方法摘要
BufferedInputStream(InputStream in)
創(chuàng)建一個新的緩沖輸入流儒飒,用來將數(shù)據(jù)寫入指定的底層輸入流谬莹。

問題:為什么緩沖區(qū)流需要接收一個普通字節(jié)流呢?
緩沖區(qū)流是為了 高效而設計的桩了,緩沖區(qū)流本身僅僅是維護了一個數(shù)組附帽。不具備讀和寫的功能。真正的讀寫還是要依賴普通的字節(jié)流井誉。

緩沖區(qū)字節(jié)流和以前字節(jié)流讀寫文件的做法區(qū)別如下圖所示:


17.png

說明:
1)使用緩沖區(qū)輸入流的時候士葫,后期我們需要數(shù)據(jù)的時候,不直接和文件進行交互送悔,而是向緩沖區(qū)索要數(shù)據(jù)慢显;
2)緩沖區(qū)只能臨時存儲數(shù)據(jù),它不能從底層讀取數(shù)據(jù)欠啤,從底層讀取數(shù)據(jù)還得需要輸入流荚藻;
3)使用緩沖區(qū)的目的只是為了提高讀寫的效率,先使用FileInputStream輸入流將硬盤上的文件讀取到緩沖區(qū)中洁段,然后在從緩沖區(qū)中的數(shù)組中取出數(shù)據(jù)存放到我們之前定義好的數(shù)組中应狱,因為兩個數(shù)組都是在內(nèi)存中,這樣交互數(shù)據(jù)會更快一點祠丝;
4)而我們之前都是從硬盤上直接將數(shù)據(jù)讀取到定義好的數(shù)組中疾呻,這樣效率會低點;

分析和步驟:
1)定義一個測試類BufferedInputStreamDemo写半;
2)在這個類中的main函數(shù)中創(chuàng)建一個可以直接和文件交互的輸入流對象in岸蜗,并指定路徑D:\test\123.txt;
3)創(chuàng)建一個緩沖區(qū)對象bufIn叠蝇,需要指定可以從底層讀取數(shù)據(jù)的流對象璃岳;
4)創(chuàng)建一個byte類型的數(shù)組buf,大小是1024,定義一個整數(shù)變量len=0悔捶;
5)使用緩沖區(qū)對象bufIn調(diào)用read()函數(shù)讀取數(shù)據(jù)并存儲到定義好的數(shù)組buf中铃慷,并轉(zhuǎn)換為字符串,輸出蜕该;
6)關閉流犁柜;

/*
 * 演示字節(jié)輸入流緩沖區(qū)
 */
public class BufferedInputStreamDemo {
    public static void main(String[] args) throws IOException {
        //創(chuàng)建一個可以直接和文件進行交互的輸入流對象
        FileInputStream fis = new FileInputStream("D:\\test\\123.txt");
        //創(chuàng)建輸入流緩沖區(qū),指定可以從底層讀取數(shù)據(jù)的流對象
        BufferedInputStream bufin = new BufferedInputStream(fis);
        //創(chuàng)建數(shù)組
        byte[] b=new byte[1024];
        //定義一個變量
        int len=0;
        while((len=bufin.read(b))!=-1)
        {
            System.out.println(new String(b,0,len));
        }
        //關閉資源
        bufin.close();
    }
}

說明:上述代碼關閉緩沖區(qū)流就可以了堂淡,不用手動關閉字節(jié)流了馋缅,因為在底層已經(jīng)關閉字節(jié)流了坛怪。

字節(jié)輸出流緩沖區(qū):
BufferedOutputStream:


18.png

BufferedOutputStream:它可以把需要寫出的數(shù)據(jù),寫到自己的緩沖區(qū)中股囊,當緩沖區(qū)寫滿袜匿,或者我們手動調(diào)用flush方法,或者最后我們關流稚疹,才會把緩沖區(qū)中的數(shù)據(jù)一次性的寫到底層文件中居灯。

構造方法摘要:
BufferedOutputStream(OutputStreamout)
創(chuàng)建一個新的緩沖輸出流,以將數(shù)據(jù)寫入指定的底層輸出流内狗。

問題:為什么緩沖區(qū)流需要接收一個普通字節(jié)流呢怪嫌?
緩沖區(qū)流是為了 高效而設計的,緩沖區(qū)流本身僅僅是維護了一個數(shù)組柳沙。不具備讀和寫的功能岩灭。真正的讀寫還是要依賴普通的字節(jié)流。

分析和步驟:
1)定義一個測試類BufferedOutputStreamDemo赂鲤;
2)在這個類中的main函數(shù)中創(chuàng)建一個可以直接和文件交互的輸出流對象fos噪径,并指定路徑D:\out.txt;
3)創(chuàng)建一個緩沖區(qū)輸出流對象bufOut数初;
4)使用緩沖區(qū)輸出流對象bufOut調(diào)用write()函數(shù)向指定文件中寫入指定的內(nèi)容找爱;
5)使用緩沖區(qū)輸出流對象bufOut調(diào)用close()函數(shù)關閉緩沖區(qū)輸出流,這樣就可以將指定的數(shù)據(jù)寫入到指定的文件中泡孩;

/*
 * 演示字節(jié)輸出流緩沖區(qū)
 */
public class BufferedOutputStreamDemo {
    public static void main(String[] args) throws IOException {
        //創(chuàng)建可以和底層文件交互的流對象
        FileOutputStream fos = new FileOutputStream("D:\\test\\out.txt");
        //創(chuàng)建輸出流緩沖區(qū)對象
        BufferedOutputStream buffOut = new BufferedOutputStream(fos);
        //寫數(shù)據(jù)
        buffOut.write("hello 狗哥哈哈".getBytes());
        //關閉緩沖區(qū)輸出流
        //buffOut.close();
         //將緩沖區(qū)中的數(shù)據(jù)刷新到輸出流中
         buffOut.flush();
    }
}

注意:
flush:刷新緩沖區(qū)车摄,把緩沖區(qū)中的有效數(shù)據(jù)刷到底層文件中,刷新完成之后仑鸥,緩沖區(qū)流還可以繼續(xù)使用吮播。
close:關閉流和底層文件之間的關聯(lián),一旦流關閉了眼俊,就不能在使用

小結:
字節(jié)流緩沖區(qū)對象屬于IO流中的高效流意狠,可以提高文件的讀寫效率。

緩沖區(qū)對象讀寫的原理:
BufferedInputStream對象:該流需要使用輸入流對象讀取字節(jié)數(shù)據(jù)泵琳,把讀取到字節(jié)數(shù)據(jù)暫時存儲在緩沖區(qū)對象內(nèi)部的數(shù)組(緩沖區(qū))中摄职,當內(nèi)部的緩沖區(qū)數(shù)組存滿了后或不需要再讀取數(shù)據(jù)了誊役,就可以從內(nèi)部的緩沖區(qū)數(shù)組中獲取字節(jié)數(shù)據(jù)获列。
BufferedOutputStream對象:該流需要使用輸出流對象寫入字節(jié)數(shù)據(jù),把要寫入的數(shù)據(jù)轉(zhuǎn)換為字節(jié)數(shù)據(jù)后暫時存儲到緩沖區(qū)數(shù)組中蛔垢,當內(nèi)部緩沖區(qū)數(shù)組存滿后或者關閉流資源或者刷新緩沖區(qū)時就可以從緩沖區(qū)數(shù)組取出字節(jié)數(shù)據(jù)寫入到文件中击孩。

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市鹏漆,隨后出現(xiàn)的幾起案子巩梢,更是在濱河造成了極大的恐慌创泄,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件括蝠,死亡現(xiàn)場離奇詭異鞠抑,居然都是意外死亡,警方通過查閱死者的電腦和手機忌警,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門搁拙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人法绵,你說我怎么就攤上這事箕速。” “怎么了朋譬?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵盐茎,是天一觀的道長。 經(jīng)常有香客問我徙赢,道長字柠,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任狡赐,我火速辦了婚禮募谎,結果婚禮上,老公的妹妹穿的比我還像新娘阴汇。我一直安慰自己数冬,他們只是感情好,可當我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布搀庶。 她就那樣靜靜地躺著拐纱,像睡著了一般。 火紅的嫁衣襯著肌膚如雪哥倔。 梳的紋絲不亂的頭發(fā)上秸架,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天,我揣著相機與錄音咆蒿,去河邊找鬼东抹。 笑死,一個胖子當著我的面吹牛沃测,可吹牛的內(nèi)容都是我干的缭黔。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼蒂破,長吁一口氣:“原來是場噩夢啊……” “哼馏谨!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起附迷,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤惧互,失蹤者是張志新(化名)和其女友劉穎哎媚,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體喊儡,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡拨与,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了艾猜。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片截珍。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖箩朴,靈堂內(nèi)的尸體忽然破棺而出岗喉,到底是詐尸還是另有隱情,我是刑警寧澤炸庞,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布钱床,位于F島的核電站,受9級特大地震影響埠居,放射性物質(zhì)發(fā)生泄漏查牌。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一滥壕、第九天 我趴在偏房一處隱蔽的房頂上張望纸颜。 院中可真熱鬧,春花似錦绎橘、人聲如沸胁孙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽涮较。三九已至,卻和暖如春冈止,著一層夾襖步出監(jiān)牢的瞬間狂票,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工熙暴, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留闺属,地道東北人。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓周霉,卻偏偏與公主長得像掂器,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子诗眨,可洞房花燭夜當晚...
    茶點故事閱讀 42,722評論 2 345

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

  • 概述 java.io 包幾乎包含了所有操作輸入唉匾、輸出需要的類。所有這些流類代表了輸入源和輸出目標匠楚。java.io ...
    Steven1997閱讀 9,178評論 1 25
  • tags:io categories:總結 date: 2017-03-28 22:49:50 不僅僅在JAVA領...
    行徑行閱讀 2,168評論 0 3
  • 一巍膘、基礎知識:1、JVM芋簿、JRE和JDK的區(qū)別:JVM(Java Virtual Machine):java虛擬機...
    殺小賊閱讀 2,365評論 0 4
  • 文/葶藶 《戰(zhàn)狼2》最近新上映的熱播電影与斤,以9.6的評分肪康,穩(wěn)居榜首,票房一路攀升撩穿。吸引了一顆顆蠢蠢欲動的心磷支,當然也...
    葶藶_閱讀 736評論 2 6
  • 今天帶外甥皮皮去吃早餐,他點了愛吃的粉湯食寡,我是餛燉雾狈,他肉包子我素包子,他給自己拿了筷子抵皱,還給我拿了筷子善榛。粉湯上來,...
    灰狼和乖乖閱讀 187評論 0 1