1. 程式人生 > 實用技巧 >JavaSE第20篇:Java之IO流上篇

JavaSE第20篇:Java之IO流上篇

核心概述:如何獲取和遍歷本地檔案及目錄資訊?如何使用讀寫本地檔案?本篇我們將學習File類來獲取本地檔案資訊,學習遞迴來遍歷檔案及目錄,學習Java中的IO流來實現對本地檔案的讀寫。

目錄

第一章:File類

1.1-概述(瞭解)

java.io.File 類是檔案和目錄路徑名的抽象表示,主要用於檔案和目錄的建立、查詢和刪除等操作。File類將檔案,資料夾和路徑封裝成了物件,提供大量的方法來操作這些物件。

1.2-File類的靜態成員(瞭解)

靜態成員

  • static String pathSeparator 與系統有關的路徑分隔符。
    • Window作業系統,分隔符是分號。
    • Linux作業系統,分隔符是冒號。
  • static String separator 與系統有關的預設名稱分隔符。
    • Window作業系統,名稱分割符號為 \。
    • Linux作業系統,名稱分隔符號為 /。

示例

public class Test01 {
    public static void main(String[] args) {
        System.out.println(File.pathSeparator);  // 輸出結果:";"
        System.out.println(File.separator);     //  輸出結果:"/"
        // 注意:不同的作業系統獲取的分隔符是不一樣的
    }
}

1.3-File類的構造方法(重要)

構造方法

  1. public File(String pathname) :通過將給定的路徑名字串轉換為抽象路徑名來建立新的 File例項。
  2. public File(String parent, String child) :從父路徑名字串和子路徑名字串建立新的 File例項。
  3. public File(File parent, String child) :從父抽象路徑名和子路徑名字串建立新的 File例項。

示例

public class Test02 {
    public static void main(String[] args) {
        // 建立File物件-方式1
        File file1 = new File("D:\\JavaCode\\BasicCode\\a");
        System.out.println(file1);  // 輸出結果: D:\JavaCode\BasicCode\a
        // 建立File物件-方式2
        File file2 = new File("D:\\JavaCode\\BasicCode\\a","1.txt");
        System.out.println(file2);  // 輸出結果:D:\JavaCode\BasicCode\a\1.txt
        // 建立File物件-方式3
        File file3 = new File(file1,"1.txt");
        System.out.println(file3);  // 輸出結果:D:\JavaCode\BasicCode\a\1.txt
    }

注意

  1. 一個File物件代表硬碟中實際存在的一個檔案或者目錄。
  2. 無論該路徑下是否存在檔案或者目錄,都不影響File物件的建立。

1.4-File物件獲取功能相關方法(重要)

方法

  1. public String getAbsolutePath():返回此File的絕對路徑名字串。
  2. public String getPath() :將此File轉換為路徑名字串。
  3. public String getName():返回由此File表示的檔案或目錄的名稱。
  4. public long length():返回由此File表示的檔案的長度。

示例

public class Test03 {
  public static void main(String[] args) {
    // 1. `public String getAbsolutePath() `:返回此File的絕對路徑名字串。
    show1();
    // 2. `public String getPath()` :將此File轉換為路徑名字串。
    show2();
    // 3. `public String getName() `:返回由此File表示的檔案或目錄的名稱。
    show3();
    // 4. `public long length() `:返回由此File表示的檔案的長度(檔案的大小)
    show4();
  }

  private static void show4() {
    // 不存在的資料夾或不存在的檔案或存在的資料夾返回的都是0
    File file1 = new File("D:\\JavaCode\\BasicCode\\a");
    System.out.println(file1.length()); // 	輸出結果:0
    File file3 = new File("D:\\JavaCode\\BasicCode\\aa");
    System.out.println(file3.length()); //  輸出結果:0
    File file2 = new File("D:\\JavaCode\\BasicCode\\a\\logo01.png");
    System.out.println(file2.length()); //  輸出結果:11610 位元組
  }

  private static void show3() {
    File file1 = new File("D:\\JavaCode\\BasicCode\\a");
    System.out.println(file1.getName()); //  	輸出結果:a
    File file2 = new File("1.txt");    //  	  輸出結果:1.txt
    System.out.println(file2.getName());
  }

  private static void show2() {
    // 檔案路徑是什麼就返回什麼
    File file1 = new File("D:\\JavaCode\\BasicCode\\1.txt");
    System.out.println(file1.getPath()); // 輸出結果:D:\JavaCode\BasicCode\1.txt
    File file2 = new File("1.txt");    // 輸出結果: 1.txt
    System.out.println(file2.getPath());
  }

  private static void show1() {
    File file1 = new File("D:\\JavaCode\\BasicCode\\1.txt");
    System.out.println(file1.getAbsoluteFile()); // 輸出結果:D:\JavaCode\BasicCode\1.txt
    File file2 = new File("1.txt");          // 輸出結果:D:\JavaCode\BasicCode\1.txt
    System.out.println(file2.getAbsoluteFile());
  }
}

1.5-絕對路徑和相對路徑(瞭解)

概念

  • 絕對路徑:從碟符開始的路徑,這是一個完整的路徑。
  • 相對路徑:相對於專案目錄的路徑,這是一個便捷的路徑,開發中經常使用。

示例

public static void main(String[] args) {
    // D盤下的bbb.java檔案
    File f = new File("D:\\bbb.java");
    System.out.println(f.getAbsolutePath());

    // 專案下的bbb.java檔案
    File f2 = new File("bbb.java");
    System.out.println(f2.getAbsolutePath());
}

1.6-File物件的判斷功能相關方法(重要)

方法

  • public boolean exists() :此File表示的檔案或目錄是否實際存在。
  • public boolean isDirectory() :此File表示的是否為目錄。
  • public boolean isFile() :此File表示的是否為檔案。

示例

public class Test04 {
    public static void main(String[] args) {
        File file = new File("G:\\typora");   // 真實存在的目錄
        // - `public boolean exists()` :此File表示的檔案或目錄是否實際存在。
        System.out.println(file.exists());      // 輸出結果: true
        // - `public boolean isDirectory()` :此File表示的是否為目錄。
        System.out.println(file.isDirectory()); // 輸出結果: true
        // - `public boolean isFile()` :此File表示的是否為檔案。
        System.out.println(file.isFile());      // 輸出結果:false
    }
}

1.7-File物件的建立刪除功能的方法(重要)

方法

  1. public boolean createNewFile() :當且僅當具有該名稱的檔案尚不存在時,建立一個新的空檔案。
  2. public boolean delete():刪除由此File表示的檔案或目錄。
  3. public boolean mkdir() :建立由此File表示的目錄。
  4. public boolean mkdirs():建立由此File表示的目錄,包括任何必需但不存在的父目錄。

示例

  public static void main(String[] args) throws IOException {
    // 1. `public boolean createNewFile()` :當且僅當具有該名稱的檔案尚不存在時,建立一個新的空檔案。
    File file1 = new File("a"); // 已經存在的檔案
    System.out.println(file1.createNewFile()); // false
    File file2 = new File("b"); // 不存在的檔案
    System.out.println(file2.createNewFile()); // true
    // 2. `public boolean delete() `:刪除由此File表示的檔案或目錄。
    File file3 = new File("c"); // 存在的檔案
    System.out.println(file3.delete()); // true
    File file4 = new File("b"); // 不存在的檔案
    System.out.println(file4.delete()); // true
    // 3. `public boolean mkdir()` :建立由此File表示的目錄。
    File file5 = new File("e"); // 不存在的檔案目錄
    System.out.println(file5.mkdir()); // true
    File file6 = new File("e//g/f"); // 多級檔案目錄
    System.out.println(file6.mkdir()); // false
    // 4. `public boolean mkdirs() `:建立由此File表示的目錄,包括任何必需但不存在的父目錄。
    System.out.println(file6.mkdirs()); // true

  }

1.8-File物件的目錄遍歷相關方法(重要)

方法

  • public File[] listFiles()返回一個File陣列,表示該File目錄中的所有的子檔案或目錄、
  • public File[] listFiles(FileFilter filter)返回一個File陣列,表示該File目錄中的所有的子檔案或目錄,filter是檔案過濾器,可以過濾不需要的檔案。

示例

public class Test06 {
    public static void main(String[] args) {
        File file = new File("F:\\JavaCode\\BaseCode");
        File[]files = file.listFiles();
        for (File item : files) {
            System.out.println(item);
        }
        /*
            輸出結果:
                F:\JavaCode\BaseCode\.idea
                F:\JavaCode\BaseCode\a.txt
                F:\JavaCode\BaseCode\Chapter01
                F:\JavaCode\BaseCode\d
                F:\JavaCode\BaseCode\e
                F:\JavaCode\BaseCode\out
        */
    }
}

FileFilter介面

java.io.FileFilter 是一個介面,是File的過濾器。 該介面的物件可以傳遞給File類的 listFiles(FileFilter)方法作為引數, 介面中只有一個方法。

方法:boolean accept(File pathname) :測試pathname是否應該包含在當前File目錄中,符合則返回true。如果方法返回true,表示需要此路徑,否則此路徑將被忽略。

示例程式碼:過濾出該目錄中所有的.java檔案

介面作為引數,需要傳遞子類物件,重寫其中方法。我們選擇匿名內部類方式,比較簡單。

public static void main(String[] args){
    File dir = new File("d:\\demo");
    File[] files = dir.listFiles(new FileFilter() {
    	@Override
    	public boolean accept(File pathname) {
            //判斷如果獲取到的是目錄,直接放行
            if(pathname.isDirectory())
            return true;
            //獲取路徑中的檔名,判斷是否java結尾,是就返回true
            return pathname.getName().toLowerCase().endsWith("java");
      	}
    });
    for(File file : files){
    	System.out.println(file);
    }
}

第二章:遞迴

2.1-遞迴概述(瞭解)

什麼是遞迴

遞迴,函式(方法)自身呼叫自身的程式設計技巧。

遞迴的分類:

  • 直接遞迴稱為方法自身呼叫自己。
  • 間接遞迴可以A方法呼叫B方法,B方法呼叫C方法,C方法呼叫A方法。

遞迴注意事項

  1. 遞迴一定要有條件限定,保證遞迴能夠停止下來,否則會發生棧記憶體溢位。
  2. 在遞迴中雖然有限定條件,但是遞迴次數不能太多。否則也會發生棧記憶體溢位。
  3. 構造方法,禁止遞迴

2.2-遞迴練習1(練習)

需求

需求:計算數字1 ~ n的和

分析

num的累和 = num + (num-1)的累和,所以可以把累和的操作定義成一個方法,遞迴呼叫。

程式碼

  // 計算數字1 ~ n的和
  public static void main(String[] args) {
    int sum = getSum(3);
    System.out.println(sum);
  }

  private static int getSum(int n) {
    // 判斷遞迴結束條件
    if(n==1) {
      return 1;
    }
    // 遞迴任務
    return n + getSum(n-1);
  }

圖解

2.3-遞迴練習2(練習)

需求

階乘所有小於及等於該數的正整數的積。n的階乘:n! = n * (n‐1) ... 3 * 2 * 1

分析

這與累和類似,只不過換成了乘法運算

  • 推理得出:n! = n * (n‐1)!

程式碼

public static void main(String[] args) {
    int result = factorial(3);
    System.out.println(result);
  }

  private static int factorial(int n) {
    if(n==1) {
      return 1;
    }
    return n * factorial(n-1);
  }

2.4-遞迴練習3(練習)

需求

列印多級目錄及檔案

程式碼

  public static void main(String[] args) {
    File file = new File("D:\\JavaCode\\BasicCode\\dir");
    readFile(file);
  }
  // 定義讀取目錄檔案的方法
  private static void readFile(File file) {
   // 獲取子檔案和子目錄
    File[]files = file.listFiles();
    // 迴圈遍歷子檔案和子目錄
    for (File f : files) {
      if(f.isDirectory()){
        readFile(f);
      }else {
        System.out.println(f);
      }
    }
  }

2.5-遞迴練習4(練習)

需求

給一個指定的目錄,遞迴實現過濾出該目錄中所有的以及巢狀目中.java檔案

程式碼實現方式1

  public static void main(String[] args) {
    File file = new File("D:\\JavaCode\\BasicCode");
    readFile(file);
  }
  // 定義讀取目錄檔案的方法
  private static void readFile(File file) {
    // 獲取子檔案和子目錄
    File[]files = file.listFiles();
    // 迴圈遍歷子檔案和子目錄
    for (File f : files) {
      if(f.isDirectory()){
        readFile(f);
      }else {
        if(f.getName().toLowerCase().endsWith(".java")){
          System.out.println(f);
        }
      }
    }
  }

程式碼事項方式2-檔案過濾器

分析:

  1. FileFilter介面作為引數,需要傳遞子類物件,重寫其中方法。我們選擇匿名內部類方式,比較簡單。

  2. accept 方法,引數為File,表示當前File下所有的子檔案和子目錄。保留住則返回true,過濾掉則返回 false。保留規則:

    1. 要麼是.java檔案。
    2. 要麼是目錄,用於繼續遍歷。
  3. 通過過濾器的作用, listFiles(FileFilter) 返回的陣列元素中,子檔案物件都是符合條件的,可以直接打

    印。

程式碼:

  public static void main(String[] args) {
    File file = new File("D:\\JavaCode\\BasicCode");
    readFile(file);
  }
  // 定義讀取目錄檔案的方法
  private static void readFile(File file) {
    // 獲取子檔案和子目錄
    File[]files = file.listFiles(new FileFilter() {
      @Override
      public boolean accept(File pathname) {
        // 符合條件的File
        return pathname.isDirectory() || pathname.getName().toLowerCase().endsWith(".java");
      }
    });
    // 迴圈遍歷子檔案和子目錄
    for (File f : files) {
      if(f.isFile()){
        System.out.println(f);
      }else {
        readFile(f);
      }
    }
  }

第三章:認識IO流

3.1-什麼是IO(瞭解)

生活中,你肯定經歷過這樣的場景。當你編輯一個文字檔案,忘記了 ctrl+s ,可能檔案就白白編輯了。當你電腦上插入一個U盤,可以把一個視訊,拷貝到你的電腦硬盤裡。那麼資料都是在哪些裝置上的呢?鍵盤、記憶體、硬碟、外接裝置等等。

我們把這種資料的傳輸,可以看做是一種資料的流動,按照流動的方向,以記憶體為基準,分為 輸入input 和 輸出output ,即流向記憶體是輸入流,流出記憶體的輸出流

Java中I/O操作主要是指使用 java.io 包下的內容,進行輸入、輸出操作。輸入也叫做讀取資料,輸出也叫做作寫出資料。

3.2-IO的分類(瞭解)

根據資料的流向分為:輸入流和輸出流。

  • 輸入流 :把資料從 其他裝置 上讀取到 記憶體 中的流。
  • 輸出流 :把資料從 記憶體 中寫出到 其他裝置 上的流。

根據資料的型別分為:位元組流和字元流。

  • 位元組流 :以位元組為單位,讀寫資料的流。
  • 字元流 :以字元為單位,讀寫資料的流。

3.3-IO的頂級父類(瞭解)

第四章:位元組流

4.1-一切皆為位元組流(瞭解)

一切檔案資料(文字、圖片、視訊等)在儲存時,都是以二進位制數字的形式儲存,都是一個一個的位元組,那麼傳輸時一 樣如此。所以,位元組流可以傳輸任意檔案資料。在操作流的時候,我們要時刻明確,無論使用什麼樣的流物件,底層傳輸的始終為二進位制資料。

提示:8個二進位制位為1個位元組,0000-0000 是1個位元組。

4.2-位元組輸出流OutputStream(重要)

概述

java.io.OutputStream 抽象類是表示位元組輸出流的所有類的超類,將指定的位元組資訊寫出到目的地。它定義了位元組輸出流的基本共性功能方法。

方法

  • public void close():關閉此輸出流並釋放與此流相關聯的任何系統資源。
  • public void write(byte[] b):將 b.length位元組從指定的位元組陣列寫入此輸出流。
  • public void write(byte[] b, int off, int len) :從指定的位元組陣列寫入 len位元組,從偏移量 off開始輸出到此輸出流。
  • public abstract void write(int b) :將指定的位元組輸出流。

注意事項

close方法,當完成流的操作時,必須呼叫此方法,釋放系統資源。

4.3-FileOutputStream類(重要)

概述

OutputStream有很多子類,我們從最簡單的一個子類開始。

java.io.FileOutputStream 類是檔案輸出流,用於將資料寫出到檔案。

構造方法

  • public FileOutputStream(File file):建立檔案輸出流以寫入由指定的 File物件表示的檔案。
  • public FileOutputStream(String name): 建立檔案輸出流以指定的名稱寫入檔案。

當你建立一個流物件時,必須傳入一個檔案路徑。該路徑下,如果沒有這個檔案,會建立該檔案。如果有這個檔案,會清空這個檔案的資料。

public class FileOutputStreamConstructor  {
    public static void main(String[] args) throws IOException{
   	 	// 使用File物件建立流物件
        File file = new File("a.txt");
        FileOutputStream fos = new FileOutputStream(file);
      
        // 使用檔名稱建立流物件
        //FileOutputStream fos = new FileOutputStream("b.txt");
    }
}

寫出位元組資料

public class Test07 {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("a.txt");
        //  【write(int b)】
        // 寫入的十進位制會轉換成二進位制儲存到a.txt
        // 讀取檔案時,檔案會按照指定的編碼格式轉換為對應的內容
        fos.write(97);  // 寫入→97→110 0001‬→記憶體→讀取→110 0001→ASCII→a
        fos.write(98);
        // 【 write(byte[] b)】
        byte[]bs = {97,98,99,101,102};
        fos.write(bs);
        // 【字串轉換位元組陣列getBytes()】
        fos.write("你好".getBytes());
        // 【write(byte[] b, int off, int len)指定長度的位元組陣列】
        fos.write("xyz".getBytes(),0,2);
        fos.close();
    }
}

資料追加續寫

問題:經過以上的演示,每次程式執行,建立輸出流物件,都會清空目標檔案中的資料。如何保留目標檔案中資料,還能 繼續新增新資料呢?

解決方案:

  • public FileOutputStream(File file, boolean append) : 建立檔案輸出流以寫入由指定的 File物件表示的檔案。
  • public FileOutputStream(String name, boolean append) : 建立檔案輸出流以指定的名稱寫入檔案。
  • 引數boolean append: true 表示追加資料, false 表示清空原有資料。
public class Test08 {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("a.txt",true);
        fos.write("我是新追加的資料".getBytes());
        fos.close();
    }
}

write方法原始碼解析

  • 我呼叫write方法寫出資料的時候,JDK原始碼中最終呼叫的方法是writeBytes()方法。
  • private native void writeBytes(byte b[], int off, int len, boolean append)throws IOException
    • 方法是本地方法是和作業系統互動的方法。
    • 作業系統本身就具有IO功能,因此JVM是呼叫作業系統中的功能實現資料的讀寫!

寫出換行

系統中的換行:

  • Windows系統裡,每行結尾是 回車+換行 ,即 \r\n
  • Unix系統裡,每行結尾只有 換行 ,即 \n
  • Mac系統裡,每行結尾是 回車 ,即 \r。從 Mac OS X開始與Linux統一。

程式碼:

    FileOutputStream fos = new FileOutputStream("b.txt",true);
    for (int i = 0; i < 10; i++) {
      fos.write(("\r\n第" + i +"行資料:" + i*100).getBytes() );
   	 }
    fos.close();

4.4-位元組輸入流InputStream(重要)

概述

java.io.InputStream 抽象類是表示位元組輸入流的所有類的超類,可以讀取位元組資訊到記憶體中。它定義了位元組輸入 流的基本共性功能方法。

方法

  • public void close() :關閉此輸入流並釋放與此流相關聯的任何系統資源。

  • public abstract int read() : 從輸入流讀取資料的下一個位元組。

  • public int read(byte[] b): 從輸入流中讀取一些位元組數,並將它們儲存到位元組陣列 b中 。

    • 使用陣列讀取,每次讀取多個位元組,減少了系統間的IO操作次數,從而提高了讀寫的效率,建議開發中使

      用。

注意:close方法,當完成流的操作時,必須呼叫此方法,釋放系統資源。

4.5-FileInputStream類(重要)

java.io.FileInputStream 類是檔案輸入流,從檔案中讀取位元組。

構造方法

  1. FileInputStream(File file) : 通過開啟與實際檔案的連線來建立一個 FileInputStream ,該檔案由檔案系 統中的 File物件 fifile命名。
  2. FileInputStream(String name): 通過開啟與實際檔案的連線來建立一個 FileInputStream ,該檔案由檔案 系統中的路徑名 name命名。

當你建立一個流物件時,必須傳入一個檔案路徑。該路徑下,如果沒有該檔案,會丟擲FileNotFoundException

public class FileInputStreamConstructor {
    public static void main(String[] args) throws IOException{
   	 	// 使用File物件建立流物件
        File file = new File("a.txt");
        FileInputStream fos = new FileInputStream(file);
      
        // 使用檔名稱建立流物件
        FileInputStream fos = new FileInputStream("b.txt");
    }
}

讀取位元組資料

讀取位元組read方法,每次可以讀取一個位元組的資料,提升為int型別,讀取到檔案末尾,返回-1,程式碼使用演示:

檔案:a.txt

abcd

讀取:a.txt檔案

public class Test09 {
    public static void main(String[] args) throws IOException {
        // 使用檔名稱建立流物件
        FileInputStream fis = new FileInputStream("a.txt");
        // 讀取資料,返回一個位元組
        int read = fis.read();
        System.out.println((char) read); // a
        read = fis.read();
        System.out.println((char) read); // b
        read = fis.read();
        System.out.println((char) read); // c
        read = fis.read();
        System.out.println((char) read); // d
        read = fis.read();
        System.out.println(read);       // -1
    }
}

注意:如果檔案中存在-1,我們在讀取檔案時也不會直接讀取到-1,因為-1是兩個位元組,即-1。每個檔案都會被作業系統賦予一個結束的標識,JVM呼叫作業系統功能實現檔案讀取的,因此作業系統讀取到檔案結束標識後,會將表示返回到JVM中,而JVM接收到檔案結束標識後,返回read()方法-1。

使用迴圈改進:

public static void main(String[] args) throws IOException{
    // 使用檔名稱建立流物件
    FileInputStream fis = new FileInputStream("a.txt");
    // 定義變數,儲存資料
    int b = 0 ;
    // 迴圈讀取
    while ((b = fis.read())!=-1) {
    	System.out.println((char)b);
    }
    // 關閉資源
    fis.close();
}

使用位元組陣列讀取

read(byte[] b),每次讀取b的長度個位元組到陣列中,返回讀取到的有效位元組個數,讀取到末尾時,返回-1 ,程式碼使用演示:

public static void main(String[] args) throws IOException{
    // 使用檔名稱建立流物件.
    FileInputStream fis = new FileInputStream("read.txt"); // 檔案中為abcde
    // 定義變數,作為有效個數
    int len ;
    // 定義位元組陣列,作為裝位元組資料的容器   
    byte[] b = new byte[2];
    // 迴圈讀取
    while (( len= fis.read(b))!=-1) {
    // 每次讀取後,把陣列變成字串列印
    	System.out.println(new String(b));
    }
    // 關閉資源
    fis.close();
}

錯誤資料d,是由於最後一次讀取時,只讀取一個位元組e,陣列中,上次讀取的資料沒有被完全替換,所以要通過len ,獲取有效的位元組,程式碼使用演示:

public static void main(String[] args) throws IOException{
    // 使用檔名稱建立流物件.
    FileInputStream fis = new FileInputStream("read.txt"); // 檔案中為abcde
    // 定義變數,作為有效個數
    int len ;
    // 定義位元組陣列,作為裝位元組資料的容器   
    byte[] b = new byte[2];
    // 迴圈讀取
    while (( len= fis.read(b))!=-1) {
    // 每次讀取後,把陣列的有效位元組部分,變成字串列印
    	System.out.println(new String(b,0,len));//  len 每次讀取的有效位元組個數
    }
    // 關閉資源
    fis.close();
}

第五章:IO異常處理

5.1-JDK7之前的處理方式(重要)

處理方式:try-catch-finally

  • 在finally中釋放資源

程式碼:

  public static void main(String[] args)  {
    // 建立輸出流物件,向指定的檔案中追加寫入資料
    FileWriter fw2 = null;
    try{
      fw2 = new FileWriter("day07_IO\\b.txt",true);
      for (int i = 0; i < 10; i++) {
        fw2.write("你好,新的世界!" + i + "\r\n");
      }
    }catch (IOException e) {
      e.printStackTrace();
    }finally {
      if(fw2!=null){
        try {
          fw2.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }

  }

5.2-JDK7中的新特性(瞭解)

處理方式:JDK7優化後的 try-with-resource 語句,該語句確保了每個資源在語句結束時關閉。所謂的資源 (resource)是指在程式完成後,必須關閉的物件。

try (建立流物件語句,如果多個,使用';'隔開) { 
    // 讀寫資料 
} catch (IOException e) { 
    e.printStackTrace(); 
}

程式碼:

public static void main(String[] args)  {
    // 建立輸出流物件,向指定的檔案中追加寫入資料
    try(FileWriter fw2 = new FileWriter("day07_IO\\b.txt",true)){
      for (int i = 0; i < 100; i++) {
        fw2.write("你好,新的世界!" + i + "\r\n");
      }
    }catch (IOException e) {
      e.printStackTrace();
    }
  }

5.3-JDK9中的新特性(瞭解)

JDK9中 try-with-resource 的改進,對於引入物件的方式,支援的更加簡潔。被引入的物件,同樣可以自動關閉, 無需手動close

格式

// 被final修飾的物件 
final Resource resource1 = new Resource("resource1"); 
// 普通物件 
Resource resource2 = new Resource("resource2"); 
// 引入方式:直接引入 
try (resource1; resource2) { 
    // 使用物件 
} catch (IOException e) { 
    e.printStackTrace(); 
}

程式碼

public static void main(String[] args) throws IOException  {
    // 建立輸出流物件,向指定的檔案中追加寫入資料
    FileWriter fw2 = new FileWriter("day07_IO\\b.txt",true);
    try(fw2){
      for (int i = 0; i < 100; i++) {
        fw2.write("你好,新的世界!" + i + "\r\n");
      }
    }catch (IOException e) {
      e.printStackTrace();
    }
  }

第六章:檔案複製案例

6.1-需求

使用位元組流可以進行任何檔案的複製,因為位元組流操作的是組成檔案的最小單元-位元組。

實現圖片檔案的複製

6.2-實現程式碼

 public static void main(String[] args) throws IOException {
    long s = System.currentTimeMillis();
    // 建立輸入流物件-用來讀取本地檔案
    FileInputStream fis = new FileInputStream("D:\\test.jpg");
    // 建立輸出流物件-用來寫入本地檔案
    FileOutputStream fos = new FileOutputStream("IODemo\\test_copy.jpg");
    // 建立位元組陣列,一次從本地讀取多個位元組
    byte[]bts = new byte[1024];
    int len = 0; // 表示讀取的有效位元組個數
    // 迴圈讀取本地資料
    while ((len=fis.read(bts))!=-1){
      // 把實際讀取的位元組寫入本地檔案
      fos.write(bts,0,len);
    }
    // 關閉輸出流資源
    fos.close();
    // 關閉輸入流資源
    fis.close();
    long e = System.currentTimeMillis();
    System.out.println("複製成功!");
    System.out.println("共耗時" + (e-s)+"毫秒");
  }

第七章:位元組緩衝流

7.1-概述(瞭解)

緩衝流:針對基礎流物件進行高效處理的流物件。或者為基礎流增加功能。

位元組緩衝流BufferedInputStreamBufferedOutputStream

緩衝流的基本原理,是在建立流物件時,會建立一個內建的預設大小的緩衝區陣列,通過緩衝區讀寫,減少系統IO次數,從而提高讀寫的效率。

7.2-位元組緩衝流的使用(重要)

BufferedOutputStream繼承OutputStream,write()方法不必從新學習。

BufferedInputStream繼承InputStream,read()方法不必從新學習。

構造方法

  • public BufferedInputStream(InputStream in) :建立一個 新的緩衝輸入流。
  • public BufferedOutputStream(OutputStream out): 建立一個新的緩衝輸出流。
// 建立位元組緩衝輸入流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("bis.txt"));
// 建立位元組緩衝輸出流
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("bos.txt"));

注意:在使用緩衝流時,必須傳遞基礎流。

效率測試

查詢API,緩衝流讀寫方法與基本的流是一致的,我們通過複製大檔案(375MB),測試它的效率。

基礎流

public static void main(String[] args) throws IOException {
    // 記錄開始時間
    long start = System.currentTimeMillis();
    // 建立流物件
    
    FileInputStream fis = new FileInputStream("jdk8.exe");
    FileOutputStream fos = new FileOutputStream("copy.exe")
   
    // 讀寫資料
    int b = 0;
    while ((b = fis.read()) != -1) {
    	fos.write(b);
    }
    
    // 記錄結束時間
    long end = System.currentTimeMillis();
    System.out.println("普通流複製時間:"+(end - start)+" 毫秒");
}
// 基礎流複製時間:1分鐘以上

緩衝流

public static void main(String[] args) throws FileNotFoundException {
    // 記錄開始時間
    long start = System.currentTimeMillis();
    // 建立流物件
  
    BufferedInputStream bis = new BufferedInputStream(new FileInputStream("jdk8.exe"));
    BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.exe"));
    // 讀寫資料
    int len = 0;
    while ((len = bis.read()) != -1) {
    	bos.write(len);
    }
   
    // 記錄結束時間
    long end = System.currentTimeMillis();
    System.out.println("緩衝流使用陣列複製時間:"+(end - start)+" 毫秒");
}
// 緩衝流複製時間:6969 毫秒,約7秒鐘

緩衝流+位元組陣列

public static void main(String[] args) throws IOException {
        // 記錄開始時間
        long start = System.currentTimeMillis();
        // 建立流物件

        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("C:\\Users\\70418\\Desktop\\test\\jdk8.exe"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("C:\\Users\\70418\\Desktop\\test\\copy2.exe"));
        // 讀寫資料
        int len = 0;
        byte[]b = new byte[1024];
        while ((len = bis.read(b)) != -1) {
            bos.write(b,0,len);
        }

        // 記錄結束時間
        long end = System.currentTimeMillis();
        System.out.println("緩衝流使用陣列複製時間:"+(end - start)+" 毫秒");
    }
// 緩衝流使用陣列複製時間:796 毫秒,約0.7秒