詳解JAVA 字節(jié)流和字符流
1、InputStream 和 Reader
InputStream 和 Reader 是所有輸入流的抽象基類,本身并不能創(chuàng)建實例來執(zhí)行輸入,但它們將成為所有輸入流的模板,所以它們的方法是所有輸入流都可使用的方法。
在 InputStream 里包含如下三個方法。
- int read():從輸入流中讀取單個字節(jié),返回所讀取的字節(jié)數(shù)據(jù)(字節(jié)數(shù)據(jù)可直接轉(zhuǎn)換為int類型)。
- int read(byte[] b):從輸入流中最多讀取 b.length 個字節(jié)的數(shù)據(jù),并將其存儲在字節(jié)數(shù)組 b 中,返回實際讀取的字節(jié)數(shù)。
- int read(byte[] b, int off, int len):從輸入流中最多讀取 len 個字節(jié)的數(shù)據(jù),并將其存儲在數(shù)組 b 中,放入數(shù)組 b 中時,并不是從數(shù)組起點開始,而是從 off 位置開始,返回實際讀取的字節(jié)數(shù)。
在 Reader 里包含如下三個方法。
- int read():從輸入流中讀取單個字符,返回所讀取的字符數(shù)據(jù)(字符數(shù)據(jù)可直接轉(zhuǎn)換為int類型)。
- int read(char[] cbuf):從輸入流中最多讀取 cbuf.length 個字符的數(shù)據(jù),并將其存儲在字符數(shù)組 cbuf 中,返回實際讀取的字符數(shù)。
- int read(char[] chuf, int off, int len):從輸入流中最多讀取 len 個字符的數(shù)據(jù),并將其存儲在字符數(shù)組 cbuf 中,放入數(shù)組 cbuf 中時,并不是從數(shù)組起點開始,而是從 off 位置開始,返回實際讀取的字符數(shù)。
對比 InputStream 和 Reader 所提供的方法,就不難發(fā)現(xiàn)這兩個基類的功能基本是一樣的。
正如前面提到的,InputStream 和 Reader 都是抽象類,本身不能創(chuàng)建實例,但它們分別有一個用于讀取文件的輸入流:FileInputStream 和 FileReader,它們都是節(jié)點流一一會直接和指定文件關(guān)聯(lián)。
下面程序示范了使用 FileInputStream 來讀取自身的效果。
public class FileInputStreamTest { public static void main(String[] args) throws IOException { // 創(chuàng)建字節(jié)輸入流 FileInputStream fis = new FileInputStream("F:\\eclipse-workspace\\demo\\src\\com\\jwen\\demo15_3\\FileInputStreamTest.java"); // 創(chuàng)建一個長度為1024的“竹筒” byte[] bbuf = new byte[1024]; // 用于保存實際讀取的字節(jié)數(shù) int hasRead = 0; // 使用循環(huán)來重復(fù)“取水”過程 while ((hasRead = fis.read(bbuf)) > 0) { // 取出“竹筒”中水滴(字節(jié)),將字節(jié)數(shù)組轉(zhuǎn)換成字符串輸入! System.out.print(new String(bbuf, 0, hasRead)); } // 關(guān)閉文件輸入流,放在finally塊里更安全 fis.close(); } }
上面程序中的粗體字代碼是使用 FileInputStream 循環(huán)“取水”的過程,運行上面程序,將會輸出上面程序的源代碼。
注意:上面程序創(chuàng)建了一個長度為1024的字節(jié)數(shù)組來讀取該文件,實際上該Java源文件的長度還不到1024字節(jié),也就是說,程序只需要執(zhí)行一次 read() 方法即可讀取全部內(nèi)容。但如果創(chuàng)建較小長度的字節(jié)數(shù)組,程序運行時在輸出中文注釋時就可能出現(xiàn)亂碼一一這是因為本文件保存時采用的是 GBK 編碼方式,在這種方式下,每個中文字符占2字節(jié),如果 read() 方法讀取時只讀到了半個中文字符,這將導(dǎo)致亂碼。
上面程序最后使用了 fis.close() 來關(guān)閉該文件輸入流,與 JDBC 編程一樣,程序里打開的文件 IO 資源不屬于內(nèi)存里的資源,垃圾回收機制無法回收該資源,所以應(yīng)該顯式關(guān)閉文件資源。Java 7 改寫了所有的 IO 資源類,它們都實現(xiàn)了 AutoCloseable 接口,因此都可通過自動關(guān)閉資源的 try 語句來關(guān)閉這些 IO 流。下面程序使用 FileReader 來讀取文件本身。
public class FileReaderTest { public static void main(String[] args) { try ( // 創(chuàng)建字符輸入流 FileReader fr = new FileReader("F:\\eclipse-workspace\\demo\\src\\com\\jwen\\demo15_3\\FileReaderTest.java")) { // 創(chuàng)建一個長度為32的“竹筒” char[] cbuf = new char[32]; // 用于保存實際讀取的字符數(shù) int hasRead = 0; // 使用循環(huán)來重復(fù)“取水”過程 while ((hasRead = fr.read(cbuf)) > 0) { // 取出“竹筒”中水滴(字符),將字符數(shù)組轉(zhuǎn)換成字符串輸入! System.out.print(new String(cbuf, 0, hasRead)); } } catch (IOException ex) { ex.printStackTrace(); } } }
上面的 FileReaderTest.java 程序與前面的 FileInputStreamTest.java 并沒有太大的不同,程序只是將字符數(shù)組的長度改為32,這意味著程序需要多次調(diào)用 read() 方法才可以完全讀取輸入流的全部數(shù)據(jù)。程序最后使用了自動關(guān)閉資源的 try 語句來關(guān)閉文件輸入流,這樣可以保證輸入流一定會被關(guān)閉。
除此之外,InputStream 和 Reader 還支持如下幾個方法來移動記錄指針。
- void mark(int readAheadLimit):在記錄指針當(dāng)前位置記錄一個標(biāo)記(mark).
- boolean markSupported():判斷此輸入流是否支持 mark() 操作,即是否支持記錄標(biāo)記。
- void reset():將此流的記錄指針重新定位到上一次記錄標(biāo)記(mark)的位置。
- long skip(long n):記錄指針向前移動個字節(jié)/字符。
2、OutputStream 和 Writer
OutputStream 和 Writer 也非常相似,兩個流都提供了如下三個方法。
- void write(int c):將指定的字節(jié)/字符輸出到輸出流中,其中 c 既可以代表字節(jié),也可以代表字符。
- void write(byte[]/char[] buf):將字節(jié)數(shù)組/字符數(shù)組中的數(shù)據(jù)輸出到指定輸出流中。
- void write(byte[]/char[] buf, int off, int len):將字節(jié)數(shù)組/字符數(shù)組中從 off 位置開始,長度為 len 的字節(jié)/字符輸出到輸出流中。
因為字符流直接以字符作為操作單位,所以 Writer 可以用字符串來代替字符數(shù)組,即以 String 對象作為參數(shù)。Writer 里還包含如下兩個方法。
- void write(String str):將字符串里包含的字符輸出到指定輸出流中。
- void write(String str, int off, int len):將字符串里從 off 位置開始,長度為 len 的字符輸出到指定輸出流中。
下面程序使用 FileInputStream 來執(zhí)行輸入,并使用 FileOutputStream 來執(zhí)行輸出,用以實現(xiàn)復(fù)制 FileOutputStreamTest.java 文件的功能。
public class FileOutputStreamTest { public static void main(String[] args) { try ( // 創(chuàng)建字節(jié)輸入流 FileInputStream fis = new FileInputStream("FileOutputStreamTest.java"); // 創(chuàng)建字節(jié)輸出流 FileOutputStream fos = new FileOutputStream("newFile.txt")) { byte[] bbuf = new byte[32]; int hasRead = 0; // 循環(huán)從輸入流中取出數(shù)據(jù) while ((hasRead = fis.read(bbuf)) > 0) { // 每讀取一次,即寫入文件輸出流,讀了多少,就寫多少。 fos.write(bbuf, 0, hasRead); } } catch (IOException ioe) { ioe.printStackTrace(); } } }
運行上面程序,將看到系統(tǒng)當(dāng)前路徑下多了一個文件:newFile.txt,該文件的內(nèi)容和 FileOutputStreamTest.java 文件的內(nèi)容完全相同。
注意:使用 Java 的 IO 流執(zhí)行輸出時,不要忘記關(guān)閉輸出流,關(guān)閉輸出流除可以保證流的物理資源被回收之外,可能還可以將輸出流緩沖區(qū)中的數(shù)據(jù) flush 到物理節(jié)點里(因為在執(zhí)行 close() 方法之前,自動執(zhí)行輸出流的 flush() 方法)。Java 的很多輸出流默認都提供了緩沖功能,其實沒有必要刻意去記憶哪些流有緩沖功能、哪些流沒有,只要正常關(guān)閉所有的輸出流即可保證程序正常。
如果希望直接輸出字符串內(nèi)容,則使用 Writer 會有更好的效果,如下程序所示。
public class FileWriterTest { public static void main(String[] args) { try (FileWriter fw = new FileWriter("poem.txt")) { fw.write("錦瑟 - 李商隱\r\n"); fw.write("錦瑟無端五十弦,一弦一柱思華年。\r\n"); fw.write("莊生曉夢迷蝴蝶,望帝春心托杜鵑。\r\n"); fw.write("滄海月明珠有淚,藍田日暖玉生煙。\r\n"); fw.write("此情可待成追憶,只是當(dāng)時已惘然。\r\n"); } catch (IOException ioe) { ioe.printStackTrace(); } } }
運行上面程序,將會在當(dāng)前目錄下輸出一個 poem.txt 文件,文件內(nèi)容就是程序中輸出的內(nèi)容。
注意:上面程序在輸出字符串內(nèi)容時,字符串內(nèi)容的最后是\r\n,這是 Windows 平臺的換行符,通過這種方式就可以讓輸出內(nèi)容換行;如果是 UNIX/Linux/BSD 等平臺,則使用 \n 就作為換行符。
以上就是詳解JAVA 字節(jié)流和字符流的詳細內(nèi)容,更多關(guān)于JAVA 字節(jié)流和字符流的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Python安裝Jupyter Notebook配置使用教程詳解
這篇文章主要介紹了Python安裝Jupyter Notebook配置使用教程詳解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09Java中Bean轉(zhuǎn)Map問題歸納總結(jié)
Java Bean轉(zhuǎn)Map的坑很多,最常見的就是類型丟失和屬性名解析錯誤的問題,下面這篇文章主要給大家介紹了關(guān)于Java中Bean轉(zhuǎn)Map問題歸納總結(jié)的相關(guān)資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下2023-06-06SpringBoot配置連接兩個或多個數(shù)據(jù)庫的常用方法
在Spring Boot應(yīng)用中連接多個數(shù)據(jù)庫或數(shù)據(jù)源可以使用多種方式,本文講給大家介紹兩種常用的方法:使用Spring Boot官方支持的多數(shù)據(jù)源配置和使用第三方庫實現(xiàn)多數(shù)據(jù)源,文章通過代碼介紹的非常詳細,需要的朋友可以參考下2023-08-08