1、IO流
1.1、概述
之前學(xué)習(xí)的File類它只能操作文件或文件夾,并不能去操作文件中的數(shù)據(jù)。真正保存數(shù)據(jù)的是文件,數(shù)據(jù)是在文件中。而File類它只是去對文件本身做操作,不能對文件中的數(shù)據(jù)進(jìn)行操作。
如果要操作文件中的數(shù)據(jù),這時(shí)必須使用Java中提供的IO流技術(shù)完成。
IO可以實(shí)現(xiàn)數(shù)據(jù)的傳輸,把數(shù)據(jù)比作是像水一樣在流動(dòng)。
IO:input/output
IO流:它的功能就是專門操作文件中的數(shù)據(jù)。
2.2、IO流分類
1)按流向分:
輸入流:讀取數(shù)據(jù),把持久設(shè)備的數(shù)據(jù)讀取到內(nèi)存中。
輸出流:寫出數(shù)據(jù),把內(nèi)存的數(shù)據(jù)寫出到持久設(shè)備。
2)按數(shù)據(jù)類型分:
計(jì)算機(jī)中一切數(shù)據(jù)都是:字節(jié)數(shù)據(jù)。
字符數(shù)據(jù):底層還是字節(jié)數(shù)據(jù),但是可以根據(jù)某些規(guī)則,把字節(jié)變成人們認(rèn)識(shí)的文字、符號(hào)等等。
字節(jié)流:數(shù)據(jù)在持久設(shè)備上都是以二進(jìn)制形式保存的。二進(jìn)制就是字節(jié)數(shù)據(jù)。Java就給出了字節(jié)流可以直接操作字節(jié)數(shù)據(jù)。
字節(jié)輸入流:InputStream
兒子:XxxInputStream
字節(jié)輸出流:OutputStream
兒子:XxxOutputStream
字符流:讀取字符數(shù)據(jù)。數(shù)據(jù)在設(shè)備上是以二進(jìn)制形式表示,但是有些二進(jìn)制合并在一起可以表示一些字符數(shù)據(jù)。
字符輸入流:Reader
兒子:XxxReader
字符輸出流:Writer
兒子:XxxWriter
IO流的分類如下圖所示:
說明:
1)字節(jié)流可以對任意類型的文件按照字節(jié)進(jìn)行讀和寫的操作;
例如:圖片、視頻、文本文件、word文檔、mp3等。
2)字符流只能對文本類型的文件進(jìn)行操作;
問題1:文本類型的文件是什么文件?
只要可以使用記事本打開并看得懂的文件就是文本文件。
例如:.java文件、.txt等文件。
而字符流只能操作文本類型的文件,也就是說如果一個(gè)文件可以使用記事本打開并能夠看懂,那么這個(gè)文件就可以使用字符流來操作,否則其他的文件都得使用字節(jié)流進(jìn)行操作。
注意:字節(jié)流也可以操作文本文件。
2 字節(jié)流
2.1、字節(jié)流介紹
字節(jié)流:它是以字節(jié)為單位,讀寫數(shù)據(jù)的。
讀寫:
讀是從持久設(shè)備上給程序讀取數(shù)據(jù)。(硬盤-------》內(nèi)存)
寫是把程序中的數(shù)據(jù)寫到持久設(shè)備上。(內(nèi)存-------》硬盤)
不管是字節(jié)流還是字符流,他們都有讀和寫的操作。
字節(jié)流:它分成字節(jié)輸入流和字節(jié)輸出流。
字節(jié)輸入流:從持久設(shè)備上把數(shù)據(jù)讀取到程序中。
字節(jié)輸出流:把程序中的數(shù)據(jù)寫到持久設(shè)備上。
所有的數(shù)據(jù)都可以使用字節(jié)流操作。
常見的數(shù)據(jù)保存形式:記事本、word文檔、圖片、音頻、視頻、壓縮文件等
2.2、字節(jié)輸出流(掌握)
之前我們在學(xué)習(xí)其他類的時(shí)候,例如異常、集合都有頂層父類或者頂層接口。那么在字節(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ù)寫到持久設(shè)備上。
OutputStream類中的函數(shù)如下所示:
注意: 學(xué)習(xí)IO流,我們是在使用Java代碼操作Java以外的其他設(shè)備。不管操作中是否有問題,最后都要斷開Java程序和這些設(shè)備之間的連接。
close方法是關(guān)閉Java和其他設(shè)備之間的連接。
write方法是把數(shù)據(jù)寫到Java關(guān)聯(lián)的設(shè)備中
write(byte[] b ) 把這個(gè)b字節(jié)數(shù)組中的所有數(shù)據(jù)寫到關(guān)聯(lián)的設(shè)備中(設(shè)備包括文件、網(wǎng)絡(luò)或者其他任何地方)。
write(byte[] b , int off , int len ) 把b字節(jié)中的數(shù)據(jù)從下標(biāo)off位置開始往出寫,共計(jì)寫len個(gè)
write(int b ) 把這個(gè)b數(shù)據(jù)寫到關(guān)聯(lián)的設(shè)備中。
OutputStream:它是抽象類,不能創(chuàng)建對象,這里我們需要使用OutputStream類的子類FileOutputStream的對象把數(shù)據(jù)寫到文件中。
構(gòu)造方法如下所示:
FileOutputStream(Filefile)
創(chuàng)建一個(gè)向指定 File 對象表示的文件中寫入數(shù)據(jù)的文件輸出流。
FileOutputStream(String name)
創(chuàng)建一個(gè)向具有指定名稱的文件中寫入數(shù)據(jù)的輸出文件流。
需求1:將一個(gè)字節(jié)byte數(shù)組{97,98,65,66}中的數(shù)據(jù)寫到D:\test1\1.txt 的文件中。
分析:
構(gòu)造函數(shù):
FileOutputStream(File file)
創(chuàng)建一個(gè)向指定 File 對象表示的文件中寫入數(shù)據(jù)的文件輸出流。
FileOutputStream(String name)
創(chuàng)建一個(gè)向具有指定名稱的文件中寫入數(shù)據(jù)的輸出文件流。
步驟:
1)創(chuàng)建一個(gè)測試類FileOutputStreamDemo;
2)在這個(gè)類中定義一個(gè)method_1函數(shù),在這個(gè)函數(shù)中創(chuàng)建FileOutputStream類的對象out,分別使用上述兩種構(gòu)造函數(shù)創(chuàng)建,并在構(gòu)造函數(shù)的參數(shù)中指定目錄路徑D:\test1\1.txt;
3)定義一個(gè)字節(jié)byte數(shù)組b,并存入值;
4)使用字節(jié)輸出流對象out調(diào)用write()函數(shù),將字節(jié)數(shù)組中的數(shù)據(jù)寫到指定的文件中;
5)使用對象out調(diào)用close()函數(shù)關(guān)閉流資源;
//需求1:將一個(gè)字節(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)建一個(gè)字節(jié)數(shù)組
byte[] b={97,98,65,66};
//將字節(jié)數(shù)組中的數(shù)據(jù)寫到指定的文件中
out.write(b);
//關(guān)閉輸出流
out.close();
}
注意:
問題1:構(gòu)造函數(shù)執(zhí)行的時(shí)候,做了什么?
A:判斷目標(biāo)文件所在的目錄路徑在硬盤上是否存在,如果路徑不存在,那么就會(huì)報(bào)系統(tǒng)找不到指定的路徑的異常FileNotFoundException。
B:如果目標(biāo)文件所在的路徑存在了,也就是說D:\test1,那么接下來就會(huì)判斷目標(biāo)文件1.txt在指定的目錄中D:\test1中是否存在:
如果不存在,替我們創(chuàng)建1.txt文件出來,并將數(shù)據(jù)寫到1.txt文件中;
如果這個(gè)文件1.txt已經(jīng)存在,這時(shí)用新的數(shù)據(jù)覆蓋掉原來文件中的數(shù)據(jù);
C:調(diào)用系統(tǒng)資源,關(guān)聯(lián)目標(biāo)文件;
舉例:其實(shí)我們平常使用記事本或者其他軟件打開.txt文件的時(shí)候都是在調(diào)用系統(tǒng)資源,進(jìn)行關(guān)聯(lián)目標(biāo)文件。
問題2:為什么要close?
A:流關(guān)閉后,就會(huì)釋放系統(tǒng)資源;
B:流關(guān)閉,就變成了垃圾數(shù)據(jù),這樣可以被垃圾回收器回收,免得沒有用的資源浪費(fèi)空間;
問題3:關(guān)于FileOutputStream輸出流的另一個(gè)構(gòu)造函數(shù)
FileOutputStream(File file)如何使用?
其實(shí)對于FileOutputStream(String name) 這個(gè)構(gòu)造函數(shù)在底層幫我們將字符串封裝成了File類的對象,作為程序員我們不用再創(chuàng)建File類的對象了,所以開發(fā)中建議使用FileOutputStream(Stringname)這個(gè)構(gòu)造函數(shù)。
需求2:使用字節(jié)輸出流把字符串?dāng)?shù)據(jù)”hello,編程”寫到硬盤D:\test1\2.txt上;
1)在上述測試類中在創(chuàng)建一個(gè)method_2()函數(shù);
2)在這個(gè)函數(shù)中創(chuàng)建FileOutputStream類的對象out,并在構(gòu)造函數(shù)的參數(shù)中指定目錄路徑D:\test1\2.txt;
3)定義一個(gè)字符串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ù)關(guān)閉流資源;
//需求2:使用字節(jié)輸出流把字符串?dāng)?shù)據(jù)”hello,狗哥”寫到硬盤上;
public static void method_2() throws IOException {
//創(chuàng)建輸出流對象
OutputStream out = new FileOutputStream("D:\\test1\\2.txt");
//定義一個(gè)字符串
String s="hello,狗哥";
/*
* 使用輸出流對象調(diào)用write函數(shù)將數(shù)據(jù)寫到硬盤上指定的文件中
* 將字符串s轉(zhuǎn)換為字節(jié)數(shù)組
*/
// out.write("hello 編程語言".getBytes());
out.write(s.getBytes());
//關(guān)閉資源
out.close();
}
需求3:演示write(int b) 了解使用謹(jǐn)慎
1)在上述測試類中在創(chuàng)建一個(gè)method_3()函數(shù);
2)在這個(gè)函數(shù)中創(chuàng)建FileOutputStream類的對象out,并在構(gòu)造函數(shù)的參數(shù)中指定目錄路徑D:\test1\3.txt;
3)使用字節(jié)輸出流對象out調(diào)用write()函數(shù),將整數(shù)97,353寫到指定的文件中;
4)使用對象out調(diào)用close()函數(shù)關(guān)閉流資源;
//演示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)用一次,只能寫出一個(gè)字節(jié)數(shù)據(jù)。
* 如果指定的數(shù)據(jù)較大,這個(gè)時(shí)候它只會(huì)把這個(gè)數(shù)據(jù)中最低位上的1個(gè)字節(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表示從下標(biāo)為off開始
* len表示寫到文件中的字節(jié)個(gè)數(shù)
*/
byte[] b={97,98,65,66};
out.write(b, 0, 2);//寫出結(jié)果是:ab
//關(guān)閉資源
out.close();
// out1.write(b);
// out.close();
// out.write(97);//a
// System.out.println(Integer.toBinaryString(353));
}
說明:
1)字節(jié)輸出流中的write方法每調(diào)用一次,只能寫出一個(gè)字節(jié)數(shù)據(jù)。
如果指定的數(shù)據(jù)較大,這個(gè)時(shí)候它只會(huì)把這個(gè)數(shù)據(jù)中最低位上的1個(gè)字節(jié)數(shù)據(jù)寫到文件中
例如:353輸出的結(jié)果是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表示從下標(biāo)為off開始
len表示寫到文件中的字節(jié)個(gè)數(shù)
3)如果使用完一個(gè)輸出流,關(guān)閉之后,那么不能繼續(xù)使用這個(gè)輸出流向該文件中繼續(xù)寫入數(shù)據(jù),否則會(huì)報(bào)異常。只能重新再創(chuàng)建一個(gè)新的輸出流,繼續(xù)向該文件中寫數(shù)據(jù),這樣后寫入的數(shù)據(jù)會(huì)覆蓋之前書寫的數(shù)據(jù);
2.3、追加數(shù)據(jù)和換行
數(shù)據(jù)追加問題:
通過以上演示我們發(fā)現(xiàn)一個(gè)問題,就是流一旦關(guān)閉,再次寫數(shù)據(jù),會(huì)覆蓋文件中原來的數(shù)據(jù)。流不關(guān)閉,一次性寫多次,也會(huì)追加。
如何解決?
使用FileOutputStream類中的其余構(gòu)造函數(shù):
FileOutputStream(File file, booleanappend) 創(chuàng)建一個(gè)向指定 File 對象表示的文件中寫入數(shù)據(jù)的文件輸出流。
FileOutputStream(String name, boolean append) 創(chuàng)建一個(gè)向具有指定name 的文件中寫入數(shù)據(jù)的文件輸出流。
說明:
A:上述構(gòu)造函數(shù)中如果第二個(gè)參數(shù)append為true,那么就在已經(jīng)存在的文件末位追加數(shù)據(jù),如果為false,那么就不會(huì)文件中已經(jīng)存在的數(shù)據(jù)末尾追加數(shù)據(jù),而是將原來的數(shù)據(jù)給覆蓋;
B:如果指定的文件在硬盤上不存在,也會(huì)創(chuàng)建這個(gè)文件;
需求:使用字節(jié)輸出流把字符串?dāng)?shù)據(jù)”你好嗎”寫到硬盤上,要求不能覆蓋文件中原有的數(shù)據(jù);
分析和步驟:
1)使用new關(guān)鍵字調(diào)用FileOutputStream類的構(gòu)造函數(shù)創(chuàng)建輸出流對象fos;
2)使用對象fos調(diào)用write函數(shù)向指定的文件添加數(shù)據(jù);
3)關(guān)閉資源;
//演示向文件末尾追加數(shù)據(jù)
//需求:使用字節(jié)輸出流把字符串?dāng)?shù)據(jù)”你好嗎”寫到硬盤上,要求不能覆蓋文件中原有的數(shù)據(jù);
public static void method_1() throws IOException {
//創(chuàng)建輸出流對象
FileOutputStream fos = new FileOutputStream("D:\\test1\\4.txt", true);
/*
* 對于以上構(gòu)造函數(shù)進(jìn)行說明:
* 如果第二個(gè)參數(shù)為true,那么就會(huì)在已經(jīng)存在的文件中的末尾處追加數(shù)據(jù),如果這個(gè)文件4.txt不存在
* 那么就會(huì)創(chuàng)建這個(gè)文件
* 如果第二個(gè)參數(shù)為false,那么向文件添加數(shù)據(jù)的時(shí)候就會(huì)覆蓋原來的數(shù)據(jù)
*/
//向文件中添加數(shù)據(jù)
fos.write("你好嗎".getBytes());
fos.write("我叫狗哥".getBytes());
//關(guān)閉資源
fos.close();
}
數(shù)據(jù)換行問題:
我們?nèi)绻霌Q行,可以在數(shù)據(jù)的末尾加:\r\n
但是:\r\n是windows系統(tǒng)識(shí)別的換行符。不同的操作系統(tǒng),換行符可能會(huì)不相同的。我們的代碼擴(kuò)展性就變差了。
解決方案:
如果我能根據(jù)系統(tǒng)來獲取對應(yīng)的換行符,就可以跨平臺(tái)。如何從系統(tǒng)獲取換行符呢?
System類中的方法:
public staticProperties getProperties() 獲取系統(tǒng)中所有的屬性這個(gè)函數(shù)的返回值是 Properties 類,這個(gè)類實(shí)現(xiàn)了Map接口,所以getProperties()這個(gè)函數(shù)獲得系統(tǒng)中所有的屬性并以鍵值對形式存在。在眾多系統(tǒng)屬性中我們想要的是行分隔符,就是類似\r\n,那么行分隔符的鍵是line.separator,也就是說通過這個(gè)鍵就可以獲得系統(tǒng)對應(yīng)的行分隔符。
這里還得需要借助一個(gè)函數(shù)通過以上的鍵line.separator獲得行分隔符的值,這個(gè)函數(shù)就是getProperty()
static String getProperty(String key) 獲取指定鍵指示的系統(tǒng)屬性。
代碼如下所示:
/*
* 換行演示
* 我們?nèi)绻霌Q行可以在添加數(shù)據(jù)的末尾書寫\r\n 就可以實(shí)現(xiàn)換行
* 但是\r\n屬于Windows系統(tǒng)中特有的方法,不能在其他系統(tǒng)中使用,也就是說不能
* 跨平臺(tái),這樣代碼的擴(kuò)展性就變差了
* 解決辦法:
* 如果我們能夠根據(jù)不同的系統(tǒng)獲取系統(tǒng)對應(yīng)的行分隔符,那么這樣代碼就可以跨平臺(tái)了,
* 那么如何獲得系統(tǒng)的的屬性,行分隔符呢?
* 通過System類,調(diào)用這個(gè)類中的函數(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());
//關(guān)閉資源
fos.close();
}
2.4、字節(jié)輸入流(掌握)
之前學(xué)習(xí)的是輸出流對象,是用來從內(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(): 關(guān)閉字節(jié)輸入流對象。
說明:
1)read():調(diào)用一次read,就可以從關(guān)聯(lián)的文件中讀取一個(gè)字節(jié)數(shù)據(jù),并返回這個(gè)字節(jié)數(shù)據(jù)。
2)read():方法可以從關(guān)聯(lián)的文件中讀取數(shù)據(jù)。所有read方法如果讀取到文件的末尾,都會(huì)返回-1。遇到-1就代表文件中的數(shù)據(jù)已經(jīng)被讀取完畢。
3)read(byte[] b) :調(diào)用一次,讀取多個(gè)字節(jié)數(shù)據(jù),把讀到的字節(jié)數(shù)據(jù)保存在傳遞的b字節(jié)數(shù)組中。返回字節(jié)數(shù)組中讀取的字節(jié)個(gè)數(shù)。注意啦:這個(gè)返回值不是數(shù)組長度。
由于InputStream類是抽象類,不能創(chuàng)建這個(gè)類的對象,但是如果想使用這個(gè)類中的函數(shù),那必須得創(chuàng)建這個(gè)類的對象,如果想要?jiǎng)?chuàng)建對象,那么只能創(chuàng)建InputStream類的子類。
由于我們這里是操作文件的,所以我們需要?jiǎng)?chuàng)建FileInputStream類的對象。
2.4.2、FileInputStream介紹
2.4.3、每次讀單個(gè)字節(jié)
演示:字節(jié)輸入流讀取數(shù)據(jù),一次讀一個(gè)字節(jié)。
構(gòu)造函數(shù):
FileInputStream(File file)
FileInputStream(String name)
讀取功能:int read()
先使用輸入流對象,從文件中讀取數(shù)據(jù),每調(diào)用一次read()方法,可以從硬盤文件中讀取一個(gè)字節(jié)數(shù)據(jù),把這個(gè)字節(jié)數(shù)據(jù)保存在一個(gè)int類型的變量中。然后判斷讀取到的這個(gè)數(shù)據(jù)也就是這個(gè)int類型的變量是否是-1,如果不是-1,說明當(dāng)前沒有讀取到文件的末尾。如果是-1,說明當(dāng)前已經(jīng)讀取到文件的末尾。
int類型的變量中就保存著當(dāng)前讀取到的那個(gè)字節(jié)數(shù)據(jù),后續(xù)步驟可以對這個(gè)數(shù)據(jù)進(jìn)行相關(guān)處理。
輸入流的使用步驟:
A:創(chuàng)建輸入流,關(guān)聯(lián)源文件;
B:讀取數(shù)據(jù);
C:釋放資源;
注意:
1)字節(jié)輸入流,構(gòu)造函數(shù)執(zhí)行時(shí),如果源文件不存在,那么拋出異常!!;
2)由于輸入流讀取的是文件中的字節(jié)數(shù)據(jù),所以要求輸入流指定的一定是文件,不能是文件夾,否則會(huì)報(bào)異常;
分析和步驟:
1)使用new關(guān)鍵字調(diào)用FileInputStream類的構(gòu)造函數(shù)創(chuàng)建指定路徑D:\test1\1.txt的輸入流對象in;
2)使用輸入流對象in調(diào)用read()函數(shù)開始讀取文件,返回一個(gè)int類型的整數(shù),并輸出最后返回值;
3)多次調(diào)用read()函數(shù)引起代碼重復(fù),我們可以考慮使用循環(huán)來實(shí)現(xiàn);
4)循環(huán)條件是返回值是-1;
/*
* 演示:字節(jié)輸入流讀取數(shù)據(jù),一次讀一個(gè)字節(jié)。
* 注意:由于輸入流讀取的是文件中的字節(jié)數(shù)據(jù),所以要求輸入流指定的一定是文件,否則會(huì)報(bào)異常
* FileNotFoundException
* 讀取功能:
* int read():表示讀取下一個(gè)字節(jié)并返回
* 輸入流的使用步驟:
* 1)創(chuàng)建輸入流;
* 2)讀取數(shù)據(jù);
* 3)關(guān)閉輸入流;
*/
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ù)一次讀一個(gè)字節(jié)數(shù)據(jù)
//第一次讀取
int i = fis.read();//i表示讀取的字節(jié)數(shù)據(jù),如果是-1說明文件的結(jié)尾
//輸出讀取的字節(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
//關(guān)閉資源
fis.close();
}
}
通過上述代碼發(fā)現(xiàn),在使用輸入流對象fis調(diào)用read()函數(shù)的時(shí)候,出現(xiàn)多次調(diào)用的情況,這樣也會(huì)導(dǎo)致代碼重復(fù),在開發(fā)中盡量簡化代碼的書寫,所以對上述代碼還得進(jìn)一步優(yōu)化:
終極版代碼單個(gè)讀取數(shù)據(jù)的代碼模板如下所示:
/*
* 通過書寫代碼發(fā)現(xiàn)上述代碼重復(fù)太多,我們可以考慮使用循環(huán)來解決上述代碼重復(fù)性的問題
* 問題:循環(huán)的循環(huán)條件是什么?
* 讀取到文件結(jié)尾,即-1則結(jié)束,所以可以讓讀取的結(jié)果是-1結(jié)束讀取文件
*/
//讀取數(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)條件 可以理解為一個(gè)光標(biāo),讀取一次,i的值改變一次
i=fis.read();
}
2.4.4、復(fù)制文件方式1練習(xí)
需求:每次讀1個(gè)字節(jié)來完成復(fù)制練習(xí)1:
復(fù)制D:\test\1.txt里面的數(shù)據(jù)到F:\2.txt文件中
分析:
數(shù)據(jù)源:一個(gè)文本文件,這里使用字節(jié)流,讀取,所以FileInputStream
目的地:一個(gè)文本文件,這里使用字節(jié)流,寫出,所以FileOutputStream
思路:
A:創(chuàng)建一個(gè)輸入流,關(guān)聯(lián)源文件
B:創(chuàng)建一個(gè)輸出流,關(guān)聯(lián)目標(biāo)文件
C:讀取數(shù)據(jù),讀取硬盤中指定文件中的數(shù)據(jù)內(nèi)容,直到讀取的返回值是-1
D:寫出數(shù)據(jù),將上述每次讀取的數(shù)據(jù)內(nèi)容都寫入到目標(biāo)文件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;
/*
* 復(fù)制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)建輸入流對象,關(guān)聯(lián)數(shù)據(jù)源文件;
* 2)創(chuàng)建輸出流對象,關(guān)聯(lián)目的地文件;
* 3)讀取數(shù)據(jù),讀取硬盤中指定文件中的數(shù)據(jù),直到讀取文件的末尾處;
* 4)寫出數(shù)據(jù),將上述每次讀取的文件中的內(nèi)容寫到目標(biāo)文件中;
* 5)關(guān)閉資源;
*/
public class FileInputStreamTest {
public static void main(String[] args) throws IOException {
//1)創(chuàng)建輸入流對象,關(guān)聯(lián)數(shù)據(jù)源文件;
FileInputStream fis = new FileInputStream("D:\\test\\1.txt");
//2)創(chuàng)建輸出流對象,關(guān)聯(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);
}
//關(guān)閉資源
fis.close();
fos.close();
}
}
2.4.5、每次讀字節(jié)數(shù)組
演示:字節(jié)輸入流,一次讀一個(gè)字節(jié)數(shù)組。
讀取功能:
1)int read(byte[] buf) 讀取數(shù)據(jù)到數(shù)組:buf中,返回的是讀取到的字節(jié)的個(gè)數(shù)len.
定義的字節(jié)數(shù)組是用來存儲(chǔ)從底層文件中讀取到的多個(gè)字節(jié)數(shù)據(jù);
2)在把讀取的字節(jié)個(gè)數(shù)保存在len中。len中保存的是真正給字節(jié)數(shù)組中讀取的字節(jié)個(gè)數(shù),如果讀取到文件末尾,也會(huì)返回-1;
一般這個(gè)數(shù)組的長度都會(huì)定義成1024的整數(shù)倍。
使用循環(huán)重復(fù)的從文件中讀取數(shù)據(jù),每次最多可以從文件中讀取1024個(gè)字節(jié)數(shù)據(jù)
需求:在D:\test\1.txt這個(gè)目錄的1.txt文件中書寫幾個(gè)字符串,如下所示:
hello
world
Java
字樣,然后使用字節(jié)輸入流一次讀一個(gè)字節(jié)數(shù)組來讀取上述路徑中的1.txt文件中的數(shù)據(jù),將每次讀取的數(shù)據(jù)輸出打印到控制臺(tái)。
分析和步驟:
1)創(chuàng)建一個(gè)輸入流對象,和D:\test\1.txt文件進(jìn)行關(guān)聯(lián);
2)定義的字節(jié)byte數(shù)組b,這個(gè)字節(jié)數(shù)組的長度是5,主要是用來存儲(chǔ)從底層文件中讀取到的多個(gè)字節(jié)數(shù)據(jù);
3)用來記錄當(dāng)前給byte數(shù)組中讀取的字節(jié)個(gè)數(shù)的變量,int len = 0;
4)先執(zhí)行fis.read()函數(shù)從底層讀取數(shù)據(jù),然后會(huì)把數(shù)據(jù)保存在我們傳遞的參數(shù)b數(shù)組中。返回值定義一個(gè)int類型的變量len記錄著讀取到字節(jié)數(shù)組中的字節(jié)數(shù);
5)輸出記錄的讀取到字節(jié)數(shù)len和將字節(jié)數(shù)組轉(zhuǎn)換后的字符串?dāng)?shù)據(jù),將字節(jié)轉(zhuǎn)換為字符串可以使用Arrays.toString(b)或者String類的構(gòu)造函數(shù);
6)由于字節(jié)數(shù)組長度是5,所以需要多次讀取,按照上述操作多次讀取1.txt文件中剩余的數(shù)據(jù),將結(jié)果輸出到控制臺(tái)上面;
/*
* 演示:字節(jié)輸入流,一次讀一個(gè)字節(jié)數(shù)組。
* int read(byte[] b) 表示定義一個(gè)byte數(shù)組,每次讀取的字節(jié)都存儲(chǔ)到這個(gè)數(shù)組中
*/
public class FileInputStreamDemo1 {
public static void main(String[] args) throws IOException {
// 創(chuàng)建輸入流對象,關(guān)聯(lián)源文件
FileInputStream fis = new FileInputStream("D:\\test\\1.txt");
//第一次讀取
//定義一個(gè)數(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
//第一次讀取結(jié)果:
/*
len=5
hello
*/
//第二次讀取
len = fis.read(b);
//輸出len
System.out.println("len="+len);
//輸出字節(jié)數(shù)組中的內(nèi)容
System.out.println(new String(b));
//第二次讀取結(jié)果:
/*
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));
//第三次讀取結(jié)果:
/*
* 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));
//第四次讀取結(jié)果:
/*
在1.txt文件中,如果最后的數(shù)據(jù)Java有回車換行,那么會(huì)輸出如下所示數(shù)據(jù):
len=5
ava\r\n
在1.txt文件中,如果最后的數(shù)據(jù)Java沒有回車換行,那么會(huì)輸出如下所示數(shù)據(jù):
len=3
ava
J
*/
}
}
說明:
1)通過上述代碼發(fā)現(xiàn)返回值len表示讀取到的字節(jié)數(shù),而不是字節(jié)數(shù)組的長度。如果讀取為5個(gè)字節(jié)數(shù),那么返回5,即,len等于5。如果讀取到的字節(jié)數(shù)是3,那么返回3,即len等于3。如果文件中沒有要讀取的數(shù)據(jù),則返回-1。
2)上述代碼中當(dāng)?shù)谒拇巫x取的時(shí)候有問題,如果文件中最后一個(gè)數(shù)據(jù)后面沒有回車換行,那么應(yīng)該只打印ava,為什么會(huì)打印:
ava
J
呢?
原因如下圖所示:
為了解決上述代碼出現(xiàn)的問題,我們更希望看到當(dāng)我們讀取幾個(gè)字節(jié)數(shù)據(jù),我們就輸出幾個(gè)字節(jié)數(shù)據(jù),所以這里我們不能在使用new String(b)構(gòu)造函數(shù),我們應(yīng)該使用new String(b,int offset,int length)構(gòu)造函數(shù),這樣做就不會(huì)出現(xiàn)上述問題。
說明:new String(b,0,len):
創(chuàng)建一個(gè)字符串對象,把b數(shù)組中的數(shù)據(jù)轉(zhuǎn)成字符串,從0位置開始,共計(jì)轉(zhuǎn)len個(gè)。
從0位置開始,因?yàn)槊看握{(diào)用輸入流的read方法的時(shí)候,把數(shù)據(jù)給byte數(shù)組中保存
這時(shí)真正是從byte數(shù)組的0位置開始存儲(chǔ)讀取的每個(gè)字節(jié)數(shù)據(jù)。
len是byte數(shù)組中的保存的真正的讀取的字節(jié)個(gè)數(shù)。
這樣做就可以做到我們讀取幾個(gè)字節(jié)數(shù)據(jù),我們就輸出幾個(gè)字節(jié)數(shù)據(jù)的目的。
代碼如下所示:
public class FileInputStreamDemo1 {
public static void main(String[] args) throws IOException {
// 創(chuàng)建輸入流對象,關(guān)聯(lián)源文件
FileInputStream fis = new FileInputStream("D:\\test\\1.txt");
//第一次讀取
//定義一個(gè)數(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));
//關(guān)閉資源
fis.close();
}
}
上面的代碼重復(fù)太多了,考慮用循環(huán)。
問題來了:循環(huán)的接收條件是什么呢?
結(jié)束條件:末尾返回-1
終極版代碼如下所示:
//定義一個(gè)數(shù)組
// byte[] b=new byte[5];
//終極版代碼模板
byte[] b=new byte[1024];//數(shù)組長度一般是1024的整數(shù)倍
//定義一個(gè)變量保存讀取字節(jié)的個(gè)數(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));
}
//關(guān)閉資源
fis.close();
2.4.6、復(fù)制文件方式2練習(xí):
需求:每次讀字節(jié)數(shù)組來完成復(fù)制練習(xí)2:
復(fù)制D:\test\1.txt中的數(shù)據(jù)到F:\2.txt文件中。
分析和步驟:
1)創(chuàng)建輸入流,關(guān)聯(lián)源文件D:\test\1.txt;
2)創(chuàng)建輸出流,關(guān)聯(lián)目標(biāo)文件F:\2.txt;
3)讀寫數(shù)據(jù);
4)關(guān)閉資源;
/*
* 需求:每次讀字節(jié)數(shù)組來完成復(fù)制練習(xí)2:
復(fù)制D:\\test\\1.txt到F:\\2.txt
*/
public class FileInputStreamTest1 {
public static void main(String[] args) throws IOException {
// 創(chuàng)建輸入流對象,關(guān)聯(lián)源文件
FileInputStream fis = new FileInputStream("D:\\test\\1.txt");
//創(chuàng)建輸出流對象,關(guān)聯(lián)目標(biāo)文件
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);
}
//關(guān)閉資源
fis.close();
fos.close();
}
}
注意:
1)使用InputStream類中的read()函數(shù)表示每次讀單個(gè)字節(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ù)寫入到新的文件中;
上述兩種辦法雖然都可以實(shí)現(xiàn),但是是有區(qū)別的,第二種字節(jié)數(shù)組效率要高,開發(fā)中優(yōu)先考慮第二種,兩種讀寫文件方式的區(qū)別如下圖所示:
復(fù)制文件的時(shí)候注意:
1、 需要定義中間的變量(容器),讓字節(jié)輸入流和輸出流可以聯(lián)系起來。然后輸入流讀取數(shù)據(jù),輸出流把讀取到的數(shù)據(jù)寫到文件中。
2、 在讀取數(shù)據(jù)的時(shí)候,調(diào)用read() 和read( 字節(jié)數(shù)組 )有嚴(yán)格的區(qū)別。
3、 在寫出數(shù)據(jù)的時(shí)候,write(字節(jié)數(shù)組) 和 write( 字節(jié)數(shù)組,0 ,長度 )有區(qū)別。
2.5、復(fù)制文件綜合練習(xí)
需求:
演示復(fù)制文件:把D盤下的1.mp3 拷貝到F盤下的1.mp3。
分析和步驟: 每次讀1個(gè)字節(jié)來完成復(fù)制
1)創(chuàng)建測試類CopyFile,在這個(gè)類中創(chuàng)建兩個(gè)函數(shù)method_1和method_2;
2)在method_1()函數(shù)中演示復(fù)制文件一次讀寫一個(gè),在這個(gè)函數(shù)中創(chuàng)建輸入流對象fis和輸出流對象fos,分別指定源文件位置D:\1.mp3和目標(biāo)文件位置F:\1.mp3;
3)獲得開始復(fù)制的系統(tǒng)時(shí)間的毫秒值start;
4)使用while循環(huán)控制讀文件的次數(shù),使用輸入流對象fis調(diào)用read()函數(shù)一次一次讀,使用輸出流對象fos調(diào)用write()函數(shù)一次一次寫;
5)獲取復(fù)制結(jié)束的時(shí)間end;
6)結(jié)束時(shí)間end減去開始復(fù)制的時(shí)間start,關(guān)閉輸入和輸出流;
//演示一次讀取一個(gè)字節(jié)
public static void method_1() throws IOException {
//創(chuàng)建輸入流對象 關(guān)聯(lián)源文件
FileInputStream fis = new FileInputStream("D:\\1.mp3");
//創(chuàng)建輸出流對象 關(guān)聯(lián)目標(biāo)文件
FileOutputStream fos=new FileOutputStream("F:\\1.mp3");
//獲取開始復(fù)制的時(shí)間
long start = System.currentTimeMillis();
//定義變量保存字節(jié)
int ch=0;
while((ch=fis.read())!=-1)
{
//寫數(shù)據(jù)
fos.write(ch);
}
//獲取結(jié)束的時(shí)間
long end = System.currentTimeMillis();
System.out.println("復(fù)制時(shí)間是:"+(end-start));//復(fù)制時(shí)間是:38406
//關(guān)閉資源
fis.close();
fos.close();
}
需求:
演示復(fù)制文件:把D盤下的1.mp3 拷貝到F盤下。
分析和步驟:每次讀字節(jié)數(shù)組來完成復(fù)制
1)步驟和上述步驟大致相同,只是需要定義一個(gè)整數(shù)變量len來記錄當(dāng)前給byte數(shù)組中讀取的字節(jié)個(gè)數(shù)的;
2)定義的字節(jié)數(shù)組buf是用來存儲(chǔ)從底層文件中讀取到的多個(gè)字節(jié)數(shù)據(jù);
3)使用read(buf)函數(shù)讀取文件的時(shí)候需要傳入一個(gè)數(shù)組參數(shù)buf;
4)每讀取一次使用輸出流對象調(diào)用write()函數(shù)向文件中寫數(shù)據(jù);
//演示一次讀取一個(gè)字節(jié)數(shù)組
public static void method_2() throws IOException {
// 創(chuàng)建輸入流對象 關(guān)聯(lián)源文件
FileInputStream fis = new FileInputStream("D:\\1.mp3");
//創(chuàng)建輸出流對象 關(guān)聯(lián)目標(biāo)文件
FileOutputStream fos = new FileOutputStream("F:\\1.mp3");
//獲取開始復(fù)制的時(shí)間
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);
}
//獲取結(jié)束復(fù)制的時(shí)間
long end = System.currentTimeMillis();
System.out.println("復(fù)制時(shí)間是:"+(end-start));//復(fù)制時(shí)間是:15
//關(guān)閉資源
fis.close();
fos.close();
}
2.6、總結(jié)輸入流讀取文件的模版代碼(掌握)
1、一次讀取一個(gè)字節(jié)的模版代碼
1)、創(chuàng)建輸入流對象
FileInputStream fis = new FileInputStream( “文件” );
注意:
1)FileInputStream類的構(gòu)造函數(shù)的參數(shù)一定要是個(gè)文件路徑,不能是文件夾路徑,因?yàn)镕ileInputStream類是用來操作文件的,是讀取文件中的內(nèi)容;
2)在構(gòu)造函數(shù)中指定的文件在硬盤上一定要存在,否則會(huì)拋異常;
2)、定義變量,記錄每次讀取到的字節(jié)數(shù)據(jù)
int b= 0;
3)、使用循環(huán)讀取數(shù)據(jù)
while( ( b = fis.read() ) !=-1 ){
//處理讀取到的數(shù)據(jù),數(shù)據(jù)在b中。
}
4)、關(guān)閉流對象
fis.close();
2、一次讀取多個(gè)字節(jié)數(shù)據(jù)的模版代碼(掌握)
1)、創(chuàng)建輸入流對象
FileInputStream fis = new FileInputStream( “文件” );
2)、定義變量,記錄讀取到的字節(jié)個(gè)數(shù)
int len = 0;
3)、定義數(shù)組保存讀取到的多個(gè)字節(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中共計(jì)有效的數(shù)據(jù)是len個(gè)
}
5)、關(guān)閉流對象
fis.close();
2.7、IO流中的異常處理模版(了解)
我們使用Java程序,操作的是Java程序以外的其他設(shè)備上的數(shù)據(jù),都有可能發(fā)生異常問題。
我們在書寫的Java程序讀寫其他設(shè)備上的數(shù)據(jù)時(shí),都要考慮異常問題。這些異常一般開發(fā)中都要開發(fā)者自己處理掉,不能直接往出拋。
說明:
1)對于輸入流FileInputStream來說,硬盤上一定要有文件,否則會(huì)報(bào)找不到文件異常;對于輸入流read()函數(shù)來說,假設(shè)有文件,但是文件沒有可讀的權(quán)限,那么對于輸入流對象調(diào)用read()函數(shù)也會(huì)報(bào)異常;對于關(guān)閉流的函數(shù)close()來說,也同樣有異常;
2)對于輸出流FileOutputStream來說,指定的路徑一定是硬盤上的文件不能是文件夾,否則會(huì)報(bào)異常;對于輸入流write()函數(shù)來說,假設(shè)有文件,但是文件沒有可寫的權(quán)限,那么對于輸出流對象調(diào)用write()函數(shù)也會(huì)報(bào)異常;對于關(guān)閉流的函數(shù)close()來說,也同樣有異常;
3)流的關(guān)閉動(dòng)作,必須被執(zhí)行。但是如果在流關(guān)閉之前,已經(jīng)出異常,那么流的關(guān)閉動(dòng)作無法執(zhí)行。
必須被執(zhí)行的動(dòng)作,放到finally中;把關(guān)閉動(dòng)作,放到finally代碼塊中后,流的定義也必須放到try塊的外面,否則看不到;在finally中關(guān)閉流的時(shí)候要先判斷流是否為null,如果不判斷那么流對象調(diào)用close()函數(shù)有可能報(bào)空指針異常。
IO流讀寫數(shù)據(jù)的模版代碼:
分析和步驟:
1)定義一個(gè)測試類IOExceptionDemo 在這個(gè)類中定義兩個(gè)函數(shù)method_1()和method_2()分別書寫讀數(shù)據(jù)和寫數(shù)據(jù)的模版處理代碼;
2)在method_1()函數(shù)中定義一個(gè)FileInputStream 類的對象fis=null;
3)在try-catch-finally代碼塊中創(chuàng)建FileInputStream 類的對象并指定讀數(shù)據(jù)的文件D:\1.txt;
4)使用一次性讀取一個(gè)字節(jié)數(shù)據(jù)來完成讀數(shù)據(jù);
5)在finally代碼塊中在關(guān)閉流之前要先判斷流對象是否存在;
6)在method_2()函數(shù)中定義一個(gè)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代碼塊中在關(guān)閉流之前要先判斷流對象是否存在;
/*
* 字節(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的大括號(hào)里面使用,不能在其他地方使用
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ù)異常了");
//后期在寫項(xiàng)目的時(shí)候,抓到異常了,這里需要寫日志文檔
}finally
{
if(fis!=null)
{
try {
//不管程序是否有異常都得需要關(guān)閉資源
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ù)的時(shí)候,如果文件不存在,會(huì)創(chuàng)建這個(gè)文件,如果文件存在,會(huì)覆蓋掉文件中原有的數(shù)據(jù)。
B:如果不想覆蓋掉原來文件中的數(shù)據(jù),在創(chuàng)建輸出流的時(shí)候,可以指定布爾值為true,這樣就會(huì)在文件的末尾追加數(shù)據(jù)。
可以使用構(gòu)造函數(shù)FileOutputStream(String name, boolean append)來實(shí)現(xiàn)。
C:write( int b ) 它只能寫出這個(gè)int數(shù)據(jù)中最低位的1個(gè)字節(jié)數(shù)據(jù)。
D:換行
//獲得系統(tǒng)中的行分隔符
Stringseparator = System.getProperty("line.separator");
//向文件中寫出數(shù)據(jù)
fos.write(("黑旋風(fēng)真帥哈哈"+separator).getBytes());
2、字節(jié)輸入流:
它不會(huì)創(chuàng)建文件,同時(shí)要求輸入流關(guān)聯(lián)的一定是文件,不能是文件夾。
字節(jié)輸入流有自己的讀取數(shù)據(jù)的模版代碼。
一次讀取一個(gè)字節(jié)數(shù)據(jù),或一次讀取多個(gè)字節(jié)數(shù)據(jù)。
3、流的操作:
不管是輸入流還是輸出流,在操作完之后都要關(guān)閉流。
2.9、字節(jié)流緩沖區(qū)介紹(了解)
通過以上演示發(fā)現(xiàn),一次讀很多字節(jié)比一次讀一個(gè)字節(jié)快。Java的設(shè)計(jì)者肯定知道這一點(diǎn)。
Java專門提供了一類流,可以實(shí)現(xiàn)高效的讀取。這種流,就是在內(nèi)部維護(hù)了一個(gè)緩沖區(qū)數(shù)組。
字節(jié)流緩沖區(qū):包括字節(jié)輸入流緩沖區(qū)和字節(jié)輸出流緩沖區(qū)。
字節(jié)輸入流緩沖區(qū):
BufferedInputStream:
說明:
BufferedInputStream:它就是一個(gè)緩沖區(qū),它內(nèi)部維護(hù)一個(gè)數(shù)組,可以從底層讀取多個(gè)數(shù)據(jù)。
構(gòu)造方法摘要
BufferedInputStream(InputStream in)
創(chuàng)建一個(gè)新的緩沖輸入流,用來將數(shù)據(jù)寫入指定的底層輸入流。
問題:為什么緩沖區(qū)流需要接收一個(gè)普通字節(jié)流呢?
緩沖區(qū)流是為了 高效而設(shè)計(jì)的,緩沖區(qū)流本身僅僅是維護(hù)了一個(gè)數(shù)組。不具備讀和寫的功能。真正的讀寫還是要依賴普通的字節(jié)流。
緩沖區(qū)字節(jié)流和以前字節(jié)流讀寫文件的做法區(qū)別如下圖所示:
說明:
1)使用緩沖區(qū)輸入流的時(shí)候,后期我們需要數(shù)據(jù)的時(shí)候,不直接和文件進(jìn)行交互,而是向緩沖區(qū)索要數(shù)據(jù);
2)緩沖區(qū)只能臨時(shí)存儲(chǔ)數(shù)據(jù),它不能從底層讀取數(shù)據(jù),從底層讀取數(shù)據(jù)還得需要輸入流;
3)使用緩沖區(qū)的目的只是為了提高讀寫的效率,先使用FileInputStream輸入流將硬盤上的文件讀取到緩沖區(qū)中,然后在從緩沖區(qū)中的數(shù)組中取出數(shù)據(jù)存放到我們之前定義好的數(shù)組中,因?yàn)閮蓚€(gè)數(shù)組都是在內(nèi)存中,這樣交互數(shù)據(jù)會(huì)更快一點(diǎn);
4)而我們之前都是從硬盤上直接將數(shù)據(jù)讀取到定義好的數(shù)組中,這樣效率會(huì)低點(diǎn);
分析和步驟:
1)定義一個(gè)測試類BufferedInputStreamDemo;
2)在這個(gè)類中的main函數(shù)中創(chuàng)建一個(gè)可以直接和文件交互的輸入流對象in,并指定路徑D:\test\123.txt;
3)創(chuàng)建一個(gè)緩沖區(qū)對象bufIn,需要指定可以從底層讀取數(shù)據(jù)的流對象;
4)創(chuàng)建一個(gè)byte類型的數(shù)組buf,大小是1024,定義一個(gè)整數(shù)變量len=0;
5)使用緩沖區(qū)對象bufIn調(diào)用read()函數(shù)讀取數(shù)據(jù)并存儲(chǔ)到定義好的數(shù)組buf中,并轉(zhuǎn)換為字符串,輸出;
6)關(guān)閉流;
/*
* 演示字節(jié)輸入流緩沖區(qū)
*/
public class BufferedInputStreamDemo {
public static void main(String[] args) throws IOException {
//創(chuàng)建一個(gè)可以直接和文件進(jìn)行交互的輸入流對象
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];
//定義一個(gè)變量
int len=0;
while((len=bufin.read(b))!=-1)
{
System.out.println(new String(b,0,len));
}
//關(guān)閉資源
bufin.close();
}
}
說明:上述代碼關(guān)閉緩沖區(qū)流就可以了,不用手動(dòng)關(guān)閉字節(jié)流了,因?yàn)樵诘讓右呀?jīng)關(guān)閉字節(jié)流了。
字節(jié)輸出流緩沖區(qū):
BufferedOutputStream:
BufferedOutputStream:它可以把需要寫出的數(shù)據(jù),寫到自己的緩沖區(qū)中,當(dāng)緩沖區(qū)寫滿,或者我們手動(dòng)調(diào)用flush方法,或者最后我們關(guān)流,才會(huì)把緩沖區(qū)中的數(shù)據(jù)一次性的寫到底層文件中。
構(gòu)造方法摘要:
BufferedOutputStream(OutputStreamout)
創(chuàng)建一個(gè)新的緩沖輸出流,以將數(shù)據(jù)寫入指定的底層輸出流。
問題:為什么緩沖區(qū)流需要接收一個(gè)普通字節(jié)流呢?
緩沖區(qū)流是為了 高效而設(shè)計(jì)的,緩沖區(qū)流本身僅僅是維護(hù)了一個(gè)數(shù)組。不具備讀和寫的功能。真正的讀寫還是要依賴普通的字節(jié)流。
分析和步驟:
1)定義一個(gè)測試類BufferedOutputStreamDemo;
2)在這個(gè)類中的main函數(shù)中創(chuàng)建一個(gè)可以直接和文件交互的輸出流對象fos,并指定路徑D:\out.txt;
3)創(chuàng)建一個(gè)緩沖區(qū)輸出流對象bufOut;
4)使用緩沖區(qū)輸出流對象bufOut調(diào)用write()函數(shù)向指定文件中寫入指定的內(nèi)容;
5)使用緩沖區(qū)輸出流對象bufOut調(diào)用close()函數(shù)關(guān)閉緩沖區(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());
//關(guān)閉緩沖區(qū)輸出流
//buffOut.close();
//將緩沖區(qū)中的數(shù)據(jù)刷新到輸出流中
buffOut.flush();
}
}
注意:
flush:刷新緩沖區(qū),把緩沖區(qū)中的有效數(shù)據(jù)刷到底層文件中,刷新完成之后,緩沖區(qū)流還可以繼續(xù)使用。
close:關(guān)閉流和底層文件之間的關(guān)聯(lián),一旦流關(guān)閉了,就不能在使用
小結(jié):
字節(jié)流緩沖區(qū)對象屬于IO流中的高效流,可以提高文件的讀寫效率。
緩沖區(qū)對象讀寫的原理:
BufferedInputStream對象:該流需要使用輸入流對象讀取字節(jié)數(shù)據(jù),把讀取到字節(jié)數(shù)據(jù)暫時(shí)存儲(chǔ)在緩沖區(qū)對象內(nèi)部的數(shù)組(緩沖區(qū))中,當(dāng)內(nèi)部的緩沖區(qū)數(shù)組存滿了后或不需要再讀取數(shù)據(jù)了,就可以從內(nèi)部的緩沖區(qū)數(shù)組中獲取字節(jié)數(shù)據(jù)。
BufferedOutputStream對象:該流需要使用輸出流對象寫入字節(jié)數(shù)據(jù),把要寫入的數(shù)據(jù)轉(zhuǎn)換為字節(jié)數(shù)據(jù)后暫時(shí)存儲(chǔ)到緩沖區(qū)數(shù)組中,當(dāng)內(nèi)部緩沖區(qū)數(shù)組存滿后或者關(guān)閉流資源或者刷新緩沖區(qū)時(shí)就可以從緩沖區(qū)數(shù)組取出字節(jié)數(shù)據(jù)寫入到文件中。