Java小白學習之路(Java基礎)之 檔案和流

2020-08-12 12:29:30

檔案和流

檔案File類

File類的使用

  • java.io.File類:檔案和檔案目錄路徑的抽象表示形式,與平臺無關

  • File 能新建、刪除、重新命名檔案和目錄,但 File 不能存取檔案內容本身。如果需要存取檔案內容本身,則需要使用輸入/輸出流。

  • 想要在Java程式中表示一個真實存在的檔案或目錄,那麼必須有一個File對 象,但是Java程式中的一個File物件,可能沒有一個真實存在的檔案或目錄。

  • File物件可以作爲參數傳遞給流的構造器

File類的使用:常用構造器
  • public File(String pathname)

    以pathname爲路徑建立File物件,可以是絕對路徑或者相對路徑,如果pathname是相對路徑,則預設的當前路徑在系統屬性user.dir中儲存。

    >絕對路徑:是一個固定的路徑,從碟符開始

    >相對路徑:是相對於某個位置開始

  • public File(String parent,String child)

    以parent爲父路徑,child爲子路徑建立File物件。

  • public File(File parent,String child)

    根據一個父File物件和子檔案路徑建立File物件

File類的使用:路徑分隔符
  • 路徑中的每級目錄之間用一個路徑分隔符隔開。

  • 路徑分隔符和系統有關:

    >windows和DOS系統預設使用「\」來表示

    >UNIX和URL使用「/」來表示

  • Java程式支援跨平臺執行,因此路徑分隔符要慎用。

  • 爲了解決這個隱患,File類提供了一個常數:public static final String separator。根據操作系統,動態的提供分隔符。

舉例:

File file1 = new File("d:\\study\\info.txt");
File file2 = new File("d:" + File.separator + "study" + File.separator + "info.txt");
File file3 = new File("d:/study");
File類的使用:常用方法

File類的獲取功能

  • public String getAbsolutePath():獲取絕對路徑

  • public String getPath() :獲取路徑

  • public String getName() :獲取名稱

  • public String getParent():獲取上層檔案目錄路徑。若無,返回null

  • public long length() :獲取檔案長度(即:位元組數)。不能獲取目錄的長度。

  • public long lastModified() :獲取最後一次的修改時間,毫秒值

  • public String[] list() :獲取指定目錄下的所有檔案或者檔案目錄的名稱陣列

  • public File[] listFiles() :獲取指定目錄下的所有檔案或者檔案目錄的File陣列

File類的重新命名功能

  • public boolean renameTo(File dest):把檔案重新命名爲指定的檔案路徑

File類的判斷功能

  • public boolean isDirectory():判斷是否是檔案目錄

  • public boolean isFile() :判斷是否是檔案

  • public boolean exists() :判斷是否存在

  • public boolean canRead() :判斷是否可讀

  • public boolean canWrite() :判斷是否可寫

  • public boolean isHidden() :判斷是否隱藏

File類的建立功能

  • public boolean createNewFile() :建立檔案。若檔案存在,則不建立,返回false

  • public boolean mkdir() :建立檔案目錄。如果此檔案目錄存在,就不建立了。

    如果此檔案目錄的上層目錄不存在,也不建立。

  • public boolean mkdirs() :建立檔案目錄。如果上層檔案目錄不存在,一併建立

    注意事項:如果你建立檔案或者檔案目錄沒有寫碟符路徑,那麼,預設在專案路徑下。

File類的刪除功能

  • public boolean delete():刪除檔案或者資料夾

    刪除注意事項:Java中的刪除不走回收站。要刪除一個檔案目錄,請注意該檔案目錄內不能包含檔案或者檔案目錄

IO流

IO流原理及流的分類

Java IO原理
  • I/O是Input/Output的縮寫, I/O技術是非常實用的技術,用於處理裝置之間的數據傳輸。如讀/寫檔案,網路通訊等。

  • Java程式中,對於數據的輸入/輸出操作以「流(stream)」 的方式進行。

  • java.io包下提供了各種「流」類和介面,用以獲取不同種類的數據,並通過標準的方法輸入或輸出數據。

  • 輸入input:讀取外部數據(磁碟、光碟等儲存裝置的數據)到程式(記憶體)中。

  • 輸出output:將程式(記憶體)數據輸出到磁碟、光碟等儲存裝置中。

流的分類
  • 按操作數據單位不同分爲:位元組流(8 bit),字元流(16 bit)

  • 按數據流的流向不同分爲:輸入流,輸出流

  • 按流的角色的不同分爲:節點流,處理流

(抽象基礎類別) 位元組流 字元流
輸入流 InputStream Reader
輸出流 OutputStream Writer
  1. Java的IO流共涉及40多個類,實際上非常規則,都是從如下4個抽象基礎類別派生的。

  2. 由這四個類派生出來的子類名稱都是以其父類別名作爲子類名後綴。
    在这里插入图片描述

IO流體系
分類 位元組輸入流 位元組輸出流 字元輸入流 字元輸出流
抽象基礎類別 InputStream OutputStream Reader Writer
存取檔案 FileInputStream FileOutputStream FileReader FileWriter
存取陣列 ByteArrayInputStream ByteArrayOutputStream CharArrayReader CharArrayWriter
存取管道 PipedInputStream PipedOutputStream PipedReader PipedWriter
存取字串 StringReader StringWriter
緩衝流 BufferedInputStream BufferedOutputStream BufferedReader BufferedWriter
轉換流 InputStreamReader InputStreamWriter
物件流 ObjectInputStream ObjectOutputStream
FilterInputStream FilterOutputStream FilterReader FilterWriter
列印流 PrintStream PrintWriter
退回輸入流 PushbackInputStream PushbackReader
特殊流 DataInputStream DataOutputStream
節點流和處理流
  • 節點流:直接從數據源或目的地讀寫數據

在这里插入图片描述

  • 處理流:不直接連線到數據源或目的地,而是「連線」在已存在的流(節點流或處理流)之上,通過對數據的處理爲程式提供更爲強大的讀寫功能。
    在这里插入图片描述
InputStream
  • int read()

    從輸入流中讀取數據的下一個位元組。返回 0 到 255 範圍內的 int 位元組值。如果因爲已經到達流末尾而沒有可用的位元組,則返回值 -1。

  • int read(byte[] b)

    從此輸入流中將最多 b.length 個位元組的數據讀入一個 byte 陣列中。如果因爲已經到達流末尾而沒有可用的位元組,則返回值 -1。否則以整數形式返回實際讀取的位元組數。

  • int read(byte[] b, int off,int len)

    將輸入流中最多 len 個數據位元組讀入 byte 陣列。嘗試讀取 len 個位元組,但讀取的位元組也可能小於該值。以整數形式返回實際讀取的位元組數。如果因爲流位於檔案末尾而沒有可用的位元組,則返回值 -1。

  • public void close() throws IOException

    關閉此輸入流並釋放與該流關聯的所有系統資源。

Reader
  • int read()

    讀取單個字元。作爲整數讀取的字元,範圍在 0 到 65535 之間 (0x00-0xffff)(2個位元組的Unicode碼),如果已到達流的末尾,則返回 -1

  • int read(char[] cbuf)

    將字元讀入陣列。如果已到達流的末尾,則返回 -1。否則返回本次讀取的字元數。

  • int read(char[] cbuf,int off,int len)

    將字元讀入陣列的某一部分。存到陣列cbuf中,從off處開始儲存,最多讀len個字元。如果已到達流的末尾,則返回 -1。否則返回本次讀取的字元數。

  • public void close() throws IOException

    關閉此輸入流並釋放與該流關聯的所有系統資源。

OutputStream
  • 將指定的位元組寫入此輸出流。write 的常規協定是:向輸出流寫入一個位元組。要寫入的位元組是參數 b 的八個低位。b 的 24 個高位將被忽略。 即寫入0~255範圍的。

  • void write(byte[] b)

    將 b.length 個位元組從指定的 byte 陣列寫入此輸出流。write(b) 的常規協定是:應該與呼叫 write(b, 0, b.length) 的效果完全相同。

  • void write(byte[] b,int off,int len)

    將指定 byte 陣列中從偏移量 off 開始的 len 個位元組寫入此輸出流。

  • public void flush()throws IOException

    重新整理此輸出流並強制寫出所有緩衝的輸出位元組,呼叫此方法指示應將這些位元組立即寫入它們預期的目標。

  • public void close() throws IOException

    關閉此輸出流並釋放與該流關聯的所有系統資源。

Writer
  • void write(int c)

    寫入單個字元。要寫入的字元包含在給定整數值的 16 個低位中,16 高位被忽略。 即

    寫入0 到 65535 之間的Unicode碼。

  • void write(char[] cbuf)

    寫入字元陣列。

  • void write(char[] cbuf,int off,int len)

    寫入字元陣列的某一部分。從off開始,寫入len個字元

  • void write(String str)

    寫入字串。

  • void write(String str,int off,int len)

    寫入字串的某一部分。

  • void flush()

    重新整理該流的緩衝,則立即將它們寫入預期目標。

  • public void close() throws IOException

    關閉此輸出流並釋放與該流關聯的所有系統資源。

位元組流(或檔案流)

讀取檔案

1.建立一個流物件,將已存在的一個檔案載入進流。

FileReader fr = new FileReader(new File(「Test.txt」));

2.建立一個臨時存放數據的陣列。

char[] ch = new char[1024];

3.呼叫流物件的讀取方法將流中的數據讀入到陣列中。

fr.read(ch);

4.關閉資源。

fr.close();

寫入檔案

1.建立流物件,建立數據存放檔案

FileWriter fw = new FileWriter(new File(「Test.txt」));

2.呼叫流物件的寫入方法,將數據寫入流

fw.write(「abc123」);

3.關閉流資源,並將流中的數據清空到檔案中。

fw.close();

注意

  • 定義檔案路徑時,注意:可以用「/」或者「\」。

  • 在寫入一個檔案時,如果使用構造器FileOutputStream(file),則目錄下有同名檔案將被覆蓋。

  • 如果使用構造器FileOutputStream(file,true),則目錄下的同名檔案不會被覆蓋,在檔案內容末尾追加內容。

  • 在讀取檔案時,必須保證該檔案已存在,否則報異常。

  • 位元組流操作位元組,比如:.mp3,.avi,.rmvb,mp4,.jpg,.doc,.ppt

  • 字元流操作字元,只能操作普通文字檔案。最常見的文字檔案:.txt,.java,.c,.cpp 等語言的原始碼。尤其注意.doc,excel,ppt這些不是文字檔案。

緩衝流

  • 爲了提高數據讀寫的速度,Java API提供了帶緩衝功能的流類,在使用這些流類時,會建立一個內部緩衝區陣列,預設使用8192個位元組(8Kb)的緩衝區。
public class BufferedInputStream extends FilterInputStream{
	private static int DEFAULT_BUFFER_SIZE = 8192;
}
  • 緩衝流要「套接」在相應的節點流之上,根據數據操作單位可以把緩衝流分爲:

    BufferedInputStream和BufferedOutputStream

    BufferedReader和BufferedWriter

  • 當讀取數據時,數據按塊讀入緩衝區,其後的讀操作則直接存取緩衝區

  • 當使用BufferedInputStream讀取位元組檔案時,BufferedInputStream會一次性從檔案中讀取8192個(8Kb),存在緩衝區中,直到緩衝區裝滿了,才重新從檔案中讀取下一個8192個位元組陣列。

  • 向流中寫入位元組時,不會直接寫到檔案,先寫到緩衝區中直到緩衝區寫滿,BufferedOutputStream纔會把緩衝區中的數據一次性寫到檔案裡。使用方法flush()可以強制將緩衝區的內容全部寫入輸出流

  • 關閉流的順序和開啓流的順序相反。只要關閉最外層流即可,關閉最外層流也會相應關閉內層節點流

  • flush()方法的使用:手動將buffer中內容寫入檔案

  • 如果是帶緩衝區的流物件的close()方法,不但會關閉流,還會在關閉流之前重新整理緩衝區,關閉後不能再寫出

轉換流

  • 轉換流提供了在位元組流和字元流之間的轉換

  • Java API提供了兩個轉換流:

    InputStreamReader:將InputStream轉換爲Reader

    OutputStreamWriter:將Writer轉換爲OutputStream

  • 位元組流中的數據都是字元時,轉成字元流操作更高效。

  • 很多時候我們使用轉換流來處理檔案亂碼問題。實現編碼和解碼的功能。

InputStreamReader

  • 實現將位元組的輸入流按指定字元集轉換爲字元的輸入流。

  • 需要和InputStream「套接」。

  • 構造器

    public InputStreamReader(InputStream in)

    public InputSreamReader(InputStream in,String charsetName)

    如: Reader isr = new InputStreamReader(System.in,」UTF-8」);

OutputStreamWriter

  • 實現將字元的輸出流按指定字元集轉換爲位元組的輸出流。

  • 需要和OutputStream「套接」。

  • 構造器

    public OutputStreamWriter(OutputStream out)

    public OutputSreamWriter(OutputStream out,String charsetName)

在这里插入图片描述

標準輸入、輸出流(瞭解)

  • 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)

列印流(瞭解)

  • 實現將基本數據型別的數據格式轉化爲字串輸出

  • 列印流:PrintStream和PrintWriter

    >提供了一系列過載的print()和println()方法,用於多種數據型別的輸出

    >PrintStream和PrintWriter的輸出不會拋出IOException異常

    >PrintStream和PrintWriter有自動flush功能

    >PrintStream 列印的所有字元都使用平臺的預設字元編碼轉換爲位元組。在需要寫入字元而不是寫入位元組的情況下,應該使用 PrintWriter 類。

    >System.out返回的是PrintStream的範例

數據流(瞭解)

  • 爲了方便地操作Java語言的基本數據型別和String的數據,可以使用數據流。

  • 數據流有兩個類:(用於讀取和寫出基本數據型別、String類的數據)

    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即可。

物件流

  • ObjectInputStream和OjbectOutputSteam

    用於儲存和讀取基本數據型別數據或物件的處理流。它的強大之處就是可以把Java中的物件寫入到數據源中,也能把物件從數據源中還原回來。

  • 序列化:用ObjectOutputStream類儲存基本型別數據或物件的機制 機製

  • 反序列化:用ObjectInputStream類讀取基本型別數據或物件的機制 機製

  • ObjectOutputStream和ObjectInputStream不能序列化static和transient修飾的成員變數

物件的序列化
  • 物件序列化機制 機製允許把記憶體中的Java物件轉換成平臺無關的二進制流,從而允許把這種二進制流持久地儲存在磁碟上,或通過網路將這種二進制流傳輸到另一個網路節點。//當其它程式獲取了這種二進制流,就可以恢復成原來的Java物件

  • 序列化的好處在於可將任何實現了Serializable介面的物件轉化爲位元組數據,使其在儲存和傳輸時可被還原

  • 序列化是 RMI(Remote Method Invoke – 遠端方法呼叫)過程的參數和返回值都必須實現的機制 機製,而 RMI 是 JavaEE 的基礎。因此序列化機制 機製是JavaEE 平臺的基礎

  • 如果需要讓某個物件支援序列化機制 機製,則必須讓物件所屬的類及其屬性是可序列化的,爲了讓某個類是可序列化的,該類必須實現如下兩個介面之一。否則,會拋出NotSerializableException異常

    >Serializable

    >Externalizable

  • 凡是實現Serializable介面的類都有一個表示序列化版本識別符號的靜態變數:

    >private static final long serialVersionUID;

    >serialVersionUID用來表明類的不同版本間的相容性。簡言之,其目的是以序列化物件進行版本控制,有關各版本反序列化時是否相容。

    >如果類沒有顯示定義這個靜態常數,它的值是Java執行時環境根據類的內部細節自動生成的。若類的範例變數做了修改,serialVersionUID 可能發生變化。故建議,顯式宣告。

  • 簡單來說,Java的序列化機制 機製是通過在執行時判斷類的serialVersionUID來驗證版本一致性的。在進行反序列化時,JVM會把傳來的位元組流中的serialVersionUID與本地相應實體類的serialVersionUID進行比較,如果相同就認爲是一致的,可以進行反序列化,否則就會出現序列化版本不一致的異常。(InvalidCastException)

使用物件流序列化物件
  • 若某個類實現了 Serializable 介面,該類的物件就是可序列化的:

    >建立一個ObjectOutputStream

    >呼叫ObjectOutputStream物件的writeObject(物件)方法輸出可序列化物件

    >注意寫出一次,操作lush()一次

  • 反序列化

    >建立一個ObjectInputStream

    >呼叫readObject()方法讀取流中的物件

  • 強調:如果某個類的屬性不是基本數據型別或 String 型別,而是另一個參照型別,那麼這個參照型別必須是可序列化的,否則擁有該型別的Field 的類也不能序列化

//序列化:將物件寫入到磁碟或者進行網路傳輸。
//要求物件必須實現序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(「data.txt"));
Person p = new Person("韓梅梅", 18, "中華大街", new Pet());
oos.writeObject(p);
oos.flush();
oos.close();
//反序列化:將磁碟中的物件數據源讀出。
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(「data.txt"));
Person p1 = (Person)ois.readObject();
System.out.println(p1.toString());
ois.close();

某個類的屬性不是基本數據型別或 String 型別,而是另一個參照型別,那麼這個參照型別必須是可序列化的,否則擁有該型別的Field 的類也不能序列化

//序列化:將物件寫入到磁碟或者進行網路傳輸。
//要求物件必須實現序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(「data.txt"));
Person p = new Person("韓梅梅", 18, "中華大街", new Pet());
oos.writeObject(p);
oos.flush();
oos.close();
//反序列化:將磁碟中的物件數據源讀出。
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(「data.txt"));
Person p1 = (Person)ois.readObject();
System.out.println(p1.toString());
ois.close();