http://www.cnblogs.com/baixl/p/4170599.html ...
主要內容
- java.io.File類的使用
- IO原理及流的分類
- 文件流
- FileInputStream / FileOutputStream / FileReader / FileWriter
-
緩衝流
- BufferedInputStream / BufferedOutputStream /
- BufferedReader / BufferedWriter
- 轉換流
- InputStreamReader / OutputStreamWriter
- 標準輸入/輸出流
- 列印流(瞭解)
- PrintStream / PrintWriter
- 數據流(瞭解)
- DataInputStream / DataOutputStream
- 對象流 ----涉及序列化、反序列化
- ObjectInputStream / ObjectOutputStream
- 隨機存取文件流
- RandomAccessFile
File類
- java.io.File類:文件和目錄路徑名的抽象表示形式,與平臺無關
- File 能新建、刪除、重命名文件和目錄,但 File 不能訪問文件內容本身。如果需要訪問文件內容本身,則需要使用輸入/輸出流。
- File對象可以作為參數傳遞給流的構造函數
-
File類的常見構造方法:
- public File(String pathname)
以pathname為路徑創建File對象,可以是絕對路徑或者相對路徑,如果pathname是相對路徑,則預設的當前路徑在系統屬性user.dir中存儲。
- public File(String parent,String child)
以parent為父路徑,child為子路徑創建File對象。
- File的靜態屬性String separator存儲了當前系統的路徑分隔符。
- 在UNIX中,此欄位為'/',在Windows中,為'\\'
常見方法:
eg:
File dir1 = new File("D:/IOTest/dir1"); if (!dir1.exists()) { // 如果D:/IOTest/dir1不存在,就創建為目錄 dir1.mkdir(); } // 創建以dir1為父目錄,名為"dir2"的File對象 File dir2 = new File(dir1, "dir2"); if (!dir2.exists()) { // 如果還不存在,就創建為目錄 dir2.mkdirs(); } File dir4 = new File(dir1, "dir3/dir4"); if (!dir4.exists()) { dir4.mkdirs(); } // 創建以dir2為父目錄,名為"test.txt"的File對象 File file = new File(dir2, "test.txt"); if (!file.exists()) { // 如果還不存在,就創建為文件 file.createNewFile();} |
Java IO原理
- IO流用來處理設備之間的數據傳輸。
- Java程式中,對於數據的輸入/輸出操作以"流(stream)" 的方式進行。
- java.io包下提供了各種"流"類和介面,用以獲取不同種類的數據,並通過標準的方法輸入或輸出數據。
流的分類
- 按操作數據單位不同分為:位元組流(8 bit),字元流(16 bit)
- 按數據流的流向不同分為:輸入流,輸出流
- 按流的角色的不同分為:節點流,處理流
(抽象基類) |
位元組流 |
字元流 |
輸入流 |
InputStream |
Reader |
輸出流 |
OutputStream |
Writer |
- Java的IO流共涉及40多個類,實際上非常規則,都是從如上4個抽象基類派生的。
- 由這四個類派生出來的子類名稱都是以其父類名作為子類名尾碼。
- 位元組流:以byte為單位傳輸
- 字元流:以char為單位傳輸
IO流體系
InputStream & Reader
- InputStream 和 Reader 是所有輸入流的基類。
-
InputStream(典型實現:FileInputStream)
- int read()
- int read(byte[] b)
- int read(byte[] b, int off, int len)
-
Reader(典型實現:FileReader)
- int read()
- int read(char [] c)
- int read(char [] c, int off, int len)
- 程式中打開的文件 IO 資源不屬於記憶體里的資源,垃圾回收機制無法回收該資源,所以應該顯式關閉文件 IO 資源。
OutputStream & Writer
-
OutputStream 和 Writer 也非常相似:
- void write(int b/int c);
- void write(byte[] b/char[] cbuf);
- void write(byte[] b/char[] buff, int off, int len);
- void flush();
- void close(); 需要先刷新,再關閉此流
-
因為字元流直接以字元作為操作單位,所以 Writer 可以用字元串來替換字元數組,即以 String 對象作為參數
- void write(String str);
- void write(String str, int off, int len);
文件流
讀取文件
1.建立一個流對象,將已存在的一個文件載入進流。
- FileReader fr = new FileReader("Test.txt");
2.創建一個臨時存放數據的數組。
- char[] ch = new char[1024];
3.調用流對象的讀取方法將流中的數據讀入到數組中。
- fr.read(ch);
FileReader fr = null; try{ fr = new FileReader("c:\\test.txt"); char[] buf = new char[1024]; int len= 0; while((len=fr.read(buf))!=-1){ System.out.println(new String(buf ,0,len));} }catch (IOException e){ System.out.println("read-Exception :"+e.toString());} finally{ if(fr!=null){ try{ fr.close(); }catch (IOException e){ System.out.println("close-Exception :"+e.toString()); } } } |
寫入文件
1.創建流對象,建立數據存放文件
- FileWriter fw = new FileWriter("Test.txt");
2.調用流對象的寫入方法,將數據寫入流
- fw.write("text");
3.關閉流資源,並將流中的數據清空到文件中。
- fw.close();
FileWriter fw = null; try{ fw = new FileWriter("Test.txt"); fw.write("text"); } catch (IOException e){ System.out.println(e.toString()); } finally{ If(fw!=null) try{ fw.close(); } catch (IOException e){ System.out.println(e.toString()); } } |
註意點:
- 定義文件路徑時,註意:可以用"/"或者"\\"。File.separator()
- 在寫入一個文件時,如果目錄下有同名文件將被覆蓋。
- 在讀取文件時,必須保證該文件已存在,否則出異常。
處理流之一:緩衝流
- 為了提高數據讀寫的速度,Java API提供了帶緩衝功能的流類,在使用這些流類時,會創建一個內部緩衝區數組
- 根據數據操作單位可以把緩衝流分為:
- BufferedInputStream 和 BufferedOutputStream
- BufferedReader 和 BufferedWriter
- 緩衝流要"套接"在相應的節點流之上,對讀寫的數據提供了緩衝的功能,提高了讀寫的效率,同時增加了一些新的方法
-
對於輸出的緩衝流,寫出的數據會先在記憶體中緩存,使用flush()將會使記憶體中的數據立刻寫出
BufferedReader br = null; BufferedWriter bw = null; try { //step1:創建緩衝流對象:它是過濾流,是對節點流的包裝 br = new BufferedReader(newFileReader("d:\\IOTest\\source.txt")); bw = new BufferedWriter(newFileWriter("d:\\IOTest\\destBF.txt")); String str = null; while ((str = br.readLine()) != null) { //一次讀取字元文本文件的一行字元 bw.write(str); //一次寫入一行字元串 bw.newLine(); //寫入行分隔符 } bw.flush(); //step2:刷新緩衝區 } catch (IOException e) { e.printStackTrace(); } finally { // step3: 關閉IO流對象 try { if (bw != null) { bw.close(); //關閉過濾流時,會自動關閉它所包裝的底層節點流 } } catch (IOException e) { e.printStackTrace(); } try { if (br != null) { br.close(); } } catch (IOException e) { e.printStackTrace(); } } |
處理流之二:轉換流
- 轉換流提供了在位元組流和字元流之間的轉換
-
Java API提供了兩個轉換流:
- InputStreamReader和OutputStreamWriter
- 位元組流中的數據都是字元時,轉成字元流操作更高效。
InputStreamReader
- 用於將位元組流中讀取到的位元組按指定字元集解碼成字元。需要和InputStream"套接"。
- 構造方法
- public InputStreamReader(InputStream in)
- public InputSreamReader(InputStream in,String charsetName)
如: Reader isr = new
InputStreamReader(System.in,"ISO5334_1");//指定字元集
OutputStreamWriter
- 用於將要寫入到位元組流中的字元按指定字元集編碼成位元組。需要和OutputStream"套接"。
- 構造方法
- public OutputStreamWriter(OutputStream out)
- public OutputStreamWriter(OutputStream out,String charsetName)
public void testMyInput() throws Exception{ FileInputStream fis = new FileInputStream("dbcp.txt"); FileOutputStream fos = new FileOutputStream("dbcp5.txt"); InputStreamReader isr = new InputStreamReader(fis,"GBK"); OutputStreamWriter osw = new OutputStreamWriter(fos,"GBK"); BufferedReader br = new BufferedReader(isr); BufferedWriter bw = new BufferedWriter(osw); String str = null; while((str = br.readLine()) != null){ bw.write(str); bw.newLine(); bw.flush(); } bw.close(); br.close();} |
補充:字元編碼
- 編碼表的由來
電腦只能識別二進位數據,早期由來是電信號。為了方便應用電腦,讓它可以識別各個國家的文字。就將各個國家的文字用數字來表示,並一一對應,形成一張表。這就是編碼表。
- 常見的編碼表
-
ASCII:美國標準信息交換碼。
- 用一個位元組的7位可以表示。
-
ISO8859-1:拉丁碼表。歐洲碼表
- 用一個位元組的8位表示。
- GB2312:中國的中文編碼表。
- GBK:中國的中文編碼表升級,融合了更多的中文文字元號。
-
Unicode:國際標準碼,融合了多種文字。
- 所有文字都用兩個位元組來表示,Java語言使用的就是unicode
- UTF-8:最多用三個位元組來表示一個字元。
- 編碼:字元串à位元組數組
- 解碼:位元組數組à字元串
- 轉換流的編碼應用
- 可以將字元按指定編碼格式存儲。
- 可以對文本數據按指定編碼格式來解讀。
- 指定編碼表的動作由構造器完成。
處理流之三:標準輸入輸出流
- System.in和System.out分別代表了系統標準的輸入和輸出設備
- 預設輸入設備是鍵盤,輸出設備是顯示器
- System.in的類型是InputStream
- System.out的類型是PrintStream,其是OutputStream的子類FilterOutputStream 的子類
-
通過System類的setIn,setOut方法對預設設備進行改變。
- public static void setIn(InputStream in)
- public static void setOut(PrintStream out)
System.out.println("請輸入信息(退出輸入e或exit):"); //把"標準"輸入流(鍵盤輸入)這個位元組流包裝成字元流,再包裝成緩衝流 BufferedReader br = new BufferedReader( new InputStreamReader(System.in)); String s = null; try { while ((s = br.readLine()) != null) { //讀取用戶輸入的一行數據 --> 阻塞程式 if (s.equalsIgnoreCase("e") || s.equalsIgnoreCase("exit")) { System.out.println("安全退出!!"); break; } //將讀取到的整行字元串轉成大寫輸出 System.out.println("-->:"+s.toUpperCase()); System.out.println("繼續輸入信息"); } } catch (IOException e) { e.printStackTrace(); } finally { try { if (br != null) { br.close(); //關閉過濾流時,會自動關閉它包裝的底層節點流 } } catch (IOException e) { e.printStackTrace(); } }
|
處理流之四:列印流(瞭解)
- 在整個IO包中,列印流是輸出信息最方便的類。
-
PrintStream(位元組列印流)和PrintWriter(字元列印流)
- 提供了一系列重載的print和println方法,用於多種數據類型的輸出
- PrintStream和PrintWriter的輸出不會拋出異常
- PrintStream和PrintWriter有自動flush功能
- System.out返回的是PrintStream的實例
FileOutputStream fos = null; try { fos = new FileOutputStream(new File("D:\\IO\\text.txt")); } catch (FileNotFoundException e) { e.printStackTrace(); }//創建列印輸出流,設置為自動刷新模式(寫入換行符或位元組 '\n' 時都會刷新輸出緩衝區) PrintStream ps = new PrintStream(fos,true); if (ps != null) { // 把標準輸出流(控制台輸出)改成文件 System.setOut(ps);} for (int i = 0; i <= 255; i++) { //輸出ASCII字元 System.out.print((char)i); if (i % 50 == 0) { //每50個數據一行 System.out.println(); // 換行 } } ps.close(); } |
處理流之五:數據流(瞭解)
- 為了方便地操作Java語言的基本數據類型的數據,可以使用數據流。
-
數據流有兩個類:(用於讀取和寫出基本數據類型的數據)
- DataInputStream 和 DataOutputStream
- 分別"套接"在 InputStream 和 OutputStream 節點流上
- DataInputStream中的方法
boolean readBoolean() byte readByte()
char readChar() float readFloat()
double readDouble() short readShort()
long readLong() int readInt()
String readUTF() void readFully(byte[] b)
- DataOutputStream中的方法
- 將上述的方法的read改為相應的write即可。
DataOutputStream dos = null; try { //創建連接到指定文件的數據輸出流對象 dos = new DataOutputStream(new FileOutputStream( "d:\\IOTest\\destData.dat")); dos.writeUTF("ab中國"); //寫UTF字元串 dos.writeBoolean(false); //寫入布爾值 dos.writeLong(1234567890L); //寫入長整數 System.out.println("寫文件成功!"); } catch (IOException e) { e.printStackTrace(); } finally { //關閉流對象 try { if (dos != null) { // 關閉過濾流時,會自動關閉它包裝的底層節點流 dos.close(); } } catch (IOException e) { e.printStackTrace(); } } |
處理流之六:對象流
- ObjectInputStream和OjbectOutputSteam
- 用於存儲和讀取對象的處理流。它的強大之處就是可以把Java中的對象寫入到數據源中,也能把對象從數據源中還原回來。
- 序列化(Serialize):用ObjectOutputStream類將一個Java對象寫入IO流中
- 反序列化(Deserialize):用ObjectInputStream類從IO流中恢復該Java對象
- ObjectOutputStream和ObjectInputStream不能序列化static和transient修飾的成員變數
對象的序列化
- 對象序列化機制允許把記憶體中的Java對象轉換成平臺無關的二進位流,從而允許把這種二進位流持久地保存在磁碟上,或通過網路將這種二進位流傳輸到另一個網路節點。當其它程式獲取了這種二進位流,就可以恢覆成原來的Java對象
- 序列化的好處在於可將任何實現了Serializable接口的對象轉化為位元組數據,使其在保存和傳輸時可被還原
- 序列化是 RMI(Remote Method Invoke – 遠程方法調用)過程的參數和返回值都必須實現的機制,而 RMI 是 JavaEE 的基礎。因此序列化機制是 JavaEE 平臺的基礎
-
如果需要讓某個對象支持序列化機制,則必須讓其類是可序列化的,為了讓某個類是可序列化的,該類必須實現如下兩個介面之一:
- Serializable
- Externalizable
-
凡是實現Serializable介面的類都有一個表示序列化版本標識符的靜態變數:
- private static final long serialVersionUID;
- serialVersionUID用來表明類的不同版本間的相容性
- 如果類沒有顯示定義這個靜態變數,它的值是Java運行時環境根據類的內部細節自動生成的。若類的源代碼作了修改,serialVersionUID 可能發生變化。故建議,顯示聲明
-
顯示定義serialVersionUID的用途
- 希望類的不同版本對序列化相容,因此需確保類的不同版本具有相同的serialVersionUID
- 不希望類的不同版本對序列化相容,因此需確保類的不同版本具有不同的serialVersionUID
使用對象流序列化對象
-
若某個類實現了 Serializable 介面,該類的對象就是可序列化的:
- 創建一個 ObjectOutputStream
- 調用 ObjectOutputStream 對象的 writeObject(對象) 方法輸出可序列化對象。註意寫出一次,操作flush()
-
反序列化
- 創建一個 ObjectInputStream
- 調用 readObject() 方法讀取流中的對象
- 強調:如果某個類的欄位不是基本數據類型或 String 類型,而是另一個引用類型,那麼這個引用類型必須是可序列化的,否則擁有該類型的 Field 的類也不能序列化
序列化:將對象寫入到磁碟或者進行網路傳輸。
要求對象必須實現序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test3.txt"));
Person p = new Person("韓梅梅",18,"中華大街",new Pet());
oos.writeObject(p);
oos.flush();
oos.close();
//反序列化:將磁碟中的對象數據源讀出。
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("test3.txt"));
Person p1 = (Person)ois.readObject();
System.out.println(p1.toString());
ois.close();
RandomAccessFile 類
-
RandomAccessFile 類支持 "隨機訪問" 的方式,程式可以直接跳到文件的任意地方來讀、寫文件
- 支持只訪問文件的部分內容
- 可以向已存在的文件後追加內容
-
RandomAccessFile 對象包含一個記錄指針,用以標示當前讀寫處的位置。RandomAccessFile 類對象可以自由移動記錄指針:
- long getFilePointer():獲取文件記錄指針的當前位置
- void seek(long pos):將文件記錄指針定位到 pos 位置
- 構造器
-
創建 RandomAccessFile 類實例需要指定一個 mode 參數,該參數指定 RandomAccessFile 的訪問模式:
- r: 以只讀方式打開
- rw:打開以便讀取和寫入
- rwd:打開以便讀取和寫入;同步文件內容的更新
- rws:打開以便讀取和寫入;同步文件內容和元數據的更新
讀取文件內容
RandomAccessFile raf = new RandomAccessFile("test.txt", "rw"); raf.seek(5); byte [] b = new byte[1024]; int off = 0; int len = 5; raf.read(b, off, len);
String str = new String(b, 0, len); System.out.println(str);
raf.close(); |
寫入文件內容
RandomAccessFile raf = new RandomAccessFile("test.txt", "rw"); raf.seek(5);
//先讀出來 String temp = raf.readLine();
raf.seek(5); raf.write("xyz".getBytes()); raf.write(temp.getBytes());
raf.close(); |
流的基本應用小節
- 流是用來處理數據的。
-
處理數據時,一定要先明確數據源,與數據目的地
- 數據源可以是文件,可以是鍵盤。
- 數據目的地可以是文件、顯示器或者其他設備。
- 而流只是在幫助數據進行傳輸,並對傳輸的數據進行處理,比如過濾處理、轉換處理等。
- 位元組流-緩衝流(重點)
- 輸入流InputStream-FileInputStream-BufferedInputStream
- 輸出流OutputStream-FileOutputStream-BufferedOutputStream
- 字元流-緩衝流(重點)
- 輸入流Reader-FileReader-BufferedReader
- 輸出流Writer-FileWriter-BufferedWriter
- 轉換流
- InputSteamReader和OutputStreamWriter
- 對象流ObjectInputStream和ObjectOutputStream(難點)
- 序列化
- 反序列化
- 隨機存取流RandomAccessFile(掌握讀取、寫入)