1. 程式人生 > >史上最騷最全最詳細的IO流教程,沒有之一!

史上最騷最全最詳細的IO流教程,沒有之一!

目錄

  • 1、告白IO流的四點明確
  • 2、File類
    • 1.1 File概述
    • 1.2 構造方法
    • 1.3 常用方法
      • 1.3.1 獲取功能的方法
      • 1.3.2 絕對路徑和相對路徑
      • 1.3.3判斷功能的方法
      • 1.3.4 建立刪除功能的方法
    • 1.4 目錄的遍歷
    • 1.5 遞迴遍歷資料夾下所有檔案以及子檔案
  • 3、初探IO流
    • 1.1 什麼是IO
    • 1.2 IO的分類
    • 1.3 關於IO的分流向說明
    • 位元組流OutputStream與InputStream的故事
    • 2.1 檔案的世界裡一切皆為位元組
    • 2.2 位元組輸出流(OutputStream)
    • 2.3 位元組輸入流(InputStream)
  • 字元流Reader和Writer的故事
  • 字元流
  • 1、 字元輸入流(Reader)
    • FileReader類
    • 構造方法
    • FileReader讀取字元資料
  • 2、字元輸出流(Writer)
    • FileWriter類
      • 構造方法
      • FileWriter寫出資料
      • 關閉close和重新整理flush
      • FileWriter的續寫和換行
      • FileReader和FileWriter類完成文字檔案複製
      • IO異常的處理
  • 1、緩衝流【掌握】
    • 1.1 簡要概述
    • 1.2 位元組緩衝流
      • 構造方法
      • 感受緩衝流的高效
    • 1.3 字元緩衝流
      • 構造方法
      • 字元緩衝流特有方法
    • 1.4 字元緩衝流練習
      • 程式碼實現
  • 2、轉換流【掌握】
    • 2.1 字元編碼與解碼
    • 字符集
    • 2.2 編碼問題導致亂碼
    • 2.3 InputStreamReader類-----(位元組流到字元流的橋樑)
      • 構造方法
      • 使用轉換流解決編碼問題
    • 2.4 OutputStreamWriter類-----(字元流到位元組流的橋樑)
      • 構造方法
      • 指定編碼構造程式碼
  • 3、序列化流【理解】
    • 3.1 何謂序列化
    • 3.2 ObjectOutputStream類
      • 構造方法
      • 序列化操作
    • 3.3 ObjectInputStream類
      • 構造方法
      • 反序列化操作1
      • 反序列化操作2
    • 3.4 序列化集合練習
      • 案例分析
      • 案例程式碼實現
  • 4、列印流【掌握】
    • 4.1 何謂列印流
    • 4.2 位元組輸出列印流PrintStream複製文字檔案
    • 4.3 字元輸出列印流PrintWriter複製文字檔案
  • 5、Properties屬性類
    • 5.1 Properties概述
    • 5.2 Properties類
      • 構造方法
      • 基本的儲存方法
      • 與流相關的方法

前言
io流用到的地方很多,就比如上傳下載,傳輸,設計模式等....基礎打紮實了,才能玩更高階的。

在博主認為真正懂IO流的優秀程式設計師每次在使用IO流之前都會明確分析如下四點:

(1)明確要操作的資料是資料來源還是資料目的(也就是要讀還是要寫)
(2)明確要操作的裝置上的資料是位元組還是文字
(3)明確資料所在的具體裝置
(4)明確是否需要額外功能(比如是否需要轉換流、高效流等)

以上四點將會在文章告白IO流的四點明確裡面小結一下,如果各位真能熟練以上四點,我覺得這篇文章你就沒必要看了,因為你已經把IO玩弄與股掌之中,萬物皆可被你盤也就也不再話下了。

@

1、告白IO流的四點明確

(1)明確要操作的資料是資料來源還是資料目的(要讀還是要寫)

    

 源:
InputStream  Reader

目的:
OutputStream  Writer

(2)明確要操作的裝置上的資料是位元組還是文字

     

 源:

          位元組: InputStream

          文字: Reader

      目的:

          位元組: OutputStream

          文字: Writer

(3)明確資料所在的具體裝置

   

   源裝置:

        硬碟:檔案 File開頭

        記憶體:陣列,字串

        鍵盤:System.in

        網路:Socket

      對應目的裝置:

        硬碟:檔案 File開頭

        記憶體:陣列,字串

        螢幕:System.out

        網路:Socket

(4)明確是否需要額外功能

  

  需要轉換—— 轉換流 InputStreamReader 、OutputStreamWriter

    需要高效—— 緩衝流Bufferedxxx

    多個源—— 序列流 SequenceInputStream

    物件序列化—— ObjectInputStream、ObjectOutputStream

    保證資料的輸出形式—— 列印流PrintStream 、Printwriter

    操作基本資料,保證位元組原樣性——DataOutputStream、DataInputStream

到這裡,我們再來看看IO流的分類吧

OK,準備好了告白IO流了咩?

2、File類

至於IO流,也就是輸入輸出流,從檔案出發到檔案結束,至始至終都離不開檔案,所以IO流還得從檔案File類講起。

1.1 File概述

java.io.File 類是專門對檔案進行操作的類,只能對檔案本身進行操作,不能對檔案內容進行操作。
java.io.File 類是檔案和目錄路徑名的抽象表示,主要用於檔案和目錄的建立、查詢和刪除等操作。

怎麼理解上面兩句話?其實很簡單!

第一句就是說File跟流無關,File類不能對檔案進行讀和寫也就是輸入和輸出!
第二句就是說File主要表示類似D:\\檔案目錄1D:\\檔案目錄1\\檔案.txt,前者是資料夾(Directory)後者則是檔案(file),而File類就是操作這兩者的類。

1.2 構造方法

在java中,一切皆是物件,File類也不例外,不論是哪個物件都應該從該物件的構造說起,所以博主來分析分析File類的構造方法。首先從API開始著手

我們主要來學習一下比較常用的三個:

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

看字描述不夠生動不夠形象不得勁?沒得事,下面進行構造舉例,馬上就生動形象了,程式碼如下:

1. 一個File物件代表硬碟中實際存在的一個檔案或者目錄。
2.  File類構造方法不會給你檢驗這個檔案或資料夾是否真實存在,因此無論該路徑下是否存在檔案或者目錄,都不影響File物件的建立。
// 檔案路徑名 
String path = "D:\\123.txt";
File file1 = new File(path); 

// 檔案路徑名
String path2 = "D:\\1\\2.txt";
File file2 = new File(path2);     -------------相當於D:\\1\\2.txt

// 通過父路徑和子路徑字串
 String parent = "F:\\aaa";
 String child = "bbb.txt";
 File file3 = new File(parent, child);  --------相當於F:\\aaa\\bbb.txt

// 通過父級File物件和子路徑字串
File parentDir = new File("F:\\aaa");
String child = "bbb.txt";
File file4 = new File(parentDir, child); --------相當於F:\\aaa\\bbb.txt

File類的注意點:

  1. 一個File物件代表硬碟中實際存在的一個檔案或者目錄。
  2. File類構造方法不會給你檢驗這個檔案或資料夾是否真實存在,因此無論該路徑下是否存在檔案或者目錄,都不影響File物件的建立。

1.3 常用方法

File的常用方法主要分為獲取功能、獲取絕對路徑和相對路徑、判斷功能、建立刪除功能的方法

1.3.1 獲取功能的方法

1、public String getAbsolutePath() :返回此File的絕對路徑名字串。

2、public String getPath() :將此File轉換為路徑名字串。

3、public String getName() :返回由此File表示的檔案或目錄的名稱。

4、public long length() :返回由此File表示的檔案的長度。

以上方法測試,程式碼如下【注意測試以自己的電腦資料夾為準】:

public class FileGet {
    public static void main(String[] args) {
        File f = new File("d:/aaa/bbb.java");     
        System.out.println("檔案絕對路徑:"+f.getAbsolutePath());
        System.out.println("檔案構造路徑:"+f.getPath());
        System.out.println("檔名稱:"+f.getName());
        System.out.println("檔案長度:"+f.length()+"位元組");

        File f2 = new File("d:/aaa");     
        System.out.println("目錄絕對路徑:"+f2.getAbsolutePath());
        System.out.println("目錄構造路徑:"+f2.getPath());
        System.out.println("目錄名稱:"+f2.getName());
        System.out.println("目錄長度:"+f2.length());
    }
}
輸出結果:
檔案絕對路徑:d:\aaa\bbb.java
檔案構造路徑:d:\aaa\bbb.java
檔名稱:bbb.java
檔案長度:2116位元組

目錄絕對路徑:d:\aaa
目錄構造路徑:d:\aaa
目錄名稱:aaa
目錄長度:3236

注意:length(),表示檔案的長度。但是File物件表示目錄,則返回值未指定。

1.3.2 絕對路徑和相對路徑

絕對路徑:一個完整的路徑,以碟符開頭,例如F://aaa.txt
相對路徑:一個簡化的路徑,不以碟符開頭,例如//aaa.txt//b.txt

1、路徑是不區分大小寫
2、路徑中的檔名稱分隔符windows使用反斜槓,反斜槓是轉義字元,兩個反斜槓代表一個普通的反斜槓

//絕對路徑
public class FilePath {
    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());
    }
}
輸出結果:
D:\bbb.java
D:\java\bbb.java

1.3.3判斷功能的方法

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

方法演示,程式碼如下:

public class FileIs {
    public static void main(String[] args) {
        File f = new File("d:\\aaa\\bbb.java");
        File f2 = new File("d:\\aaa");
        // 判斷是否存在
        System.out.println("d:\\aaa\\bbb.java 是否存在:"+f.exists());
        System.out.println("d:\\aaa 是否存在:"+f2.exists());
        // 判斷是檔案還是目錄
        System.out.println("d:\\aaa 檔案?:"+f2.isFile());
        System.out.println("d:\\aaa 目錄?:"+f2.isDirectory());
    }
}
輸出結果:
d:\aaa\bbb.java 是否存在:true
d:\aaa 是否存在:true
d:\aaa 檔案?:false
d:\aaa 目錄?:true

1.3.4 建立刪除功能的方法

  • public boolean createNewFile() :檔案不存在,建立一個新的空檔案並返回true,檔案存在,不建立檔案並返回false
  • public boolean delete() :刪除由此File表示的檔案或目錄。
  • public boolean mkdir() :建立由此File表示的目錄。
  • public boolean mkdirs() :建立由此File表示的目錄,包括任何必需但不存在的父目錄。

其中,mkdirs()mkdir()方法類似,但mkdir(),只能建立一級目錄,mkdirs()可以建立多級目錄比如//a//b//c,所以開發中一般用mkdirs();

這些方法中值得注意的是createNewFile方法以及mkdir與mkdirs的區別

方法測試,程式碼如下:

public class FileCreateDelete {
    public static void main(String[] args) throws IOException {
        // 檔案的建立
        File f = new File("aaa.txt");
        System.out.println("是否存在:"+f.exists()); // false
        System.out.println("是否建立:"+f.createNewFile()); // true
        System.out.println("是否建立:"+f.createNewFile()); // 以及建立過了所以再使用createNewFile返回false
        System.out.println("是否存在:"+f.exists()); // true
        
        // 目錄的建立
        File f2= new File("newDir");    
        System.out.println("是否存在:"+f2.exists());// false
        System.out.println("是否建立:"+f2.mkdir()); // true
        System.out.println("是否存在:"+f2.exists());// true

        // 建立多級目錄
        File f3= new File("newDira\\newDirb");
        System.out.println(f3.mkdir());// false
        File f4= new File("newDira\\newDirb");
        System.out.println(f4.mkdirs());// true
      
        // 檔案的刪除
        System.out.println(f.delete());// true
      
        // 目錄的刪除
        System.out.println(f2.delete());// true
        System.out.println(f4.delete());// false
    }
}

注意:delete方法,如果此File表示目錄,則目錄必須為空才能刪除。

1.4 目錄的遍歷

  • public String[] list() :返回一個String陣列,表示該File目錄中的所有子檔案或目錄。

  • public File[] listFiles() :返回一個File陣列,表示該File目錄中的所有的子檔案或目錄。

public class FileFor {
    public static void main(String[] args) {
        File dir = new File("G:\游標");
      
        //獲取當前目錄下的檔案以及資料夾的名稱。
        String[] names = dir.list();
        for(String name : names){
            System.out.println(name);
        }
        //獲取當前目錄下的檔案以及資料夾物件,只要拿到了檔案物件,那麼就可以獲取更多資訊
        File[] files = dir.listFiles();
        for (File file : files) {
            System.out.println(file);
        }
    }
}


listFiles在獲取指定目錄下的檔案或者資料夾時必須滿足下面兩個條件

1,指定的目錄必須存在

2,指定的必須是目錄。否則容易引發返回陣列為null,出現NullPointerException異常

1.5 遞迴遍歷資料夾下所有檔案以及子檔案

不說啥了,直接上程式碼:

package File;

import java.io.File;

//遞迴遍歷資料夾下所有的檔案
public class RecursionDirectory {
    public static void main(String[] args) {
      File file=new File("D:\\java專屬IO測試");
        Recursion(file);
    }
    public static void Recursion(File file){
        //1、判斷傳入的是否是目錄
        if(!file.isDirectory()){
            //不是目錄直接退出
            return;
        }
        //已經確保了傳入的file是目錄
        File[] files = file.listFiles();
        //遍歷files
        for (File f: files) {
            //如果該目錄下檔案還是個資料夾就再進行遞迴遍歷其子目錄
            if(f.isDirectory()){
                //遞迴
                Recursion(f);
            }else {
                //如果該目錄下檔案是個檔案,則列印對應的名字
                System.out.println(f.getName());
            }

        }
    }
}

如果對上面的程式碼有疑問,可以隨時聯絡我,博主一直都在!

3、初探IO流

1.1 什麼是IO

我想在座各位肯定經歷都過這樣的場景。當你編輯一個文字檔案也好用eclipse打程式碼也罷,忘記了ctrl+s ,在你關閉檔案的哪一瞬間手殘點了個不該點的按鈕,但你反應過來,心早已拔涼拔涼的了。

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

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

1.2 IO的分類

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

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

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

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

分類之後對應的超類(V8提示:超類也就是父類的意思)
| | 輸入流 | 輸出流
|--|--|--|
| 位元組流 | 位元組輸入流 InputStream |位元組輸出流 OutputStream |
| 字元流 | 字元輸入流 Reader|字元輸出流 Writer|

注:
由這四個類的子類名稱基本都是以其父類名作為子類名的字尾。
如:InputStream的子類FileInputStream。
如:Reader的子類FileReader。

1.3 關於IO的分流向說明

啥都不說了,看圖吧

位元組流OutputStream與InputStream的故事

OutputStream與InputStream的繼承關係

2.1 檔案的世界裡一切皆為位元組

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

2.2 位元組輸出流(OutputStream)

java.io.OutputStream抽象類是表示位元組輸出流的所有類的超類(父類),將指定的位元組資訊寫出到目的地。它定義了位元組輸出流的基本共性功能方法,不要問我OutputStream為啥可以定義位元組輸出流的基本共性功能方法,熊dei啊,上一句說過了OutputStream是位元組輸出流的所有類的超類,繼承知識,懂?(如果是真的不理解的小白同學,可以點選藍色字型繼承進入補習)

位元組輸出流的基本共性功能方法:

1、 public void close() :關閉此輸出流並釋放與此流相關聯的任何系統資源。
2、 public void flush() :重新整理此輸出流並強制任何緩衝的輸出位元組被寫出。
3、 public void write(byte[] b):將 b.length個位元組從指定的位元組陣列寫入此輸出流。
4、 public void write(byte[] b, int off, int len) :從指定的位元組陣列寫入 len位元組,從偏移量 off開始輸出到此輸出流。 也就是說從off個位元組數開始讀取一直到len個位元組結束
5、 public abstract void write(int b) :將指定的位元組輸出流。

以上五個方法則是位元組輸出流都具有的方法,由父類OutputStream定義提供,子類都會共享以上方法

FileOutputStream類

OutputStream有很多子類,我們從最簡單的一個子類FileOutputStream開始。看名字就知道是檔案輸出流,用於將資料寫出到檔案。

FileOutputStream構造方法

不管學啥子,只有是物件,就從構造方法開始!

1、 public FileOutputStream(File file):根據File物件為引數建立物件。
2、 public FileOutputStream(String name): 根據名稱字串為引數建立物件。

推薦第二種構造方法【開發常用】:

FileOutputStream outputStream = new FileOutputStream("abc.txt");

就以上面這句程式碼來講,類似這樣建立位元組輸出流物件都做了三件事情:
1、呼叫系統功能去建立檔案【輸出流物件才會自動建立】
2、建立outputStream物件
3、把foutputStream物件指向這個檔案

注意:
建立輸出流物件的時候,系統會自動去對應位置建立對應檔案,而建立輸出流物件的時候,檔案不存在則會報FileNotFoundException異常,也就是系統找不到指定的檔案異常。

當你建立一個流物件時,必須直接或者間接傳入一個檔案路徑。比如現在我們建立一個FileOutputStream流物件,在該路徑下,如果沒有這個檔案,會建立該檔案。如果有這個檔案,會清空這個檔案的資料。有興趣的童鞋可以測試一下,具體程式碼如下:

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

FileOutputStream寫出位元組資料

使用FileOutputStream寫出位元組資料主要通過Write方法,而write方法分如下三種

public void write(int b)
public void write(byte[] b)
public void write(byte[] b,int off,int len)  //從`off`索引開始,`len`個位元組
  1. 寫出位元組:write(int b) 方法,每次可以寫出一個位元組資料,程式碼如下:
public class IoWrite {
    public static void main(String[] args) throws IOException {
        // 使用檔名稱建立流物件
        FileOutputStream fos = new FileOutputStream("fos.txt");     
        // 寫出資料
        fos.write(97); // 寫出第1個位元組
        fos.write(98); // 寫出第2個位元組
        fos.write(99); // 寫出第3個位元組
        // 關閉資源
        fos.close();
    }
}
輸出結果:
abc
  1. 雖然引數為int型別四個位元組,但是隻會保留一個位元組的資訊寫出。
  2. 流操作完畢後,必須釋放系統資源,呼叫close方法,千萬記得。
  1. 寫出位元組陣列:write(byte[] b),每次可以寫出陣列中的資料,程式碼使用演示:
public class FOSWrite {
    public static void main(String[] args) throws IOException {
        // 使用檔名稱建立流物件
        FileOutputStream fos = new FileOutputStream("fos.txt");     
        // 字串轉換為位元組陣列
        byte[] b = "麻麻我想吃烤山藥".getBytes();
        // 寫出位元組陣列資料
        fos.write(b);
        // 關閉資源
        fos.close();
    }
}
輸出結果:
麻麻我想吃烤山藥
  1. 寫出指定長度位元組陣列:write(byte[] b, int off, int len) ,每次寫出從off索引開始,len個位元組,程式碼如下:
public class FOSWrite {
    public static void main(String[] args) throws IOException {
        // 使用檔名稱建立流物件
        FileOutputStream fos = new FileOutputStream("fos.txt");     
        // 字串轉換為位元組陣列
        byte[] b = "abcde".getBytes();
        // 寫出從索引2開始,2個位元組。索引2是c,兩個位元組,也就是cd。
        fos.write(b,2,2);
        // 關閉資源
        fos.close();
    }
}
輸出結果:
cd

FileOutputStream實現資料追加續寫、換行

經過以上的程式碼測試,每次程式執行,每次建立輸出流物件,都會清空目標檔案中的資料。如何保留目標檔案中資料,還能繼續追加新資料呢?並且實現換行呢?其實很簡單,這個時候我們又要再學習FileOutputStream的另外兩個構造方法了,如下:

1、public FileOutputStream(File file, boolean append)

2、public FileOutputStream(String name, boolean append)

這兩個構造方法,第二個引數中都需要傳入一個boolean型別的值,true 表示追加資料,false 表示不追加也就是清空原有資料。這樣建立的輸出流物件,就可以指定是否追加續寫了,至於Windows換行則是 \n\r ,下面將會詳細講到。

實現資料追加續寫程式碼如下:

public class FOSWrite {
    public static void main(String[] args) throws IOException {
        // 使用檔名稱建立流物件
        FileOutputStream fos = new FileOutputStream("fos.txt",true);     
        // 字串轉換為位元組陣列
        byte[] b = "abcde".getBytes();
        // 寫出從索引2開始,2個位元組。索引2是c,兩個位元組,也就是cd。
        fos.write(b);
        // 關閉資源
        fos.close();
    }
}
檔案操作前:cd
檔案操作後:cdabcde

Windows系統裡,換行符號是\r\n ,具體程式碼如下:

public class FOSWrite {
    public static void main(String[] args) throws IOException {
        // 使用檔名稱建立流物件
        FileOutputStream fos = new FileOutputStream("fos.txt");  
        // 定義位元組陣列
        byte[] words = {97,98,99,100,101};
        // 遍歷陣列
        for (int i = 0; i < words.length; i++) {
            // 寫出一個位元組
            fos.write(words[i]);
            // 寫出一個換行, 換行符號轉成陣列寫出
            fos.write("\r\n".getBytes());
        }
        // 關閉資源
        fos.close();
    }
}

輸出結果:
a
b
c
d
e
  • 回車符\r和換行符\n
    • 回車符:回到一行的開頭(return)。
    • 換行符:下一行(newline)。
  • 系統中的換行:
    • Windows系統裡,每行結尾是 回車+換行 ,即\r\n
    • Unix系統裡,每行結尾只有 換行 ,即\n
    • Mac系統裡,每行結尾是 回車 ,即\r。從 Mac OS X開始與Linux統一。

2.3 位元組輸入流(InputStream)

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

位元組輸入流的基本共性功能方法:

1、 public void close() :關閉此輸入流並釋放與此流相關聯的任何系統資源。
2、public abstract int read(): 從輸入流讀取資料的下一個位元組。

3、 public int read(byte[] b): 該方法返回的int值代表的是讀取了多少個位元組,讀到幾個返回幾個,讀取不到返回-1

FileInputStream類

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

FileInputStream的構造方法

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

同樣的,推薦使用第二種構造方法:

 FileInputStream inputStream = new FileInputStream("a.txt");

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

構造舉例,程式碼如下:

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

FileInputStream讀取位元組資料

  1. 讀取位元組:read方法,每次可以讀取一個位元組的資料,提升為int型別,讀取到檔案末尾,返回-1,程式碼測試如下【read.txt檔案中內容為abcde】:
public class FISRead {
    public static void main(String[] args) throws IOException{
        // 使用檔名稱建立流物件
        FileInputStream fis = new FileInputStream("read.txt");//read.txt檔案中內容為abcde
        // 讀取資料,返回一個位元組
        int read = fis.read();
        System.out.println((char) read);
        read = fis.read();
        System.out.println((char) read);
        read = fis.read();
        System.out.println((char) read);
        read = fis.read();
        System.out.println((char) read);
        read = fis.read();
        System.out.println((char) read);
        // 讀取到末尾,返回-1
        read = fis.read();
        System.out.println( read);
        // 關閉資源
        fis.close();
    }
}
輸出結果:
a
b
c
d
e
-1

迴圈改進讀取方式,程式碼使用演示:

public class FISRead {
    public static void main(String[] args) throws IOException{
        // 使用檔名稱建立流物件
        FileInputStream fis = new FileInputStream("read.txt");
        // 定義變數,儲存資料
        int b ;
        // 迴圈讀取
        while ((b = fis.read())!=-1) {
            System.out.println((char)b);
        }
        // 關閉資源
        fis.close();
    }
}
輸出結果:
a
b
c
d
e
  1. 使用位元組陣列讀取:read(byte[] b),每次讀取b的長度個位元組到陣列中,返回讀取到的有效位元組個數,讀取到末尾時,返回-1 ,程式碼使用演示:
public class FISRead {
    public static void main(String[] args) throws IOException{
        // 使用檔名稱建立流物件.
        FileInputStream fis = new FileInputStream("read.txt"); // read.txt檔案中內容為abcde
        // 定義變數,作為有效個數
        int len ;
        // 定義位元組陣列,作為裝位元組資料的容器   
        byte[] b = new byte[2];
        // 迴圈讀取
        while (( len= fis.read(b))!=-1) {
            // 每次讀取後,把陣列變成字串列印
            System.out.println(new String(b));
        }
        // 關閉資源
        fis.close();
    }
}

輸出結果:
ab
cd
ed

由於read.txt檔案中內容為abcde,而錯誤資料d,是由於最後一次讀取時,只讀取一個位元組e,陣列中,上次讀取的資料沒有被完全替換【注意是替換,看下圖】,所以要通過len ,獲取有效的位元組

程式碼如下:

public class FISRead {
    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();
    }
}

輸出結果:
ab
cd
e

在開發中一般強烈推薦使用陣列讀取檔案,程式碼如下:

package io;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class input2 {
    public static void main(String args[]){
        FileInputStream inputStream = null;
        try {
            inputStream = new FileInputStream("a.txt");
            int len = 0 ;
            byte[] bys = new byte[1024];
            while ((len = inputStream.read(bys)) != -1) {
                System.out.println(new String(bys,0,len));
            }
        
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}

位元組流FileInputstream複製圖片

複製圖片原理

程式碼實現

複製圖片檔案,程式碼如下:

public class Copy {
    public static void main(String[] args) throws IOException {
        // 1.建立流物件
        // 1.1 指定資料來源
        FileInputStream fis = new FileInputStream("D:\\test.jpg");
        // 1.2 指定目的地
        FileOutputStream fos = new FileOutputStream("test_copy.jpg");

        // 2.讀寫資料
        // 2.1 定義陣列
        byte[] b = new byte[1024];
        // 2.2 定義長度
        int len;
        // 2.3 迴圈讀取
        while ((len = fis.read(b))!=-1) {
            // 2.4 寫出資料
            fos.write(b, 0 , len);
        }

        // 3.關閉資源
        fos.close();
        fis.close();
    }
}

注:複製文字、圖片、mp3、視訊等的方式一樣。

到這裡,已經從File類講到了位元組流OutputStream與InputStream,而現在將主要從字元流Reader和Writer的故事開展。

字元流Reader和Writer的故事

字元流Reader和Writer的故事從它們的繼承圖開始,啥都不說了,直接看圖

字元流

字元流的由來:因為資料編碼的不同,因而有了對字元進行高效操作的流物件,字元流本質其實就是基於位元組流讀取時,去查了指定的碼錶,而位元組流直接讀取資料會有亂碼的問題(讀中文會亂碼),這個時候小白同學就看不懂了,沒事,咋們先來看個程式:

package IO;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;

public class CharaterStream {
    public static void main(String[] args) throws Exception {
        //FileInputStream為操作檔案的字元輸入流
        FileInputStream inputStream = new FileInputStream("a.txt");//內容為哥敢摸屎

        int len;
        while ((len=inputStream.read())!=-1){
           System.out.print((char)len);
        }

    }
}
執行結果:   ??¥??¢????±

具體現狀分析

話說,就是你哥我敢摸si,那你哥我肯定也不認識這玩意啊: ??¥??¢????±

位元組流讀取中文字元時,可能不會顯示完整的字元,那是因為一箇中文字元佔用多個位元組儲存。

那位元組流就沒辦法了嗎?不,位元組流依舊有辦法,只是麻煩了點,程式碼如下:

public class CharaterStream {
    public static void main(String[] args) throws Exception {

        FileInputStream inputStream = new FileInputStream("a.txt");
        byte[] bytes = new byte[1024];
        int len;
        while ((len=inputStream.read(bytes))!=-1){
           System.out.print(new String(bytes,0,len));
        }
    }
}
執行結果: 哥敢摸屎

這是為啥呢?沒錯解碼的正是String,檢視new String()的原始碼,String構造方法有解碼功能,並且預設編碼是utf-8,程式碼如下:

this.value = StringCoding.decode(bytes, offset, length);
 
 再點進decode,循序漸進發現,預設編碼是UTF-8

儘管位元組流也能有辦法決絕亂碼問題,但是還是比較麻煩,於是java就有了字元流,字元為單位讀寫資料,字元流專門用於處理文字檔案。如果處理純文字的資料優先考慮字元流,其他情況就只能用位元組流了(圖片、視訊、等等只文字例外)。

從另一角度來說:字元流 = 位元組流 + 編碼表

1、 字元輸入流(Reader)

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

字元輸入流的共性方法:

1、public void close() :關閉此流並釋放與此流相關聯的任何系統資源。
2、 public int read(): 從輸入流讀取一個字元。
3、 public int read(char[] cbuf): 從輸入流中讀取一些字元,並將它們儲存到字元陣列 cbuf

FileReader類

java.io.FileReader類是讀取字元檔案的便利類。構造時使用系統預設的字元編碼和預設位元組緩衝區。

構造方法

1、FileReader(File file): 建立一個新的 FileReader ,給定要讀取的File物件。
2、 FileReader(String fileName): 建立一個新的 FileReader ,給定要讀取的檔案的字串名稱。

構造方法的使用就算不寫應該都很熟悉了吧,程式碼如下:

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

FileReader讀取字元資料

  1. 讀取字元:read方法,每次可以讀取一個字元的資料,提升為int型別,讀取到檔案末尾,返回-1,迴圈讀取,程式碼使用演示:
public class FRRead {
    public static void main(String[] args) throws IOException {
        // 使用檔名稱建立流物件
        FileReader fr = new FileReader("a.txt");
        // 定義變數,儲存資料
        int b ;
        // 迴圈讀取
        while ((b = fr.read())!=-1) {
            System.out.println((char)b);
        }
        // 關閉資源
        fr.close();
    }
}

至於讀取的寫法類似位元組流的寫法,只是讀取單位不同罷了。

2、字元輸出流(Writer)

java.io.Writer抽象類是字元輸出流的所有類的超類(父類),將指定的字元資訊寫出到目的地。它同樣定義了字元輸出流的基本共性功能方法。

字元輸出流的基本共性功能方法:

1、void write(int c) 寫入單個字元。
2、void write(char[] cbuf)寫入字元陣列。
3、 abstract void write(char[] cbuf, int off, int len)寫入字元陣列的某一部分,off陣列的開始索引,len寫的字元個數。
4、 void write(String str)寫入字串。
5、void write(String str, int off, int len) 寫入字串的某一部分,off字串的開始索引,len寫的字元個數。
6、void flush()重新整理該流的緩衝。
7、void close() 關閉此流,但要先重新整理它。

FileWriter類

java.io.FileWriter類是寫出字元到檔案的便利類。構造時使用系統預設的字元編碼和預設位元組緩衝區。

構造方法

1、 FileWriter(File file): 建立一個新的 FileWriter,給定要讀取的File物件。
2、FileWriter(String fileName): 建立一個新的 FileWriter,給定要讀取的檔案的名稱。

依舊是熟悉的構造舉例,程式碼如下:

public class FileWriterConstructor {
    public static void main(String[] args) throws IOException {
        // 第一種:使用File物件建立流物件
        File file = new File("a.txt");
        FileWriter fw = new FileWriter(file);
      
        // 第二種:使用檔名稱建立流物件
        FileWriter fw = new FileWriter("b.txt");
    }
}

FileWriter寫出資料

寫出字元:write(int b) 方法,每次可以寫出一個字元資料,程式碼使用演示:

public class FWWrite {
    public static void main(String[] args) throws IOException {
        // 使用檔名稱建立流物件
        FileWriter fw = new FileWriter("fw.txt");     
        // 寫出資料
        fw.write(97); // 寫出第1個字元
        fw.write('b'); // 寫出第2個字元
        fw.write('C'); // 寫出第3個字元
        
        //關閉資源時,與FileOutputStream不同。 如果不關閉,資料只是儲存到緩衝區,並未儲存到檔案。
        // fw.close();
    }
}
輸出結果:
abC

【注意】關閉資源時,與FileOutputStream不同。 如果不關閉,資料只是儲存到緩衝區,並未儲存到檔案。

關閉close和重新整理flush

因為內建緩衝區的原因,如果不關閉輸出流,無法寫出字元到檔案中。但是關閉的流物件,是無法繼續寫出資料的。如果我們既想寫出資料,又想繼續使用流,就需要flush 方法了。

flush :重新整理緩衝區,流物件可以繼續使用。
close:先重新整理緩衝區,然後通知系統釋放資源。流物件不可以再被使用了。

flush還是比較有趣的,童鞋們不自己執行一下還真不好體會,現在博主就寫個程式讓你體會體會:
字元流

public class FlushDemo {
    public static void main(String[] args) throws Exception {
        //源   也就是輸入流【讀取流】 讀取a.txt檔案
        FileReader fr=new FileReader("a.txt");  //必須要存在a.txt檔案,否則報FileNotFoundException異常
        //目的地  也就是輸出流
        FileWriter fw=new FileWriter("b.txt");  //系統會自動建立b.txt,因為它是輸出流!
        int len;
        while((len=fr.read())!=-1){
           fw.write(len);
        }
   注意這裡是沒有使用close關閉流,開發中不能這樣做,但是為了更好的體會flush的作用
    }
}


執行效果是怎麼樣的呢?答案是b.txt檔案中依舊是空的,是的並沒有任何東西,為啥呢?熊dei啊,我在上面就用紅色字型特別標註過了,就是這句話: 【注意】關閉資源時,與FileOutputStream不同。 如果不關閉,資料只是儲存到緩衝區,並未儲存到檔案。這個時候反應過來了吧,可見實踐例子的重要性,程式設計就是這樣,不去敲,永遠學不會!!!所以一定要去敲,博主沒敲過10萬行程式碼真的沒有臉出去說自己是學java的。所以,大家一定要多思考,多敲啊!!!

所以,我們在以上的程式碼中再新增下面三句程式碼,就完美了,b.txt檔案就能複製到原始檔的資料了!

  fr.close();
  fw.flush();
  fw.close();

flush()這個函式是清空的意思,用於清空緩衝區的資料流,進行流的操作時,資料先被讀到記憶體中,然後再用資料寫到檔案中,那麼當你資料讀完時,我們如果這時呼叫close()方法關閉讀寫流,這時就可能造成資料丟失,為什麼呢?因為,讀入資料完成時不代表寫入資料完成,一部分資料可能會留在快取區中,這個時候flush()方法就格外重要了。

好了,接下來close使用程式碼如下:

public class FWWrite {
    public static void main(String[] args) throws IOException {
        // 使用檔名稱建立流物件
        FileWriter fw = new FileWriter("fw.txt");
        // 寫出資料,通過flush
        fw.write('刷'); // 寫出第1個字元
        fw.flush();
        fw.write('新'); // 繼續寫出第2個字元,寫出成功
        fw.flush();
      
        // 寫出資料,通過close
        fw.write('關'); // 寫出第1個字元
        fw.close();
        fw.write('閉'); // 繼續寫出第2個字元,【報錯】java.io.IOException: Stream closed
        fw.close();
    }
}

即便是flush方法寫出了資料,操作的最後還是要呼叫close方法,釋放系統資源。

FileWriter的續寫和換行

續寫和換行:操作類似於FileOutputStream操作(上一篇部落格講到過),直接上程式碼:

public class FWWrite {
    public static void main(String[] args) throws IOException {
        // 使用檔名稱建立流物件,可以續寫資料
        FileWriter fw = new FileWriter("fw.txt",true);     
        // 寫出字串
        fw.write("哥敢");
        // 寫出換行
        fw.write("\r\n");
        // 寫出字串
        fw.write("摸屎");
        // 關閉資源
        fw.close();
    }
}
輸出結果:
哥敢
摸屎

FileReader和FileWriter類完成文字檔案複製

直接上程式碼:

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class CopyFile {
    public static void main(String[] args) throws IOException {
        //建立輸入流物件
        FileReader fr=new FileReader("F:\\新建資料夾\\aa.txt");//檔案不存在會丟擲java.io.FileNotFoundException
        //建立輸出流物件
        FileWriter fw=new FileWriter("C:\\copyaa.txt");
        /*建立輸出流做的工作:
         *      1、呼叫系統資源建立了一個檔案
         *      2、建立輸出流物件
         *      3、把輸出流物件指向檔案        
         * */
        //文字檔案複製,一次讀一個字元
        copyMethod1(fr, fw);
        //文字檔案複製,一次讀一個字元陣列
        copyMethod2(fr, fw);
        
        fr.close();
        fw.close();
    }

    public static void copyMethod1(FileReader fr, FileWriter fw) throws IOException {
        int ch;
        while((ch=fr.read())!=-1) {//讀資料
            fw.write(ch);//寫資料
        }
        fw.flush();
    }

    public static void copyMethod2(FileReader fr, FileWriter fw) throws IOException {
        char chs[]=new char[1024];
        int len=0;
        while((len=fr.read(chs))!=-1) {//讀資料
            fw.write(chs,0,len);//寫資料
        }
        fw.flush();
    }
}

CopyFile

最後再次強調:
字元流,只能操作文字檔案,不能操作圖片,視訊等非文字檔案。當我們單純讀或者寫文字檔案時 使用字元流 其他情況使用位元組流

IO異常的處理

我們在學習的過程中可能習慣把異常丟擲,而實際開發中並不能這樣處理,建議使用try...catch...finally 程式碼塊,處理異常部分,格式程式碼如下:

public class HandleException1 {
    public static void main(String[] args) {
        // 宣告變數
        FileWriter fw = null;
        try {
            //建立流物件
            fw = new FileWriter("fw.txt");
            // 寫出資料
            fw.write("哥敢摸si"); //哥敢摸si
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fw != null) {
                    fw.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

如果對異常不是特別熟練的童鞋可以參考這篇文章【java基礎之異常】死了都要try,不淋漓盡致地catch我不痛快!

好了,到這裡,字元流Reader和Writer的故事的到這裡了!

前面主要寫了一些基本的流作為IO流的入門。從這裡開始將要見識一些更強大的流。比如能夠高效讀寫的緩衝流,能夠轉換編碼的轉換流,能夠持久化儲存物件的序列化流等等,而這些強大的流都是在基本的流物件基礎之上而來的!這些強大的流將伴隨著我們今後的開發!

1、緩衝流【掌握】

1.1 簡要概述

首先我們來認識認識一下緩衝流,也叫高效流,是對4個FileXxx 流的“增強流”。

緩衝流的基本原理:

1、使用了底層流物件從具體裝置上獲取資料,並將資料儲存到緩衝區的陣列內。
2、通過緩衝區的read()方法從緩衝區獲取具體的字元資料,這樣就提高了效率。
3、如果用read方法讀取字元資料,並存儲到另一個容器中,直到讀取到了換行符時,將另一個容器臨時儲存的資料轉成字串返回,就形成了readLine()功能。

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

緩衝書寫格式為BufferedXxx,按照資料型別分類:

  • 位元組緩衝流:BufferedInputStreamBufferedOutputStream
  • 字元緩衝流:BufferedReaderBufferedWriter

1.2 位元組緩衝流

構造方法

  • public BufferedInputStream(InputStream in) :建立一個新的緩衝輸入流,注意引數型別為InputStream。
  • public BufferedOutputStream(OutputStream out): 建立一個新的緩衝輸出流,注意引數型別為OutputStream。

構造舉例程式碼如下:

//構造方式一: 建立位元組緩衝輸入流【但是開發中一般常用下面的格式申明】
FileInputStream fps = new FileInputStream(b.txt);
BufferedInputStream bis = new BufferedInputStream(fps)

//構造方式一: 建立位元組緩衝輸入流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("b.txt"));

///構造方式二: 建立位元組緩衝輸出流
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("b.txt"));

感受緩衝流的高效

緩衝流讀寫方法與基本的流是一致的,我們通過複製370多MB的大檔案,測試它的效率。

  1. 基本流,程式碼如下:
public class BufferedDemo {
    public static void main(String[] args) throws FileNotFoundException {
        // 記錄開始時間
        long start = System.currentTimeMillis();
        // 建立流物件
        try (
            FileInputStream fis = new FileInputStream("py.exe");//exe檔案夠大
            FileOutputStream fos = new FileOutputStream("copyPy.exe")
        ){
            // 讀寫資料
            int b;
            while ((b = fis.read()) != -1) {
                fos.write(b);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 記錄結束時間
        long end = System.currentTimeMillis();
        System.out.println("普通流複製時間:"+(end - start)+" 毫秒");
    }
}
不好意思十分鐘過去了還在玩命複製中...
  1. 緩衝流,程式碼如下:
public class BufferedDemo {
    public static void main(String[] args) throws FileNotFoundException {
        // 記錄開始時間
        long start = System.currentTimeMillis();
        // 建立流物件
        try (
         BufferedInputStream bis = new BufferedInputStream(new FileInputStream("py.exe"));
         BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copyPy.exe"));
        ){
        // 讀寫資料
            int b;
            while ((b = bis.read()) != -1) {
                bos.write(b);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 記錄結束時間
        long end = System.currentTimeMillis();
        System.out.println("緩衝流複製時間:"+(end - start)+" 毫秒");
    }
}

緩衝流複製時間:8016 毫秒

有的童鞋就要說了,我要更快的速度!最近看速度與激情7有點上頭,能不能再快些?答案是當然可以

想要更快可以使用陣列的方式,程式碼如下:

public class BufferedDemo {
    public static void main(String[] args) throws FileNotFoundException {
        // 記錄開始時間
        long start = System.currentTimeMillis();
        // 建立流物件
        try (
         BufferedInputStream bis = new BufferedInputStream(new FileInputStream("py.exe"));
         BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copyPy.exe"));
        ){
            // 讀寫資料
            int len;
            byte[] bytes = new byte[8*1024];
            while ((len = bis.read(bytes)) != -1) {
                bos.write(bytes, 0 , len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 記錄結束時間
        long end = System.currentTimeMillis();
        System.out.println("緩衝流使用陣列複製時間:"+(end - start)+" 毫秒");
    }
}
緩衝流使用陣列複製時間:521 毫秒  

1.3 字元緩衝流

構造方法

相同的來看看其構造,其格式以及原理和位元組緩衝流是一樣一樣的!

  • public BufferedReader(Reader in) :建立一個新的緩衝輸入流,注意引數型別為Reader。
  • public BufferedWriter(Writer out): 建立一個新的緩衝輸出流,注意引數型別為Writer。

構造舉例,程式碼如下:

// 建立字元緩衝輸入流
BufferedReader br = new BufferedReader(new FileReader("b.txt"));
// 建立字元緩衝輸出流
BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt"));

字元緩衝流特有方法

字元緩衝流的基本方法與普通字元流呼叫方式一致,這裡不再闡述,我們來看字元緩衝流具備的特有方法。

  • BufferedReader:public String readLine(): 讀一行資料。 讀取到最後返回null
  • BufferedWriter:public void newLine(): 換行,由系統屬性定義符號。

readLine方法演示程式碼如下:

public class BufferedReaderDemo {
    public static void main(String[] args) throws IOException {
         // 建立流物件
        BufferedReader br = new BufferedReader(new FileReader("a.txt"));
        // 定義字串,儲存讀取的一行文字
        String line  = null;
        // 迴圈讀取,讀取到最後返回null
        while ((line = br.readLine())!=null) {
            System.out.print(line);
            System.out.println("------");
        }
        // 釋放資源
        br.close();
    }
}

newLine方法演示程式碼如下:

public class BufferedWriterDemo throws IOException {
  public static void main(String[] args) throws IOException  {
      // 建立流物件
      BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt"));
      // 寫出資料
      bw.write("哥");
      // 寫出換行
      bw.newLine();
      bw.write("敢");
      bw.newLine();
      bw.write("摸屎");
      bw.newLine();
      bw.write("你敢嗎?");
      bw.newLine();
      // 釋放資源
      bw.close();
  }
}
輸出效果:
哥
敢
摸屎
你敢嗎?

1.4 字元緩衝流練習

字元緩衝流練習啥捏?先放鬆一下吧各位,先欣賞欣賞我寫的下面的詩篇

6.你說你的程式叫簡單,我說我的程式碼叫詩篇
1.一想到你我就哦豁豁豁豁豁豁豁豁豁豁....哦nima個頭啊,完全不理人家受得了受不了
8.Just 簡單你和我 ,Just 簡單程式設計師
3.約了地點卻忘了見面 ,懂得寂寞才明白浩瀚
5.沉默是最大的發言權
2.總是喜歡坐在電腦前, 總是喜歡工作到很晚
7.向左走 又向右走,我們轉了好多的彎
4.你從來就不問我,你還是不是那個程式設計師

欣賞完了咩?沒錯咋們就練習如何使用緩衝流的技術把上面的詩篇歸順序,都編過號了~就是前面的1到8的編號~

分析:首先用字元輸入緩衝流建立個源,裡面放沒有排過序的文字,之後用字元輸出緩衝流建立個目標接收,排序的過程就要自己寫方法了哦,可以從每條詩詞的共同點“.”符號下手!

程式碼實現

public class BufferedTest {
    public static void main(String[] args) throws IOException {
        // 建立map集合,儲存文字資料,鍵為序號,值為文字
        HashMap<String, String> lineMap = new HashMap<>();

        // 建立流物件  源
        BufferedReader br = new BufferedReader(new FileReader("a.txt"));
        //目標
        BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt"));

        // 讀取資料
        String line  = null;
        while ((line = br.readLine())!=null) {
            //