第10章 流
2019獨角獸企業重金招聘Python工程師標準>>>
File 類
- java.io.File類:文件和目錄路徑名的抽象表示形式,與平臺無關。
- File 能新建、刪除、重命名文件和目錄,但 File 不能訪問文件內容本身。 如果需要訪問文件內容本身,則需要使用 輸入/輸出流。
- File對象可以作為參數傳遞給流的構造函數。
File的靜態屬性
String separator存儲了當前系統的路徑分隔符。 在UNIX中,此字段為‘/’,在Windows中,為‘\’。
File類的常見構造方法
public File(String pathname)
以pathname為路徑創建File對象,可以是絕對路徑或者相對路徑,如果pathname是相對路徑,則默認的當前路徑在系統屬性user.dir中存儲。
public File(String parent,String child)
以parent為父路徑,child為子路徑創建File對象。
File類的常見方法
- 訪問文件名:
getName() getPath() getAbsoluteFile() getAbsolutePath() getParent() renameTo(File newName)
- 文件檢測:
exists() canWrite() canRead() isFile() isDirectory()
- 獲取常規文件信息:
lastModified() length()
- 文件操作相關:
createNewFile() delete()
- 目錄操作相關:
mkDir() mkDirs() list() listFiles()
File dir1 = new File("D:/IOTest/dir1"); // 如果D:/IOTest/dir1不存在,就創建為目錄 if (!dir1.exists()) {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()) { // 如果還不存在,就創建為文件try {file.createNewFile();} catch (IOException e) {e.printStackTrace();} }練習
利用File構造器,new 一個目錄file
1)在其中創建多個文件和目錄。
2)編寫方法,實現刪除file中文件的操作。
Java IO原理
- IO流用來處理設備之間的數據傳輸。
- Java程序中,對于數據的輸入/輸出操作以“流(stream)”的方式進行。
- java.io包下提供了各種“流”類和接口,用以獲取不同種類的數據,并通過標準的方法輸入或輸出數據。
- 輸入input:讀取外部數據(磁盤、光盤等存儲設備的數據)到程序(內存)中。
- 輸出output:將程序(內存)數據輸出到磁盤、光盤等存儲設備中。
流的分類
- 按操作數據單位不同分為:字節流(8 bit),字符流(16 bit) 。
- 按數據流的流向不同分為:輸入流,輸出流。
- 按流的角色的不同分為:節點流,處理流。
- Java的IO流共涉及40多個類,實際上非常規則,都是從如下4個抽象基類派生的。
- 由這四個類派生出來的子類名稱都是以其父類名作為子類名后綴。
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 是所有輸出流的基類。
- OutputStream(典型實現:FileOutputStream)。
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);
文件流
讀取文件
- 建立一個流對象,將已存在的一個文件加載進流。 FileReader fr = new FileReader(“Test.txt”);
- 創建一個臨時存放數據的數組。 char[] ch = new char[1024];
- 調用流對象的讀取方法將流中的數據讀入到數組中。 fr.read(ch);
寫入文件
-
創建流對象,建立數據存放文件。
FileWriter fw = new FileWriter("Test.txt") -
調用流對象的寫入方法,將數據寫入流。
fw.write("text") -
關閉流資源,并將流中的數據清空到文件中。
fw.close()
例:
FileWriter fw = null; try {fw = new FileWriter("Test.txt");fw.write("text"); } catch (IOException e) {e.printStackTrace(); } finally {if (fw != null) {try {fw.close();} catch (IOException e) {System.out.println(e.toString());}} }注意
- 定義文件路徑時,注意:可以用“/”或者“\”。
- 在寫入一個文件時,如果目錄下有同名文件將被覆蓋。
- 在讀取文件時,必須保證該文件已存在,否則出異常。
處理流之一:緩沖流
- 為了提高數據讀寫的速度,Java API提供了帶緩沖功能的流類,在使用這些流類時,會創建一個內部緩沖區數組。
- 根據數據操作單位可以把緩沖流分為:
BufferedInputStream 和 BufferedOutputStream
BufferedReader 和 BufferedWriter
- 緩沖流要“套接”在相應的節點流之上,對讀寫的數據提供了緩沖的功能,提高了讀寫的效率,同時增加了一些新的方法。
- 對于輸出的緩沖流,寫出的數據會先在內存中緩存,使用flush()將會使內存中的數據立刻寫出。
練習
分別使用
節點流:FileInputStream、FileOutputStream
和
緩沖流:BufferedInputStream、BufferedOutputStream
實現文本文件/圖片/視頻文件的復制。
并比較二者在數據復制方面的效率。
處理流之二:轉換流
- 轉換流提供了在字節流和字符流之間的轉換。
- 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 os)
public OutputSreamWriter(OutputStream os,String charsetName)
補充:字符編碼
- 編碼表的由來
計算機只能識別二進制數據,早期由來是電信號。為了方便應用計算機,讓它可以識別各個國家的文字。就將各個國家的文字用數字來表示,并一一對應,形成一張表。這就是編碼表。
- 常見的編碼表
ASCII:美國標準信息交換碼。用一個字節的7位可以表示。 ISO8859-1:拉丁碼表。歐洲碼表。用一個字節的8位表示。 GB2312:中國的中文編碼表。 GBK:中國的中文編碼表升級,融合了更多的中文文字符號。 Unicode:國際標準碼,融合了多種文字。所有文字都用兩個字節來表示,Java語言使用的就是unicode。 UTF-8:最多用三個字節來表示一個字符。
- 編碼:字符串 → 字節數組
- 解碼:字節數組 → 字符串
- 轉換流的編碼應用
可以將字符按指定編碼格式存儲。 可以對文本數據按指定編碼格式來解讀。 指定編碼表的動作由構造器完成。
練習
將“你好”兩個字符查指定的utf-8的碼表,獲取對應的數字,并寫入到text.txt文件中。
public class Test {public static void main(String[] args) throws IOException {OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("text.txt"), "utf-8");osw.write("你好");osw.close();//讀取硬盤上的文件數據,將獲取到的數據查指定utf-8 的碼表來解析該數據。InputStreamReader isr = new InputStreamReader(new FileInputStream("text.txt"), "utf-8");char[] buf = new char[10];int num = isr.read(buf);String s = new String(buf, 0, num);System.out.println(s);//傳入編碼表的方法都會拋出不支持編碼異常(UnsupportedEncodingException);} }處理流之三:標準輸入輸出流
- 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)
例題
從鍵盤輸入字符串,要求將讀取到的整行字符串轉成大寫輸出。然后繼續進行輸入操作,直至當輸入“e”或者“exit”時,退出程序。
public class Test {public static void main(String[] args) {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();}}} }或者
public class Test5 {public static void main(String[] args) {Scanner input = new Scanner(System.in);System.out.println("請輸入字符串:");while (input.hasNext()) {String line = input.nextLine();if ("e".equals(line) || "exit".equals(line)) {System.exit(0);}String upperStr = line.toUpperCase();System.out.println(upperStr);}} }處理流之四:打印流(了解)
- 在整個IO包中,打印流是輸出信息最方便的類。
- PrintStream(字節打印流)和PrintWriter(字符打印流)。
提供了一系列重載的print和println方法,用于多種數據類型的輸出。 PrintStream和PrintWriter的輸出不會拋出異常。 PrintStream和PrintWriter有自動flush功能。 System.out返回的是PrintStream的實例。
public class Test {public static void main(String[] args) {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()
char readChar()
float readFloat()
double readDouble()
byte readByte()
short readShort()
int readInt()
long readLong()
String readUTF()
void readFully(byte[] b)
- DataOutputStream中的方法
將上述的方法的read改為相應的write即可。
public class Test {public static void main(String[] args) {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 的類也不能序列化。
RandomAccessFile 類
- RandomAccessFile 類支持 “隨機訪問” 的方式,程序可以直接跳到文件的任意地方來讀、寫文件。
支持只訪問文件的部分內容。
可以向已存在的文件后追加內容。
- RandomAccessFile 對象包含一個記錄指針,用以標示當前讀寫處的位置。RandomAccessFile 類對象可以自由移動記錄指針:
long getFilePointer():獲取文件記錄指針的當前位置。 void seek(long pos):將文件記錄指針定位到 pos 位置。
- 構造器
public RandomAccessFile(File file, String mode)
public RandomAccessFile(String name, String mode)
- 創建 RandomAccessFile 類實例需要指定一個 mode 參數,該參數指定 RandomAccessFile 的訪問模式:
r: 以只讀方式打開。
rw:打開以便讀取和寫入。
rwd:打開以便讀取和寫入;同步文件內容的更新。
rws:打開以便讀取和寫入;同步文件內容和元數據的更新。
讀取文件內容
public class Test {public static void main(String[] args) throws Exception {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();} }寫入文件內容
public class Test {public static void main(String[] args) throws Exception {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
轉載于:https://my.oschina.net/mondayer/blog/3027711
總結
- 上一篇: yum仓库、源以及编译安装笔记
- 下一篇: Go语言很好很强大,但我有几个问题想吐槽