JavaSE第20篇:Java之IO流上篇

核心概述:如何獲取和遍歷本地文件及目錄信息?如何使用讀寫本地文件巍沙?本篇我們將學習File類來獲取本地文件信息盼理,學習遞歸來遍歷文件及目錄净神,學習Java中的IO流來實現(xiàn)對本地文件的讀寫竿报。

第一章:File類

1.1-概述(了解)

java.io.File 類是文件和目錄路徑名的抽象表示铅乡,主要用于文件和目錄的創(chuàng)建、查找和刪除等操作烈菌。File類將文件阵幸,文件夾和路徑封裝成了對象,提供大量的方法來操作這些對象芽世。

1.2-File類的靜態(tài)成員(了解)

靜態(tài)成員

  • static String pathSeparator 與系統(tǒng)有關(guān)的路徑分隔符挚赊。
    • Window操作系統(tǒng),分隔符是分號济瓢。
    • Linux操作系統(tǒng)咬腕,分隔符是冒號。
  • static String separator 與系統(tǒng)有關(guān)的默認名稱分隔符葬荷。
    • Window操作系統(tǒng),名稱分割符號為 \纽帖。
    • Linux操作系統(tǒng)宠漩,名稱分隔符號為 /。

示例

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

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

構(gòu)造方法

  1. public File(String pathname) :通過將給定的路徑名字符串轉(zhuǎn)換為抽象路徑名來創(chuàng)建新的 File實例懊直。
  2. public File(String parent, String child) :從父路徑名字符串和子路徑名字符串創(chuàng)建新的 File實例扒吁。
  3. public File(File parent, String child) :從父抽象路徑名和子路徑名字符串創(chuàng)建新的 File實例。

示例

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

注意

  1. 一個File對象代表硬盤中實際存在的一個文件或者目錄室囊。
  2. 無論該路徑下是否存在文件或者目錄雕崩,都不影響File對象的創(chuàng)建魁索。

1.4-File對象獲取功能相關(guān)方法(重要)

方法

  1. public String getAbsolutePath():返回此File的絕對路徑名字符串。
  2. public String getPath() :將此File轉(zhuǎn)換為路徑名字符串盼铁。
  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轉(zhuǎn)換為路徑名字符串鹏控。
    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()); //  輸出結(jié)果:0
    File file3 = new File("D:\\JavaCode\\BasicCode\\aa");
    System.out.println(file3.length()); //  輸出結(jié)果:0
    File file2 = new File("D:\\JavaCode\\BasicCode\\a\\logo01.png");
    System.out.println(file2.length()); //  輸出結(jié)果:11610 字節(jié)
  }

  private static void show3() {
    File file1 = new File("D:\\JavaCode\\BasicCode\\a");
    System.out.println(file1.getName()); //     輸出結(jié)果:a
    File file2 = new File("1.txt");    //     輸出結(jié)果: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()); // 輸出結(jié)果:D:\JavaCode\BasicCode\1.txt
    File file2 = new File("1.txt");    // 輸出結(jié)果: 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()); // 輸出結(jié)果:D:\JavaCode\BasicCode\1.txt
    File file2 = new File("1.txt");          // 輸出結(jié)果:D:\JavaCode\BasicCode\1.txt
    System.out.println(file2.getAbsoluteFile());
  }
}

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

概念

  • 絕對路徑:從盤符開始的路徑肤寝,這是一個完整的路徑当辐。
  • 相對路徑:相對于項目目錄的路徑,這是一個便捷的路徑鲤看,開發(fā)中經(jīng)常使用缘揪。

示例

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對象的判斷功能相關(guān)方法(重要)

方法

  • 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());      // 輸出結(jié)果: true
        // - `public boolean isDirectory()` :此File表示的是否為目錄澡刹。
        System.out.println(file.isDirectory()); // 輸出結(jié)果: true
        // - `public boolean isFile()` :此File表示的是否為文件呻征。
        System.out.println(file.isFile());      // 輸出結(jié)果:false
    }
}

1.7-File對象的創(chuàng)建刪除功能的方法(重要)

方法

  1. public boolean createNewFile() :當且僅當具有該名稱的文件尚不存在時,創(chuàng)建一個新的空文件罢浇。
  2. public boolean delete():刪除由此File表示的文件或目錄陆赋。
  3. public boolean mkdir() :創(chuàng)建由此File表示的目錄。
  4. public boolean mkdirs():創(chuàng)建由此File表示的目錄嚷闭,包括任何必需但不存在的父目錄攒岛。

示例

  public static void main(String[] args) throws IOException {
    // 1. `public boolean createNewFile()` :當且僅當具有該名稱的文件尚不存在時,創(chuàng)建一個新的空文件胞锰。
    File file1 = new File("a"); // 已經(jīng)存在的文件
    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()` :創(chuàng)建由此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() `:創(chuàng)建由此File表示的目錄嗅榕,包括任何必需但不存在的父目錄顺饮。
    System.out.println(file6.mkdirs()); // true

  }

1.8-File對象的目錄遍歷相關(guān)方法(重要)

方法

  • public File[] listFiles()返回一個File數(shù)組,表示該File目錄中的所有的子文件或目錄凌那、
  • public File[] listFiles(FileFilter filter)返回一個File數(shù)組兼雄,表示該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);
        }
        /*
            輸出結(jié)果:
                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)方法作為參數(shù)佃乘, 接口中只有一個方法囱井。

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

示例代碼:過濾出該目錄中所有的.java文件

接口作為參數(shù)千扶,需要傳遞子類對象,重寫其中方法骆捧。我們選擇匿名內(nèi)部類方式澎羞,比較簡單。

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結(jié)尾,是就返回true
            return pathname.getName().toLowerCase().endsWith("java");
        }
    });
    for(File file : files){
        System.out.println(file);
    }
}

第二章:遞歸

2.1-遞歸概述(了解)

什么是遞歸

遞歸枫攀,函數(shù)(方法)自身調(diào)用自身的編程技巧括饶。

遞歸的分類:

  • 直接遞歸稱為方法自身調(diào)用自己。
  • 間接遞歸可以A方法調(diào)用B方法来涨,B方法調(diào)用C方法图焰,C方法調(diào)用A方法。

遞歸注意事項

  1. 遞歸一定要有條件限定蹦掐,保證遞歸能夠停止下來技羔,否則會發(fā)生棧內(nèi)存溢出。
  2. 在遞歸中雖然有限定條件卧抗,但是遞歸次數(shù)不能太多藤滥。否則也會發(fā)生棧內(nèi)存溢出。
  3. 構(gòu)造方法,禁止遞歸

2.2-遞歸練習1(練習)

需求

需求:計算數(shù)字1 ~ n的和

分析

num的累和 = num + (num-1)的累和社裆,所以可以把累和的操作定義成一個方法拙绊,遞歸調(diào)用。

代碼

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

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

圖解

image

2.3-遞歸練習2(練習)

需求

階乘所有小于及等于該數(shù)的正整數(shù)的積泳秀。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();
    // 循環(huán)遍歷子文件和子目錄
    for (File f : files) {
      if(f.isDirectory()){
        readFile(f);
      }else {
        System.out.println(f);
      }
    }
  }

2.5-遞歸練習4(練習)

需求

給一個指定的目錄标沪,遞歸實現(xiàn)過濾出該目錄中所有的以及嵌套目中.java文件

代碼實現(xiàn)方式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();
    // 循環(huán)遍歷子文件和子目錄
    for (File f : files) {
      if(f.isDirectory()){
        readFile(f);
      }else {
        if(f.getName().toLowerCase().endsWith(".java")){
          System.out.println(f);
        }
      }
    }
  }

代碼事項方式2-文件過濾器

分析:

  1. FileFilter接口作為參數(shù),需要傳遞子類對象嗜傅,重寫其中方法谨娜。我們選擇匿名內(nèi)部類方式,比較簡單磺陡。

  2. accept 方法,參數(shù)為File,表示當前File下所有的子文件和子目錄币他。保留住則返回true坞靶,過濾掉則返回 false。保留規(guī)則:

    1. 要么是.java文件蝴悉。
    2. 要么是目錄彰阴,用于繼續(xù)遍歷。
  3. 通過過濾器的作用拍冠, listFiles(FileFilter) 返回的數(shù)組元素中尿这,子文件對象都是符合條件的,可以直接打

    印庆杜。

代碼:

  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");
      }
    });
    // 循環(huán)遍歷子文件和子目錄
    for (File f : files) {
      if(f.isFile()){
        System.out.println(f);
      }else {
        readFile(f);
      }
    }
  }

第三章:認識IO流

3.1-什么是IO(了解)

生活中射众,你肯定經(jīng)歷過這樣的場景。當你編輯一個文本文件晃财,忘記了 ctrl+s 叨橱,可能文件就白白編輯了。當你電腦上插入一個U盤断盛,可以把一個視頻罗洗,拷貝到你的電腦硬盤里。那么數(shù)據(jù)都是在哪些設(shè)備上的呢钢猛?鍵盤伙菜、內(nèi)存、硬盤命迈、外接設(shè)備等等贩绕。

我們把這種數(shù)據(jù)的傳輸,可以看做是一種數(shù)據(jù)的流動躺翻,按照流動的方向丧叽,以內(nèi)存為基準,分為 輸入input 和 輸出output 公你,即流向內(nèi)存是輸入流踊淳,流出內(nèi)存的輸出流

Java中I/O操作主要是指使用 java.io 包下的內(nèi)容陕靠,進行輸入迂尝、輸出操作。輸入也叫做讀取數(shù)據(jù)剪芥,輸出也叫做作寫出數(shù)據(jù)垄开。

3.2-IO的分類(了解)

image

根據(jù)數(shù)據(jù)的流向分為:輸入流和輸出流。

  • 輸入流 :把數(shù)據(jù)從 其他設(shè)備 上讀取到 內(nèi)存 中的流税肪。
  • 輸出流 :把數(shù)據(jù)從 內(nèi)存 中寫出到 其他設(shè)備 上的流溉躲。

根據(jù)數(shù)據(jù)的類型分為:字節(jié)流和字符流榜田。

  • 字節(jié)流 :以字節(jié)為單位,讀寫數(shù)據(jù)的流锻梳。
  • 字符流 :以字符為單位箭券,讀寫數(shù)據(jù)的流。

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

image

第四章:字節(jié)流

4.1-一切皆為字節(jié)流(了解)

一切文件數(shù)據(jù)(文本疑枯、圖片辩块、視頻等)在存儲時,都是以二進制數(shù)字的形式保存荆永,都是一個一個的字節(jié)废亭,那么傳輸時一 樣如此。所以具钥,字節(jié)流可以傳輸任意文件數(shù)據(jù)豆村。在操作流的時候,我們要時刻明確氓拼,無論使用什么樣的流對象你画,底層傳輸?shù)氖冀K為二進制數(shù)據(jù)。

提示:8個二進制位為1個字節(jié)桃漾,0000-0000 是1個字節(jié)坏匪。

4.2-字節(jié)輸出流OutputStream(重要)

概述

java.io.OutputStream 抽象類是表示字節(jié)輸出流的所有類的超類,將指定的字節(jié)信息寫出到目的地撬统。它定義了字節(jié)輸出流的基本共性功能方法适滓。

方法

  • public void close():關(guān)閉此輸出流并釋放與此流相關(guān)聯(lián)的任何系統(tǒng)資源。
  • public void write(byte[] b):將 b.length字節(jié)從指定的字節(jié)數(shù)組寫入此輸出流恋追。
  • public void write(byte[] b, int off, int len) :從指定的字節(jié)數(shù)組寫入 len字節(jié)凭迹,從偏移量 off開始輸出到此輸出流。
  • public abstract void write(int b) :將指定的字節(jié)輸出流苦囱。

注意事項

close方法嗅绸,當完成流的操作時,必須調(diào)用此方法撕彤,釋放系統(tǒng)資源鱼鸠。

4.3-FileOutputStream類(重要)

概述

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

java.io.FileOutputStream類是文件輸出流蚀狰,用于將數(shù)據(jù)寫出到文件。

構(gòu)造方法

  • public FileOutputStream(File file):創(chuàng)建文件輸出流以寫入由指定的 File對象表示的文件职员。
  • public FileOutputStream(String name): 創(chuàng)建文件輸出流以指定的名稱寫入文件麻蹋。

當你創(chuàng)建一個流對象時,必須傳入一個文件路徑焊切。該路徑下扮授,如果沒有這個文件芳室,會創(chuàng)建該文件。如果有這個文件刹勃,會清空這個文件的數(shù)據(jù)渤愁。

public class FileOutputStreamConstructor  {
    public static void main(String[] args) throws IOException{
        // 使用File對象創(chuàng)建流對象
        File file = new File("a.txt");
        FileOutputStream fos = new FileOutputStream(file);
      
        // 使用文件名稱創(chuàng)建流對象
        //FileOutputStream fos = new FileOutputStream("b.txt");
    }
}

寫出字節(jié)數(shù)據(jù)

public class Test07 {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("a.txt");
        //  【write(int b)】
        // 寫入的十進制會轉(zhuǎn)換成二進制存儲到a.txt
        // 讀取文件時,文件會按照指定的編碼格式轉(zhuǎn)換為對應的內(nèi)容
        fos.write(97);  // 寫入→97→110 0001→內(nèi)存→讀取→110 0001→ASCII→a
        fos.write(98);
        // 【 write(byte[] b)】
        byte[]bs = {97,98,99,101,102};
        fos.write(bs);
        // 【字符串轉(zhuǎn)換字節(jié)數(shù)組getBytes()】
        fos.write("你好".getBytes());
        // 【write(byte[] b, int off, int len)指定長度的字節(jié)數(shù)組】
        fos.write("xyz".getBytes(),0,2);
        fos.close();
    }
}

數(shù)據(jù)追加續(xù)寫

問題:經(jīng)過以上的演示深夯,每次程序運行,創(chuàng)建輸出流對象诺苹,都會清空目標文件中的數(shù)據(jù)咕晋。如何保留目標文件中數(shù)據(jù),還能 繼續(xù)添加新數(shù)據(jù)呢收奔?

解決方案:

  • public FileOutputStream(File file, boolean append) : 創(chuàng)建文件輸出流以寫入由指定的 File對象表示的文件掌呜。
  • public FileOutputStream(String name, boolean append) : 創(chuàng)建文件輸出流以指定的名稱寫入文件。
  • 參數(shù)boolean append: true 表示追加數(shù)據(jù)坪哄, false 表示清空原有數(shù)據(jù)质蕉。
public class Test08 {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("a.txt",true);
        fos.write("我是新追加的數(shù)據(jù)".getBytes());
        fos.close();
    }
}

write方法源碼解析

  • 我調(diào)用write方法寫出數(shù)據(jù)的時候,JDK源代碼中最終調(diào)用的方法是writeBytes()方法翩肌。
  • private native void writeBytes(byte b[], int off, int len, boolean append)throws IOException
    • 方法是本地方法是和操作系統(tǒng)交互的方法模暗。
    • 操作系統(tǒng)本身就具有IO功能,因此JVM是調(diào)用操作系統(tǒng)中的功能實現(xiàn)數(shù)據(jù)的讀寫念祭!

寫出換行

系統(tǒng)中的換行:

  • Windows系統(tǒng)里兑宇,每行結(jié)尾是 回車+換行 ,即 \r\n 粱坤;
  • Unix系統(tǒng)里隶糕,每行結(jié)尾只有 換行 ,即 \n 站玄;
  • Mac系統(tǒng)里枚驻,每行結(jié)尾是 回車 ,即 \r株旷。從 Mac OS X開始與Linux統(tǒng)一再登。

代碼:

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

4.4-字節(jié)輸入流InputStream(重要)

概述

java.io.InputStream 抽象類是表示字節(jié)輸入流的所有類的超類,可以讀取字節(jié)信息到內(nèi)存中灾常。它定義了字節(jié)輸入 流的基本共性功能方法霎冯。

方法

  • public void close() :關(guān)閉此輸入流并釋放與此流相關(guān)聯(lián)的任何系統(tǒng)資源。

  • public abstract int read() : 從輸入流讀取數(shù)據(jù)的下一個字節(jié)钞瀑。

  • public int read(byte[] b): 從輸入流中讀取一些字節(jié)數(shù)沈撞,并將它們存儲到字節(jié)數(shù)組 b中 。

    • 使用數(shù)組讀取雕什,每次讀取多個字節(jié)缠俺,減少了系統(tǒng)間的IO操作次數(shù)显晶,從而提高了讀寫的效率,建議開發(fā)中使

      用壹士。

注意:close方法磷雇,當完成流的操作時,必須調(diào)用此方法躏救,釋放系統(tǒng)資源唯笙。

4.5-FileInputStream類(重要)

java.io.FileInputStream類是文件輸入流,從文件中讀取字節(jié)盒使。

構(gòu)造方法

  1. FileInputStream(File file) : 通過打開與實際文件的連接來創(chuàng)建一個 FileInputStream 崩掘,該文件由文件系 統(tǒng)中的 File對象 fifile命名。
  2. FileInputStream(String name): 通過打開與實際文件的連接來創(chuàng)建一個 FileInputStream 少办,該文件由文件 系統(tǒng)中的路徑名 name命名苞慢。

當你創(chuàng)建一個流對象時,必須傳入一個文件路徑英妓。該路徑下挽放,如果沒有該文件,會拋出FileNotFoundException

public class FileInputStreamConstructor {
    public static void main(String[] args) throws IOException{
        // 使用File對象創(chuàng)建流對象
        File file = new File("a.txt");
        FileInputStream fos = new FileInputStream(file);
      
        // 使用文件名稱創(chuàng)建流對象
        FileInputStream fos = new FileInputStream("b.txt");
    }
}

讀取字節(jié)數(shù)據(jù)

讀取字節(jié)read方法,每次可以讀取一個字節(jié)的數(shù)據(jù)蔓纠,提升為int類型辑畦,讀取到文件末尾,返回-1贺纲,代碼使用演示:

文件:a.txt

abcd

讀群焦搿:a.txt文件

public class Test09 {
    public static void main(String[] args) throws IOException {
        // 使用文件名稱創(chuàng)建流對象
        FileInputStream fis = new FileInputStream("a.txt");
        // 讀取數(shù)據(jù),返回一個字節(jié)
        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是兩個字節(jié),即-1懈叹。每個文件都會被操作系統(tǒng)賦予一個結(jié)束的標識乖杠,JVM調(diào)用操作系統(tǒng)功能實現(xiàn)文件讀取的,因此操作系統(tǒng)讀取到文件結(jié)束標識后澄成,會將表示返回到JVM中胧洒,而JVM接收到文件結(jié)束標識后,返回read()方法-1墨状。

使用循環(huán)改進:

public static void main(String[] args) throws IOException{
    // 使用文件名稱創(chuàng)建流對象
    FileInputStream fis = new FileInputStream("a.txt");
    // 定義變量卫漫,保存數(shù)據(jù)
    int b = 0 ;
    // 循環(huán)讀取
    while ((b = fis.read())!=-1) {
        System.out.println((char)b);
    }
    // 關(guān)閉資源
    fis.close();
}

使用字節(jié)數(shù)組讀取

read(byte[] b),每次讀取b的長度個字節(jié)到數(shù)組中肾砂,返回讀取到的有效字節(jié)個數(shù)列赎,讀取到末尾時,返回-1 镐确,代碼使用演示:

public static void main(String[] args) throws IOException{
    // 使用文件名稱創(chuàng)建流對象.
    FileInputStream fis = new FileInputStream("read.txt"); // 文件中為abcde
    // 定義變量包吝,作為有效個數(shù)
    int len 饼煞;
    // 定義字節(jié)數(shù)組,作為裝字節(jié)數(shù)據(jù)的容器   
    byte[] b = new byte[2];
    // 循環(huán)讀取
    while (( len= fis.read(b))!=-1) {
    // 每次讀取后,把數(shù)組變成字符串打印
        System.out.println(new String(b));
    }
    // 關(guān)閉資源
    fis.close();
}

錯誤數(shù)據(jù)d砖瞧,是由于最后一次讀取時,只讀取一個字節(jié)e嚷狞,數(shù)組中褂乍,上次讀取的數(shù)據(jù)沒有被完全替換裂允,所以要通過len 十饥,獲取有效的字節(jié),代碼使用演示:

public static void main(String[] args) throws IOException{
    // 使用文件名稱創(chuàng)建流對象.
    FileInputStream fis = new FileInputStream("read.txt"); // 文件中為abcde
    // 定義變量,作為有效個數(shù)
    int len 帅刊;
    // 定義字節(jié)數(shù)組弟灼,作為裝字節(jié)數(shù)據(jù)的容器   
    byte[] b = new byte[2];
    // 循環(huán)讀取
    while (( len= fis.read(b))!=-1) {
    // 每次讀取后,把數(shù)組的有效字節(jié)部分掩驱,變成字符串打印
        System.out.println(new String(b芒划,0,len));//  len 每次讀取的有效字節(jié)個數(shù)
    }
    // 關(guān)閉資源
    fis.close();
}

第五章:IO異常處理

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

處理方式:try-catch-finally

  • 在finally中釋放資源

代碼:

  public static void main(String[] args)  {
    // 創(chuàng)建輸出流對象欧穴,向指定的文件中追加寫入數(shù)據(jù)
    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優(yōu)化后的 try-with-resource 語句涮帘,該語句確保了每個資源在語句結(jié)束時關(guān)閉拼苍。所謂的資源 (resource)是指在程序完成后,必須關(guān)閉的對象调缨。

try (創(chuàng)建流對象語句疮鲫,如果多個,使用';'隔開) { 
    // 讀寫數(shù)據(jù) 
} catch (IOException e) { 
    e.printStackTrace(); 
}

代碼:

public static void main(String[] args)  {
    // 創(chuàng)建輸出流對象,向指定的文件中追加寫入數(shù)據(jù)
    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 的改進,對于引入對象的方式伤哺,支持的更加簡潔燕侠。被引入的對象,同樣可以自動關(guān)閉立莉, 無需手動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  {
    // 創(chuàng)建輸出流對象绢彤,向指定的文件中追加寫入數(shù)據(jù)
    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-需求

使用字節(jié)流可以進行任何文件的復制杖虾,因為字節(jié)流操作的是組成文件的最小單元-字節(jié)。

實現(xiàn)圖片文件的復制

image

6.2-實現(xiàn)代碼

 public static void main(String[] args) throws IOException {
    long s = System.currentTimeMillis();
    // 創(chuàng)建輸入流對象-用來讀取本地文件
    FileInputStream fis = new FileInputStream("D:\\test.jpg");
    // 創(chuàng)建輸出流對象-用來寫入本地文件
    FileOutputStream fos = new FileOutputStream("IODemo\\test_copy.jpg");
    // 創(chuàng)建字節(jié)數(shù)組,一次從本地讀取多個字節(jié)
    byte[]bts = new byte[1024];
    int len = 0; // 表示讀取的有效字節(jié)個數(shù)
    // 循環(huán)讀取本地數(shù)據(jù)
    while ((len=fis.read(bts))!=-1){
      // 把實際讀取的字節(jié)寫入本地文件
      fos.write(bts,0,len);
    }
    // 關(guān)閉輸出流資源
    fos.close();
    // 關(guān)閉輸入流資源
    fis.close();
    long e = System.currentTimeMillis();
    System.out.println("復制成功!");
    System.out.println("共耗時" + (e-s)+"毫秒");
  }

第七章:字節(jié)緩沖流

7.1-概述(了解)

緩沖流:針對基礎(chǔ)流對象進行高效處理的流對象肢专。或者為基礎(chǔ)流增加功能嚷往。

字節(jié)緩沖流BufferedInputStreamBufferedOutputStream

緩沖流的基本原理柠衅,是在創(chuàng)建流對象時皮仁,會創(chuàng)建一個內(nèi)置的默認大小的緩沖區(qū)數(shù)組,通過緩沖區(qū)讀寫,減少系統(tǒng)IO次數(shù)贷祈,從而提高讀寫的效率趋急。

7.2-字節(jié)緩沖流的使用(重要)

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

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

構(gòu)造方法

  • public BufferedInputStream(InputStream in) :創(chuàng)建一個 新的緩沖輸入流粟耻。
  • public BufferedOutputStream(OutputStream out): 創(chuàng)建一個新的緩沖輸出流查近。
// 創(chuàng)建字節(jié)緩沖輸入流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("bis.txt"));
// 創(chuàng)建字節(jié)緩沖輸出流
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("bos.txt"));

注意:在使用緩沖流時,必須傳遞基礎(chǔ)流挤忙。

效率測試

查詢API霜威,緩沖流讀寫方法與基本的流是一致的,我們通過復制大文件(375MB)册烈,測試它的效率戈泼。

基礎(chǔ)流

public static void main(String[] args) throws IOException {
    // 記錄開始時間
    long start = System.currentTimeMillis();
    // 創(chuàng)建流對象
    
    FileInputStream fis = new FileInputStream("jdk8.exe");
    FileOutputStream fos = new FileOutputStream("copy.exe")
   
    // 讀寫數(shù)據(jù)
    int b = 0;
    while ((b = fis.read()) != -1) {
        fos.write(b);
    }
    
    // 記錄結(jié)束時間
    long end = System.currentTimeMillis();
    System.out.println("普通流復制時間:"+(end - start)+" 毫秒");
}
// 基礎(chǔ)流復制時間:1分鐘以上

緩沖流

public static void main(String[] args) throws FileNotFoundException {
    // 記錄開始時間
    long start = System.currentTimeMillis();
    // 創(chuàng)建流對象
  
    BufferedInputStream bis = new BufferedInputStream(new FileInputStream("jdk8.exe"));
    BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.exe"));
    // 讀寫數(shù)據(jù)
    int len = 0;
    while ((len = bis.read()) != -1) {
        bos.write(len);
    }
   
    // 記錄結(jié)束時間
    long end = System.currentTimeMillis();
    System.out.println("緩沖流使用數(shù)組復制時間:"+(end - start)+" 毫秒");
}
// 緩沖流復制時間:6969 毫秒,約7秒鐘

緩沖流+字節(jié)數(shù)組

public static void main(String[] args) throws IOException {
        // 記錄開始時間
        long start = System.currentTimeMillis();
        // 創(chuàng)建流對象

        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"));
        // 讀寫數(shù)據(jù)
        int len = 0;
        byte[]b = new byte[1024];
        while ((len = bis.read(b)) != -1) {
            bos.write(b,0,len);
        }

        // 記錄結(jié)束時間
        long end = System.currentTimeMillis();
        System.out.println("緩沖流使用數(shù)組復制時間:"+(end - start)+" 毫秒");
    }
// 緩沖流使用數(shù)組復制時間:796 毫秒,約0.7秒
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末赏僧,一起剝皮案震驚了整個濱河市矮冬,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌次哈,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件吆录,死亡現(xiàn)場離奇詭異窑滞,居然都是意外死亡,警方通過查閱死者的電腦和手機恢筝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門哀卫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人撬槽,你說我怎么就攤上這事此改。” “怎么了侄柔?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵共啃,是天一觀的道長。 經(jīng)常有香客問我暂题,道長移剪,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任薪者,我火速辦了婚禮纵苛,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己攻人,他們只是感情好取试,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著怀吻,像睡著了一般瞬浓。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上烙博,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天瑟蜈,我揣著相機與錄音,去河邊找鬼渣窜。 笑死铺根,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的乔宿。 我是一名探鬼主播位迂,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼详瑞!你這毒婦竟也來了掂林?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤坝橡,失蹤者是張志新(化名)和其女友劉穎泻帮,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體计寇,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡锣杂,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了番宁。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片元莫。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖蝶押,靈堂內(nèi)的尸體忽然破棺而出踱蠢,到底是詐尸還是另有隱情,我是刑警寧澤棋电,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布茎截,位于F島的核電站,受9級特大地震影響赶盔,放射性物質(zhì)發(fā)生泄漏稼虎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一招刨、第九天 我趴在偏房一處隱蔽的房頂上張望霎俩。 院中可真熱鬧哀军,春花似錦、人聲如沸打却。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽柳击。三九已至猿推,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間捌肴,已是汗流浹背蹬叭。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留状知,地道東北人秽五。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像饥悴,于是被迫代替她去往敵國和親坦喘。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345