JAVA IO API使用詳解
一.理論準(zhǔn)備
流是個(gè)抽象的概念,是對(duì)輸入輸出設(shè)備的抽象,Java程序中,對(duì)于數(shù)據(jù)的輸入/輸出操作都是以“流”的方式進(jìn)行,設(shè)備可以是文件、網(wǎng)絡(luò)、內(nèi)存等。流具有方向性,至于是輸入流還是輸出流則是一個(gè)相對(duì)的概念,一般以程序(小馬哥說(shuō)的是機(jī)器)為參考,如果數(shù)據(jù)的流向是程序至設(shè)備,我們成為輸出流,反之我們稱為輸入流,可以將流想象成一個(gè)“水流管道”(很多資料都這么講的),自然就出現(xiàn)了方向的概念。
流把I/O設(shè)備內(nèi)部的具體操作給隱藏起來(lái)了。所有InputStream和Reader的派生類都有一個(gè)基本的,繼承下來(lái)的,能讀取單個(gè)或byte數(shù)組的read( )方法。
Java分為字節(jié)流(Stream結(jié)尾)和字符流(Reader、Write結(jié)尾),再分為輸入流(InputStream、Reader)和輸出流(OutputStream、Write),輸入輸出相對(duì)于內(nèi)存而言。在讀字符的時(shí)候用字符流,如文本文件、XML(我想xml明明是字母字符組成的,屬于ASCII文件,為何不用stream讀取呢?)等。在讀二進(jìn)制文件時(shí)候用字節(jié)流,如RAR、EXE等不是文本以外的文件(圖片)。Buffered開頭的流只是加了緩沖區(qū),為了讀寫提高效率。字符流不能直接輸出,需要轉(zhuǎn)換成字節(jié)流才能輸出(這個(gè)確實(shí)是剛知道的)!
Java 2 SDK中有三種基本類型的節(jié)點(diǎn):文件(file)、內(nèi)存(memory)、管道(pipe)。
下面來(lái)看鄭莉教材上IO章節(jié)的那個(gè)經(jīng)典圖片。
繼承自InputStream/OutputStream的流都是用于向程序中輸入/輸出數(shù)據(jù),且數(shù)據(jù)的單位都是字節(jié)(byte=8bit),如圖,深色的為節(jié)點(diǎn)流,淺色的為處理流。
繼承自Reader/Writer的流都是用于向程序中輸入/輸出數(shù)據(jù),且數(shù)據(jù)的單位都是字符(2byte=16bit),如圖,深色的為節(jié)點(diǎn)流,淺色的為處理流。
二.用法分析
Java IO的一般使用原則(部分來(lái)自百度文庫(kù)):
(1) 按數(shù)據(jù)來(lái)源(去向)分類:
是文件: FileInputStream, FileOutputStream, FileReader, FileWriter
是byte[]:ByteArrayInputStream, ByteArrayOutputStream
是Char[]: CharArrayReader, CharArrayWriter
是String: StringBufferInputStream, StringReader, StringWriter
網(wǎng)絡(luò)數(shù)據(jù)流:InputStream, OutputStream, Reader, Writer
(2) 按是否格式化輸出分:
要格式化輸出:PrintStream, PrintWriter
(3) 按是否要緩沖分:
要緩沖:BufferedInputStream, BufferedOutputStream, BufferedReader, BufferedWriter。
(4) 按數(shù)據(jù)格式分:
二進(jìn)制格式(只要不能確定是純文本的): InputStream, OutputStream及其所有帶Stream結(jié)束的子類
純文本格式(含純英文與漢字或其他編碼方式);Reader, Writer及其所有帶Reader, Writer的子類
(5) 按輸入輸出分:
輸入:Reader, InputStream類型的子類;輸出:Writer, OutputStream類型的子類
(6) 特殊需要:
從Stream到Reader,Writer的轉(zhuǎn)換類:InputStreamReader, OutputStreamWriter
對(duì)象輸入輸出:ObjectInputStream, ObjectOutputStream
進(jìn)程間通信:PipeInputStream, PipeOutputStream, PipeReader, PipeWriter
合并輸入:SequenceInputStream
更特殊的需要:PushbackInputStream, PushbackReader, LineNumberInputStream, LineNumberReader
(7) 決定使用哪個(gè)類以及它的構(gòu)造進(jìn)程的一般準(zhǔn)則如下(不考慮特殊需要):
考慮最原始的數(shù)據(jù)格式是什么:是否為文本?是輸入還是輸出?是否需要轉(zhuǎn)換流:InputStreamReader, OutputStreamWriter?數(shù)據(jù)來(lái)源(去向)是什么:文件??jī)?nèi)存?網(wǎng)絡(luò)?是否要緩沖:bufferedReader (特別注明:一定要注意的是readLine()是否有定義,有什么比read, write更特殊的輸入或輸出方法)是否要格式化輸出:print。
三.若干實(shí)例
還是寒假時(shí)候?qū)懙?,?quán)當(dāng)復(fù)習(xí)了,折疊代碼的插件找不到了,先看著吧。
1.System.in
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; /* * System.in是InputStream static final的,包含了多態(tài),叫同步式或者阻塞式 * 讀取ASCII和二進(jìn)制文件(圖片),而字母就是ASCII字符(個(gè)人理解)。 */ public class TestSystemIn { public static void main(String[] args) { InputStreamReader isr = new InputStreamReader(System.in); BufferedReader br = new BufferedReader(isr);//有readline String s = null; try { s = br.readLine(); while(s!=null) { if(s.equalsIgnoreCase("exit")) { break; } System.out.println(s.toUpperCase()); s = br.readLine(); } br.close(); }catch (IOException e) { e.printStackTrace(); } } }
2.buffer
import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; public class TestBuffer { public static void main(String[] args) { try { //查看修改日就可以判斷文件是否是新建的了 BufferedWriter bw = new BufferedWriter(new FileWriter("d:/java.txt")); BufferedReader br = new BufferedReader(new FileReader("d:/java.txt")); String s = null; for(int i=1; i<100; i++) { s = String.valueOf(Math.random()); bw.write(s); bw.newLine();//換行 } //刷新該流的緩沖,br沒(méi)有該方法 bw.flush(); while((s=br.readLine())!=null) { System.out.println(s); } bw.close(); br.close(); }catch (IOException e) { e.printStackTrace(); } } }
3.FileInputStream
import java.io.*; public class TestFileInputStream { public static void main(String[] args) { FileInputStream in = null; try { in = new FileInputStream("e:/1.txt"); }catch(FileNotFoundException e) { System.out.println("找不到文件"); System.exit(-1); } //下面表示找到了文件 int tag = 0; try { long num = 0; while((tag = in.read())!=-1) { //read是字節(jié)流,若是有漢字就顯示不正常了,使用reader就解決了 System.out.print((char)tag); num++; } in.close(); System.out.println(); System.out.println("共讀取了" + num + "字符"); }catch(IOException e1) {//read和close都會(huì)拋出IOException System.out.println("文件讀取錯(cuò)誤"); System.exit(-1); } } }
4.FileOutputStream實(shí)現(xiàn)復(fù)制功能
import java.io.*; /* * 實(shí)現(xiàn)復(fù)制功能 */ public class TestFileOutputStream { public static void main(String[] args) { int b = 0; FileInputStream in = null; FileOutputStream out = null; try { in = new FileInputStream("d:/java.txt"); //下面的若是不存在的話會(huì)自動(dòng)建立 out = new FileOutputStream("d:/my_java.txt"); while((b=in.read())!=-1) { out.write(b); } in.close(); out.close(); }catch(FileNotFoundException e) { System.out.println("找不到指定文件"); System.exit(-1); }catch(IOException e1) { System.out.println("文件復(fù)制錯(cuò)誤"); System.exit(-1); } System.out.println("文件已復(fù)制"); } }
5.ObjectOutputStream與Serializable
import java.io.*; /* * transient(透明的),可以用來(lái)修飾成員變量, * 當(dāng)進(jìn)行序列化時(shí)不予考慮,修飾int 的話,不管原來(lái)的值是多少 * 輸出的就是0 */ public class TestObjectIO { public static void main(String[] args) throws Exception { T t = new T(); t.k = 8; FileOutputStream fos = new FileOutputStream("d:/1.txt"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(t); oos.flush(); oos.close(); FileInputStream fis = new FileInputStream("d:/1.txt"); ObjectInputStream ois = new ObjectInputStream(fis); T tRead = (T)ois.readObject(); System.out.println(tRead.i + " " + tRead.j + " " + tRead.k); } } class T implements Serializable { int i = 10; int j = 9; double d = 2.3; int k = 15; }
6.轉(zhuǎn)換編碼方式
import java.io.*; /* * 中文windows默認(rèn)GBK編碼方式 * 追加的內(nèi)容顯示為問(wèn)號(hào),不知道咋回事 */ public class TestTransForm { public static void main(String[] args) { try { OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("d:/java.txt")); osw.write("你好123");//可以直接寫入字符串,包括中文,因?yàn)橥膺叺氖亲址? System.out.println("編碼方式:" + osw.getEncoding());//ISO8859_1是西歐語(yǔ)言,又叫l(wèi)atin-1,此時(shí)未考慮東方人,國(guó)標(biāo)(ISO)為Unicode osw.close(); osw = new OutputStreamWriter(new FileOutputStream("d:/java.txt",true),"ISO8859_1");//true表示追加 osw.write("這是追加的內(nèi)容"); System.out.println("編碼方式:" + osw.getEncoding()); osw.close(); }catch(IOException e) { e.printStackTrace(); } } }
7.輸出重定向
import java.io.*; /* * Print流屬于輸出流,提供了重載的方法很多, * PrintWriter和PrintStream不會(huì)拋異常,用戶通過(guò)檢測(cè)錯(cuò)誤狀態(tài)獲取信息, * 包含自動(dòng)flush功能,有什么用呢,在jsp里也要輸出一些東西, * 但不必每次拋異常。 */ public class TestPrintStream { public static void main(String[] args) { PrintStream ps = null; try { FileOutputStream fos = new FileOutputStream("d:/java.txt"); ps = new PrintStream(fos); }catch (IOException e) { e.printStackTrace(); } if(ps!=null) { System.setOut(ps);//輸出重定向 } int ln = 0; for(char c=0; c<65535; c++) { System.out.print(c + " "); if(ln++>100) { System.out.println(); ln = 0; } } } }
8.DataStream
import java.io.*; public class TestDataStream { public static void main(String[] args) { //先在內(nèi)存里分配一個(gè)字節(jié)數(shù)組,再有一個(gè) OutputStream,再加上一個(gè)數(shù)據(jù)流 ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(baos); try {//寫出讀入 dos.writeDouble(Math.random()); dos.writeBoolean(true); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); System.out.println(bais.available());//共幾個(gè)字節(jié)可用 DataInputStream dis = new DataInputStream(bais); ////先寫的先讀(隊(duì)列),下面這兩個(gè)輸出不可以調(diào)換,否則就先輸出了double里的一個(gè)字節(jié) System.out.println(dis.readDouble()); System.out.println(dis.readBoolean()); dos.close(); dis.close(); }catch (IOException e) { e.printStackTrace(); } } }
四.小問(wèn)題
為什么Writer/Reader不繼承自Stream呢?字符最終也要轉(zhuǎn)換成二進(jìn)制呀。Writer/Readre繼承OutputStream/InputStream,這樣的繼承層次不是更好,為什么要單獨(dú)做一個(gè)呢,而且Stream也有些子類能夠?qū)崿F(xiàn)字符串的讀寫。大神回答:?jiǎn)我宦氊?zé)。太牽強(qiáng)了。
- java調(diào)用百度定位api服務(wù)獲取地理位置示例
- 詳解Java的JDBC API中事務(wù)的提交和回滾
- Java中的Calendar日歷API用法完全解析
- Java通過(guò)JsApi方式實(shí)現(xiàn)微信支付
- java使用淘寶API讀寫json實(shí)現(xiàn)手機(jī)歸屬地查詢功能代碼
- 用Java實(shí)現(xiàn)全國(guó)天氣預(yù)報(bào)的api接口調(diào)用示例
- Java8新日期時(shí)間API的20個(gè)使用示例
- android monkey自動(dòng)化測(cè)試改為java調(diào)用monkeyrunner Api
- java調(diào)用中國(guó)天氣網(wǎng)api獲得天氣預(yù)報(bào)信息的方法
- 5個(gè)Java API使用技巧
相關(guān)文章
Springboot如何同時(shí)裝配兩個(gè)相同類型數(shù)據(jù)庫(kù)
這篇文章主要介紹了Springboot如何同時(shí)裝配兩個(gè)相同類型數(shù)據(jù)庫(kù),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11詳解Java中的checked異常和unchecked異常區(qū)別
這篇文章主要介紹了詳解Java中的checked異常和unchecked異常區(qū)別,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-02-02鄰接表無(wú)向圖的Java語(yǔ)言實(shí)現(xiàn)完整源碼
這篇文章主要介紹了鄰接表無(wú)向圖的Java語(yǔ)言實(shí)現(xiàn)完整源碼,具有一定借鑒價(jià)值,需要的朋友可以參考下。2017-12-12SpringBoot自動(dòng)配置深入探究實(shí)現(xiàn)原理
在springboot的啟動(dòng)類中可以看到@SpringBootApplication注解,它是SpringBoot的核心注解,也是一個(gè)組合注解。其中@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan三個(gè)注解尤為重要。今天我們就來(lái)淺析這三個(gè)注解的含義2022-08-08詳解SpringBoot如何刪除引用jar包中的無(wú)用bean
為了趕速度和直接將之前多模塊的maven項(xiàng)目中的部分模塊,直接以jar包的形式引入到新項(xiàng)目中了,雖然省去了不少開發(fā)時(shí)間,導(dǎo)致項(xiàng)目臃腫,啟動(dòng)很慢。本文將用@ComponentScan注解去實(shí)現(xiàn)讓項(xiàng)目只加載自己需要的bean,需要的可以參考一下2022-06-06Mybatis自動(dòng)創(chuàng)建表和更新表結(jié)構(gòu)
這篇文章主要介紹了Mybatis自動(dòng)創(chuàng)建表和更新表結(jié)構(gòu)的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-06-06Springboot設(shè)置默認(rèn)訪問(wèn)路徑方法實(shí)現(xiàn)
這篇文章主要介紹了Springboot設(shè)置默認(rèn)訪問(wèn)路徑方法實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12詳解使用Spring的BeanPostProcessor優(yōu)雅的實(shí)現(xiàn)工廠模式
這篇文章主要介紹了詳解使用Spring的BeanPostProcessor優(yōu)雅的實(shí)現(xiàn)工廠模式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07