javaio流_万字长文+思维导图帮你梳理 Java IO 流,还学不会你来打我(值得收藏)...
?前言
在上一篇的文章獲取不錯的瀏覽量后,繼續加更的念頭一直徘徊在心中,本來是想花段時間深入學習tomcat的,可是tomcat的源碼中就有至關重要的NIO,于是得先整一下NIO,但是NIO的基礎是BIO,于是這篇文章就先寫IO流吧。
學習NIO(非阻塞IO),千萬不能被IO阻塞住哇!
IO流在java中其實是很重要的一塊知識點,難度還好,但是容易被忽視,因為工作中真正寫IO的代碼少之又少。
IO的難點在于
導致很多開發學過io流一段時間后,寫不出一段正確的io流代碼。
本文將從理論+代碼的方式由淺入深的帶大家學習IO流,通過圖解的方式來記憶常用的IO流。
文末有IO總結的思維導圖,很多博文采用的都是上來一張圖,我覺得對于閱讀者來說很容易陷進去,所以建議理清各個流后再去看思維導圖。
File
在正式的介紹IO流之前,我覺得應該介紹一下File類,該類主要是對文件和目錄的抽象表示,因為學習io流第一反應就是文件,該類提供了對文件的創建、刪除、查找等操作。主要有以下特點
File類的常用構造方法
| File(String pathname) | 將路徑字符串抽象為File實例,路徑字符串可以是相對路徑,也可以為絕對路徑 |
| File(String parent, String child) | 從父路徑名和子路徑名來構建File實例 |
| File(File parent, String child) | 根據父File實例和子路徑名來構建File實例 |
如下示例,表示這幾種文件和目錄的代碼
//?pathnameFile?liuBei?=?new?File("D:/三國/劉備.jpg");
//?String?parent,?String?child
File?guanYu?=?new?File("D:/三國",?"關羽.jpg");
//?目錄
File?sanGuo?=?new?File("D:/三國");
//?File?parent,?String?child
File?zhangFei?=?new?File(sanGuo,?"張飛.txt");
//?可以聲明不存在的文件
File?zhuGeLiang?=?new?File(sanGuo,?"諸葛亮.txt");
絕對路徑和相對路徑
絕對路徑:從盤符開始的路徑,表示一個完整的路徑。(經常使用) 相對路徑:相對于當前項目目錄的路徑
File?f?=?new?File("D:/bbb.java");//?D:\bbb.java
System.out.println(f.getAbsolutePath());
File?f2?=?new?File("bbb.java");
//?F:\code\ad\bbb.java
System.out.println(f2.getAbsolutePath());
路徑分隔符和換行符
路徑分隔符
java有常量separator表示路徑分隔符
public?static?final?String?separator?=?""?+?separatorChar;換行符
File的常用方法
創建、刪除
| boolean createNewFile() throws IOException | 當該名稱的文件不存在時,創建一個由該抽象路徑名的空文件并返回true,當文件存在時,返回false |
| boolean mkdir() | 創建由此抽象路徑名命名的目錄 |
| boolean mkdirs() | 創建由此抽象路徑名命名的目錄,包括任何必需但不存在的父目錄。級聯創建目錄 |
| boolean delete() | 刪除由此抽象路徑名表示的文件或目錄 |
上述方法比較簡單,其中需要注意的是
- 創建多級目錄時,mkdir創建失敗,返回false,mkdirs創建成功,返回true(推薦使用mkdirs)
- 刪除目錄時,目錄內不為空時,刪除失敗,返回false, 即只能刪除文件或者空目錄
//?返回false?創建失敗
boolean?mkdir?=?shuiHu.mkdir();
//?返回true?創建失敗
boolean?mkdirs?=?shuiHu.mkdirs();
File?four?=?new?File("D:/四大名著");
//?返回false?刪除目錄時必須目錄為空才能刪除成功
boolean?delete?=?four.delete();
File?shuiHu?=?new?File("D:/四大名著/水滸傳");
//?true?正確刪除了水滸傳目錄
boolean?delete1?=?shuiHu.delete();
File?liuBei?=?new?File("D:/三國/劉備.jpg");
//?返回true?正確刪除了劉備.jpg文件
boolean?delete2?=?liuBei.delete();
文件檢測
| boolean isDirectory() | 判斷是否是目錄 |
| boolean isFile() | 判斷是否是文件 |
| boolean exists() | 判斷文件或目錄是否存在 |
| boolean canWrite() | 文件是否可寫 |
| boolean canRead() | 文件是否可讀 |
| boolean canExecute() | 文件是否可執行 |
| long lastModified() | 返回文件的上次修改時間 |
注意的是
- 文件或目錄不存在時, isDirectory() 或 isFile() ?返回false
- 可讀、可寫、可執行是對操作系統給文件賦予的權限
//?文件或目錄不存在時?返回false
System.out.println(xiYou.isDirectory());
文件獲取
| String getAbsolutePath() | 返回File對象的絕對路徑字符串 |
| String getPath() | 將此抽象路徑名轉換為路徑名字符串 |
| String getName() | 返回文件或目錄的名稱 |
| long length() | 返回由此File表示的文件的字節數 |
| String[] list() | 返回目錄中的文件和目錄的名稱字符串數組 |
| File[] listFiles() | 返回目錄中的文件和目錄的File對象數組 |
注意
- length() 返回的是文件的字節數,目錄的 長度是0
- getPath()在用絕對路徑表示的文件時相同,用相對路徑表示的文件時不同
- listFiles和list方法的調用,必須是實際存在的目錄,否則返回null
- listFiles和list 可以傳入FilenameFilter的實現類,用于按照文件名稱過濾文件
//?0
System.out.println(shuiHu.length());
File?liuBei?=?new?File("D:/三國/劉備.jpg");
//?24591
System.out.println(liuBei.length());
File?f?=?new?File("D:/bbb.java");
?//?D:\bbb.java
System.out.println(f.getPath());
File?f2?=?new?File("bbb.java");
//?bbb.java
System.out.println(f2.getPath());
File?sanGuo2?=?new?File("D:/三國2");
//?該目錄不存在,返回null
String[]?list?=?sanGuo2.list();
過濾文件的接口
@FunctionalInterfacepublic?interface?FilenameFilter?{
???//?參數為目錄和指定過濾名稱
????boolean?accept(File?dir,?String?name);
}
擴展(由讀者自己實現)
讀取目錄下所有的文件以及目錄,包括子目錄下所有的文件及目錄
IO流
上一章節學習了使用File類創建、查找、刪除文件,但是無法讀取、傳輸文件中的內容。
IO流主要是讀取、傳輸、寫入數據內容的。
I: input,O:output
這里的主體說的都是程序(即內存),從外部設備中讀取數據到程序中 即為輸入流,從程序中寫出到外部程序中即為輸出流
IO的分類
本地IO和網絡IO
本地IO主要是操作本地文件,例如在windows上復制粘貼操作文件,都可以使用java的io來操作
網絡IO主要是通過網絡發送數據,或者通過網絡上傳、下載文件,我們每天上網無時無刻不在體驗著IO的傳輸
按流向分,輸入流和輸出流
按數據類型分:字節流和字符流
按功能分:節點流和處理流
- 程序直接操作目標設備的類稱為節點流
- 對節點流進行裝飾,功能、性能進行增強,稱為處理流
IO流主要的入口是數據源,下面列舉常見的源設備和目的設備
源設備
目的設備
本文先探討本地IO的字節流和字符流,先列舉字節流和字符流的公共方法
| void close() throws IOException | 流操作完畢后,必須釋放系統資源,調用close方法,一般放在finally塊中保證一定被執行! |
注意:
- 程序中打開的IO資源不屬于內存資源,垃圾回收機制無法回收該資源,需要顯式的關閉文件資源
- 下面的代碼示例中就不顯示的調用close方法,也不會處理IOException,只是為了代碼的簡潔,方便閱讀
字節流
一切皆為字節
一切文件數據(文本、圖片、視頻等)在存儲時,都是以二進制的形式保存,都可以通過使用字節流傳輸。
InputStream是字節輸入流的頂層抽象
//?Closeable有close()方法public?abstract?class?InputStream?implements?Closeable?{}
核心方法如下
| int read() throws IOException; | 每次讀取一個字節的數據,提升為int類型,讀取到文件末尾時返回 -1 |
| int read(byte b[])throws IOException | 每次讀取到字節數組中,返回讀取到的有效字節個數,讀取到末尾時返回 -1(常用) |
| int read(byte b[], int off, int len) | 每次讀取到字節數組中,從偏移量off開始,長度為len,返回讀取到的有效字節個數,讀取到末尾時返回 -1 |
OutputStream是字節輸出流的頂層抽象
//?Flushable里面有flush()方法public?abstract?class?OutputStream?implements?Closeable,?Flushable?{}
核心方法如下
| void write(int b) throws IOException; | 將int值寫入到輸出流中 |
| void write(byte[] b) throws IOException; | 將字節數組寫入到輸出流中 |
| void write(byte b[], int off, int len) throws IOException | 將字節數組從偏移量off開始,寫入len個長度到輸出流中 |
| void flush() throws IOException | 刷新輸出流并強制緩沖的字節被寫出 |
文件節點流
InputStream有很多的實現類,先介紹下文件節點流,即目標設備是文件,輸入流和輸出流對應的是
FileInputStream和FileOutputStream
FileInputStream主要從磁盤文件中讀取數據,常用構造方法如下
public?FileInputStream(File?file)?throws?FileNotFoundException{}public?FileInputStream(String?name)?throws?FileNotFoundException{};
當傳入的文件不存在時,運行時會拋出FileNotFoundException異常
FileInputStream?fileInputStream?=?new?FileInputStream(file);
//?核心代碼
int?b;
while?((b?=?fileInputStream.read())?!=?-1?){
????System.out.print((char)?b);
}
//?輸出結果
abcde
FileInputStream?fileInputStream?=?new?FileInputStream(file);
//?核心代碼
byte[]?data?=?new?byte[2];
while?(fileInputStream.read(data)?!=?-1)?{
????System.out.println(new?String(data));
}
//?輸出結果
ab
cd
ed
上述代碼由于最后一次讀取時,只讀取一個字節 e ,數組中還是上次的數據cd,只替換了e,所以最后輸出了ed
下面是使用FileInputStream讀取的正確姿勢
File?file?=?new?File("D:/三國/諸葛亮.txt");FileInputStream?fileInputStream?=?new?FileInputStream(file);
//?核心代碼
byte[]?data?=?new?byte[2];
int?len;
while?((len?=?fileInputStream.read(data))?!=?-1)?{
????//?len?為每次讀取的有效的字節個數
????System.out.println(new?String(data,?0,?len));
}
//?輸出結果
ab
cd
e
注意:使用數組讀取,每次讀取多個字節,減少了系統間的IO操作次數,從而提高了效率,建議使用
源碼解析
public?int?read()?throws?IOException?{????return?read0();
}
private?native?int?read0()?throws?IOException;
public?int?read(byte?b[])?throws?IOException?{
???return?readBytes(b,?0,?b.length);
}
private?native?int?readBytes(byte?b[],?int?off,?int?len)?throws?IOException;
上面列了read()和read(byte[])的源碼,可見都是調用native的方法,涉及底層的系統調用。
如果用read()讀取文件,每讀取一個字節就要訪問一次硬盤,這種效率較低。
如果用read(byte[])讀取文件,一次讀取多個字節,當文件很大時,也會頻繁訪問硬盤。如果一次讀取超多字節,效率也不會高。
FileOutputStream主要是向磁盤文件中寫出數據,常用構造方法如下
| FileOutputStream(File file) throws FileNotFoundException | 使用一個File對象來構建一個FileOutputStream |
| FileInputStream(String name) throws FileNotFoundException | 使用一個文件名來構建一個FileOutputStream |
| FileOutputStream(File file, boolean append) ? ? throws FileNotFoundException | append傳true時,會對文件進行追加 |
| FileOutputStream(String name, boolean append) ? ? throws FileNotFoundException | append傳true時,會對文件進行追加 |
注意:
上述構造方法執行后,如果file不存在,會自動創建該文件
如果file存在,append沒有傳或者傳了false,會清空文件的數據
如果file存在,append傳了true,不會清空文件的數據
FileOutputStream?fos?=?new?FileOutputStream(file);
FileOutputStream?fos1?=?new?FileOutputStream("D:/三國/司馬懿.txt");
//?上述代碼中執行完后,趙云.txt和司馬懿.txt都會自動創建出來
向文件寫數據
FileOutputStream?fos?=?new?FileOutputStream("D:/三國/司馬懿.txt");fos.write(96);
fos.write(97);
fos.write(98);
//?文件內容為?abc
FileOutputStream?fos?=?new?FileOutputStream("D:/三國/趙云.txt");
fos.write("三國趙云".getBytes());
//?文件內容為?三國趙云
上述代碼每執行一次,文件里的內容就會被覆蓋,有時候這不是我們想要的場景,我們一般是想追加文件
FileOutputStream?fos?=?new?FileOutputStream("D:/三國/趙云.txt",?true);fos.write("有萬夫不當之勇".getBytes());
fos.close();
//?文件內容為?三國趙云有萬夫不當之勇
應用場景
開發中涉及文件的上傳、下載、傳輸都是用的這個節點流,會結合裝飾后的處理流一起使用,在緩沖流部分有介紹。
擴展(由讀者自己實現)
利用文件節點流實現文件的復制
內存節點流
ByteArrayInputStream是從內存的字節數組中讀取數據
public?ByteArrayInputStream(byte?buf[])?{}注意:不需要close數據源和拋出IOException,因為不涉及底層的系統調用
ByteArrayOutputStream是向內存字節數組中寫數據,內部維護了一個數組
public?ByteArrayOutputStream()?{???//?內部維護了一個可變的字節數組
???//?protected?byte?buf[];
????this(32);
}
內存節點流代碼示例
ByteArrayInputStream?bis?=?new?ByteArrayInputStream("data".getBytes());ByteArrayOutputStream?bos?=?new?ByteArrayOutputStream();
int?len?=?0;
while?((len?=?bis.read())?!=?-1){
????bos.write(len);
}
//?輸出data
System.out.println(new?String(bos.toByteArray()));
應用場景
字符流
字符流封裝了更加適合操作文本字符的方法
Reader用于讀取文本字符
public?abstract?class?Reader?implements?Readable,?Closeable?{}核心方法
| int read() throws IOException | 從輸入流中讀取一個字符,讀到文件末尾時返回-1 |
| int read(char cbuf[]) throws IOException | 從輸入流中讀取字符到char數組中 |
Writer用于寫出文本字符
public?abstract?class?Writer?implements?Appendable,?Closeable,?Flushable?{}核心方法
| void write(int c) throws IOException | 寫入單個字符到輸出流中 |
| void write(char[] cbuf) throws IOException | 寫入字符數組到輸出流中 |
| void write(char[] cbuf, int off, int len) ?throws IOException | 寫入字符數組的一部分,偏移量off開始,長度為len到輸出流中 |
| void write(String str) throws IOException | 直接寫入字符串到輸出流中(常用) |
| void write(String str, int off, int len) throws IOException | 寫入字符串的一部分,偏移量off開始,長度為len |
| Writer append(char c) throws IOException | 追加字符到輸出流中 |
文件節點流
字符流操作純文本字符的文件是最合適的,主要有FileReader和FileWriter
FileReader主要是向磁盤文件中寫出數據,常用構造方法如下
public?FileReader(String?fileName)?throws?FileNotFoundException{}public?FileReader(File?file)?throws?FileNotFoundException?{}
注意:當讀取的文件不存在時,會拋出FileNotFoundException,這點和FileInputStream一致
int?b;
while?((b?=?fileReader.read())?!=?-1)?{
????System.out.println((char)?b);
}
int?len;
char[]?data?=?new?char[2];
while?((len?=?fileReader.read(data))?!=?-1)?{
????System.out.println(new?String(data,?0,?len));
}
//?兩個字符兩個字符依次讀取
FileWriter構造方法如下,和FileOutStream構造方法類似,和FileOutputStream類似。
public?FileWriter(String?fileName)?throws?IOException?{}public?FileWriter(String?fileName,?boolean?append)?throws?IOException?{}
public?FileWriter(File?file)?throws?IOException{}
public?FileWriter(File?file,?boolean?append)?throws?IOException?{}
常用的寫數據進文件的方法
FileWriter?fileWriter?=?new?FileWriter("D:/三國/孫權.txt");fileWriter.write(97);?
fileWriter.write('b');?
fileWriter.write('C');?
fileWriter.write("權");?
fileWriter.append("力");
注意:
- 如果不執行close()或者flush()方法,數據只是保存到緩沖區,不會保存到文件。這點和與FileOutputStream不同,原因見 字節流和字符流的共同點章節
應用場景
純文本文件的io操作,配合處理流一起實現。
內存節點流
字符流也有對應的內存節點流,常用的有StringWriter和CharArrayWriter
StringWriter是向內部的StringBuffer對象寫數據。
//?定義public?class?StringWriter?extends?Writer?{
????private?StringBuffer?buf;
????public?StringWriter()?{
????????buf?=?new?StringBuffer();
????????lock?=?buf;
????}
}
//?應用
StringWriter?sw?=?new?StringWriter();
sw.write("hello");
StringBuffer?buffer?=?sw.getBuffer();
//?輸出hello
System.out.println(buffer.toString());
CharArrayWriter是向內部的char數組寫數據
//?定義public?class?CharArrayWriter?extends?Writer?{
????protected?char?buf[];
}
//?應用?
CharArrayWriter?caw?=?new?CharArrayWriter();
caw.write("hello");
char[]?chars?=?caw.toCharArray();
for?(char?c?:?chars)?{
?//?輸出了h?e?l?l?o
?System.out.println(c);
}
四種常用節點流的使用總結
字節流和字符流的共同點
注意到,OutputStream、Reader、Writer都實現了Flushable接口,Flushable接口有flush()方法
flush():強制刷新緩沖區的數據到目的地,刷新后流對象還可以繼續使用
close(): 強制刷新緩沖區后關閉資源,關閉后流對象不可以繼續使用
緩沖區:可以理解為內存區域,程序頻繁操作資源(如文件)時,性能較低,因為讀寫內存較快,利用內存緩沖一部分數據,不要頻繁的訪問系統資源,是提高效率的一種方式
具體的流只要內部有維護了緩沖區,必須要close()或者flush(),不然不會真正的輸出到文件中
處理流
上面的章節介紹了字節流和字符流的常用節點流,但是真正開發中都是使用更為強大的處理流
處理流是對節點流在功能上、性能上的增強
字節流的處理流的基類是FilterInputStream和FilterOutputStream
緩沖流(重點)
前面說了節點流,都是直接使用操作系統底層方法讀取硬盤中的數據,緩沖流是處理流的一種實現,增強了節點流的性能,為了提高效率,緩沖流類在初始化對象的時候,內部有一個緩沖數組,一次性從底層流中讀取數據到數組中,程序中執行read()或者read(byte[])的時候,就直接從內存數組中讀取數據。
分類
字節緩沖流:BufferedInputStream , BufferedOutputStream
字符緩沖流:BufferedReader , BufferedWriter
字節緩沖流
可見構造方法傳入的是節點流,是對節點流的裝飾
//?內部默認8192?=8*1024?即8M的緩沖區public?BufferedInputStream(InputStream?in)?{
???//?8192????
???//?內部維護了下面這樣的字節數組
????//?protected?volatile?byte?buf[];
????this(in,?DEFAULT_BUFFER_SIZE);
}
public?BufferedOutputStream(OutputStream?out)?{
????????this(out,?8192);
}
這里使用復制一部1G的電影來感受緩沖流的強大
FileInputStream?fis?=?new?FileInputStream("D:/三國/視頻.mp4");
FileOutputStream?fos?=?new?FileOutputStream("D:/三國/拷貝.mp4");
int?data;
while?((data?=?fis.read())?!=?-1)?{
????fos.write(data);
}
log.info("拷貝電影耗時:{}ms",?System.currentTimeMillis()?-?start);
//?五分鐘還沒拷好,關閉程序了...
FileInputStream?fis?=?new?FileInputStream("D:/三國/視頻.mp4");
FileOutputStream?fos?=?new?FileOutputStream("D:/三國/拷貝.mp4");
int?len;
byte[]?data?=?new?byte[1024?*?1024?*?1024];
while?((len?=?fis.read(data))?!=?-1)?{
????fos.write(data,?0,?len);
}
log.info("拷貝電影耗時:{}ms",?System.currentTimeMillis()?-?start);
//?拷貝電影耗時:4651ms
BufferedInputStream?fis?=?new?BufferedInputStream(new?FileInputStream("D:/三國/視頻.mp4"));
BufferedOutputStream?fos?=?new?BufferedOutputStream(new?FileOutputStream("D:/三國/拷貝.mp4"));
int?data;
while?((data?=?fis.read())?!=?-1)?{
????fos.write(data);
}
log.info("拷貝電影耗時:{}ms",?System.currentTimeMillis()?-?start);
//?拷貝電影耗時:39033ms
BufferedInputStream?fis?=?new?BufferedInputStream(new?FileInputStream("D:/三國/視頻.mp4"));
BufferedOutputStream?fos?=?new?BufferedOutputStream(new?FileOutputStream("D:/三國/拷貝.mp4"));
int?len;
byte[]?data?=?new?byte[8?*?1024];
while?((len?=?fis.read(data))?!=?-1)?{
????fos.write(data,?0,?len);
}
log.info("拷貝電影耗時:{}ms",?System.currentTimeMillis()?-?start);
//?拷貝電影耗時:1946ms
由上述四個例子可以得出結論,緩沖流讀取數據比普通流讀取數據快很多!
注意:采用邊讀邊寫的方式,一次傳輸幾兆的數據效率比較高,如果采用先把文件的數據都讀入內存,在進行寫出,這樣讀寫的次數是較小,但是占用太大的內存空間,一次讀太大的數據也嚴重影響效率!
字符緩沖流
對字符節點流的裝飾,下面是字符緩沖流的構造方法
public?BufferedReader(Reader?in)?{?//?private?static?int?defaultCharBufferSize?=?8192;
?//?內部維護了一個字符數組
????//?private?char?cb[];
????this(in,?defaultCharBufferSize);
}
public?BufferedWriter(Writer?out)?{
????????this(out,?defaultCharBufferSize);
}
字符緩沖流的特有方法
| BufferedReader | String readLine() throws IOException | 一行行讀取,讀取到最后一行返回null |
| BufferedWriter | void newLine() throws IOException | 寫一個換行符到文件中,實現換行 |
BufferedReader?br?=?new?BufferedReader(new?FileReader("D:/三國/趙云.txt"));
BufferedWriter?bw?=?new?BufferedWriter(new?FileWriter("D:/三國/趙子龍.txt"));
String?line?=?null;
while?((line?=?br.readLine())!=null)?{
??System.out.println(line);
??bw.write(line);
??bw.newLine();
}
//?結果
我乃常山趙子龍
于萬軍從中,取上將首級
緩沖流的正確姿勢
緩沖流是IO流中最重要的知識點,下面通過代碼實現正確用IO流的姿勢
BufferedInputStream?bis?=?null;BufferedOutputStream?bos?=?null;
try?{
????bis?=?new?BufferedInputStream(new?FileInputStream(new?File("D:/三國/視頻.mp4")));
????bos?=?new?BufferedOutputStream(new?FileOutputStream(new?File("D:/三國/拷貝.mp4")));
????int?len;
????//?一次傳輸8M的文件,實際測試這里傳輸的大小并不影響傳輸的速度
????byte[]?data?=?new?byte[8?*?1024];
????while?((len?=?bis.read(data))?!=?-1)?{
????????bos.write(data,?0,?len);
????}
}?catch?(IOException?e)?{
????log.error("error",?e);
}?finally?{
???//?finally塊中關閉流,確保資源一定被關閉
????if?(bis?!=?null)?{
????????try?{
????????????bis.close();
????????}?catch?(IOException?e)?{
????????????log.error("error",?e);
????????}
????}
????if?(bos?!=?null)?{
????????try?{
????????????bos.close();
????????}?catch?(IOException?e)?{
????????????log.error("error",?e);
????????}
????}
}
轉換流
字符編碼與字符集
字符編碼
計算機存儲的數據都是二進制的,而我們在電腦上看到的數字、英文、漢字等都是二進制轉換的結果
將字符轉換成二進制,為編碼
將二進制轉換為字符,為解碼
字符編碼 就是 自然語言和二進制的對應規則
字符集
就是一個編碼表,常見的字符集有ASCII字符集、GBK字符集、Unicode字符集等,具體各個編碼的介紹在這里就不介紹了。
IDEA中,使用 FileReader 讀取項目中的文本文件。IDEA可以設置為GBK 編碼,當讀取Windows系統中創建的默認的UTF8文本文件時,就會出現亂碼 。
如下例
idea的字符集設置 默認是UTF-8,這里修改為GBK
運行的代碼及結果
FileReader?fileReader?=?new?FileReader("D:/sanguo/utf8.txt");int?read;
while?((read?=?fileReader.read())?!=?-1)?{
????System.out.print((char)read);
}
//?浣犲ソ
InputStreamReader
Reader的子類,讀取字節,并使用指定的字符集將其解碼為字符。字符集可以自己指定,也可以使用平臺的默認字符集。
構造方法如下
//?使用平臺默認字符集public?InputStreamReader(InputStream?in)?{}
//?指定字符集
public?InputStreamReader(InputStream?in,?String?charsetName)throws?UnsupportedEncodingException{}
讀取文件的“你好",文件默認的字符集是UTF8
//?創建流對象,默認UTF8編碼InputStreamReader?isr?=?new?InputStreamReader(new?FileInputStream("D:/三國/utf8.txt"));
//?創建流對象,指定GBK編碼
InputStreamReader?isr2?=?new?InputStreamReader(new?FileInputStream("D:/三國/utf8.txt"),?"GBK");
int?read;
while?((read?=?isr.read())?!=?-1)?{
????System.out.println((char)?read);
}
while?((read?=?isr2.read())?!=?-1)?{
????System.out.println((char)?read);
}
//?輸出結果
你好
浣犲ソ
OutputStreamWriter
Writer的子類,使用指定的字符集將字符編碼為字節。字符集可以自己指定,也可以使用平臺的默認字符集。
構造方法如下
//?使用平臺默認字符集public?OutputStreamWriter(OutputStream?out)?{}
//?使用平臺默認字符集
public?OutputStreamWriter(OutputStream?out,?String?charsetName)throws?UnsupportedEncodingException{}
如下面的代碼,將你好寫入文件。寫入后兩個文件的字符集不一樣,文件大小也不同
//?創建流對象,默認UTF8編碼OutputStreamWriter?osw?=?new?OutputStreamWriter(new?FileOutputStream("D:/三國/黃忠.txt"));
osw.write("你好");?//?保存為6個字節
//?創建流對象,指定GBK編碼
OutputStreamWriter?osw2?=?new?OutputStreamWriter(new?FileOutputStream("D:/三國/馬超.txt"),"GBK");
osw2.write("你好");//?保存為4個字節
對象流
序列化
jdk提供了對象序列化的方式,該序列化機制將對象轉為二進制流,二進制流主要包括對象的數據、對象的類型、對象的屬性。可以將java對象轉為二進制流寫入文件中。文件會持久保存了對象的信息。
同理,從文件中讀出對象的信息為反序列化的過程
對象想序列化,滿足的條件:
ObjectOutputStream
該類實現將對象序列化后寫出到外部設備,如硬盤文件
public?ObjectOutputStream(OutputStream?out)?throws?IOException{}常用方法
| void writeObject(Object obj) throws IOException | 將指定的對象寫出 |
如下代碼,將User對象寫入文件中
public?class?User?implements?Serializable?{????private?static?final?long?serialVersionUID?=?8289102797441171947L;
????private?String?name;
????private?Integer?age;
}
//?下面是將對象輸出到文件的核心代碼
User?user?=?new?User("馬超",20);
//?創建序列化流對象
ObjectOutputStream?out?=?new?ObjectOutputStream(new?FileOutputStream("D:/三國/馬超.txt"));
//?寫出對象
out.writeObject(user);
注意:
生成的文件內容如下
ObjectInputStream
該類將ObjectOutputStream寫出的對象反序列化成java對象
public?ObjectInputStream(InputStream?in)?throws?IOException?常用方法
| Object readObject() ? ? throws IOException, ClassNotFoundException | 讀取對象 |
//?強轉為user
User?user?=?(User)?in.readObject();
System.out.println(user);
//?輸出內容
User(name=馬超,?age=20)
對象和字節數組的轉換
利用對象流和字節數組流結合 ,可以實現java對象和byte[]之間的互轉
//?將對象轉為byte[]public?static??byte[]?t1(T?t)?{
????ByteArrayOutputStream?bos?=?new?ByteArrayOutputStream();
????ObjectOutputStream?oos?=?new?ObjectOutputStream(bos);
????oos.writeObject(t);return?bos.toByteArray();
}//?將byte[]轉為對象public?static??T?t2(byte[]?data)?throws?IOException,?ClassNotFoundException?{
????ByteArrayInputStream?bos?=?new?ByteArrayInputStream(data);
????ObjectInputStream?oos?=?new?ObjectInputStream(bos);return?(T)?oos.readObject();
}
管道流(了解)
管道流主要用于兩個線程間的通信,即一個線程通過管道流給另一個線程發數據
注意:線程的通信一般使用wait()/notify(),使用流也可以達到通信的效果,并且可以傳遞數據
使用的類是如下
- PipedInputStream和PipedOutStream
- PipedReader和PipedWriter
這里使用字節流為例
class?Sender?implements?Runnable?{????private?PipedOutputStream?pos;
????private?String?msg;
????public?Sender(String?msg)?{
????????this.pos?=?new?PipedOutputStream();
????????this.msg?=?msg;
????}
????@Override
????public?void?run()?{
?????? pos.write(msg.getBytes());
????}
????public?PipedOutputStream?getPos()?{
????????return?pos;
????}
}
class?Receiver?implements?Runnable?{
????private?PipedInputStream?pis;
????public?Receiver()?{
????????this.pis?=?new?PipedInputStream();
????}
????@Override
????public?void?run()?{
?????????byte[]?data?=?new?byte[1024];
????????????int?len;
????????????while?((len?=?pis.read(data))?!=?-1)?{
????????????????System.out.println(new?String(data,?0,?len));
????????????}
????}
}
????
Sender?sender?=?new?Sender("hello");
Receiver?receiver?=?new?Receiver();
receiver.getPis().connect(sender.getPos());
new?Thread(sender).start();
new?Thread(receiver).start();
//?控制臺輸出??hello
輸入與輸出流(了解)
System.in和System.out代表了系統標準的輸入、輸出設備
默認輸入設備是鍵盤,默認輸出設備是控制臺
可以使用System類的setIn,setOut方法對默認設備進行改變
我們開發中經常使用的輸出到控制臺上的內容的方法。
System.out.println("a");System.out.print("b");
class?System{
????public?final?static?InputStream?in?=?null;
????public?final?static?PrintStream?out?=?null;
}
public?PrintStream(String?fileName)?throws?FileNotFoundException{}
數據流(了解)
主要方便讀取Java基本類型以及String的數據,有DataInputStream 和 DataOutputStream兩個實現類
DataOutputStream?dos?=?new?DataOutputStream(new?FileOutputStream("D:/三國/周瑜.txt"));dos.writeUTF("周瑜");
dos.writeBoolean(false);
dos.writeLong(1234567890L);
DataInputStream?dis?=?new?DataInputStream(new?FileInputStream("D:/三國/周瑜.txt"));
String?s?=?dis.readUTF();
System.out.println(s);
boolean?b?=?dis.readBoolean();
System.out.println(b);
//?輸出
周瑜
false
IO流總結
以上各個章節詳細介紹了各個流,可見流的種類比較多,記憶確實增加了困難。但是可以通過思維導圖的方式整理出來,方便記憶。
字節流的導圖
字符流的導圖
按照功能劃分
輸入、輸出對應關系
結語
短期的加更計劃
文章篇幅較長,給看到這里的小伙伴點個大大的贊!由于作者水平有限,文章中難免會有錯誤之處,歡迎小伙伴們反饋指正。
如果覺得文章對你有幫助,麻煩 點贊、評論、轉發、在看 、關注 走起
你的支持是我加更最大的動力!!!
另
瑣碎時間想看一些技術文章,可以去公眾號菜單欄翻一翻我分類好的內容,應該對部分童鞋有幫助。同時看的過程中發現問題歡迎留言指出,不勝感謝~。另外,有想多了解哪些方面內容的可以留言(什么時候,哪篇文章下留言都行),附菜單欄截圖(PS:很多人不知道公眾號菜單欄是什么)
END
我知道你 “在看”
總結
以上是生活随笔為你收集整理的javaio流_万字长文+思维导图帮你梳理 Java IO 流,还学不会你来打我(值得收藏)...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 系统接口502异常_基于SpringBo
- 下一篇: 树莓派python_使用Python简单