學習目的
- 了解java流的概念
- 了解java對流的分類
- 掌握java流的對象創(chuàng)建,以及常用方法的使用
- 掌握java流常見的應用場景
- 掌握重點流:FileStream、PrintStream、ObjectStream
- 了解序列化與反序列化的概念和使用
I/O流
概念
流 在java中指的是文件的轉(zhuǎn)移過程,通常是指文件在計算機硬盤和計算機內(nèi)存之間的傳遞方式。該傳遞過程包括了文件數(shù)據(jù)和方向。-
字節(jié)流繼承結(jié)構(gòu)圖
InputStream 和 OutputStream 繼承結(jié)構(gòu)圖.png -
字符流繼承結(jié)構(gòu)圖
Reader 和 Writer 繼承結(jié)構(gòu)圖.png 流與文件路徑
輸入流對文件的讀取和輸出流對文件的寫入,都需要提前知道文件的路徑在哪,一般使用相對路徑和絕對路徑來存儲文件。
- 相對路徑:在IDEA或eclipse中,相對路徑是項目/工程的根目錄,即項目所在的路徑就是相對路徑。項目工程所在的根目錄才是相對路徑起點,注意項目底下的子模塊路徑不是相對路徑的起點;
- 絕對路徑:絕對路徑是任意盤符下所保存的文件路徑,引用絕對路徑的文件時,必須將絕對路徑從盤符開始具體寫明。
1、 IO流與Properties
- .properties文件
以.properties為后綴名的文件是java中的屬性配置文件,通常存儲一些可改變的配置信息。該文件的存儲內(nèi)容與其后綴名一樣,采用properties集合存儲key=value,即采用Map的鍵值對形式存儲,且properties的鍵與值得類型只能為String類型。在properties文件中允許key重復,但key重復時,會覆蓋value值。 - .properties文件原由
在開發(fā)中,經(jīng)常遇到改變頻繁的數(shù)據(jù),因此可以將這些數(shù)據(jù)單獨寫到一個配置文件中,使用程序動態(tài)讀取。將來只需要修改這個文件的數(shù)據(jù)內(nèi)容,java代碼不需要改動,不需要重新編譯,服務器也不需要重啟,就可以拿到動態(tài)的信息。 - IO讀取.properties文件
將.properties文件作為一個File對象,使用輸入流來讀取該文件的內(nèi)容,就可以獲取得到需要的配置信息,可以讓配置信息更加靈活多變,且不會觸及修改java源代碼(修改違背開閉原則)。 - 示例(假設已存在對應的.properties文件)
//Properties是一個Map集合,key和value都是String類型
//將XXX.properties文件中的數(shù)據(jù)讀取,就是加載到Properties對象當中
// 新建一個輸入流對象
FileReader reader = new FileReader("路徑\\XXX.properties");
// 新建一個Map集合(具體是Hashtable的子類Properties)
Properties pro = new Properties();
// 調(diào)用Properties對象的load方法將屬性配置文件中的數(shù)據(jù)加載到Map集合中
pro.load(reader); // 文件中的數(shù)據(jù)順著管道加載到Map集合中
// 通過Properties的key來獲取對應value
String username = pro.getProperty("username");
System.out.println(username);
一.字節(jié)字符流
流概念
文件由字符或字節(jié)構(gòu)成,將文件加載到內(nèi)存 或 將內(nèi)存數(shù)據(jù)輸出到文件,需要有輸入和輸出流的支持。因此可以把輸入和輸出流分為了兩個,字節(jié)輸入 和 字符輸入,字節(jié)輸出流 和 字符輸出流。以流的方向分類
- 輸入流:Input,Java中 將文件或其它輸入設備的數(shù)據(jù) 從硬盤加載到內(nèi)存的過程;
- 輸出流:Output,Java中 將內(nèi)存中的數(shù)據(jù) 保存到硬盤中的文件或其他輸出設備的過程。

- 以讀取文件數(shù)據(jù)的方式分類
- 字節(jié)流Stream:以Stream結(jié)尾命名的流都是字節(jié)流,字節(jié)流一次讀取1個字節(jié)byte(即8個二進制位)。
- 字符流Reader、Writer:以Reader或Writer 結(jié)尾命名的流都是字符流,字符流一次讀取一個字符char(兩個字節(jié)16個二進制位)。
-
流關系圖
流關系圖.png - 區(qū)別
- 字節(jié)流:每次讀取一個字節(jié),使用byte[]數(shù)組存儲、讀寫多個字節(jié);
- 字符流:每次讀取一個字符,流本身自帶并使用char[]數(shù)組存儲、讀寫多個字符。
1.1 字節(jié)輸入流<InputStream>
概念
InputStream 就是字節(jié)輸入流,是一個抽象類。所有繼承了 InputStream 的類都是字節(jié)輸入流,InputStream是用來從文件中讀取基本數(shù)據(jù)類型值的 Java API接口。主要子類
- 文件輸入流<FileInputStream>:
- 對象輸入流<ObjectInputStream>:
- 過濾輸入流<FilterInputStream>:
- 緩存輸入流<BufferedInputStream>:

- 字段
- int MAX_SKIP_BUFFER_SIZE = 2048:用于確定 跳過時要使用的最大緩沖區(qū)大小。
- 常用方法
- int read() :從輸入流讀取下一個數(shù)據(jù)字節(jié),讀到文件末尾時返回 -1;
- int read(byte[] b) :從輸入流中每次最多讀取b.length個字節(jié),并將讀取的數(shù)據(jù)存儲在緩沖區(qū)數(shù)組b中;
- int read(byte[] b, int off, int len) :將輸入流中 從偏移量off開始的最多l(xiāng)en個數(shù)據(jù)字節(jié)讀入byte數(shù)組b中;
- long skip(long n):跳過和丟棄此輸入流中數(shù)據(jù)的 n個字節(jié)(從下標0開始);
- void close() :關閉此輸入流并釋放與該流關聯(lián)的所有系統(tǒng)資源,finally中執(zhí)行釋放。
1.2 字節(jié)輸出流<OutputStream>
- 概念
OutputStream 就是字節(jié)輸出流,是一個抽象類,所有繼承了 OutputStream 的類都是字節(jié)輸出流。OuputStream是用來將 基本數(shù)據(jù)類型數(shù)據(jù)寫入文件的 Java API接口。 - 主要子類
- 文件輸出流<FileOutputStream>:
- 對象輸出流<ObjectOutputStream>:
- 過濾輸出流<FilterOutputStream>:
- 緩存流<BufferedOutputStream>:
- 打印流<PrintStream>:

- 主要方法
- void write(int b):將指定的字節(jié)內(nèi)容寫入此輸出流,通過輸出流將字節(jié)數(shù)據(jù)寫到文件中;
- void write(byte[] b) :將 b.length個字節(jié)從指定的字節(jié)數(shù)組寫入此輸出流;
- void write(byte[] b, int off, int len) :將指定字節(jié)數(shù)組中 從偏移量off 開始的 len個字節(jié)寫入此輸出流;
- void flush() :write()寫完后,刷新此輸出流,并強制寫出所有管道中緩沖的輸出字節(jié);
- void close() :關閉此輸出流并釋放與此流有關的所有系統(tǒng)資源。
1.3 字符輸入流<Reader>
概念
Reader 就是字符輸入流,是一個抽象類,所有繼承了 Reader 的類都是字符輸入流。Reader是用于讀取字符流的抽象類,子類必須實現(xiàn)其read(char[], int, int) 和 close()方法。同時,多數(shù)子類將重寫其定義的一些方法,以提供更高的效率和其他功能。主要子類
- 緩存輸入流<BufferedReader >:
- 字節(jié)轉(zhuǎn)字符輸入流<InputStreamReader >:
- 字符文件輸入流<FileReader >:

- 字段
- Object lock:用于同步 針對此流的操作的對象。
- 主要方法
- Reader():無參構(gòu)造,創(chuàng)建一個新的字符輸入流對象,并同步自身;
- Reader(Object lock):帶參構(gòu)造,創(chuàng)建一個新的字符輸入流對象,并同步給定的對象;
- int read() :讀取單個字符,如果已讀到文件末尾,則返回 -1;
- int read(char[] cbuf) :將輸入流讀取得到的字符數(shù)據(jù)存入char[]數(shù)組;
- int read(char[] cbuf, int off, int len) :從數(shù)組的偏移量off開始,將len個字符讀入數(shù)組的某一部分;
- long skip(long n):讀取某個文件時跳過n個字符,從文件下標0開始;
- void close() :關閉該流并釋放與此流有關的所有系統(tǒng)資源。
1.4 字符輸出流<Writer>
概念
Writer 就是字符輸出流,是一個抽象類,所有繼承了 Writer 的類都是字符輸出流。Writer是寫入字符流的抽象類,子類必須實現(xiàn)其write(char[], int, int)、flush() 和 close()方法,完成數(shù)據(jù)到文件的寫入。同時,多數(shù)子類將重寫其定義的一些方法,以提供更高的效率和其他功能。主要子類
- 緩存流<BufferedWriter>:
- 字節(jié)到字符轉(zhuǎn)換流<OutputStreamWriter>:
- 字符文件輸出流<FileWriter>:
- 打印流<PrintWriter>:

- 主要方法
- Writer append(char c) :將指定字符追加到此 writer;
- void write(int c) :往文件中寫入單個字符,寫入前先清空文件原內(nèi)容;
- void write(char[] cbuf) :往文件中寫入字符數(shù)組,每次最多寫入cbuf.length個字符;
- void write(char[] cbuf, int off, int len) :往字符數(shù)組的某一部分,從字符數(shù)組的下標off開始,寫入len個長度字符到文件中;
- void write(String str) :往文件中寫入字符串;
- void write(String str, int off, int len) :往文件中寫入字符串的某一部分,從字符串下標off開始,寫入len個長度字符;
- void flush() :刷新此輸出流,將管道中的數(shù)據(jù)刷出寫入到文件中;
- void close() :關閉此流并釋放與此流有關的所有系統(tǒng)資源,但要先執(zhí)行 flush() 刷新它。
1.5 flush()方法
flush 的含義是刷新緩沖區(qū),就是將緩存區(qū)中的數(shù)據(jù)寫到磁盤上,而不再把數(shù)據(jù)放到內(nèi)存里。在執(zhí)行 os.close()時,會默認執(zhí)行 os.flush(),編寫代碼時可以不用顯示的調(diào)用。
二、文件流
- 概念
文件流是指在程序開發(fā)中,完成對文件的讀寫操作的流稱為文件流。
該流用于讀取和寫入普通文本、圖像、音頻、視頻等格式文件。 - 分類
- 文件字節(jié)輸入流<FileInputStream>
- 文件字節(jié)輸出流<FileOutputStream>
- 文件字符輸入流<FileReader >
- 文件字符輸出流<FileWriter>
- 區(qū)別
- 字節(jié)流:每次讀取一個字節(jié),使用byte[]數(shù)組存儲、讀寫多個字節(jié);可以讀取所有的形式文件(文本、視頻、音頻、圖片等);
- 字符流:每次讀取一個字符,使用char[]數(shù)組存儲、讀寫多個字符;只適合于讀取普通文本文件。
2.1 文件字節(jié)輸入流<FileInputStream,掌握>
- 概念
FileInputStream是從文件系統(tǒng)(硬盤)中的某個文件中 獲得輸入字節(jié)。主要按照字節(jié)的方式讀取硬盤上的文件內(nèi)容,每次讀取一個字節(jié)。FileInputStream 用于讀取諸如圖像數(shù)據(jù)之類的原始字節(jié)流。
<文件內(nèi)容在計算機底層都是以 二進制的補碼形式存在,1字節(jié) = 8位>。 - 常用方法
- FileInputStream(String name):帶參構(gòu)造方法,根據(jù)指定文件路徑創(chuàng)建字節(jié)輸入流對象;
- int read() :從輸入流讀取文件的下一個數(shù)據(jù)字節(jié),若已到達文件末尾則返回 -1;
- int read(byte[] b) :從 輸入流中每一次讀取 最多b.length個字節(jié),并將讀取的內(nèi)容存儲在緩沖區(qū)的byte數(shù)組中;
- int available():每一次讀取時,剩余未讀的字節(jié)個數(shù),結(jié)合 read(byte[] b) 方法作為byte[ available() ]數(shù)組的容量 最佳,但過大時會超出byte數(shù)組的容量;
- long skip(long n):跳過n個字節(jié)"選擇"讀取,返回讀取時實際跳過的字節(jié)數(shù);
- void close() :關閉此輸入流并釋放與該流關聯(lián)的所有系統(tǒng)資源。
-
FileInputStream讀取文件圖解
FileInputStream讀取文件.png - 代碼實現(xiàn)
//創(chuàng)建文件輸入流引用
FileInputStream fis = null;
try {
//創(chuàng)建 從指定路徑讀取文件的 輸入流對象,
fis = new FileInputStream("c:\\test.txt");
int b = 0;//存儲每次讀取文件返回的字節(jié)數(shù)據(jù) 個數(shù)
while ((b = fis.read()) != -1) {
//System.out.print(b);//輸出讀取到的單個字節(jié)
System.out.print((char)b);//將讀取得到的單個字節(jié) 轉(zhuǎn)換成字符輸出
}
//采用read(byte[] bytes)方法讀取時,結(jié)合使用available()作為bytes數(shù)組的容量,但數(shù)據(jù)過大時不能使用
// byte[] bytes = new byte[fis.available()];
//采用字節(jié)數(shù)組存儲字節(jié)流讀取文件的數(shù)據(jù)
byte[] b1 = new byte[4];
int readcount = 0;//存儲每次讀取數(shù)組 返回的字節(jié)個數(shù)
//當返回讀取的字節(jié)個數(shù)不為0
while ((readcount = fis.read(b1)) != -1){
//將每一次讀取byte數(shù)組中的數(shù)據(jù)轉(zhuǎn)換為字符串輸出,每次讀取幾個就輸出幾個
String s1 = new String(b1,0,readcount);
System.out.print(s1);//
}
}catch(FileNotFoundException e) {
e.printStackTrace();
}catch(IOException e) {
e.printStackTrace();
}finally { //當輸入流為空時,關閉流,釋放內(nèi)存
try {
if (fis != null){
is.close();
}
}catch(IOException e) {}
}
- 注意點
- 常常使用 fis.read() != -1判斷是否讀取到了文件末尾;
- 使用 byte數(shù)組存儲讀取文件的數(shù)據(jù)時,想要每次讀取幾個就輸出幾個,就使用一個變量temp 接收每次讀取的個數(shù);
- 采用字節(jié)輸入流讀取漢字文本文件時,因為每個漢字占據(jù)兩個字節(jié),而字節(jié)輸入流每次只能讀取一個字節(jié),導致每個漢字的讀取分為"不完整的"兩部分,從而產(chǎn)生亂碼。
2.2 文件字節(jié)輸出流<FileOutputStream,掌握>
概念
文件輸出流是 將數(shù)據(jù)寫入文件的輸出流。"寫入文件"是否可用 或 能否被創(chuàng)建,取決于基礎平臺,某些平臺一次只允許一個文件流對象打開文件進行寫入。若所涉及的文件已經(jīng)打開,則此類中的構(gòu)造方法將失?。ㄒ粋€文件無法被兩個輸出流同時寫入數(shù)據(jù))。特點
文件字節(jié)輸出流按照 字節(jié)方式寫出文件,適用于寫入諸如圖像數(shù)據(jù)之類的原始字節(jié)的流。經(jīng)典案例
文件復制:首先判斷該文件是否存在。文件存在,則(采用輸入流)讀取文件,讀取后將該文件(采用輸出流)另寫一份保存到磁盤上,完成文件的復制備份。-
文件字節(jié)輸出圖
文件字節(jié)輸出流.png 常用方法
- FileOutputStream(String name):帶參構(gòu)造,創(chuàng)建一個 向指定文件寫入數(shù)據(jù)的 字節(jié)輸出流對象,寫入時默認清空文件的所有內(nèi)容再寫入;
- FileOutputStream(String name, boolean append):帶參構(gòu)造,創(chuàng)建一個 向指定文件寫入數(shù)據(jù)的 字節(jié)輸出流對象;第二個參數(shù)為 true,表示將字節(jié)追加寫入文件末尾處,而不是清空文件再寫入文件開始處;
- void write(int b):輸出流將指定的字節(jié)b寫入文件,一次寫入一個長度的字節(jié);
- void write(byte[] b) :輸出流每次將最多 b.length 個字節(jié)從指定 byte 數(shù)組寫入此文件,通常byte數(shù)組是由輸入流讀取得到的"復制內(nèi)容";
- void writeBytes(byte b[], int off, int len, boolean append):輸出流將數(shù)組的一部分內(nèi)容寫入文件,并使用append判斷是清空原文件內(nèi)容寫入,或是在原文件的末尾追加寫入;
- void close() :關閉此輸出流 并釋放與此流有關的所有系統(tǒng)資源。
- 代碼實現(xiàn)
//創(chuàng)建文件輸出流引用
FileOutputStream fos = null;
try {
//創(chuàng)建 指定路徑寫出文件的 輸出流文件對象,若文件不存在則在該路徑新建文件,若文件存在則清空文件的內(nèi)容再寫入
fos = new FileOutputStream("d:\\test.txt.bak");
//創(chuàng)建一個輸出流對象,寫入時不清空已存在文件的內(nèi)容,而是在文件末尾追加寫入的內(nèi)容
// fos = new FileOutputStream("d:\\test.txt.bak",true);
//寫入單個字符
fos.write(1);
//寫入字符串
fos.write("Hello world!");
//寫入char數(shù)組
char[] ac = {'我','是','中','國','人'};
fos.write(ac);
//寫入完成,刷新管道
fos.flush();
System.out.println("文件復制完畢!");
}catch(FileNotFoundException e) {
e.printStackTrace();
}catch(IOException e) {
e.printStackTrace();
}finally {
// 分開try,不要一起try, 一起try時,其中一個出現(xiàn)異常,可能會影響到另一個流的關閉。
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
- 注意點
- 對于FileOutPutStream要寫入的文件,若文件在對象創(chuàng)建前已存在,直接對已存在文件進行寫入;若文件不存在,則會在指定路徑下新建文件再寫入;
- FileOutPutStream對象的創(chuàng)建:調(diào)用構(gòu)造器時,注意區(qū)分單參構(gòu)造和雙參構(gòu)造,單參構(gòu)造會清空寫入文件的所有內(nèi)容再將新數(shù)據(jù)寫進;雙參構(gòu)造append若是true時,則不會清空文件內(nèi)容,而在文件末尾追加寫入的數(shù)據(jù)。
2.3 文件字符輸入流<FileReader >
- 概念
FileReader是用來讀取字符文件的便捷類。主要按照 字符(雙字節(jié))的方式讀取文件內(nèi)容,即每次讀取兩個字節(jié)。<底層使用兩個字節(jié)位00110101 10111001>。 -
文件字符輸入流圖
文件字符輸入流.png - 常用方法
- FileReader(String fileName):帶參構(gòu)造,在指定路徑下創(chuàng)建一個字符輸入流對象,讀取文件數(shù)據(jù)(底層調(diào)用FileInputStream對象的創(chuàng)建方式);
- int read() :從輸入流讀取單個字符,以int類型變量接收讀取的字符;
- int read(char[] c) :從輸入流 將字符讀入數(shù)組,一次讀入最多c.length個長度字符;
- void close() :關閉該流并釋放與此流有關的所有系統(tǒng)資源。
- 代碼實現(xiàn)
//創(chuàng)建字符輸入流引用
FileReader fileReader = null;
try {
//創(chuàng)建實際的字符輸入流對象
fileReader = new FileReader("");
//讀取單個字符
fileReader.read();
//用于存儲 每次讀取返回的字節(jié)個數(shù)
int readcount = 0;
char[] ac = new char[8];
//讀取數(shù)組
fileReader.read(ac);
while ((readcount = fileReader.read(ac)) !=-1){
//讀取數(shù)組的一部分
fileReader.read(ac, 0, readcount);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
2.4 文件字符輸出流<FileWriter>
- 概念
FileWriter是用來寫入字符文件的便捷類,每一次輸出按照字符方式(雙字節(jié))寫出文件。 -
文件字符輸出流圖
文件字符輸出流.png - 常用方法
- FileWriter(String fileName):帶參構(gòu)造,創(chuàng)建一個 向指定名稱的文件中寫入數(shù)據(jù)的 字符輸出流對象;
- FileWriter(String fileName, boolean append):帶參構(gòu)造,創(chuàng)建一個 向指定名稱的文件中寫入數(shù)據(jù)的 字符輸出流對象;第二個參數(shù)為 true,表示將字節(jié)寫入文件末尾處,而不是寫入文件開始處;
- void flush() :刷新此輸出流,將可能殘留在管道中的數(shù)據(jù)刷出到文件中(一般在關閉流之前刷新);
- void close() :關閉此流并釋放與此流有關的所有系統(tǒng)資源,但要先執(zhí)行 flush() 刷新;
- void write(int c) :在輸出流中寫入單個字符;
- void write(String str) :在輸出流中寫入字符串。
- 代碼實現(xiàn)
//創(chuàng)建一個文件字符輸出流引用
FileWriter fileWriter = null;
try {
//創(chuàng)建實際的字符輸出流對象
fileWriter = new FileWriter("");
//創(chuàng)建實際的字符輸出流對象,true表示在輸出文件后面進行append追加內(nèi)容
//fileWriter = new FileWriter("",true);
//寫入單個字符
fileWriter.write(1);
//寫入字符串
fileWriter.write("Hello world!");
//寫入char數(shù)組
char[] ac = {'我','是','中','國','人'};
fileWriter.write(ac);
//寫入完成,刷新管道
fileWriter.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
if (fileWriter != null){
try {
fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
三、緩沖流<BufferedStream>
概念
緩沖流主要是通過減少物理讀取次數(shù)(程序從硬盤中讀取文件的次數(shù))的流,通過將硬盤數(shù)據(jù)讀取到一個緩沖管道中,下一次直接從緩沖管道中讀取,減少內(nèi)存與硬盤的交流次數(shù)。緩沖流是為了提高效率而存在的。分類
- 緩沖字節(jié)輸入流<BufferedInputStream>
- 緩沖字節(jié)輸出流<BufferedOutputStream>
- 緩沖字符輸入流<BufferedReader>
- 緩沖字符輸出流<BufferedWriter>
- 特點
- 構(gòu)造方法節(jié)點流:緩沖流的構(gòu)造方法中需要一個節(jié)點流作為第一參數(shù),不像文件流直接使用文件路徑或文件創(chuàng)建對象,緩沖流必須使用父類的字節(jié)輸入/輸出流、字符輸入輸出流的對象作為第一參數(shù);
- 流自帶緩沖區(qū):緩沖流中自帶有數(shù)據(jù)緩沖區(qū),無需像文件流一樣創(chuàng)建自定義byte[]數(shù)組或char[]數(shù)組存儲 字節(jié)/字符輸入流讀取文件的數(shù)據(jù);
- 節(jié)點流、包裝流與處理流
- 節(jié)點流:指的是在一個流的構(gòu)造方法內(nèi)還需要另外一個流作為參數(shù),而作為參數(shù)的流就稱為節(jié)點流,在關閉流釋放資源時,只需關閉外部流,不需要關閉節(jié)點流(外部流的close()底層自動關閉節(jié)點流);
- 包裝流/ 處理流:指的是使用節(jié)點流作為其構(gòu)造方法參數(shù)的流,實際上對文件進行操作處理的流。
3.1 緩沖字節(jié)輸入流<BufferedInputStream>
- 概念
BufferedInputStream為另一個輸入流InputStream擴展功能,添加了緩沖輸入、支持 mark() 和 reset()方法的能力。在創(chuàng)建 BufferedInputStream 時,會創(chuàng)建一個內(nèi)部緩沖區(qū)數(shù)組,在讀取或跳過流中的字節(jié)時,可根據(jù)需要從包含的輸入流再次填充該內(nèi)部緩沖區(qū),一次填充多個字節(jié)。 - 特點
- mark()方法 記錄輸入流中的某個點;
- reset()方法使得在 從包含的輸入流中獲取新字節(jié) 之前,再次讀取自最后一次標記后讀取的所有字節(jié)。
- 字段
- int DEFAULT_BUFFER_SIZE = 8192:默認字符緩沖區(qū)的大?。?/li>
- int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8:該流支持的最大緩沖區(qū)大??;
- volatile byte buf[]:存儲 從字節(jié)輸入流中讀取的數(shù)據(jù) 的內(nèi)部緩沖數(shù)組;
- int count:比緩沖區(qū)中最后一個有效字節(jié)的索引 大1的索引;
- int pos:緩沖區(qū)中的當前位置position;
- int markpos = -1:最后一次調(diào)用mark()方法時 緩沖區(qū)的位置。
- 常用方法
- BufferedInputStream(InputStream in):帶參構(gòu)造,創(chuàng)建一個默認緩沖區(qū)大小的字節(jié)緩存輸入流對象,底層使用輸入流 in;
- BufferedInputStream(InputStream in, int size):帶參構(gòu)造,創(chuàng)建一個size長度的指定緩沖區(qū)大小的 字節(jié)緩存輸入流對象,底層使用輸入流 in;
- int read():從輸入流中讀取數(shù)據(jù)的下一個字節(jié);
- int read(byte b[], int off, int len):將輸入流中的 len個數(shù)據(jù)字節(jié)讀入byte 數(shù)組中,從數(shù)組的下標off開始存儲;
- long skip(long n):跳過和丟棄此輸入流中數(shù)據(jù)的 n個字節(jié);
- void mark(int readlimit):在此輸入流中標記當前的位置;
- void close():關閉此輸入流并釋放與該流關聯(lián)的所有系統(tǒng)資源。
//創(chuàng)建字節(jié)輸入流對象--緩沖流中的節(jié)點流
FileInputStream fis = new FileInputStream("");
//創(chuàng)建緩沖字節(jié)輸出流對象--參數(shù)為節(jié)點流
BufferedInputStream bis = new BufferedInputStream(fis);
//創(chuàng)建緩沖字節(jié)輸出流對象--參數(shù)為節(jié)點流,且緩沖流本身自帶的緩沖區(qū)大小為128字節(jié)
BufferedInputStream biss = new BufferedInputStream(fis,128);
//查看緩沖流本身緩沖區(qū)的大小
bis.available();
//讀取單個字節(jié)
bis.read();
byte[] b1 = new byte[32];
//讀取字節(jié)數(shù)組
bis.read(b1);
int readcount = 0;
//讀取字節(jié)數(shù)組的一部分
bis.read(b1,0,readcount);
//關閉流資源,釋放內(nèi)存
bis.close();
3.2 緩沖字節(jié)輸出流<BufferedOutputStream>
- 概念
BufferedOutputStream類是實現(xiàn)緩沖的輸出流。通過設置這種輸出流,應用程序可以將各個字節(jié)寫入底層輸出流InputStream中,不必針對每次字節(jié)寫入都調(diào)用底層系統(tǒng)。 - 字段
- byte buf[]:存儲數(shù)據(jù)的內(nèi)部緩沖區(qū);
- int count:緩沖區(qū)中的有效字節(jié)數(shù)。
- 常用方法
- BufferedOutputStream(OutputStream out):帶參構(gòu)造,創(chuàng)建一個默認緩沖區(qū)大小為8192的 字節(jié)緩存輸出流對象,底層使用輸出流 out;
- BufferedOutputStream(OutputStream out, int size):帶參構(gòu)造,創(chuàng)建一個指定size長度的緩沖區(qū)大小的 字節(jié)緩存輸入流對象,底層使用輸出流 out;
- void flushBuffer():刷新此字符緩沖區(qū);
- void write(int b):將字符數(shù)據(jù)b寫入緩沖輸出流;
- void write(byte b[], int off, int len):將字符數(shù)組中指定len個長度的數(shù)據(jù)寫入緩沖輸出流,從數(shù)組中的下標off開始;
- void flush():刷新此字符緩沖輸出流,將管道中的數(shù)據(jù)刷新到文件中。
//創(chuàng)建字節(jié)輸出流對象--作為緩沖流的節(jié)點流
FileOutputStream fos = new FileOutputStream("");
//創(chuàng)建緩沖字節(jié)流輸出對象
BufferedOutputStream bos = new BufferedOutputStream(fos);
//創(chuàng)建緩沖字節(jié)流輸出對象,且緩沖流自身的緩沖區(qū)大小為28字節(jié)
//BufferedOutputStream boss = new BufferedOutputStream(fos,128);
//寫出單個字節(jié)
bos.write(12);
//寫出字節(jié)數(shù)組
byte[] b1 = new byte[128];
bos.write(b1);
int writecount = 0;
//寫出字節(jié)數(shù)組的一部分
bos.write(b1,0,writecount);
//刷新緩沖流
bos.flush();
//關閉緩沖流
bos.close();
3.3 緩沖字符輸入流<BufferedReader>
- 概念
BufferedReader是從字符輸入流中讀取文本(不是直接從文件中),緩沖各個字符,從而實現(xiàn)字符、數(shù)組和行的高效讀取。 BufferedReader可以指定緩沖區(qū)的大小,或者使用默認大小。大多數(shù)情況下,默認值足夠大。 - 特點
緩沖指定文件的輸入。如果沒有緩沖,每次調(diào)用 read()或 readLine()方法都會導致從文件中讀取字節(jié),再將其轉(zhuǎn)換為字符后返回,是極其低效的。 - 字段
- Reader in:字符輸入流對象(節(jié)點流),用于BufferedReader構(gòu)造方法中的第一參數(shù);
- char cb[]:流本身自帶用于存儲 從字符輸入流中讀取的數(shù)據(jù)的字符數(shù)組;
- int defaultCharBufferSize = 8192:默認自身緩沖區(qū)的大小為8192字節(jié)。
- 常用方法
- BufferedReader(Reader in):創(chuàng)建一個默認大小輸入緩沖區(qū)的緩沖字符輸入流,需要一個節(jié)點流Reader作為對象;
- BufferedReader(Reader in, int sz):帶參構(gòu)造,創(chuàng)建一個指定sz大小輸入緩沖區(qū)的緩沖字符輸入流;
- int read():從字符輸入流中讀取單個字符;
- read(char cbuf[], int off, int len):將字符讀入到數(shù)組中的某一部分;
- String readLine(): 從字符輸入流中直接讀取一個文本行,遇到指定字符時某行終止(換行 '\n'、回車 '\r' 、回車后跟著的換行),該方法本身讀取不到換行符;
- long skip(long n):從字符輸出流中讀取時,跳過指定n個字符,從下標0開始;
- void close():關閉該緩沖流,釋放內(nèi)存。
3.4 緩沖字符輸出流<BufferedWriter>
- 概念
BufferedWriter將文本寫入字符輸出流,緩沖各個字符,從而提供單個字符、數(shù)組和字符串的高效寫入??梢灾付ň彌_區(qū)的大小,或接受默認的大小。大多數(shù)情況下,默認值足夠大。 - 特點
將緩沖 PrintWriter對文件的輸出。如果沒有緩沖,每次調(diào)用 print()方法都會將字符轉(zhuǎn)換為字節(jié),再寫入到文件,是極其低效的。 - 字段
- Writer out:字符輸出對象,用于BufferedWriter構(gòu)造方法中;
- char cb[]:用于存儲 從字符輸入流中讀取的數(shù)據(jù)的 字符數(shù)組;
- int defaultCharBufferSize = 8192:默認緩沖區(qū)大小8192字節(jié)。
- 常用方法
- BufferedWriter(Writer out):帶參構(gòu)造,創(chuàng)建一個使用默認大小輸出緩沖區(qū)的緩沖字符輸出流;
- BufferedWriter(Writer out, int sz):帶參構(gòu)造,創(chuàng)建一個使用給定大小輸出緩沖區(qū)的新緩沖字符輸出流;
- void ensureOpen():確認該輸出流打開(正在使用可輸出);
- void write(int c):寫入單個字符, c-是指定要寫入字符;
- void write(char cbuf[], int off, int len):將字符數(shù)組中的某一部分寫入流,從字符數(shù)組的下標off開始,寫入len個長度字符;
- void write(String s, int off, int len):將字符串的某一部分寫入流,從字符串的下標off開始,寫入len個長度字符;
- void newLine():往流中寫入一個行分隔符;
- void flush():刷新該流的緩沖,將管道中緩沖的數(shù)據(jù)刷新到文件中;
- void flushBuffer():刷新緩沖數(shù)據(jù);
- void close():關閉此流,關閉前要先使用flush()刷新管道中的緩沖數(shù)據(jù)。
四、轉(zhuǎn)換流
- 概念
轉(zhuǎn)換流就是將一種流的方法轉(zhuǎn)換成另一種流的方式。通常是字節(jié)流轉(zhuǎn)字符流(對比基本數(shù)據(jù)類型的轉(zhuǎn)換,小轉(zhuǎn)大可以自動轉(zhuǎn),大轉(zhuǎn)小會損失精度,單字節(jié)容納不下雙字節(jié))。 - 主要子類
- 字節(jié)轉(zhuǎn)字符輸入<InputStreamReader>:
- 字節(jié)轉(zhuǎn)字符輸出<OutputStreamWriter>:
- 用途
作為一個"包裝類",將字節(jié)流轉(zhuǎn)換為字符流,結(jié)合緩沖流的構(gòu)造方法中的節(jié)點流一起使用更合適。
4.1 字節(jié)轉(zhuǎn)字符輸入<InputStreamReader>
- 概念
InputStreamReader就是將字節(jié)流輸入流轉(zhuǎn)換成字符輸入流,由字面理解就是InputStream → Reader。它使用指定的字符編碼讀取字節(jié) 并將其解碼為字符,使用的字符集可以由名稱指定、顯式給定,或者接受平臺默認的字符集。 - 特點
- 字段
- StreamDecoder sd:流解碼器對象,用于處理字節(jié)轉(zhuǎn)字符期間的編碼和解碼問題。
- 常用方法
- InputStreamReader(InputStream in):帶參構(gòu)造,創(chuàng)建一個使用默認字符集的 字節(jié)轉(zhuǎn)字符輸入對象,底層使用字節(jié)輸入流對象in;
- InputStreamReader(InputStream in, String charsetName):帶參構(gòu)造,創(chuàng)建使用指定字符集的 字節(jié)轉(zhuǎn)字符輸入對象;
- InputStreamReader(InputStream in, Charset cs):帶參構(gòu)造,創(chuàng)建使用給定字符集的 字節(jié)轉(zhuǎn)字符輸入對象;
- InputStreamReader(InputStream in, CharsetDecoder dec):帶參構(gòu)造,創(chuàng)建使用給定字符集解碼器的 字節(jié)轉(zhuǎn)字符輸入對象;
- boolean ready():判斷此流是否已經(jīng)準備好用于讀取,讀取前判斷再使用;
- int read():讀取單個字符,如果已到達流的末尾,則返回 -1 ;
- int read(char cbuf[], int offset, int length):將字符讀入到數(shù)組中的某一部分,從數(shù)組下標offset開始存儲,讀取length個長度;
- void close():關閉該流并釋放與之關聯(lián)的所有資源。
- 代碼示例
//創(chuàng)建字符輸入流
FileReader fr = new FileReader("");
//創(chuàng)建字節(jié)緩沖流--內(nèi)部參數(shù)為字符流對象
BufferedReader br1 = new BufferedReader(fr);
//創(chuàng)建字節(jié)輸入流
FileInputStream fio = new FileInputStream("");
//將字節(jié)輸入流fio 轉(zhuǎn)成 字符輸入流
InputStreamReader isr = new InputStreamReader(fio);
//創(chuàng)建字節(jié)緩沖流--內(nèi)部參數(shù)為字符流對象
BufferedReader br = new BufferedReader(isr);
//合并-字節(jié)流--字節(jié)轉(zhuǎn)字符流--字符緩沖流(字符流對象參數(shù))
BufferedReader bfr = new BufferedReader(new InputStreamReader(new FileInputStream("")));
bfr = new BufferedReader(new InputStreamReader(System.in));
String s = null;
while ((s=br.readLine()) != null) {
System.out.println(s);
//讀取到字母 q時退出循環(huán)
if ("q".equals(s)) {
break;
}
}
if (bfr != null) {
bfr.close();
}
4.2 字節(jié)轉(zhuǎn)字符輸出<OutputStreamWriter>
- 概念
OutputStreamWriter就是將字節(jié)流輸出流轉(zhuǎn)換成字符輸出流,由字面理解就是OutputStream → Writer。它使用指定的字符編碼將 要寫入流中的字符編碼成字節(jié),使用的字符集可以由名稱指定、顯式給定,否則接受平臺默認的字符集。 - 特點
- 字段
- StreamDecoder sd:流解碼器對象,用于處理字節(jié)轉(zhuǎn)字符期間的編碼和解碼問題。
- 常用方法
- OutputStreamWriter(OutputStream out):帶參構(gòu)造,創(chuàng)建使用默認字符編碼的 字節(jié)轉(zhuǎn)字符輸出對象;
- OutputStreamWriter(OutputStream out, String charsetName):帶參構(gòu)造,創(chuàng)建使用指定字符集的 字節(jié)轉(zhuǎn)字符輸出對象;
- OutputStreamWriter(OutputStream out, Charset cs):帶參構(gòu)造,
創(chuàng)建使用給定字符集的 字節(jié)轉(zhuǎn)字符輸出對象; - OutputStreamWriter(OutputStream out, CharsetEncoder enc):帶參構(gòu)造, 創(chuàng)建使用給定字符集編碼器的 字節(jié)轉(zhuǎn)字符輸出對象;
- write(int c):寫入單個字符;
- write(char cbuf[], int off, int len):將字符數(shù)組中的某一部分寫入流,從數(shù)組的下標off開始,寫入len個長度;
- void write(String str, int off, int len):將字符串中的某一部分寫入流,從字符串的下標off開始,寫入len個長度;
- void flushBuffer():刷新此流緩沖區(qū);
- void flush():刷新該流的緩沖,將管道中緩沖的數(shù)據(jù)全都寫入文件;
- void close():關閉此流,但要先flush()刷新它。
//創(chuàng)建字節(jié)輸出流對象
FileOutputStream fos = new FileOutputStream("");
//將字節(jié)輸出流--轉(zhuǎn)換--字符輸出流
OutputStreamWriter osw = new OutputStreamWriter(fos);
//創(chuàng)建字節(jié)輸出流對象
FileOutputStream fous = new FileOutputStream("");
//將字節(jié)輸出流--轉(zhuǎn)換--字符輸出流
OutputStreamWriter os = new OutputStreamWriter(fous);
//將字符輸出流--轉(zhuǎn)換--緩沖字符輸出流
BufferedWriter bw = new BufferedWriter(os);
//合并-字節(jié)輸出流--字符輸出流--緩沖字符輸出流()
BufferedWriter buw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("")));
buw.flush();
五、打印流
- 概念
打印流即從程序到內(nèi)存輸出的流,由java程序向控制臺打印輸出,即完成對屏幕打印的重定向。 - 特點
System.out 就是打印輸出,默認輸出到控制臺。而打印流 PrintStream可以重定向輸出到文件,System.out.println不再輸出到屏幕,而是輸出到指定文件。 - 分類
- 打印字節(jié)流<PrintStream>
- 打印字符流<PrintWriter>
- 輸出重定向原理
System.out默認是PrintStream向控制臺輸出;
PrintStream 構(gòu)造時,使用指定輸出路徑的輸出流對象作為參數(shù),導致PrintStream的輸出不再默認,而是向指定輸出路徑輸出,從而完成輸出重定向。但前提也需要System.setOut才可以修改輸出方向。
- System.setOut(new PrintStream(new OutPutStream("指定路徑")));
5.1 打印字節(jié)流<PrintStream,掌握>
- 概念
PrintStream構(gòu)造器底層需要一個輸出流作為參數(shù)(節(jié)點流),因此是其他輸出流的擴展,為其他輸出流添加更多功能。PrintStream默認完成由java程序向控制臺打印輸出,即完成對屏幕打印的重定向。 - 特點
- PrintStream永遠不會拋出 IOException;
- PrintStream在寫入 byte數(shù)組之后自動調(diào)用 flush()方法完成刷新;
- 調(diào)用println()方法,表示寫入一個換行符或字節(jié) ('\n');
- PrintStream不需要close()關閉;
- 特點
System.out就是 PrintStream,System.out默認輸出到控制臺。
而當打印流PrintStream使用 指定路徑的輸出流作為節(jié)點流參數(shù) 來構(gòu)造時,可以重定向輸出到文件,System.out.println不再輸出到屏幕,而是輸出到指定文件。 - 字段
- boolean autoFlush:自動刷新標志;
- BufferedWriter textOut:字符輸出對象;
- OutputStreamWriter charOut字節(jié)轉(zhuǎn)字符輸出對象;
- Formatter formatter:格式化程序,對輸出流中的數(shù)據(jù)格式化;
- 常用方法
- void flush():刷新該流的緩沖,通過將所有緩沖的輸出字節(jié)寫入到底層輸出流;
- void close():關閉流,通過刷新流然后關閉底層輸出流完成此操作;
- void write(int b):將指定的字節(jié)b寫入此流,如果字節(jié)為新行 且啟用了自動刷新,則調(diào)用 flush 方法;
- void write(byte[] buf,int off, int len):將byte 數(shù)組的 len長度個字節(jié)從指定下標 off開始寫入此流。如果啟用自動刷新,則調(diào)用 flush 方法;
- void print(Object obj):打印數(shù)據(jù),按照平臺的默認字符編碼將 String.valueOf(obj)生成的字符串轉(zhuǎn)換為字節(jié),并完全以write(int)方法的方式寫入這些字節(jié);
- void println():通過寫入行分隔符字符串終止當前行,行分隔符字符串由系統(tǒng)屬性line.separator 定義,不一定是單個換行符 '\n';
- PrintStream format(String format, Object args):使用指定格式字符串和參數(shù) 將格式化字符串寫入此輸出流中;
- PrintStream append(CharSequence csq):將指定字符序列csq添加到此輸出流;
- PrintStream append(char c):將指定字符c添加到此輸出流。
- 代碼示例
//創(chuàng)建字節(jié)輸出流引用
FileOutputStream os = null;
try {
System.out.println("asdfkjfd;lldffdfdrerere");//未改變輸出方向,默認向控制臺輸出
//創(chuàng)建具體的輸出流對象
os = new FileOutputStream("c:/console.txt");
//創(chuàng)建標準輸出流對象--采用節(jié)點流參數(shù),將指定文件輸出的方向
PrintStream ps = new PrintStream(os);
//PrintStream ps = System.out;
System.setOut(ps);//改變輸出方向
//System.setOut(new PrintStream(os));//合并--改變輸出方向
System.out.println("asdfkjfd;lldffdfdrerere");//向指定輸出流的文件console.txt輸出
}catch(FileNotFoundException e) {
e.printStackTrace();
}catch(IOException e) {
e.printStackTrace();
}finally { //關閉流資源,釋放內(nèi)存
try {
if (os != null) {
os.close();
}
}catch(IOException e) {}
}
5.2 打印字符流<PrintWriter>
- 概念
打印輸入流,相當于System.in,完成 接收屏幕輸入。 - 特點
- 字段
-Writer out:字符輸出流對象,構(gòu)造器時使用;
- boolean autoFlush:自動刷新標志;
- Formatter formatter:格式化對象,對輸出流中的數(shù)據(jù)格式化;
- PrintStream psOut = null:字節(jié)打印流對象;
- String lineSeparator:行分隔符,用于print()方法中進行行分隔的標志;
- 常用方法
- PrintWriter (Writer out):帶參構(gòu)造,創(chuàng)建一個不帶自動行刷新的字符打印流對象,底層調(diào)用字節(jié)輸出流對象;
- void flush():刷新該流的緩沖,底層重寫了Flushable接口和Writer類的方法;
- void close():關閉該流并釋放與之關聯(lián)的所有系統(tǒng)資源;
- void write(int c):寫入單個字符c;
- void write(char buf[], int off, int len):寫入字符數(shù)組的某一部分,從數(shù)組的下標off開始,寫入len個長度;
- void write(char buf[]):寫入字符數(shù)組,此方法不能從 Writer類繼承,因為它必須取消 I/O 異常;
- void write(String s, int off, int len):寫入字符串的某一部分;
- void newLine():往流中寫入一個行分隔符;
- void print(Object obj):打印數(shù)據(jù),按照平臺的默認字符編碼將 String.valueOf(obj)生成的字符串轉(zhuǎn)換為字節(jié),并完全以write(int)方法的方式寫入這些字節(jié);
- void println():通過寫入行分隔符字符串終止當前行,底層調(diào)用newLine()方法;
- PrintWriter append(CharSequence csq):將指定字符序列csq添加到此輸出流;
- PrintWriter append(char c):將指定字符c添加到此輸出流。
六、對象流<掌握>
- 概念
對象流是可以將 Java對象 轉(zhuǎn)換成二進制進行文件操作的流,通常對象流的輸入和輸出是以字節(jié)單位進行的。 - 分類
- 對象字節(jié)輸入流<ObjectInputStream>
- 對象字節(jié)輸出流<ObjectOutputStream>
- 常用方法
- writeObject(Object obj):向硬盤下的指定文件打印輸出該obj對象;
- readObject():從硬盤指定的文件中讀取出對象到內(nèi)存中。
6.1 對象字節(jié)輸入流<ObjectInputStream,掌握>
- 概念
ObjectInputStream類是對 使用了 ObjectOutputStream類寫入的基本數(shù)據(jù)和對象進行反序列化的對象輸入流,從磁盤中將對象反序列化到內(nèi)存中。 - 特點
- 字段
- 常用方法
- 代碼示例
//創(chuàng)建文件輸入流引用
FileInputStream fis = null;
//創(chuàng)建對象輸入流引用
ObjectInputStream ois = null;
//創(chuàng)建實際的文件輸入流對象--作為節(jié)點流參數(shù)
fis = new FileInputStream("students");
//創(chuàng)建實際的對象輸入流對象--完成反序列化的流
ois = new ObjectInputStream(fis);
//將文件中的對象反序列化到內(nèi)存中--返回的是Object類型對象
Object o = ois.readObject();
//將反序列化的對象進行類型強轉(zhuǎn)
Student st = (Student)o;
//獲取反序列對象的各個屬性
System.out.println(st.getName());
System.out.println(st.getAge());
if (ois != null){
ois.close();
}
if (fis != null){
fis.close();
}
}
6.2 對象字節(jié)輸出流<ObjectOutputStream,掌握>
- 概念
ObjectOutputStream是一個用于將java對象分段寫入(序列化)到硬盤中的對象輸出流。 - 特點
- static class Caches:靜態(tài)內(nèi)部類--緩存,使用HashMap存儲數(shù)據(jù),使用ReferenceQueue參考隊列存儲消息。
- 字段
- 常用方法
- 代碼示例
//創(chuàng)建需要進行序列化的java對象
Student stu = new Student("zhangsan",23);
//創(chuàng)建文件輸出流引用--作為節(jié)點參數(shù)
FileOutputStream fos = null;
//創(chuàng)建對象輸出流引用--將java序列化寫出的流
ObjectOutputStream oos = null;
//創(chuàng)建實際的文件輸出流對象--指定輸出路徑
fos = new FileOutputStream("stu");
//創(chuàng)建實際的對象輸出流對象,使用節(jié)點流參數(shù)
oos = new ObjectOutputStream(fos);
//將java對象序列化寫出
oos.writeObject(stu);
//刷新輸出流
oos.flush();
//關閉流資源
if (fos != null){
fos.close();
}
if (oos != null){
oos.close();
}
}
6.3 序列化與反序列化<Serilizable接口>
- 概念
Serilizable接口是一個序列化接口,是標記接口,該接口內(nèi)沒有任何方法。實現(xiàn)該接口只是作為一個標記給JVM看,然后JVM認為實現(xiàn)了該接口的類型對象可以進行序列化,因此該類的所有屬性和方法都會自動序列化,不必關心具體序列化的過程。 -
圖解
對象序列化與反序列化圖解.png - 前提
需要進行序列化或反序列化的對象必須實現(xiàn)Serilizable接口。
//必須實現(xiàn)Serilizable接口作為JVM認可的序列化標記
public class Student implements Serializable {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
//提供get()和set()方法,并重寫toString()方法
}
6.3.1 序列化
- 概念
對象流將 Java對象轉(zhuǎn)換成二進制 寫入磁盤的過程叫做序列化,底層采用輸出流作為節(jié)點流參數(shù)。 - 特點
每一個java對象都會是較大的文件,因此序列化時,對象輸出流會將Java對象進行切割分段,再寫入到硬盤文件當中。 - 代碼示例
//創(chuàng)建需要序列化的java對象
Student stu = new Student("haha",21);
//創(chuàng)建對象輸出流引用--序列化使用的寫出流
ObjectOutputStream obj = null;
try {
//創(chuàng)建實際的對象輸出流對象--采用文件輸出流作為參數(shù),指定序列化路徑
obj = new ObjectOutputStream(new FileOutputStream("students"));
//將需要序列化的java對象寫出序列化到硬盤中
obj.writeObject(stu);
} catch (IOException e) {
e.printStackTrace();
}finally {
if (obj != null){
try {
obj.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 注意點
- 序列化多個對象:當需要同時序列化多個對象時,提前將多個對象存儲到數(shù)組或集合當中<建議使用泛型>,在序列化時ObjectOutputStream.writeObject(集合對象)即可一次序列化多個對象。采用集合序列化多個對象到硬盤中的文件,存儲的也是一個集合對象,而不是散開的單個泛型對象。
- 若想采用每次序列化單個對象的方式逐次序列化多個對象,那么會在序列化到第二個對象時出現(xiàn)錯誤。
6.3.2 反序列化
- 概念
對象流從磁盤讀出完整 Java 對象到內(nèi)存中的過程叫做反序列化,底層采用輸入流作為節(jié)點流參數(shù)。 - 代碼示例
//創(chuàng)建反序列化的對象輸入流引用
ObjectInputStream ois = null;
try {
//創(chuàng)建實際的對象輸入流對象,使用文件輸入流作為參數(shù)
ois = new ObjectInputStream( new FileInputStream("c:/Person.dat"));
//反序列化,將對象從磁盤讀出,并轉(zhuǎn)化為對應類型的對象
Person person = (Person)ois.readObject();
//獲取反序列化后對象的屬性
System.out.println(person.name);
}catch(ClassNotFoundException e) {
e.printStackTrace();
}catch(FileNotFoundException e) {
e.printStackTrace();
}catch(IOException e) {
e.printStackTrace();
}finally { //關閉流,釋放資源
try {
if (ois != null) {
ois.close();
}
}catch(IOException e) {}
}
-
注意點
反序列化多個對象:若序列化文件中的對象是集合對象(即使集合里面存儲了多個泛型對象),那么反序列化時 ObjectInputStream.readObject()讀取返回的obj也是一個集合對象,因此返回時可以采用集合引用<建議使用泛型>來接收返回的集合對象。再對返回的集合對象進行迭代遍歷和獲取集合元素(單個對象)的信息。
6.4 transient關鍵字
- 概念
transient意為游離的。在一個需要序列化的類中,明確有些屬性需要序列化,而其他屬性不需要被序列化時,使用transient關鍵字修飾該屬性將不會被序列化。 - 作用
只需要實現(xiàn)Serilizable接口,將不需要序列化的屬性添加關鍵字transient修飾,序列化對象的時候,該屬性就不會序列化。 - 使用場景
如一個用戶類有一些敏感信息<密碼,銀行卡號>,為了安全起見,不希望在網(wǎng)絡操作中被傳輸(被序列化,包括本地序列化緩存)。因此在該屬性上加上 transient關鍵字,以免被序列化。即transient修飾的字段的生命周期僅存于調(diào)用者的內(nèi)存中,而不會寫到磁盤里持久化。
class Person implements Serializable{
String name;
transient String password;//采用 transient 關鍵字修飾此屬性,序列化時會忽略<即不會被序列化>
}
6.5 serialVersionUID 屬性
- 概念
serialVersionUID 意為序列化版本號,序列化的對象在存儲時都有一個標識版本號,使用該屬性修飾的類保證了序列化版本號唯一。 - 特點
- serialVersionUID屬性使用public static final修飾;
- 若不使用序列化版本號,在多次對原類修改添加后進行序列化時,會產(chǎn)生多個版本不一樣的對象,反序列化時也會不清楚到底反序列化哪一個版本;
- 實現(xiàn)Serilizable接口時,系統(tǒng)默認提供序列化版本號,但是強烈建議程序員自定義類時手動添加寫上。
- 作用
- java驗證類唯一性的標志之一:java的序列化機制是通過判斷類的序列化ID號來驗證 序列化對象版本一致性的;
- 保證序列化與反序列化的文件相同且唯一:擁有serialVersionUID屬性修飾的類,即使其實例對象在進行一次序列化,之后無論對該對象進行多少次添加或修改,重新對"新對象"序列化時,得到的還是同一個對象;
- 防止序列化兼容問題:解決序列化時對象不唯一問題,對象修改后也不出現(xiàn)序列化的版本問題,而是擁有統(tǒng)一的版本號。
-
圖解serialVersionUID
serialVersionUID圖解.png - 使用方式
class Person implements Serializable{
//加入版本號,防止序列化兼容問題,解決序列化時不一致
private static final long serialVersionUID = -11111111L;
String name;
int age;
boolean sex;
}
- IDEA生成序列化版本號
File --> Settings --> Editor --> Inspections -->搜索serializable --> Serializable Class without serialVersionUID --> 打鉤 --> ok --> 對實現(xiàn)了Serializable接口的類 atl+回車 --> Add serialVersionUID
七、數(shù)據(jù)流<DataStream>
- 概念
數(shù)據(jù)流既是數(shù)據(jù)專屬的流,用于讀取文件中的數(shù)據(jù)及其數(shù)據(jù)類型,或用于寫出數(shù)據(jù)及其數(shù)據(jù)類型,數(shù)據(jù)流讀取保存或?qū)懭肷傻奈募皇瞧胀ㄎ谋疚募荒苤苯哟蜷_。 - 分類
- DataOutputStream:數(shù)據(jù)輸出流,可以將數(shù)據(jù)連同數(shù)據(jù)的類型一并寫入文件,寫出的文件不是普通文本文件;
- DataInputStream:數(shù)據(jù)輸入流,對于DataOutputStream寫出的文件,只能使用DataInputStream流去讀,并且讀取時需要提前知道寫入時的數(shù)據(jù)順序。讀的順序需要和寫的順序一致。才可以正常取出數(shù)據(jù)。
- 作用特點
- 安全性:使用DataOutputStream寫出的數(shù)據(jù)連帶其數(shù)據(jù)類型,更加安全易讀;
- 加密性:對于DataOutputStream寫出的文件必須要DataInputStream流去讀,否則亂碼,更加安全加密。
- DataOutputStream
FileOutputStream fos = new FileOutputStream("data");
// 創(chuàng)建數(shù)據(jù)專屬的字節(jié)輸出流--使用節(jié)點流
DataOutputStream dos = new DataOutputStream(fos);
//合并--
//DataOutputStream dos = new DataOutputStream(new FileOutputStream("data"));
// 寫數(shù)據(jù)
byte b = 100;
short s = 200;
int i = 300;
long l = 400L;
float f = 3.0F;
double d = 3.14;
boolean sex = false;
char c = 'a';
// 把數(shù)據(jù)以及數(shù)據(jù)的類型一并寫入到文件當中。
dos.writeByte(b);
dos.writeShort(s);
dos.writeInt(i);
dos.writeLong(l);
dos.writeFloat(f);
dos.writeDouble(d);
dos.writeBoolean(sex);
dos.writeChar(c);
// 寫出完成時,刷新管道
dos.flush();
// 關閉外層流(包裝流)
dos.close();
- DataInputStream
FileInputStream fis = new FileInputStream("data");
// 創(chuàng)建數(shù)據(jù)專屬的字節(jié)輸入流--使用節(jié)點流
DataInputStream dis = new DataInputStream(fis);
//合并--
DataInputStream dis = new DataInputStream(new FileInputStream("data"));
// 開始讀取DataOutputStream寫出的文件--前提知道寫出的數(shù)據(jù)順序
byte b = dis.readByte();
short s = dis.readShort();
int i = dis.readInt();
long l = dis.readLong();
float f = dis.readFloat();
double d = dis.readDouble();
boolean sex = dis.readBoolean();
char c = dis.readChar();
//關閉流資源
dis.close();
八、File 類
- 概念
File 類是文件和目錄路徑名的抽象表示形式,指的是一類可以直接對文件進行操作的類(不管是硬盤上的還是內(nèi)存緩存中的)。 - 特點
File類不是具體的文件,只是一條路徑,可以是目錄也可以是具體文件所在目錄,底層封裝了文件的路徑和路徑狀態(tài)。 - 字段
- String path:文件的路徑,可以是根目錄或絕對路徑;
- PathStatus status = null:路徑的狀態(tài),
- enum PathStatus { INVALID, CHECKED }:
- int prefixLength:文件前綴長度;
- char separatorChar = fs.getSeparator():文件分隔符。
- 常用方法
- File(String pathname):構(gòu)造方法,根據(jù)指定路徑創(chuàng)建一個File文件類對象;
- File(String parent, String child):構(gòu)造方法,
- String getName():
- booLean exists():判斷 根據(jù)指定路徑創(chuàng)建的文件類對象是否存在,即在指定路徑下是否能找到對應文件;
- boolean createNewFile():若指定路徑下的文件類對象不存在,則在該路徑下以文件形式創(chuàng)建相應的文件;
- boolean mkdir():若指定路徑下的文件類對象不存在,則在該路徑下以目錄形式創(chuàng)建相應的目錄文件;
- boolean mkdirs():若指定路徑下的文件類對象不存在,則在該路徑下以多級目錄形式創(chuàng)建相應的目錄文件;
- String getParent():獲取此路徑下創(chuàng)建的文件類對象的文件父目錄;
- File getParentFile():獲取此路徑下創(chuàng)建的文件類對象的文件父目錄,并根據(jù)該目錄生成新的文件類對象;
- String getPath():獲取該文件類對象的原文件路徑;
- String getAbsolutePath():獲取該文件類對象的文件絕對路徑;
- boolean isDirectory():判斷該文件類對象是否是一個目錄;
- boolean isFile():判斷該文件類對象是否是一個文件;
- boolean isHidden():判斷該文件類對象是否是一個隱藏文件;
- long lastModified(): 獲取該文件類對象的文件最后一次的修改時間,返回的是自1970年1月1日開始的毫秒數(shù);
- long length():獲取該文件類對象的文件大??;
- File[] listFiles():獲取當前目錄下所有的子文件,返回的是由多級子目錄構(gòu)成的文件數(shù)組,可通過foreach循環(huán)遍歷該數(shù)組得到所有的子文件。
- 代碼實例
//根據(jù)指定路徑創(chuàng)建一個文件類對象
File f = new File("");
// 判斷該路徑下的創(chuàng)建的文件類對象 (文件)是否存在
System.out.println(f.exists());
// 如果路徑下文件不存在,則以文件的形式創(chuàng)建出來
/*if(!f1.exists()) {
// 以文件形式新建
f1.createNewFile();
}*/
// 如果路徑下文件不存在,則以單級目錄的形式創(chuàng)建出來
/*if(!f1.exists()) {
// 以目錄的形式新建。
f1.mkdir();
}*/
// 如果路徑下文件不存在,則以多級目錄的形式創(chuàng)建出來
File f2 = new File("D:/a/b/c/d/e/f");
/*if(!f2.exists()) {
// 以多重目錄的形式新建。
f2.mkdirs();
}*/
File f3 = new File("D:\\course\\01-開課\\學習方法.txt");
// 獲取文件的父路徑
String parentPath = f3.getParent();
// 獲取文件的父路徑文件
File parentFile = f3.getParentFile();
//獲取文件的絕對路徑
System.out.println("獲取絕對路徑:" + parentFile.getAbsolutePath());
常見面試題
- 使用字節(jié)文件流實現(xiàn)一個文件的復制?
答:
創(chuàng)建字節(jié)輸入流對象FileInputStream;
創(chuàng)建字節(jié)輸出流對象FileOutPutStream;
輸入流對象從指定路徑下read()讀取文件,并將讀取的內(nèi)容使用字節(jié)數(shù)組byte[]存儲;
使用變量返回值判斷文件是否有內(nèi)容可讀;
輸出流對象將輸入流讀取的字節(jié)數(shù)組中的內(nèi)容write()寫出;
刷新輸出流;
關閉所有流,釋放資源;
重點:一邊讀,一邊寫,每次讀取多少個就寫出多少個。
public class StreamCopyTest {
public static void main(String[] args) {
//創(chuàng)建字節(jié)輸入流引用
FileInputStream fis = null;
//創(chuàng)建字節(jié)輸出流引用
FileOutputStream fos = null;
try {
//創(chuàng)建實際的字節(jié)輸入流對象
fis = new FileInputStream("");
//創(chuàng)建實際的字節(jié)輸出流對象--輸出文件若存在則覆蓋,不存在則新建
fos = new FileOutputStream("");
//創(chuàng)建字節(jié)數(shù)組存儲輸入流讀取的數(shù)據(jù)
byte[] bs = new byte[1024 * 1024];//1024kb = 1byte,1mb = 1024kb
//用于計算每次讀取文件返回的字節(jié)個數(shù),-1表示已讀到文件末尾
int readcount = 0;
//一邊讀取,一邊輸出,讀取多少個,輸出多少個
while ((readcount = fis.read(bs)) != -1){
fos.write(bs,0,readcount);
}
//輸出完成時,必須刷新管道
fos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
//關閉流資源,釋放內(nèi)存
if (fis != null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fos != null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
- 使用字符文件流實現(xiàn)一個文件的復制?
答:
創(chuàng)建字符輸入流對象FileReader
創(chuàng)建字符輸出流對象FileWriter
創(chuàng)建字符數(shù)組char[]存儲字符輸入流read讀取的文件數(shù)據(jù)
定義變量返回值 判斷文件是否有數(shù)據(jù)可讀
字符輸入流從文件讀取數(shù)據(jù)
字符輸出流將讀取到的數(shù)據(jù)寫出到文件
刷新輸出流
關閉所有資源
public class WriterCopyTest {
public static void main(String[] args) {
//創(chuàng)建字符輸入流引用
FileReader fr = null;
//創(chuàng)建字符輸出流引用
FileWriter fw = null;
try {
//創(chuàng)建實際的字符輸入流對象
fr = new FileReader("");
//創(chuàng)建實際的字符輸出流對象,若輸出文件已存在則覆蓋,若不存在則新建
fw = new FileWriter("");
//創(chuàng)建字符數(shù)組存儲輸入流每一次讀取的數(shù)據(jù)
char[] cs = new char[1024*1024];
//用于判斷每一次讀取文件返回的字節(jié)個數(shù),-1表示已讀到文件末尾
int readchar = 0;
//一邊讀取,一邊寫出,讀取多少個寫出多少個
while ((readchar = fr.read(cs)) != -1){
fw.write(cs,0,readchar);
}
//輸出完成時,必須刷新管道
fw.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
//關閉流資源,釋放內(nèi)存
if (fr != null){
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fw != null){
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
- 使用一個字節(jié)緩沖流 或 字符緩沖流實現(xiàn)一個文件的復制?
答:
BufferInputStream和BufferOutPutStream
BufferReader和BufferWriter
- 實現(xiàn)從D盤的多個目錄及文件復制到C盤下?
答:
使用File類獲取得到多個目錄(數(shù)組)
遍歷File數(shù)組中得到的所有File對象
判斷File數(shù)組中的File對象是目錄還是文件
(是目錄繼續(xù)往下遍歷,是文件則直接復制到另外一個盤下)
確定源目錄的路徑 和 目標目錄的路徑
對File對象采用IO流進行邊讀邊寫
如何確定對應的目錄路徑?
public static void main(String[] args) {
// 拷貝源
File srcFile = new File("D:\\course\\02-JavaSE\\document");
// 拷貝目標
File destFile = new File("C:\\a\\b\\c");
// 調(diào)用方法完成拷貝
copyDir(srcFile, destFile);
}
/**
* 拷貝目錄
* @param srcFile 拷貝源
* @param destFile 拷貝目標
*/
private static void copyDir(File srcFile, File destFile) {
if(srcFile.isFile()) {
// srcFile如果是一個文件(文件下面不再有目錄),遞歸結(jié)束
// 并且是文件即需要拷貝,復制--一邊讀一邊寫
FileInputStream in = null;
FileOutputStream out = null;
try {
// 從源路徑讀取文件
in = new FileInputStream(srcFile);
// 處理得出要復制到的目標路徑--路徑的轉(zhuǎn)換很難很重要,需要多重字符串截取和拼接
//先獲取目標路徑的盤符根目錄--再對源路徑盤符下的子目錄路徑進行復制拼接
String path = (destFile.getAbsolutePath().endsWith("\\") ? destFile.getAbsolutePath() : destFile.getAbsolutePath() + "\\") + srcFile.getAbsolutePath().substring(3);
//將讀取的文件寫入到新的盤符目錄下
out = new FileOutputStream(path);
// 一邊讀一邊寫,
byte[] bytes = new byte[1024 * 1024]; // 一次復制1MB
int readCount = 0;
while((readCount = in.read(bytes)) != -1){
out.write(bytes, 0, readCount);
}
//寫入完成時刷新管道
out.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally{ //關閉流資源,釋放內(nèi)存
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return;
}
// 獲取源路徑下的所有子目錄(文件)
File[] files = srcFile.listFiles();
for(File file : files){
// 獲取所有文件(包括目錄和文件)的絕對路徑
//若是源路徑下的File是一個目錄,則在目標盤符下創(chuàng)建對應目錄;若是一個File文件,則結(jié)束遞歸開始復制
if(file.isDirectory()){
// 在目標盤符下新建對應的目錄
//D:\course\02-JavaSE\document\JavaSE進階講義 源目錄
//C:\course\02-JavaSE\document\JavaSE進階講義 目標目錄
//源file目錄
String srcDir = file.getAbsolutePath();
//新file要復制到的新目錄
String destDir = (destFile.getAbsolutePath().endsWith("\\") ? destFile.getAbsolutePath() : destFile.getAbsolutePath() + "\\") + srcDir.substring(3);
//使用新目錄創(chuàng)建出File文件對象
File newFile = new File(destDir);
if(!newFile.exists()){
newFile.mkdirs();
}
}
// 遞歸調(diào)用--很難想到使用遞歸
copyDir(file, destFile);
}
}








