1 File

1.1 File類的概述和構造方法

  1. File: 它是文件和目錄路徑名的抽象表示
    • 文件和目錄是可以通過File封裝成對象的
    • 對于File而言,其封裝的并不是一個真正存在的文件,僅僅是一個路徑名而已。它可以是存在的,也可以是不存在的。將來是要通過具體的操作把這個路徑的內容轉換為具體存在的
方法名說明
File(String pathname)通過將給定的路徑名字符串轉換為抽象路徑名來創建新的File實例
File(String parent, String child)從父路徑名字符串和子路徑名字符串創建新的File實例
File(File parent, String child)從父抽象路徑名和子路徑名字符串創建新的File實例

1.2 File類的創建功能

方法名說明
public boolean createNewFile()當具有該名稱的文件不存在時,創建一個由該抽象路徑名命名的新空文件
public boolean mkdir()創建由此抽象路徑名命名的目錄
public boolean mkdirs()創建由此抽象路徑名命名的目錄,包括任何必需但不存在的父目錄

1.3 File類的刪除功能

方法名說明
public boolean delete()刪除由此抽象路徑名表示的文件或目錄
  1. 絕對路徑和相對路徑的區別

    • 絕對路徑: ==完整的路徑名==,不需要任何其他信息就可以定位它所表示的文件。
    • 相對路徑: 必須使用取自其他路徑名的信息進行解釋。
  2. 刪除目錄時的注意事項
    • 如果一個目錄中有內容(目錄,文件),不能直接刪除。應該先刪除目錄中的內容,最后才能刪除目錄

1.4 File類判斷和獲取功能

方法名說明
public boolean isDirectory()測試此抽象路徑名表示的File是否為目錄
public boolean isFile()測試此抽象路徑名表示的File是否為文件
public boolean exists()測試此抽象路徑名表示的File是否存在
public String getAbsolutePath()返回此抽象路徑名的絕對路徑名字符串
public String getPath()將此抽象路徑名轉換為路徑名字符串
public String getName()返回由此抽象路徑名表示的文件或目錄的名稱
public String[] list()返回此抽象路徑名表示的目錄中的文件和目錄的名稱字符串數組
public File[] listFiles()返回此抽象路徑名表示的目錄中的文件和目錄的File對象數組

1.5 遞歸

  1. 遞歸概述: 以編程的角度來看,遞歸指的是方法定義中調用方法本身的現象

  2. 遞歸解決問題的思路
    • 把一個復雜的問題層層轉化為一個與原問題相似的規模較小的問題來求解遞歸策略只需少量的程序就可描述出解題過程所需要的多次重復計算
  3. 遞歸解決問題要找到兩個內容
    • 遞歸出口: 否則會出現內存溢出
    • 遞歸規則: 與原問題相似的規模較小的問題

2 字節流

2.1 IO流概述和分類

  1. IO流概述

    • IO: 輸入/輸出(Input/Output)
    • 流: 是一種抽象概念,是對數據傳輸的總稱。也就是說數據在設備間的傳輸稱為流,流的本質是數據傳輸
    • IO流就是用來處理設備間數據傳輸問題的
      • 常見的應用: 文件復制、文件上傳、文件下載
  2. IO流分類

    • 按照數據的流向

      • 輸入流: 讀數據
      • 輸出流: 寫數據
    • 按照數據類型來分
      • 字節流
      • 字節輸入流、字節輸出流
      • 字符流
      • 字符輸入流、字節輸出流

    一般來說,我們說IO流的分類是按照==數據類型==來分的

  3. 那么這兩種流都在什么情況下使用呢?

如果數據通過Window自帶的記事本軟件打開,我們還可以==讀懂里面的內容==,就使用字符流,否則使用字節流。如果你不知道該使用哪種類型的流,就使用字節流

2.2 字節流寫數據

  1. 字節流抽象基類

    • InputStream: 這個抽象類是表示字節輸入流的所有類的超類

    • OutputStream: 這個抽象類是表示字節輸出流的所有類的超類
    • 子類名特點: 類名稱都是以其父類名作為子類名的后綴
  2. FileOutputStream: 文件輸出流用于將數據寫入File

    • FileOutputStream(String name): 創建文件輸出流以指定的名稱寫入文件
  3. 使用字節輸出流寫數據的步驟:
    • 創建字節輸出流對象(調用系統功能創建了文件,創建字節輸出流對象,讓字節輸出流對象指向文件)
    • 調用字節輸出流對象的寫數據方法
    • 釋放資源(關閉此文件輸出流并釋放與此流相關聯的任何系統資源)

2.3 字節流寫數據的3種方式

方法名說明
void write(int b)將指定的字節寫入此文件輸出流,一次寫一個字節數據
void write(byte[] b)將b.length字節從指定的字節數組寫入此文件輸出流,一次寫一個字節數組數據
void write(byte[] b, int off, int len)將len字節從指定的字節數組開始,從偏移量off開始寫入此文件輸出流,一次寫一個字節數組的部分數據

2.4 字節流寫數據的兩個小問題

  1. 字節流寫數據如何實現換行呢?
    • 寫完數據后,加換行符
      • windows: /r/n
      • linux: /n
      • mac: /r
  2. 字節流寫數據如何實現追加寫入呢?
    • public FileOutputStream(String name, boolean append)
    • 創建文件輸出流以指定的名稱寫入文件。如果第二個參數為true,則字節將寫入文件的末尾而不是開頭

2.5 字節流寫數據加異常處理

finally: 在異常處理時提供finally塊來執行所有清除操作。比如說IO流中的釋放資源

特點: 被finally控制的語句一定會執行,除非JVM退出

try {    可能出現異常的代碼;} catch (異常類名 變量名) {    異常的處理代碼;} finally {    執行所有清楚操作;}

2.6 字節流讀數據

  1. FilelnputStream: 從文件系統中的文件獲取輸入字節
    • FileInputStream(String name): 通過打開與實際文件的連接來創建一個FileInputStream,該文件由文件系統中的路徑名name命名
  2. 使用字節輸入流讀數據的步驟:
    1. 創建字節輸入流對象
    2. 調用字節輸入流對象的讀數據方法
      1. int read(): 一次讀一個字節數據
      2. int read(byte[] b): 一次讀一個字節數組數據
    3. 釋放資源

2.7 字節緩沖流

  1. 字節緩沖流:

    • BufferOutputStream: 該類實現緩沖輸出流。通過設置這樣的輸出流,應用程序可以向底層輸出流寫入字節,而不必為寫入的每個字節導致底層系統的調用
    • BufferedInputStream: 創建BufferedInputStream將創建一個內部緩沖區數組。當從流中讀取或跳過字節時,內部緩沖區將根據需要從所包含的輸入流中重新填充,一次很多字節
  2. 構造方法:

    • 字節緩沖輸出流: BufferedOutputStream(OutputStream out)
    • 字節緩沖輸入流: BufferedInputStream(InputStream in)
  3. 為什么構造方法需要的是字節流,而不是具體的文件或者路徑呢?
    • 字節緩沖流==僅僅提供緩沖區==,而真正的讀寫數據還得依靠基本的字節流對象進行操作

3 字符流

3.1 為什么會出現字符流

  1. 由于字節流操作中文不是特別的方便,所以Java就提供字符流

    • ==字符流 = 字節流+編碼表==
  2. 用字節流復制文本文件時,文本文件也會有中文,但是沒有問題,原因是最終底層操作會自動進行字節拼接成中文,如何識別是中文的呢?
    • 漢字在存儲的時候,無論選擇哪種編碼存儲,第一個字節都是負數

3.2 編碼表

  1. 基礎知識:

    • 計算機中儲存的信息都是用==二進制==數表示的; 我們在屏幕上看到的英文、漢字等字符是二進制數轉換之后
      的結果
    • 按照某種規則,將字符存儲到計算機中,稱為==編碼==。反之,將存儲在計算機中的二進制數按照某種規則解析顯示出來,稱為==解碼==。這里強調一下: 按照A編碼存儲,必須按照A編碼解析,這樣才能顯示正確的文本符號,否則就會導致亂碼現象
      • 字符編碼: 就是一套自然語言的字符與二進制數之間的對應規則(A, 65)
  2. 字符集:

    • 是一個系統支持的所有字符的集合,包括各國家文字、標點符號、圖形符號、數字等
    • 計算機要準確的存儲和識別各種字符集符號,就需要進行字符編碼,一套字符集必然至少有一套字符編碼。常見字符集有ASCII字符集、GBXXX字符集、Unicode字符集等
  3. ASCIl字符集:

    • ASCII(American Standard Code for Information Interchange,美國信息交換標準代碼): 是基于拉丁字母的一套電腦編碼系統,用于顯示現代英語,主要包括控制字符(回車鍵、退格、換行鍵等)和可顯示字符(英文大小寫字符、阿拉伯數字和西文符號)
    • 基本的ASCII字符集,使用7位表示一個字符,共128字符。ASCII的擴展字符集使用8位表示一個字符,共256字符,方便支持歐洲常用字符。是一個系統支持的所有字符的集合,包括各國家文字、標點符號、圖形符號、數字等
  4. GBXXX字符集:
    • GB2312: 簡體中文碼表。一個小于127的字符的意義與原來相同,但兩個大于127的字符連在一起時,就表示一個漢字,這樣大約可以組合了包含7000多個簡體漢字,此外數學符號、羅馬希臘的字母、日文的假名等都編進去了,連在ASCII里本來就有的數字、標點、字母都統統重新編了兩個字節長的編碼,這就是常說的"全角"字符,而原來在127號以下的那些就叫"半角"字符了
    • ==GBK==: 最常用的中文碼表。是在GB2312標準基礎上的擴展規范,使用了雙字節編碼方案,共收錄了21003個漢字,完全兼容GB2312標準,同時支持繁體漢字以及白韓漢字等
    • GB18030: 最新的中文碼表。收錄漢字70244個,采用多字節編碼,每個字可以由1個、2個或4個字節組成。支持中國國內少數民族的文字,同時支持繁體漢字以及日韓漢字等
  5. Unicode字符集:
    • 為表達任意語言的任意字符而設計,是業界的一種標準,也稱為統一碼、標準萬國碼。它最多使用4個字節的數字來表達每個字母、符號,或者文字。有三種編碼方案,UTF-8、UTF-16和UTF32。最為常用的UTF-8編碼
    • ==UTF-8編碼==: 可以用來表示Unicode標準中任意字符,它是電子郵件、網頁及其他存儲或傳送文字的應中,優先采用的編碼。互聯網工程工作小組(IETF)要求所有互聯網協議都必須支持UTF-8編碼。它使用一至四個字節為每個字符編碼
      • 編碼規則:
      • 128個US-ASCII字符,只需一個字節編碼
      • 拉丁文等字符,需要二個字節編鵒
      • 大部分常用字(含中文),使用三個字節編碼
      • 其他極少使用的Unicode輔助字符,使用四字節編碼

==注意: 采用何種規則編碼,就要采用對應規則解碼,否則就會出現亂碼==

3.3 字符串中的編碼解碼問題

  1. 編碼
    • byte[] getBytes(): 使用平臺的默認字符集將該String編碼為一系列字節,將結果存儲到新的字節數組中
    • byte[] getBytes(String charsetName): 使用指定的字符集將該String編碼為一系列字節,將結果存儲到新的字節數組中
  2. 解碼
    • String(byte[] bytes): 通過使用平臺的默認字符集解碼指定的字節數組來構造新的String
    • String(byte[] bytes, String charsetName): 通過指定的字符集解碼指定的字節數組來構造新的String

3.4 字符流中的編碼解碼問題

  1. 字符流抽象基類

    • Reader: 字符輸入流的抽象類
    • Writer: 字符輸出流的抽象類
  2. 字符流中和編碼解碼問題相關的兩個類:
    • InputStreamReader
    • OutputStreamWriter

3.5 字符流寫數據的5種方式

方法名說明
void write(int c)寫一個字符
void write(char[] cbuf)寫入一個字符數組
void write(char[] cbuf, int off, int len)寫入字符數組的一部分
void write(String str)寫一個字符串
void write(String str, int off, int len)寫一個字符串的一部分
方法名說明
flush()刷新流,還可以繼續寫數據
close()關閉流,釋放資源,但是在關閉之前會先刷新流。一旦關閉,就不能再寫數據

3.6 字符流讀數據的2種方式

方法名說明
int read()一次讀一個字符數據
int read(char[] cbuf)一次讀一個字符數組數據
  1. FileReader: 用于讀取字符文件的便捷類
    • FileReader(String fileName)
  2. FileWriter: 用于寫入字符文件的便捷類
    • FileWriter(String fileName)

3.7 字符緩沖流

  1. 字符緩沖流:

    • BufferedWriter: 將文本寫入字符輸出流,緩沖字符,以提供單個字符、數組和字符串的高效寫入,可以指定緩沖區大小,或者可以接受默認大小。默認值足夠大,可用于大多數用途
    • BufferedReader: 從字符輸入流讀取文本,緩沖字符,以提供字符、數組和行的高效讀取,可以指定緩沖區大小,或者可以使用默認大小。默認值足夠大,可用于大多數用途
  2. 構造方法:
    • BufferedWriter(Writer out)
    • BufferedReader(Reader in)

3.8 字符緩沖流特有功能

  1. BufferedWriter:

    • void newLine(): 寫一行行分隔符,行分隔符字符串由系統屬性定義
  2. BufferedReader:
    • public String readLine(): 讀一行文字。結果包含行的內容的字符串,不包括任何行終止字符,如果流的結尾已經到達,則為null

3.9 IO流小結

==小結: 字節流可以復制任意文件數據,有4種方式一般采用字節緩沖流一次讀寫一個字節數組的方式==


==小結: 字符流只能復制文本數據,有5種方式,一般采用字符緩沖流的特有功能==

3.10 復制文件的異常處理

  1. try...catch...finally的做法
try {    可能出現異常的代碼;} catch(異常類名 變量名) {    異常的處理代碼;} finally {    執行所有清除操作;}
  1. JDK的改進方案
try(定義流對象) {    可能出現異常的代碼;} catch(異常類名 變量名) {    異常的處理代碼;}// 自動釋放資源
  1. JDK9改進方案
// 定義輸入流對象// 定義輸出流對象try (輸入流對象 : 輸出流對象) {    可能出現異常的代碼;} catch(異常類名 變量名) {    異常的處理代碼;}// 自動釋放資源

4 特殊操作流

4.1 標準輸入輸出流

  1. System類中有兩個靜態的成員變量:

    • public static final InputStream in: 標準輸入流。通常該流對應于鍵盤輸入或油由主機環境或用戶指定的另一個輸入源
    • public static final PrintStream out: 標準輸出流。通常該流對應于顯示輸出或由主機環境或用戶指定的另一個輸出目標
  2. 自己實現鍵盤錄入數據:

    • BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
  3. 寫起來太麻煩,Java就提供了一個類實現鍵盤錄入

    • Scanner sc = new Scanner(System.in);
  4. 輸出語句的本質: 是一個標準的輸出流
    • PrintStream ps = System.out;
    • PrintStream類有的方法,System.out都可以使用

4.2 打印流

  1. 打印流分類:

    • 字節打印流: PrintStream
    • 字符打印流: PrintWriter
  2. 打印流的特點:

    • 只負責輸出數據,不負責讀取數據
    • 有自己的特有方法
  3. 字節打印流
    • PrintStream(String fileName): 使用指定的文件名創建新的打印流
    • 使用繼承父類的方法寫數據,查看的時候會轉碼; 使用自己的特有方法寫數據,查看的數據原樣輸出

4.3 對象序列化流

  1. 對象序列化: 就是將對象保存到磁盤中,或者在網絡中傳輸對象

    • 這種機制就是使用一個字節序列表示一個對象,該字節序列包含: 對象的類型、對象的數據和對象中存儲的屬性等信息字節序列寫到文件之后,相當于文件中持久保存了一個對象的信息
    • 反之,該字節序列還可以從文件中讀取回來,重構對象,對它進行反序列化
  2. 要實現序列化和反序列化就要使用對象序列化流和對象反序列化流:
    • 對象序列化流: ObjectOutputStream
    • 對象反序列化流: ObjectInputStream

4.3.1 對象序列化流

  1. 對象序列化流: ObjectOutputStream

    • 將Java對象的原始數據類型和圖形寫入OutputStream。可以使用ObjectInputStream讀取(重構)對象。可以通過使用流的文件來實現對象的持久存儲。如果流是網絡套接字流,則可以在另一個主機上或另一個進程中重構對象
  2. 構造方法:

    • ObjectOutputStream(OutputStream out): 創建一個寫入指定的OutputStream的ObjectOutputStream
  3. 序列化對象的方法:

    • void writeObject(Object obj): 將指定的對象寫入ObjectOutputStream
  4. 注意:
    • 一個對象要想被序列化,該對象所屬的類必須必須==實現Serializable接口==
    • Serializable是一個==標記接口==,實現該接口不需要重寫任何方法

4.3.2 對象反序列化流

  1. 對象反序列化流: ObjectInputStream

    • ObjectInputStream反序列化先前使用ObjectOutputStream編寫的原始數據和對象
  2. 構造方法:
    • ObjectInputStream(InputStream in): 創建從指定的IlnputStream讀取的ObjectInputStream
  3. 反序列化對象的方法:
    • Object readObject(): 從ObjectInputStream讀取一個對象

4.3.3 問題

  1. 用對象序列化流序列化了一個對象后,假如我們修改了對象所屬的類文件,讀取數據會不會出問題呢?
    • 會出問題,拋出==InvalidClassException==異常
  2. 如果出問題了,如何解決呢?
    • 給對象所屬的類加一個==serialVersionUID==
      private static final long serialVersionUID = 42L;
  3. 如果一個對象中的某個成員變量的值不想被序列化,又該如何實現呢?
    • 給該成員變量加==transient==關鍵字修飾,該關鍵字標記的成員變量不參與序列化過程

4.4 Properties

  1. Properties概述:

    • 是一個Map體系的集合類
    • Properties可以保存到流中或從流中加載
  2. Properties作為集合的特有方法:
方法名說明
Object setProperty(String key, String value)設置集合的鍵和值,都是String類型,底層調用Hashtable方法put
String getProperty(String key)使用此屬性列表中指定的鍵搜索屬性
Set/ stringPropertyNames()從該屬性列表中返回一個不可修改的鍵集,其中鍵及其對應的值是字符串
  1. Properties和IO流結合的方法:
方法名說明
void load(InputStream inStream)從輸入字節流讀取屬性列表(鍵健和元素對)
==void load(Reader reader)==從輸入字符流讀取屬性列表(鍵和元素對)
void store(OutputStream out, String comments)將此屬性列表(鍵和元素對)寫入此Properties表中,以適合于使用load(InputStream)方法的格式寫入輸出字節流
==void store(Writer writer, String comments)==將此屬性列表(鍵和元素對)寫入此Properties表中,以適合使用load(Reader)方法的格式寫入輸出字符流