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流的分類如下圖所示:
說明:
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
OutputStream:它是字節(jié)輸出流的頂層父類。它可以把字節(jié)數(shù)據(jù)寫給JVM髓窜,JVM在交給操作系統(tǒng)扇苞,操作系統(tǒng)把數(shù)據(jù)寫到持久設備上。
OutputStream類中的函數(shù)如下所示:
注意: 學習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ù)寫到文件中。
構造方法如下所示:
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ù)的文件輸出流。
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唤锉。
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ù)
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:
java.lang.Object
|----java.io.InputStream:屬于抽象類陈哑。是IO中所有的字節(jié)輸入流的父類妻坝。
該類中定義了所有字節(jié)輸入流的共性功能
InputStream類中的共性功能:
說明:close(): 關閉字節(jié)輸入流對象。
說明:
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介紹
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
呢紊撕?
原因如下圖所示:
為了解決上述代碼出現(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ū)別如下圖所示:
復制文件的時候注意:
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:
說明:
BufferedInputStream:它就是一個緩沖區(qū)豺鼻,它內(nèi)部維護一個數(shù)組综液,可以從底層讀取多個數(shù)據(jù)。
構造方法摘要
BufferedInputStream(InputStream in)
創(chuàng)建一個新的緩沖輸入流儒飒,用來將數(shù)據(jù)寫入指定的底層輸入流谬莹。
問題:為什么緩沖區(qū)流需要接收一個普通字節(jié)流呢?
緩沖區(qū)流是為了 高效而設計的桩了,緩沖區(qū)流本身僅僅是維護了一個數(shù)組附帽。不具備讀和寫的功能。真正的讀寫還是要依賴普通的字節(jié)流井誉。
緩沖區(qū)字節(jié)流和以前字節(jié)流讀寫文件的做法區(qū)別如下圖所示:
說明:
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:
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ù)寫入到文件中击孩。