java IO流學習總結
近期學習了Java的IO流,嘗試着總結一下。
java.io 包下的IO流很多:
其中,以Stream結尾的為字節流,以Writer或者Reader結尾的為字符流。所有的輸入流都是抽象類IuputStream(字節輸入流)或者抽象類Reader(字符輸入流)的子類,所有的輸出流都是抽象類OutputStream(字節輸出流)或者抽象類Writer(字符輸出流)的子類。字符流能實現的功能字節流都能實現,反之不一定。如:圖片,視頻等二進制文件,只能使用字節流讀寫。
1、字符流FileReader和FileWriter
FileReader類
構造方法摘要 | |
---|---|
FileReader(File file) 在給定從中讀取數據的 File 的情況下創建一個新 FileReader。 |
|
FileReader(FileDescriptor fd) 在給定從中讀取數據的 FileDescriptor 的情況下創建一個新 FileReader。 |
|
FileReader(String fileName) 在給定從中讀取數據的文件名的情況下創建一個新 FileReader。 |
FileWriter類
構造方法摘要 | |
---|---|
FileWriter(File file) 根據給定的 File 對象構造一個 FileWriter 對象。 |
|
FileWriter(File file, boolean append) 根據給定的 File 對象構造一個 FileWriter 對象。 |
|
FileWriter(FileDescriptor fd) 構造與某個文件描述符相關聯的 FileWriter 對象。 |
|
FileWriter(String fileName) 根據給定的文件名構造一個 FileWriter 對象。 |
|
FileWriter(String fileName, boolean append) 根據給定的文件名以及指示是否附加寫入數據的 boolean 值來構造 FileWriter 對象。 |
使用FileReader和FileWriter類完成文本文件復制:

1 import java.io.FileReader; 2 import java.io.FileWriter; 3 import java.io.IOException; 4 5 public class CopyFile { 6 public static void main(String[] args) throws IOException { 7 //創建輸入流對象 8 FileReader fr=new FileReader("C:\\Test\\copyfrom.txt");//文件不存在會拋出java.io.FileNotFoundException 9 //創建輸出流對象 10 FileWriter fw=new FileWriter("C:\\Test\\copyto.txt"); 11 /*創建輸出流做的工作: 12 * 1、調用系統資源創建了一個文件 13 * 2、創建輸出流對象 14 * 3、把輸出流對象指向文件 15 * */ 16 //文本文件復制,一次讀一個字符 17 method1(fr, fw); 18 //文本文件復制,一次讀一個字符數組 19 method2(fr, fw); 20 21 fr.close(); 22 fw.close(); 23 } 24 25 public static void method1(FileReader fr, FileWriter fw) throws IOException { 26 int ch; 27 while((ch=fr.read())!=-1) {//讀數據 28 fw.write(ch);//寫數據 29 } 30 fw.flush(); 31 } 32 33 public static void method2(FileReader fr, FileWriter fw) throws IOException { 34 char chs[]=new char[1024]; 35 int len=0; 36 while((len=fr.read(chs))!=-1) {//讀數據 37 fw.write(chs,0,len);//寫數據 38 } 39 fw.flush(); 40 } 41 }
2、字符緩沖流BufferedReader和BufferedWriter
字符緩沖流具備文本特有的表現形式,行操作
public class BufferedReader extends Reader
(1)從字符輸入流中讀取文本,緩沖各個字符,從而實現字符、數組和行的高效讀取。
(2)可以指定緩沖區的大小,或者可使用默認的大小。大多數情況下,默認值就足夠大了。
(3)通常,Reader 所作的每個讀取請求都會導致對底層字符或字節流進行相應的讀取請求。因此,建議用 BufferedReader 包裝所有其 read() 操作可能開銷很高的 Reader(如 FileReader 和 InputStreamReader)。例如,
BufferedReader in
= new BufferedReader(new FileReader("foo.in"));
(4)將緩沖指定文件的輸入。如果沒有緩沖,則每次調用 read() 或 readLine() 都會導致從文件中讀取字節,並將其轉換為字符后返回,而這是極其低效的。
public class BufferedWriter extends Writer
(1)將文本寫入字符輸出流,緩沖各個字符,從而提供單個字符、數組和字符串的高效寫入。
(2)可以指定緩沖區的大小,或者接受默認的大小。在大多數情況下,默認值就足夠大了。
(3)該類提供了 newLine() 方法,它使用平台自己的行分隔符概念,此概念由系統屬性 line.separator 定義。並非所有平台都使用新行符 ('\n') 來終止各行。因此調用此方法來終止每個輸出行要優於直接寫入新行符。
(4)通常 Writer 將其輸出立即發送到底層字符或字節流。除非要求提示輸出,否則建議用 BufferedWriter 包裝所有其 write() 操作可能開銷很高的 Writer(如 FileWriters 和 OutputStreamWriters)。例如,
PrintWriter out
= new PrintWriter(new BufferedWriter(new FileWriter("foo.out")));
(5)緩沖 PrintWriter 對文件的輸出。如果沒有緩沖,則每次調用 print() 方法會導致將字符轉換為字節,然后立即寫入到文件,而這是極其低效的。
使用BufferedReader和BufferedWriter完成文件復制

1 import java.io.BufferedReader; 2 import java.io.BufferedWriter; 3 import java.io.FileReader; 4 import java.io.FileWriter; 5 import java.io.IOException; 6 7 public class CopyFile2 { 8 public static void main(String[] args) throws IOException { 9 //創建輸入流對象 10 BufferedReader br=new BufferedReader(new FileReader("C:\\Test\\copyfrom.txt"));//文件不存在會拋出java.io.FileNotFoundException 11 //創建輸出流對象 12 BufferedWriter bw=new BufferedWriter(new FileWriter("C:\\Test\\copyto.txt")); 13 //文本文件復制 14 char [] chs=new char[1024]; 15 int len=0; 16 while((len=br.read(chs))!=-1) { 17 bw.write(chs, 0, len); 18 } 19 //釋放資源 20 br.close(); 21 bw.close(); 22 } 23 }
緩沖區的工作原理:1、使用了底層流對象從具體設備上獲取數據,並將數據存儲到緩沖區的數組內。2、通過緩沖區的read()方法從緩沖區獲取具體的字符數據,這樣就提高了效率。3、如果用read方法讀取字符數據,並存儲到另一個容器中,直到讀取到了換行符時,將另一個容器臨時存儲的數據轉成字符串返回,就形成了readLine()功能。
3、字節流FileInputStream和FileOutputStream
public class FileInputStream extends InputStream
(1)FileInputStream
從文件系統中的某個文件中獲得輸入字節。哪些文件可用取決於主機環境。
(2)FileInputStream
用於讀取諸如圖像數據之類的原始字節流。
構造方法摘要 | |
---|---|
FileInputStream(File file) 通過打開一個到實際文件的連接來創建一個 FileInputStream ,該文件通過文件系統中的 File 對象 file 指定。 |
|
FileInputStream(FileDescriptor fdObj) 通過使用文件描述符 fdObj 創建一個 FileInputStream ,該文件描述符表示到文件系統中某個實際文件的現有連接。 |
|
FileInputStream(String name) 通過打開一個到實際文件的連接來創建一個 FileInputStream ,該文件通過文件系統中的路徑名 name 指定。 |
public class FileOutputStream extends OutputStream
(1)文件輸出流是用於將數據寫入 File
或 FileDescriptor
的輸出流。文件是否可用或能否可以被創建取決於基礎平台。特別是某些平台一次只允許一個 FileOutputStream(或其他文件寫入對象)打開文件進行寫入。在這種情況下,如果所涉及的文件已經打開,則此類中的構造方法將失敗。(2) FileOutputStream
用於寫入諸如圖像數據之類的原始字節的流。
構造方法摘要 | |
---|---|
FileOutputStream(File file) 創建一個向指定 File 對象表示的文件中寫入數據的文件輸出流。 |
|
FileOutputStream(File file, boolean append) 創建一個向指定 File 對象表示的文件中寫入數據的文件輸出流。 |
|
FileOutputStream(FileDescriptor fdObj) 創建一個向指定文件描述符處寫入數據的輸出文件流,該文件描述符表示一個到文件系統中的某個實際文件的現有連接。 |
|
FileOutputStream(String name) 創建一個向具有指定名稱的文件中寫入數據的輸出文件流。 |
|
FileOutputStream(String name, boolean append) 創建一個向具有指定 name 的文件中寫入數據的輸出文件流。 |
例:使用字節流復制圖片
1 import java.io.FileInputStream; 2 import java.io.FileOutputStream; 3 import java.io.IOException; 4 5 public class CopImg { 6 public static void main(String[] args) throws IOException { 7 FileInputStream fin=new FileInputStream("C:\\Users\\Administrator\\Desktop\\Img.jpg"); 8 FileOutputStream fout=new FileOutputStream("C:\\Users\\Administrator\\Desktop\\ImgCopy.jpg"); 9 int len=0; 10 byte[] buff=new byte[1024]; 11 while((len=fin.read(buff))!=-1) { 12 fout.write(buff, 0, len); 13 } 14 fin.close(); 15 fout.close(); 16 } 17 }
4、字節緩沖流BufferedInputStream和BufferedOutputStream
public class BufferedInputStream extends FilterInputStream
BufferedInputStream
為另一個輸入流添加一些功能,即緩沖輸入以及支持 mark
和 reset
方法的能力。在創建 BufferedInputStream
時,會創建一個內部緩沖區數組。在讀取或跳過流中的字節時,可根據需要從包含的輸入流再次填充該內部緩沖區,一次填充多個字節。mark
操作記錄輸入流中的某個點,reset
操作使得在從包含的輸入流中獲取新字節之前,再次讀取自最后一次 mark
操作后讀取的所有字節。
public class BufferedOutputStream extends FilterOutputStream
該類實現緩沖的輸出流。通過設置這種輸出流,應用程序就可以將各個字節寫入底層輸出流中,而不必針對每次字節寫入調用底層系統。
例:使用字節緩沖流實現圖片的復制
1 import java.io.BufferedInputStream; 2 import java.io.BufferedOutputStream; 3 import java.io.FileInputStream; 4 import java.io.FileOutputStream; 5 import java.io.IOException; 6 7 public class CopyImg { 8 public static void main(String[] args) throws IOException { 9 BufferedInputStream bfin=new BufferedInputStream(new FileInputStream("C:\\Users\\Administrator\\Desktop\\Img.jpg")); 10 BufferedOutputStream bfout=new BufferedOutputStream(new FileOutputStream("C:\\Users\\Administrator\\Desktop\\ImgCopybuff.jpg")); 11 int len=0; 12 byte[] buff=new byte[1024]; 13 while((len=bfin.read(buff))!=-1) { 14 bfout.write(buff, 0, len); 15 } 16 bfin.close(); 17 bfout.close(); 18 } 19 }
5、轉換流:InputStreamReader和OutputStreamWriter
InputStreamReader和OutputStreamWriter是字符和字節的橋梁,也可稱之為字符轉換流。原理:字節流+編碼。
FileReader和FileWriter作為子類,僅作為操作字符文件的便捷類存在。當操作的字符文件,使用的是默認編碼表時可以不用父類,而直接使用子類完成操作,簡化代碼。
一旦要指定其他編碼時,不能使用子類,必須使用字符轉換流。
public class InputStreamReader extends Reader
(1)InputStreamReader 是字節流通向字符流的橋梁:它使用指定的 charset
讀取字節並將其解碼為字符。它使用的字符集可以由名稱指定或顯式給定,或者可以接受平台默認的字符集。
(2)每次調用 InputStreamReader 中的一個 read() 方法都會導致從底層輸入流讀取一個或多個字節。要啟用從字節到字符的有效轉換,可以提前從底層流讀取更多的字節,使其超過滿足當前讀取操作所需的字節。
(3)為了達到最高效率,可以考慮在 BufferedReader 內包裝 InputStreamReader。例如:
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));//重要

1 import java.io.BufferedReader; 2 import java.io.FileWriter; 3 import java.io.IOException; 4 import java.io.InputStream; 5 import java.io.InputStreamReader; 6 import java.io.Reader; 7 8 /** 9 * 使用標准輸入流,讀取鍵盤錄入的數據,存儲到項目根目錄下的a.txt中 10 * 將字節輸入流轉換成字符輸入流,InputStreamReader 11 */ 12 public class InputStreamReaderDemo { 13 public static void main(String[] args) throws IOException { 14 //創建輸入流對象 15 BufferedReader r=new BufferedReader(new InputStreamReader(System.in)); 16 //創建輸出流對象 17 FileWriter fw=new FileWriter("a.txt"); 18 //讀寫數據 19 char[] chs=new char[1024]; 20 int len; 21 while((len=r.read(chs))!=-1) { 22 fw.write(chs,0,len); 23 fw.flush(); 24 } 25 //釋放資源 26 r.close(); 27 fw.close(); 28 29 } 30 31 public static void method2() throws IOException { 32 //創建輸入流對象 33 InputStream is=System.in; 34 Reader r=new InputStreamReader(is); 35 //創建輸出流對象 36 FileWriter fw=new FileWriter("a.txt"); 37 38 //讀寫數據 39 char[] chs=new char[1024]; 40 int len; 41 while((len=r.read(chs))!=-1) { 42 fw.write(chs,0,len); 43 fw.flush(); 44 } 45 //釋放資源 46 is.close(); 47 fw.close(); 48 } 49 50 public static void method1() throws IOException { 51 //創建輸入流對象 52 InputStream is=System.in; 53 //創建輸出流對象 54 FileWriter fw=new FileWriter("a.txt"); 55 56 //讀寫數據 57 byte[] bys=new byte[1024]; 58 int len; 59 while((len=is.read(bys))!=-1) { 60 fw.write(new String(bys,0,len)); 61 fw.flush(); 62 } 63 //釋放資源 64 is.close(); 65 fw.close(); 66 } 67 }
public class OutputStreamWriter extends Writer
(1)OutputStreamWriter 是字符流通向字節流的橋梁:可使用指定的 charset
將要寫入流中的字符編碼成字節。它使用的字符集可以由名稱指定或顯式給定,否則將接受平台默認的字符集。
(2)每次調用 write() 方法都會導致在給定字符(或字符集)上調用編碼轉換器。在寫入底層輸出流之前,得到的這些字節將在緩沖區中累積。可以指定此緩沖區的大小,不過,默認的緩沖區對多數用途來說已足夠大。注意,傳遞給 write() 方法的字符沒有緩沖。
(3)為了獲得最高效率,可考慮將 OutputStreamWriter 包裝到 BufferedWriter 中,以避免頻繁調用轉換器。例如:
Writer out = new BufferedWriter(new OutputStreamWriter(System.out));//重要
例如:利用標准輸出流將文本輸出到命令行

1 import java.io.BufferedReader; 2 import java.io.BufferedWriter; 3 import java.io.FileNotFoundException; 4 import java.io.FileReader; 5 import java.io.IOException; 6 import java.io.OutputStream; 7 import java.io.OutputStreamWriter; 8 import java.io.Writer; 9 10 /** 11 * 讀取項目目錄下的文件copy.java,並輸出到命令行 12 * 由於標准輸出流是字節輸出流,所以只能輸出字節或者字節數組,但是我們讀取到的數據是字符串,如果想進行輸出, 13 * 還需要轉換成字節數組(method1)。 14 * 要想通過標准輸出流輸出字符串,把標准輸出流轉換成一種字符輸出流即可(method2)。 15 */ 16 public class OutputStreamWriterDemo { 17 public static void main(String[] args) throws IOException { 18 //創建輸入流 19 BufferedReader br=new BufferedReader(new FileReader("copy.java")); 20 //創建輸出流 21 BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(System.out)); 22 String line;//用於接收讀到的數據 23 while((line=br.readLine())!=null) { 24 bw.write(line); 25 bw.write("\r\n"); 26 } 27 br.close(); 28 bw.close(); 29 } 30 31 public static void method2() throws FileNotFoundException, IOException { 32 //創建輸入流 33 BufferedReader br=new BufferedReader(new FileReader("copy.java")); 34 //創建輸出流 35 //OutputStream os=System.out; 36 Writer w=new OutputStreamWriter(System.out);//多態,父類引用指向子類對象 37 String line;//用於接收讀到的數據 38 while((line=br.readLine())!=null) { 39 w.write(line); 40 w.write("\r\n"); 41 } 42 br.close(); 43 w.close(); 44 } 45 46 public static void method1() throws FileNotFoundException, IOException { 47 //創建輸入流 48 BufferedReader br=new BufferedReader(new FileReader("copy.java")); 49 //創建輸出流 50 OutputStream os=System.out; 51 String line;//用於接收讀到的數據 52 while((line=br.readLine())!=null) { 53 os.write(line.getBytes()); 54 os.write("\r\n".getBytes()); 55 } 56 br.close(); 57 os.close(); 58 } 59 }

1 import java.io.FileInputStream; 2 import java.io.FileNotFoundException; 3 import java.io.FileOutputStream; 4 import java.io.IOException; 5 import java.io.InputStreamReader; 6 import java.io.OutputStreamWriter; 7 import java.io.UnsupportedEncodingException; 8 9 public class TransStreamDemo { 10 public static void main(String[] args) throws IOException { 11 writeCN(); 12 readCN(); 13 } 14 15 public static void readCN() throws UnsupportedEncodingException, FileNotFoundException, IOException { 16 //InputStreamReader將字節數組使用指定的編碼表解碼成文字 17 InputStreamReader isr=new InputStreamReader(new FileInputStream("temp.txt"),"utf-8"); 18 char[] buff=new char[1024]; 19 int len=isr.read(buff); 20 System.out.println(new String(buff,0,len)); 21 isr.close(); 22 } 23 24 public static void writeCN() throws UnsupportedEncodingException, FileNotFoundException, IOException { 25 //OutputStreamWriter將字符串按照指定的編碼表轉成字節,再使用字符流將這些字節寫出去 26 OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("temp.txt"),"utf-8");//本身是字符流,傳入字節流 27 osw.write("你好"); 28 osw.close(); 29 } 30 }
6、打印流PrintWriter和PrintStream
public class PrintWriter extends Writer
(1)向文本輸出流打印對象的格式化表示形式。此類實現在 PrintStream
中的所有 print 方法。不能輸出字節,但是可以輸出其他任意類型。
(2)與 PrintStream
類不同,如果啟用了自動刷新,則只有在調用 println、printf 或 format 的其中一個方法時才可能完成此操作,而不是每當正好輸出換行符時才完成。這些方法使用平台自有的行分隔符概念,而不是換行符。
(3)此類中的方法不會拋出 I/O 異常,盡管其某些構造方法可能拋出異常。客戶端可能會查詢調用 checkError()
是否出現錯誤。
1 import java.io.FileWriter; 2 import java.io.IOException; 3 import java.io.PrintWriter; 4 /** 5 * 注意:創建FileWriter對象時boolean參數表示是否追加; 6 * 而創建打印流對象時boolean參數表示是否自動刷新 7 */ 8 public class PrintWriterDemo { 9 public static void main(String[] args) throws IOException { 10 //PrintWriter pw=new PrintWriter("print.txt"); 11 PrintWriter pw=new PrintWriter(new FileWriter("print.txt"),true); 12 pw.write("測試打印流"); 13 pw.println("此句之后換行"); 14 pw.println("特有功能:自動換行和自動刷新"); 15 pw.println("利用構造器設置自動刷新"); 16 pw.close(); 17 } 18 }
使用字符打印流復制文本文件:

1 import java.io.BufferedReader; 2 import java.io.FileReader; 3 import java.io.FileWriter; 4 import java.io.IOException; 5 import java.io.PrintWriter; 6 /** 7 * 使用打印流復制文本文件 8 */ 9 public class PrintWriterDemo { 10 public static void main(String[] args) throws IOException { 11 BufferedReader br=new BufferedReader(new FileReader("copy.java")); 12 PrintWriter pw=new PrintWriter("printcopy.java"); 13 String line; 14 while((line=br.readLine())!=null) { 15 pw.println(line); 16 } 17 br.close(); 18 pw.close(); 19 } 20 }
public class PrintStream extends FilterOutputStreamimplements Appendable, Closeable
(1)PrintStream
為其他輸出流添加了功能(增加了很多打印方法),使它們能夠方便地打印各種數據值表示形式(例如:希望寫一個整數,到目的地整數的表現形式不變。字節流的write方法只將一個整數的最低字節寫入到目的地,比如寫fos.write(97),到目的地(記事本打開文件)會變成字符'a',需要手動轉換:fos.write(Integer.toString(97).getBytes());而采用PrintStream:ps.print(97),則可以保證數據的表現形式)。
1 //PrintStream的print方法 2 public void print(int i) { 3 write(String.valueOf(i)); 4 }
(2)與其他輸出流不同,PrintStream
永遠不會拋出IOException
;而是,異常情況僅設置可通過checkError
方法測試的內部標志。
另外,為了自動刷新,可以創建一個PrintStream
;這意味着可在寫入 byte 數組之后自動調用flush
方法,可調用其中一個println
方法,或寫入一個換行符或字節 ('\n'
)。(3)PrintStream
打印的所有字符都使用平台的默認字符編碼轉換為字節。在需要寫入字符而不是寫入字節的情況下,應該使用類。
PrintWriter
使用字節打印流復制文本文件:

1 import java.io.BufferedReader; 2 import java.io.FileReader; 3 import java.io.IOException; 4 import java.io.PrintStream; 5 6 public class PrintStreamDemo { 7 public static void main(String[] args) throws IOException { 8 BufferedReader br=new BufferedReader(new FileReader("copy.java")); 9 PrintStream ps=new PrintStream("printcopy2.java"); 10 String line; 11 while((line=br.readLine())!=null) { 12 ps.println(line); 13 } 14 br.close(); 15 ps.close(); 16 } 17 }
7、對象操作流ObjectInputStream和ObjectOutputStream
public class ObjectOutputStream extends OutputStream implements ObjectOutput, ObjectStreamConstants
(1)ObjectOutputStream 將 Java 對象的基本數據類型和圖形寫入 OutputStream。只能使用 ObjectInputStream 讀取(重構)對象。
(2)只能將支持 java.io.Serializable 接口的對象寫入流中。
(3)writeObject 方法用於將對象寫入流中。所有對象(包括 String 和數組)都可以通過 writeObject 寫入。可將多個對象或基元寫入流中。必須使用與寫入對象時相同的類型和順序從相應 ObjectInputstream 中讀回對象。
構造方法:ObjectOutputStream(OutputStream out)
創建寫入指定 OutputStream 的 ObjectOutputStream。
public class ObjectInputStream extends InputStream implements ObjectInput, ObjectStreamConstants
(1)ObjectInputStream 對以前使用 ObjectOutputStream 寫入的基本數據和對象進行反序列化。
(2)只有支持 java.io.Serializable 或 java.io.Externalizable 接口的對象才能從流讀取。
(3)readObject
方法用於從流讀取對象。應該使用 Java 的安全強制轉換來獲取所需的類型。在 Java 中,字符串和數組都是對象,所以在序列化期間將其視為對象。讀取時,需要將其強制轉換為期望的類型。
例:對象讀寫:

1 import java.io.Serializable; 2 //學生類 3 public class Student implements Serializable{ 4 private static final long serialVersionUID = -8942780382144699003L; 5 String name; 6 int age; 7 public Student(String name,int age){ 8 this.name=name; 9 this.age=age; 10 } 11 @Override 12 public String toString() { 13 return "Student [name=" + name + ", age=" + age + "]"; 14 } 15 }

1 import java.io.EOFException; 2 import java.io.FileInputStream; 3 import java.io.FileNotFoundException; 4 import java.io.FileOutputStream; 5 import java.io.IOException; 6 import java.io.ObjectInputStream; 7 import java.io.ObjectOutputStream; 8 9 /** 10 * 使用對象輸出流寫對象和對象輸入流讀對象 11 *注意:如果Student沒有序列化,會拋出java.io.NotSerializableException 12 *Serializable:序列號,是一個標識接口,只起標識作用,沒有方法 13 *當一個類的對象需要IO流進行讀寫的時候,這個類必須實現接口 14 */ 15 public class ObjectOperate { 16 public static void main(String[] args) throws IOException, ClassNotFoundException { 17 writeObject(); 18 //創建對象輸入流的對象 19 ObjectInputStream ois=new ObjectInputStream(new FileInputStream("a.txt")); 20 //讀取對象 21 try { 22 while(true){ 23 Object obj=ois.readObject(); 24 System.out.println(obj); 25 } 26 }catch(EOFException e){ 27 System.out.println("讀到了文件末尾"); 28 } 29 30 //釋放資源 31 ois.close(); 32 33 } 34 35 public static void writeObject() throws FileNotFoundException, IOException { 36 //創建對象輸出流的對象 37 FileOutputStream fos=new FileOutputStream("a.txt"); 38 ObjectOutputStream oos=new ObjectOutputStream(fos); 39 //創建學生對象 40 Student s1=new Student("張三",20); 41 Student s2=new Student("李四",30); 42 Student s3=new Student("王五",10); 43 //寫出學生對象 44 oos.writeObject(s1); 45 oos.writeObject(s2); 46 oos.writeObject(s3); 47 //釋放資源 48 } 49 }

1 import java.io.FileInputStream; 2 import java.io.FileNotFoundException; 3 import java.io.FileOutputStream; 4 import java.io.IOException; 5 import java.io.ObjectInputStream; 6 import java.io.ObjectOutputStream; 7 import java.util.ArrayList; 8 /** 9 * 使用對象輸出流寫對象和對象輸入流讀對象 10 *解決讀取對象出現異常的問題,使用集合類 11 */ 12 public class ObjectOperate2 { 13 public static void main(String[] args) throws IOException, ClassNotFoundException { 14 listMethod(); 15 //創建對象輸入流對象 16 ObjectInputStream ois=new ObjectInputStream(new FileInputStream("b.txt")); 17 //讀取數據 18 Object obj=ois.readObject(); 19 //System.out.println(obj); 20 //向下轉型 21 ArrayList<Student> list=(ArrayList<Student>) obj; 22 for(Student s:list) { 23 System.out.println(s); 24 } 25 //釋放資源 26 ois.close(); 27 } 28 29 public static void listMethod() throws IOException, FileNotFoundException { 30 //創建對象輸出流的對象 31 ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("b.txt")); 32 //創建集合類 33 ArrayList<Student> list=new ArrayList<Student>(); 34 //添加學生對象 35 list.add(new Student("zhangsan",20)); 36 list.add(new Student("lisi",30)); 37 //寫出集合對象 38 oos.writeObject(list); 39 //釋放資源 40 oos.close(); 41 } 42 43 }
序列化接口Serializable的作用:沒有方法,不需要覆寫,是一個標記接口為了啟動一個序列化功能。唯一的作用就是給每一個需要序列化的類都分配一個序列版本號,這個版本號和類相關聯。在序列化時,會將這個序列號也一同保存在文件中,在反序列化時會讀取這個序列號和本類的序列號進行匹配,如果不匹配會拋出java.io.InvalidClassException.
注意:靜態數據不會被序列化,因為靜態數據在方法區,不在對象里。
或者使用transient關鍵字修飾,也不會序列化。
8、SequenceInputStream
表示其他輸入流的邏輯串聯。它從輸入流的有序集合開始,並從第一個輸入流開始讀取,直到到達文件末尾,接着從第二個輸入流讀取,依次類推,直到到達包含的最后一個輸入流的文件末尾為止。
案例:媒體文件切割與合並

1 import java.io.File; 2 import java.io.FileInputStream; 3 import java.io.FileOutputStream; 4 import java.io.IOException; 5 import java.util.Properties; 6 7 public class CutFile { 8 /** 9 * 將一個媒體文件切割成碎片 10 * 思路:1、讀取源文件,將源文件的數據分別復制到多個文件 11 * 2、切割方式有兩種:按照碎片個數切,或者按照指定大小切 12 * 3、一個輸入流對應多個輸出流 13 * 4、每個碎片都需要編號,順序不能錯 14 * @throws IOException 15 */ 16 public static void main(String[] args) throws IOException { 17 File srcFile=new File("C:\\Users\\Administrator\\Desktop\\Test\\img.jpg"); 18 File partsDir=new File("C:\\Users\\Administrator\\Desktop\\cutFiles"); 19 splitFile(srcFile,partsDir); 20 21 } 22 //切割文件 23 private static void splitFile(File srcFile, File partsDir) throws IOException { 24 if(!(srcFile.exists()&&srcFile.isFile())) { 25 throw new RuntimeException("源文件不是正確文件或者不存在"); 26 } 27 if(!partsDir.exists()) { 28 partsDir.mkdirs(); 29 } 30 FileInputStream fis=new FileInputStream(srcFile); 31 FileOutputStream fos=null; 32 33 byte[] buf=new byte[1024*60]; 34 35 int len=0; 36 int count=1; 37 while((len=fis.read(buf))!=-1) { 38 //存儲碎片文件 39 fos=new FileOutputStream(new File(partsDir,(count++)+".part")); 40 fos.write(buf, 0, len); 41 fos.close(); 42 } 43 /*將源文件和切割的信息也保存起來,隨着碎片文件一起發送 44 * 信息:源文件的名稱 45 * 碎片的個數 46 *將這些信息單獨封裝到一個文件中 47 *還要一個輸出流完成此操作 */ 48 49 String fileName=srcFile.getName(); 50 int partCount=count; 51 fos=new FileOutputStream(new File(partsDir,count+".properties")); 52 // fos.write(("fileName="+fileName+System.lineSeparator()).getBytes()); 53 // fos.write(("fileCount="+Integer.toString(partCount)).getBytes()); 54 Properties prop=new Properties(); 55 prop.setProperty("fileName", srcFile.getName()); 56 prop.setProperty("partCount", Integer.toString(partCount)); 57 //將屬性集中的信息持久化 58 prop.store(fos, "part file info"); 59 fis.close(); 60 fos.close(); 61 62 } 63 }

1 import java.io.File; 2 import java.io.FileFilter; 3 import java.io.FileInputStream; 4 import java.io.FileNotFoundException; 5 import java.io.FileOutputStream; 6 import java.io.IOException; 7 import java.io.SequenceInputStream; 8 import java.util.ArrayList; 9 import java.util.Collections; 10 import java.util.Enumeration; 11 import java.util.List; 12 import java.util.Properties; 13 14 public class mergeFile { 15 public static void main(String[] args) throws IOException { 16 File pathDir=new File("C:\\Users\\Administrator\\Desktop\\cutFiles"); 17 //獲取配置文件 18 File configFile=getconfigFile(pathDir); 19 //獲取配置文件信息的屬性集 20 Properties prop=getProperties(configFile); 21 System.out.println(prop); 22 //獲取屬性集信息,將屬性集信息傳遞到合並方法中 23 merge(pathDir,prop); 24 } 25 26 private static Properties getProperties(File configFile) throws IOException { 27 FileInputStream fis=null; 28 Properties prop=null; 29 try { 30 //讀取流和配置文件相關聯 31 fis=new FileInputStream(configFile); 32 prop=new Properties(); 33 //流中的數據加載到集合中 34 prop.load(fis); 35 }finally { 36 if(fis!=null) { 37 fis.close(); 38 } 39 } 40 return prop; 41 } 42 43 public static File getconfigFile(File pathDir) { 44 //判斷是否存在properties文件 45 if(!(pathDir.exists()&&pathDir.isDirectory())) { 46 throw new RuntimeException(pathDir.toString()+"不是有效目錄"); 47 } 48 File[] files=pathDir.listFiles(new FileFilter() { 49 @Override 50 public boolean accept(File pathname) { 51 return pathname.getName().endsWith(".properties"); 52 } 53 54 }); 55 if(files.length!=1) { 56 throw new RuntimeException(pathDir.toString()+"properties擴展名的文件不存在或者不唯一"); 57 } 58 File configFile=files[0]; 59 return configFile; 60 } 61 62 public static void merge(File pathDir, Properties prop) throws FileNotFoundException, IOException { 63 String fileName=prop.getProperty("fileName"); 64 int partCount=Integer.valueOf(prop.getProperty("partCount")); 65 List<FileInputStream> list=new ArrayList<FileInputStream>(); 66 for(int i=1;i<partCount;i++) { 67 list.add(new FileInputStream(pathDir.toString()+"\\"+i+".part")); 68 } 69 //List自身無法獲取Enumeration工具類,到Collection中找 70 Enumeration<FileInputStream> en=Collections.enumeration(list); 71 SequenceInputStream sis=new SequenceInputStream(en); 72 FileOutputStream fos=new FileOutputStream(pathDir.toString()+"\\"+fileName); 73 byte[] buf=new byte[1024]; 74 int len=0; 75 while((len=sis.read(buf))!=-1) { 76 fos.write(buf,0,len); 77 } 78 fos.close(); 79 sis.close(); 80 } 81 82 }
9、用於操作數組和字符串的流對象
ByteArrayInputStream ByteArrayOutputStream
CharArrayReader CharArrayWriter
StringReader StringWriter
關閉這些流都是無效的,因為這些都未調用系統資源,不需要拋IO異常。

1 import java.io.ByteArrayInputStream; 2 import java.io.ByteArrayOutputStream; 3 4 /** 5 * 源和目的都是內存的讀寫過程 6 *用流的思想操作數組中的數據 7 */ 8 public class ByteArrayStreamDemo { 9 public static void main(String[] args) { 10 //源:內存 11 ByteArrayInputStream bis=new ByteArrayInputStream("andhhshad".getBytes()); 12 //目的:內存 13 ByteArrayOutputStream bos=new ByteArrayOutputStream();//內部有個可自動增長的數組 14 //因為都是源和目的都是內存,沒有調用底層資源,所以不要關閉,即使調用了close也沒有任何效果,關閉后仍然可使用,不會拋出異常。 15 int ch=0; 16 while((ch=bis.read())!=-1) { 17 bos.write(ch); 18 } 19 20 System.out.println(bos.toString()); 21 } 22 }
10、RandomAccessFile

1 import java.io.FileNotFoundException; 2 import java.io.IOException; 3 import java.io.RandomAccessFile; 4 5 /** 6 * 注意:隨機讀寫。數據需要規律。用RandomAccessFile類需要明確要操作的數據的位置。 7 * 8 */ 9 public class RandomAccessFileDemo { 10 public static void main(String[] args) throws IOException { 11 //writeFile(); 12 RandomAccessFile raf=new RandomAccessFile("tempFile\\test.txt","r"); 13 raf.seek(8*1);//讀第二個人 14 byte[] buf=new byte[4]; 15 raf.read(buf); 16 String name=new String(buf); 17 System.out.println("name="+name); 18 19 int age=raf.readInt(); 20 System.out.println("age="+age); 21 raf.close(); 22 } 23 24 public static void writeFile() throws FileNotFoundException, IOException { 25 //明確要操作的位置,可以多個線程操作同一份文件而不沖突。多線程下載的基本原理。 26 RandomAccessFile raf=new RandomAccessFile("tempFile\\test.txt","rw"); 27 28 raf.write("張三".getBytes()); 29 raf.writeInt(97);//保證字節原樣性 30 31 raf.write("李四".getBytes()); 32 raf.writeInt(99);//保證字節原樣性 33 34 System.out.println(raf.getFilePointer());//獲取隨機指針 35 raf.seek(8*2);//設置指針 36 raf.write("王五".getBytes()); 37 raf.writeInt(100);//保證字節原樣性 38 raf.close(); 39 } 40 }
11、File類:
File: 文件和目錄路徑名的抽象表示形式,File類的實例是不可改變的
(1)File類常用功能
File: 文件和目錄路徑名的抽象表示形式,File類的實例是不可改變的 * File類的構造方法: * File(String pathname) 將指定的路徑名轉換成一個File對象 * File(String parent,String child) 根據指定的父路徑和文件路徑創建對象 * File(File parent,String child) * File類常用功能: * 創建:boolean createNewFile():當指定文件(或文件夾)不存在時創建文件並返回true,否則返回false,路徑不存在則拋出異常 * boolean mkdir() :當指定文件(或文件夾)不存在時創建文件夾並返回true,否則返回false * boolean mkdirs() :創建指定文件夾,所在文件夾目錄不存在時,則順道一塊創建
* 刪除:boolean delete():刪除文件 注意:要刪除一個目錄,需要先刪除這個目錄下的所有子文件和子目錄 * 獲取:File getAbsoluteFile() * File getParentFile() * String getAbsolutePath() * String getParent() * String getPath() * long lastModified()
* 判斷: boolean exists(); * boolean isAbsolute() * boolean isDirectory() * boolean isFile() * boolean isHidden() * 修改:boolean renameTo(File dest): 將當前File對象所指向的路徑修改為指定File所指的路徑 (修改文件名稱)
案例:打印指定文件(夾)及其所有子目錄
1 import java.io.File; 2 /**
3 *打印目錄 4 */
5 public class FileDemo1 { 6 public static void main(String[] args){ 7 File f=new File("E:\\BaiduNetdiskDownload"); 8 printTree( f,0); 9 } 10
11 public static void printTree(File f,int level) { 12 for(int j=0;j<level;j++) { 13 System.out.print("\t"); 14 } 15 System.out.println(f.getAbsolutePath()); 16 if(f.isDirectory()) { 17 level++; 18 File strs[]=f.listFiles(); 19 for(int i=0;i<strs.length;i++) { 20 File f0=strs[i]; 21 printTree(f0,level+1); 22 } 23 } 24 } 25 }
(2)File類重要方法之過濾器
String[] list()
String[] list(FilenameFilter)
File[] listFiles()
File[] listFiles(FilenameFilter)
File[] listFiles(FileFilter filter)
File類的list方法可以獲取目錄下的各個文件,傳入過濾器還能按照特定需求取出需要的文件。下面來看一下過濾器怎么用的。首先看
String[] list(FilenameFilter)
查看FilenameFilter源碼,發現其實是一個接口:
public interface FilenameFilter { /** * Tests if a specified file should be included in a file list. * * @param dir the directory in which the file was found. * @param name the name of the file. * @return <code>true</code> if and only if the name should be * included in the file list; <code>false</code> otherwise. */
boolean accept(File dir, String name); }
那么我們要想使用過濾器,應該先實現接口,假設我們要找某個文件夾下的txt文件:
1 import java.io.File; 2 import java.io.FilenameFilter; 3 4 public class FileDemo { 5 public static void main(String[] args) { 6 File file = new File("C:\\Test"); 7 if(file.isDirectory()) { 8 String[] list=file.list(new FilenameFilterbytxt());//傳入過濾器 9 for(String l:list) { 10 System.out.println(l); 11 } 12 } 13 } 14 } 15 16 class FilenameFilterbytxt implements FilenameFilter{ 17 @Override 18 public boolean accept(File dir, String name) { 19 // TODO Auto-generated method stub 20 return name.endsWith(".txt");//當文件名以.txt結尾時返回true. 21 } 22 23 }
但是我們看到第8行代碼只是傳入了過濾器,那么accept方法是如何被調用的呢?查看
String[] list(FilenameFilter) 的源碼:
1 /*list(FilenameFilter)源碼解析*/
2 public String[] list(FilenameFilter filter) { 3 String names[] = list();//調用list()方法獲取所有名稱
4 if ((names == null) || (filter == null)) { 5 return names; 6 } 7 List<String> v = new ArrayList<>();//用於保存過濾后的文件名
8 for (int i = 0 ; i < names.length ; i++) {//遍歷 9 //調用filter的accept方法,傳入當前目錄this和遍歷到的名稱names[i]
10 if (filter.accept(this, names[i])) { 11 v.add(names[i]);//滿足過濾器條件的添加到集合中
12 } 13 } 14 return v.toArray(new String[v.size()]);//將集合轉成數組返回,限定增刪操作
15 }
也就是說,我們實現的accept方法是在構造器中被調用的。
類似地,FileFilter 也是一個接口,采用匿名內部類的方式實現接口,使用File[] listFiles(FileFilter filter)獲取目錄下所有文件夾:
1 import java.io.File; 2 import java.io.FileFilter; 3 4 public class FileDemo { 5 public static void main(String[] args) { 6 File file = new File("C:\\Test"); 7 if(file.isDirectory()) { 8 File[] list=file.listFiles(new FileFilter() { 9 @Override 10 public boolean accept(File pathname) { 11 // TODO Auto-generated method stub 12 return pathname.isDirectory(); 13 } 14 15 }); 16 17 for(File f:list) { 18 System.out.println(f); 19 } 20 } 21 } 22 }
File[] listFiles(FileFilter filter) 方法的源碼如下:
1 /*File[] listFiles(FileFilter filter)源碼解析*/ 2 public File[] listFiles(FileFilter filter) { 3 String ss[] = list();//調用list()獲取所有的名稱數組 4 if (ss == null) return null;//健壯性判斷,數組為null則返回 5 ArrayList<File> files = new ArrayList<>();//創建File類型集合 6 for (String s : ss) {//遍歷 7 File f = new File(s, this);//private File(String child, File parent)私有構造調用 8 if ((filter == null) || filter.accept(f))//條件判斷 9 files.add(f);//添加到集合 10 } 11 return files.toArray(new File[files.size()]);//集合轉成數組返回 12 }
12、IO流使用規律總結:
(1)明確要操作的數據是數據源還是數據目的(要讀還是要寫)
源:InputStream Reader
目的:OutputStream Writer
(2)明確要操作的設備上的數據是字節還是文本
源:
字節:InputStream
文本:Reader
目的:
字節:OutputStream
文本:Writer
(3)明確數據所在的具體設備
源設備:
硬盤:文件 File開頭
內存:數組,字符串
鍵盤:System.in
網絡:Socket
目的設備:
硬盤:文件 File開頭
內存:數組,字符串
屏幕:System.out
網絡:Socket
(4)明確是否需要額外功能?
需要轉換——轉換流 InputStreamReader OutputStreamWriter
需要高效——緩沖流Bufferedxxx
多個源——序列流 SequenceInputStream
對象序列化——ObjectInputStream,ObjectOutputStream
保證數據的輸出形式——打印流PrintStream Printwriter
操作基本數據,保證字節原樣性——DataOutputStream,DataInputStream