目錄
一、數(shù)據(jù)流概述
二、流的關(guān)閉
2.1 使用 close() 方法
2.2 使用 try-finally
2.3 使用?try-with-resources
三、字符流的讀寫
3.1 Reader 類
3.2 Writer 類
四、字節(jié)流的讀寫
4.1?InputStream?類
4.2?OutputStream?類
一、數(shù)據(jù)流概述
1)在 Java 中,文件的操作分為兩類 | |
操作文件系統(tǒng) | 通過 File 類,在系統(tǒng)中進(jìn)行增、刪、查等操作。 |
操作文件內(nèi)容 | 通過 數(shù)據(jù)流對象,在文件中讀取或?qū)懭雰?nèi)容。(下文介紹該類) |
2)什么是數(shù)據(jù)流? | |
數(shù)據(jù)流是一個抽象概念,水流可以流動,而數(shù)據(jù)也具有跟水流類似的特點,同時兩者也都可以被容器容納。 |
3)與文件的類型分類類似,數(shù)據(jù)流也有兩種分類 | |
字符流 | 文本文件是指保存合法字符的文件,字符以字符串形式保存。以字符形式傳輸?shù)牧鲗ο?,被稱為字符流。 |
字節(jié)流 | 二進(jìn)制文件是指文件保存的是二進(jìn)制數(shù)據(jù)。以字節(jié)形式傳輸?shù)牧鲗ο?,被稱為字節(jié)流。 |
Java標(biāo)準(zhǔn)庫中提供的用于讀寫文件的流對象有很多類,但是這些類都可以歸納到上述兩個大的種類中。 |
4)流也分為輸入流和輸出流 | |||
每一個種類的流對象,都會有自己的輸入流和輸出流,在Java中也使用不同的類來表示。 | |||
字符流 | 輸入流類:Reader | ||
輸出流類:Writer | |||
字節(jié)流 | 輸入流類:InputStream | ||
輸出流類:OutputStream | |||
下文將介紹上述流對象的使用。 |
二、流的關(guān)閉
1)流為什么需要關(guān)閉? | |
這里的流是指文件的數(shù)據(jù)流,每打開一個文件,就會在內(nèi)存中建立一個PCB。 PCB通過文件描述符表對這些打開的文件進(jìn)行描述,不使用文件了卻不關(guān)閉文件,則文件描述附表會一直被占用。 類似于內(nèi)存泄漏,上述的情況就造成了文件資源泄露。 文件描述符表有存儲上限的,一旦沒有關(guān)閉的文件超過文件描述符表的上限,就會拋出異常。 雖然 Java 有垃圾回收機制,但該機制適用于內(nèi)存資源的回收。而此處泄露的是文件資源。 因此,只有在使用完畢后,關(guān)閉流,才能釋放文件描述符表這樣的文件資源,才不會造成文件資源泄露。 |
2)怎么關(guān)閉流對象? | |||
流對象主要通過以下三種方式進(jìn)行關(guān)閉: | |||
<1> | 使用 close() 方法 | ||
<2> | 使用 try-finally | ||
<3> | 使用?try-with-resources |
2.1 使用 close() 方法
每個數(shù)據(jù)流的類,通常都會有一個 close() 方法,用于將這個流的對象關(guān)閉。 |
使用方式:直接用流對象調(diào)用?close() 方法即可。 |
缺點:直接用流對象調(diào)用?close() 方法固然可行,但程序如果結(jié)構(gòu)復(fù)雜,極可能出現(xiàn)忘記調(diào)用,或雖然代碼中有調(diào)用 close(),卻因為代碼結(jié)構(gòu)問題或拋出異常問題,而無法執(zhí)行到這個方法。 |
2.2 使用 try-finally
try-finally 語法的含義是執(zhí)行 try 代碼塊中的代碼,無論這些代碼是正常執(zhí)行完畢還是拋出異常,最終都必須執(zhí)行 finally 代碼塊中的代碼。 |
語法演示:
try{
//創(chuàng)建流對象;
//需要執(zhí)行的代碼;
}finally{
流對象.close();
}
2.3 使用?try-with-resources
Java 還提供了一種更簡潔明了的方式,來幫助程序員更好的管理資源。 |
try-with-resources 是指將使用后需要關(guān)閉的資源,在 try 關(guān)鍵字后使用 () 進(jìn)行包裹,這樣在程序運行出 try 代碼塊后,() 中包裹的資源將被自動釋放。 |
語法演示:
try( //創(chuàng)建流對象,將需要在代碼塊運行完成后關(guān)閉的資源放在這里 ){
//需要執(zhí)行的代碼;
}
在下文的代碼演示中,將統(tǒng)一使用這種關(guān)閉文件資源的方式。 |
三、字符流的讀寫
字符流通過?Reader 類對數(shù)據(jù)進(jìn)行讀,Reader 是一種輸入流。 |
字符流通過 Writer 類對數(shù)據(jù)進(jìn)行寫,Writer 是一種輸出流。 |
Reader 類和 Writer 類都是抽象類,創(chuàng)建實例時需要使用他們的子類。 |
3.1 Reader 類
Reader 使用 read() 方法讀取數(shù)據(jù),read() 方法如果返回 -1 表示讀取到文件末尾,read() 方法有以下三種方法重載 |
read() :無參數(shù),一次讀取一個字符。 |
read(char[] cbuf) :以數(shù)組為參數(shù),最多讀取 cbuf.length 字符的數(shù)據(jù)到數(shù)組中,返回實際讀取的字符數(shù)量。 |
read(char[] cbuf, int off, int len) :以數(shù)組為參數(shù),最多讀取 cbuf.length 字符的數(shù)據(jù)到數(shù)組中,會從數(shù)組的第 off 個元素開始,將 len 長度的字符填入數(shù)組中,返回實際讀取的字符數(shù)量。 |
代碼演示使用 read() 方法:
//存在當(dāng)前文件,路徑為"C:/Test/A/test.txt",內(nèi)容為"加油gogogo";
public static void main(String[] args) throws IOException {
//打開文件C:/Test/A/test.txt;
try(Reader reader = new FileReader("C:/Test/A/test.txt")){
while (true){
//讀取一個字符;
int ch = reader.read();
//判斷是否到達(dá)文件末尾;
if (ch == -1){
break;
}
//打印讀取到的字符;
System.out.println((char)ch);
}
}
}
//運行結(jié)果:
加
油
g
o
g
o
g
o
代碼演示使用?read(char[] cbuf) 方法:
//存在當(dāng)前文件,路徑為"C:/Test/A/test.txt",內(nèi)容為"加油gogogo";
public static void main(String[] args) throws IOException {
//打開文件C:/Test/A/test.txt;
try(Reader reader = new FileReader("C:/Test/A/test.txt")){
while (true){
//創(chuàng)建一個數(shù)組,用來存儲讀取到的字符;
char[] cbuf = new char[1024];
//將讀取到的字符存入cbuf數(shù)組中,返回實際讀取到的字符個數(shù);
int len = reader.read(cbuf);
//判斷是否到達(dá)文件末尾;
if (len == -1){
break;
}
//打印cbuf數(shù)組中的字符;
for (int i = 0; i < len; i++){
System.out.print(cbuf[i]);
}
}
}
}
//運行結(jié)果:
加油gogogo
3.2 Writer 類
Writer 使用 write() 方法寫入數(shù)據(jù),write() 方法有以下五種方法重載 |
write(int c) :每次寫入一個字符。 |
write(String str) :每次寫入一個字符串; |
write(char[] cbuf) :每次寫入多個字符; |
write(String str, int off, int len) :每次寫入一個字符串,從字符串中的off位置開始去寫,寫len長度; |
write(char[] cbuf, int off, int len) :每次寫入多個字符,從字符數(shù)組中的off位置開始去寫,寫len長度; |
由于寫入的數(shù)據(jù)可能保存在緩沖區(qū),未來得及寫入文件中。因此,為確保我們可以及時看到寫入的內(nèi)容,Writer 類提供了 flush() 方法,用于刷新緩沖區(qū)。 |
代碼演示使用?write(int c)?方法:
//存在當(dāng)前文件,路徑為"C:/Test/A/test.txt",內(nèi)容為"加油gogogo";
public static void main(String[] args) throws IOException {
//打開文件C:/Test/A/test.txt;
try(Writer writer = new FileWriter("C:/Test/A/test.txt")){
//在文件中寫入對應(yīng)字符串;
writer.write("我的愿望是,世界和平!");
//刷新緩沖區(qū);
writer.flush();
}
}
//運行結(jié)果:
打開文件 C:/Test/A/test.txt ,
發(fā)現(xiàn)原來的內(nèi)容"加油gogogo"被替換為"我的愿望是,世界和平!"
通過上述代碼,我們可以發(fā)現(xiàn),原先文件中的文本內(nèi)容被覆蓋了。 |
在上述代碼中,每次寫入新的文本前,都會將原有的文本先清空再寫入。 |
如果不想清空原有文本,而是在原有文本之后繼續(xù)寫入,則需要在打開文件時,在 ?FileWriter 的構(gòu)造方法的參數(shù)中填入另一個 boolean 類型的參數(shù)。當(dāng)參數(shù)為 true 時,則表示追加文本。 |
代碼演示追加文本:
//存在當(dāng)前文件,路徑為"C:/Test/A/test.txt",內(nèi)容為"加油gogogo";
public static void main(String[] args) throws IOException {
//打開文件C:/Test/A/test.txt ,參數(shù)中添加true;
try(Writer writer = new FileWriter("C:/Test/A/test.txt",true)){
//在文件中寫入對應(yīng)字符串;
writer.write("我的愿望是,世界和平!");
//刷新緩沖區(qū);
writer.flush();
}
}
//運行結(jié)果:
打開文件 C:/Test/A/test.txt ,
發(fā)現(xiàn)內(nèi)容為"加油gogogo我的愿望是,世界和平!"
四、字節(jié)流的讀寫
字節(jié)流通過?InputStream?類對數(shù)據(jù)進(jìn)行讀,InputStream?是一種輸入流。 |
字節(jié)流通過 OutputStream?類對數(shù)據(jù)進(jìn)行寫,OutputStream?是一種輸出流。 |
InputStream?類和 OutputStream?類都是抽象類,創(chuàng)建實例時需要使用他們的子類。 |
4.1?InputStream?類
InputStream?使用 read() 方法讀取數(shù)據(jù),read() 方法如果返回 -1 表示讀取到文件末尾,read() 方法有以下三種方法重載 |
read() :無參數(shù),一次讀取一個字節(jié)。 |
read(byte[] b) :以數(shù)組為參數(shù),最多讀取 b.length 字節(jié)的數(shù)據(jù)到數(shù)組中,返回實際讀取的字節(jié)數(shù)量。 |
read(byte[] cbuf, int off, int len) :以數(shù)組為參數(shù),最多讀取 b.length 字節(jié)的數(shù)據(jù)到數(shù)組中,會從數(shù)組的第 off 個元素開始,將 len 長度的字節(jié)填入數(shù)組中,返回實際讀取的字節(jié)數(shù)量。 |
代碼演示使用 read() 方法:
//存在當(dāng)前文件,路徑為"C:/Test/A/test.txt",內(nèi)容為"abc";
public static void main(String[] args) throws IOException {
//打開文件C:/Test/A/test.txt;
try (InputStream is = new FileInputStream("C:/Test/A/test.txt")){
while (true){
//讀取一個字節(jié);
int n = is.read();
//判斷是否到達(dá)文件末尾;
if(n == -1){
break;
}
//打??;
System.out.printf("%c ",n);
System.out.println(n);
}
}
}
//運行結(jié)果:
a 97
b 98
c 99
代碼演示使用?read(byte[] b) 方法:
//存在當(dāng)前文件,路徑為"C:/Test/A/test.txt",內(nèi)容為"abc";
public static void main(String[] args) throws IOException {
//打開文件C:/Test/A/test.txt;
try (InputStream is = new FileInputStream("C:/Test/A/test.txt")){
while (true){
//創(chuàng)建一個數(shù)組,用來存儲讀取到的字節(jié);
byte[] b = new byte[1024];
//將讀取到的字節(jié)存入b數(shù)組中,返回實際讀取到的字節(jié)個數(shù);
int len = is.read(b);
//判斷是否到達(dá)文件末尾;
if(len == -1){
break;
}
//打??;
for(int i=0;i<len;i++){
System.out.printf("%c ",b[i]);
System.out.println(b[i]);
}
}
}
}
//運行結(jié)果:
a 97
b 98
c 99
根據(jù)使用的字符集的不同,一個中文字符通常會占用兩到三個字節(jié)。如果我們需要從文件中讀取中文字符會非常麻煩。 |
因此,可以使用 Scanner 類,來幫助我們從字節(jié)流結(jié)果中獲取中文字符。 |
代碼演示字節(jié)流獲取中文字符的方法:
//存在當(dāng)前文件,路徑為"C:/Test/A/test.txt",內(nèi)容為"我的愿望是,世界和平!";
public static void main(String[] args) throws IOException {
//打開文件C:/Test/A/test.txt;
//將字節(jié)流通過 Scanner 類轉(zhuǎn)換為字符流,指定通過“utf8”字符集進(jìn)行轉(zhuǎn)換;
try (InputStream is = new FileInputStream("C:/Test/A/test.txt");
Scanner sc = new Scanner(is,"utf8")){
//循環(huán)判斷是否有下一行;
while (sc.hasNext()){
//獲得下一行;
String str = sc.next();
//打??;
System.out.println(str);
}
}
}
//運行結(jié)果:
我的愿望是,世界和平!
4.2?OutputStream?類
OutputStream?使用 write() 方法寫入數(shù)據(jù),write() 方法有以下三種方法重載 |
write(int b) :每次寫入一個字節(jié)。 |
write(char[] cbuf) :每次寫入多個字節(jié); |
write(char[] cbuf, int off, int len) :每次寫入多個字符,從字符數(shù)組中的off位置開始去寫,寫len長度; |
由于寫入的數(shù)據(jù)可能保存在緩沖區(qū),未來得及寫入文件中。因此,為確保我們可以及時看到寫入的內(nèi)容,OutputStream?類提供了 flush() 方法,用于刷新緩沖區(qū)。 |
由于字節(jié)流傳輸?shù)氖亲止?jié)數(shù)據(jù),但我們?nèi)孕枰宰址问捷斎?,因此下文將以兩種將字符轉(zhuǎn)換為字節(jié)的方法分別進(jìn)行演示。 |
代碼演示使用?write(int b)?方法(以?getBytes() 轉(zhuǎn)換字符的方式):
//存在當(dāng)前文件,路徑為"C:/Test/A/test.txt";
public static void main(String[] args) throws IOException {
//打開文件C:/Test/A/test.txt;
try(OutputStream writer = new FileOutputStream("C:/Test/A/test.txt")){
//需要在文件中寫入的字符串;
String str = "我的愿望是,世界和平!";
//將字符轉(zhuǎn)換為字節(jié)并寫入文件;
writer.write(str.getBytes());
//刷新緩沖區(qū);
writer.flush();
}
}
//運行結(jié)果:
打開文件 C:/Test/A/test.txt ,
文件內(nèi)容為"我的愿望是,世界和平!"
代碼演示使用?write(int b)?方法(以?PrintWriter 包裹流對象的方式):
//存在當(dāng)前文件,路徑為"C:/Test/A/test.txt";
public static void main(String[] args) throws IOException {
//打開文件C:/Test/A/test.txt;
try(OutputStream writer = new FileOutputStream("C:/Test/A/test.txt")){
//使用PrintWriter類將輸入的字符自動轉(zhuǎn)換為字節(jié);
PrintWriter printWriter = new PrintWriter(writer);
//寫入文件;
printWriter.write("我的愿望是,世界和平!");
//刷新緩沖區(qū);
writer.flush();
}
}
//運行結(jié)果:
打開文件 C:/Test/A/test.txt ,
文件內(nèi)容為"我的愿望是,世界和平!"
閱讀指針 -> 《?網(wǎng)絡(luò)編程 -- 網(wǎng)絡(luò)通信基礎(chǔ)(協(xié)議和協(xié)議分層、數(shù)據(jù)封裝和分用)?》文章來源:http://www.zghlxwxcb.cn/news/detail-758985.html
<JavaEE> 網(wǎng)絡(luò)編程 -- 網(wǎng)絡(luò)通信基礎(chǔ)(協(xié)議和協(xié)議分層、數(shù)據(jù)封裝和分用)-CSDN博客介紹網(wǎng)絡(luò)通信基礎(chǔ),包括IP地址、端口、協(xié)議等內(nèi)容,著重介紹了TCP/IP五層模型和網(wǎng)絡(luò)通信中的封裝和分用。https://blog.csdn.net/zzy734437202/article/details/134998416文章來源地址http://www.zghlxwxcb.cn/news/detail-758985.html
到了這里,關(guān)于<JavaEE> 文件IO -- 數(shù)據(jù)流和文件內(nèi)容操作(Reader 和 Writer 、InputStream 和 OutputStream)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!