Java中IO流解析及代碼實例詳解

1、IO流

1.流和流的分類

什麼是IO流?

I:Input (輸入)

O: Ouput(輸出)

IO流的分類?

有多種分類方式:

  • 一種方式是按照流的方向進行分類:

以內存作為參照物

往內存中去,叫做輸入(Input)。或者叫做讀(Read) .

從內存中出來,叫做輸出(output)。或者叫做寫(write) .

  • 另一種方式是按照讀取藪據方式不同進行分類:
    • 有的流是按照字節的方式讀取數據,一次讀取1個字節byte,等同於一次讀取8個二進制。
    • 這種流是萬能的,什麼類型的文件都可以讀取。包括:文本文件,圖片,聲音文件,視頻……
    • 有的流是按照字符的方式讀取數據的,一次讀取一個字符,這種流是為瞭方便讀取普通文本文件而存在的,這種流不能讀取:圖片、聲音、視頻等文件。隻能讀取純文本文件,連word文件都無法讀取

總結:流的分類

輸入流、輸出流、字節流、字符流

Java IO流的四大傢族

java.io.InputStream 字節輸入流
java.io.OutputStream 字節輸出流
java.io.Reader 字符輸入流
java.io.Writer 字符輸出流

註意:

  • 四個都是抽象類。
  • 所有流都是實現瞭java.io.Closeable接口,都是可關閉的
    • 流是一個管道,是內存和硬盤之間的通道。用完後一定要關閉,不然會占用很多資源。
  • 所有的輸出流都實現瞭java.io.Flushable接口,都是可刷新的
    • 最終輸出後,一定要記得調用flush()方法。目的是將管道中剩餘未輸出的數據強行輸出完(清空管道)
    • 如果沒有flash(),可能會導致丟失數據
  • 在java中隻要”類名”以strean結尾的都是字節流。以”Reader/writer”結尾的都是字符流

java.io包下需要掌握的流有16個:

文件專屬:

java . io.FileInputstream

java.io.FileOutputstream

java.io.FileReader

java.io.Filewriter

轉換流:(將字節流轉換成字符流)

java . io . InputstreamReader

java . io. outputstreamWriter

緩沖流專屬:

java. io. BufferedReader

java.io.BufferedWriter

java.io. BufferedInputstream

java . io.Bufferedoutputstrean

數據流專屬:

java . io . DataInputstream

java .io. Dataoutputstrean

標準輸出流:

java . io . Printwriter

java . io . Printstream

對象專屬流:

java.io.ObjectInputstream

java.io.ObjectOutputstream

一下子看到這麼多流。不用怕,隻需要掌握一組的使用方法就可以瞭,使用其他的都是一樣,一通百通。

下面以萬能的字節流(FileInputStream、FileOutputStream)來舉例說明。

2.如何使用流

1、輸入流(讀文件):FileInputStream

方法一

使用FileInputStream的read()

步驟:

1、 創建流對象

​ new FileInputStream(參數)

​ 參數可以是一個File類、String字符串表示的文件路徑

2、使用流的方法對文件進行讀

輸入流調用read()方法,每次從流中讀取一個數據字節。返回值是讀到的字節的ASCII值

讀取一個後,他會指向下一個字符,如果到達末尾。返回-1,可以用while()來實現。

3、關閉流

調用close()方法

實例:

package com.io.stream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class TestFileInputStream {
    //定義一個流對象
    private static FileInputStream fis;
    public static void main(String[] args) {
        try {
            //1、 創建流對象.參數可以是一個File類、String字符串表示的文件路徑
            fis = new FileInputStream("E:\\aaa.txt");//在e盤下有一個aaa.txt的文件夾,內容為(abcdefg)
            //2、使用流的方法對文件進行讀
            while (true) {
                int read = fis.read();//註意這裡返回的是int類型,代表是字符的ASCII值
                if (read==-1) {//讀到最後會返回-1,退出條件
                    break;
                }
                System.out.println(read);
            }
        } catch (FileNotFoundException e) {//異常細粒化,不同異常可以選擇不同的處理方式
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                //3、關閉流
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

/*輸出
97
98
99
100
101
102
103
*/

int read()

該方法因為每次都是隻讀取一個字節,這樣內存和硬盤交互太頻繁 ,開銷大且效率不高。一般不使用,但是要瞭解,這是基礎。

方法二

一般使用這種方法

使用read(byte[] b)

一次讀取最多 b.length 個字節,減少內存和硬盤交互,提高執行效率

該方法返回的是讀到的字節的數量

使用步驟:和前面隻有第二步有些差別

byte[] b= new byte[3];

int readNum = fis.read(b);

解析

  • 新建一個byte[]數組b,代表我們一次需要讀多少個字符。讀完這麼多字符後,下次一讀是從當前讀到的位置開始,和上面一樣
  • 將byte數組b傳入read()方法中,返回一個我們byte數組的大小,此時readNum值為3

那麼我們讀出來的數據在哪裡呢。自然是存放在byte數組裡。我們打印b數組看,發現b數組裡存放的讀到的ascii值

細節:

因為數組的長度是我們設定的。如果文件讀到最後,不一定剛好符合我們設定的長度,最後一組的長度隻有小於或等於數組的長度。如果小於它會返回剩餘的字符的數量,讀不到返回-1,我們可以利用這個作為循環退出的條件。

另外一個,因為他的長度小於b數組,那麼它的值也是無法填滿b數組的,此時,它隻更新b數組中前面的n個讀到的值,b數組後面的是上一個讀取時讀取到的值。

實例:

仍然讀取aaa.txt文件,內容為(abcdefg)

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Arrays;
public class TestFileInputStream2 {
    private static FileInputStream fis;
    public static void main(String[] args) {
        try {
            fis = new FileInputStream("IO流\\aaa.txt");
            //準備一個byte數組
            byte[] b= new byte[3];
            int readNum = 0;
            while (true) {
                //先進行讀操作。
                readNum=fis.read(b);
                //判斷
                if (readNum == -1) {
                    break;
                }
                //將字符數組轉為String,後面兩個參數為起始位置,和長度
                String str = new String(b, 0, readNum);
                System.out.println(str);
            }
            /*輸出
				abc
				def
				g
			*/
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                //關閉流
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

其他常用的方法

int available()

返回流中還有多少個字符沒有讀。(相當於獲得文件讀到的位置到末尾字符的個數,如果沒開始讀,返回文件中總字符的數量)

使用這個可以對一些比較小的(字符長度)文件一次讀完,如果不超過byte數組的范圍的話

public class TestFileInputStream3 {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("IO流\\aaa.txt");
        byte[] bytes = new byte[fis.available()];//創建一個和文件字符數量一樣大的byte數組,文件不能太大。
        fis.read(bytes);
        System.out.println(new String(bytes));//b轉為字符串,輸出abcdefg
    }
}

skip(long n)

顧名思義:從輸入流中跳過並丟棄 n 個字節的數據。

2、輸出流(寫文件):FileOutputStream

和輸入的流程差不多,過程也基本相同

write(byte[] b)

註意:

  • byte的范圍是-128~127,超過會報錯
  • 寫完之後,一定要刷新
  • 如果當前路徑下文件已存在,會將原文件清空,再寫入(慎用)。
    • 如果它是一個目錄,而不是一個常規文件,會報錯。
    • 或者該文件不存在,但無法創建它,會報錯。
  • 如果當前路徑下文件不存在,會新建文件。
package com.io.stream.output;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/**
 * @Author cyh
 * @Date 2021/8/7 17:21
 */
public class Test1 {
    private static FileOutputStream fos;
    public static void main(String[] args) {
        try {
            //新建一個輸出流,參數是輸出的路徑,最後的為文件名。
            fos = new FileOutputStream("IO流\\bbb.txt");
            //新建一個byte數組,裡面對應的是abcd的ascii值
            byte[] bytes = {97, 98, 99, 100,101,127}; //註意byte的范圍是-128~127
            //輸出流調用write方法寫入byte數組,以文件形式保存到上面的路徑
            fos.write(bytes);
            //寫完之後,一定要刷新
            fos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

追加寫文件

想要追加寫文件,則需要在新建流的的時候,加一個參數true。

表示將字節寫入文件末尾處,而不是寫入文件開始處

//新建一個輸出流,參數是輸出的路徑,最後的為文件名。增加true表示開啟追加寫
fos = new FileOutputStream("IO流\\bbb.txt",true);

註意:和上面一樣,不一樣的點隻是追加寫。

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

將指定 byte 數組中從偏移量 off 開始的 len 個字節寫入此文件輸出流.

意思就是,寫入的內容為off~len。如果off是0,len是數組長度,那就全部寫入;如果off是數組長度-1,len是數組長度,那就隻寫入瞭一個字符

3.文件的拷貝

  • 使用FileInputStream+FileOutputStream即可完成文件的拷貝
  • 拷貝的過程應該是一邊讀、一邊寫
  • 使用上面的字節流拷貝文件的時候,文件類型隨意,是萬能的。什麼文件都可以拷貝

實例

package com.io.stream.copy;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/**
 * @Author cyh
 * @Date 2021/8/7 17:53
 */
public class TestCopy {
    private static FileInputStream fis;
    private static FileOutputStream fos;
    public static void main(String[] args) {
        try {
            //創建一個輸入流
            fis = new FileInputStream("D:\\edgeDownload\\VSCodeUserSetup-x64-1.55.0.exe");
            //創建一個輸出流
            fos = new FileOutputStream("C:\\Users\\PC\\Desktop\\copy\\b\\VSCodeUserSetup-x64-1.55.0.exe");
            //核心,一邊讀、一邊寫
            byte[] bytes = new byte[1024*1024];//1MB,一次最多拷貝1MB
            int readNum=0;
            while((readNum=fis.read(bytes))!=-1){
                fos.write(bytes,0,readNum);//將bytes全部寫入
            }
            //刷新
            fos.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //資源要分開釋放,不然一個出錯會導致另一個無法釋放
            try {
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

總結

本篇文章就到這裡瞭,希望能給你帶來幫助,也希望您能夠多多關註WalkonNet的更多內容!

推薦閱讀: