您的位置:首頁 > 軟件教程 > 教程 > 面試官:字節(jié)流可以處理一切文件為什么還需要字符流呢?

面試官:字節(jié)流可以處理一切文件為什么還需要字符流呢?

來源:好特整理 | 時間:2024-06-15 09:46:12 | 閱讀:172 |  標(biāo)簽: 字節(jié) 面試   | 分享到:

一、寫在開頭 在計算機(jī)領(lǐng)域中百分之九十以上的程序擁有著和外部設(shè)備交互的功能,這就是我們常說的IO(Input/Output:輸入/輸出),所謂輸入就是外部數(shù)據(jù)導(dǎo)入計算機(jī)內(nèi)存中的過程,輸出則是將內(nèi)存或者說程序中的數(shù)據(jù)導(dǎo)入到外部存儲中,如數(shù)據(jù)庫、文件以及其他本地磁盤等。 二、什么是IO流 這種輸入輸出往

一、寫在開頭

在計算機(jī)領(lǐng)域中百分之九十以上的程序擁有著和外部設(shè)備交互的功能,這就是我們常說的IO(Input/Output:輸入/輸出),所謂輸入就是外部數(shù)據(jù)導(dǎo)入計算機(jī)內(nèi)存中的過程,輸出則是將內(nèi)存或者說程序中的數(shù)據(jù)導(dǎo)入到外部存儲中,如數(shù)據(jù)庫、文件以及其他本地磁盤等。

二、什么是IO流

這種輸入輸出往往遵循著先入先出,順序存取的特點,像水流一般,因此我們稱這樣的操作為流(Stream),如下我們根據(jù)不同的標(biāo)準(zhǔn),將IO流分為幾個門類:
面試官:字節(jié)流可以處理一切文件為什么還需要字符流呢?

根據(jù)數(shù)據(jù)流向:

  1. 輸入流:數(shù)據(jù)流向程序
  2. 輸出流:數(shù)據(jù)從程序流出。

根據(jù)處理單位:

  1. 字節(jié)流:一次讀入或讀出是8位二進(jìn)制;
  2. 字符流:一次讀入或讀出是16位二進(jìn)制
  3. JDK 中后綴是 Stream 是字節(jié)流;后綴是 Reader,Writer 是字符流。

根據(jù)功能點:

  1. 節(jié)點流:直接與數(shù)據(jù)源相連,讀入或?qū)懗觯?
  2. 處理流:與節(jié)點流一塊使用,在節(jié)點流的基礎(chǔ)上,再套接一層。

三、輸入與輸出

在java.io包中多達(dá)40多個類,它們的基類來源于InputStream、OutputStream、Reader、Writer這四個,我們一一看過。

3.1 InputStream(字節(jié)輸入流)

InputStream作為所有字節(jié)輸入流的父類,主要作用是將外部數(shù)據(jù)讀取到內(nèi)存中,主要方法如下(JDK8):

  1. read():返回輸入流中下一個字節(jié)的數(shù)據(jù)。返回的值介于 0 到 255 之間。如果未讀取任何字節(jié),則代碼返回 -1 ,表示文件結(jié)束。
  2. read(byte b[ ]) : 從輸入流中讀取一些字節(jié)存儲到數(shù)組 b 中。如果數(shù)組 b 的長度為零,則不讀取。如果沒有可用字節(jié)讀取,返回 -1。如果有可用字節(jié)讀取,則最多讀取的字節(jié)數(shù)最多等于 b.length , 返回讀取的字節(jié)數(shù)。這個方法等價于 read(b, 0, b.length)。
  3. read(byte b[], int off, int len):在read(byte b[ ]) 方法的基礎(chǔ)上增加了 off 參數(shù)(偏移量)和 len 參數(shù)(要讀取的最大字節(jié)數(shù))。
  4. skip(long n):忽略輸入流中的 n 個字節(jié) ,返回實際忽略的字節(jié)數(shù)。
  5. available():返回輸入流中可以讀取的字節(jié)數(shù)。
  6. close():關(guān)閉輸入流釋放相關(guān)的系統(tǒng)資源。
  7. markSupported() :該輸入流是否支持mark()和reset()方法。
  8. mark(int readlimit) :標(biāo)志輸入流的當(dāng)前位置,隨后調(diào)用reset()方法將該流重新定位到最近標(biāo)記的位置;參數(shù)readlimit表示:在標(biāo)記位置失效前可以讀取字節(jié)的最大限制。
  9. reset() :將此流重新定位到最后一次對此輸入流調(diào)用 mark 方法時的位置。

面試官:字節(jié)流可以處理一切文件為什么還需要字符流呢?

我們使用FileInputStream(文件字節(jié)輸入流)進(jìn)行如上方法的使用測試:

public class Test {
    public static void main(String[] args) throws IOException {
        try (InputStream fis = new FileInputStream("E:\\input.txt")) {
            System.out.println("可讀取字節(jié)數(shù):"
                    + fis.available());
            int content;
            long skip = fis.skip(3);
            System.out.println("忽略字節(jié)數(shù):" + skip);
            System.out.print("剩余全量字節(jié):");
            while ((content = fis.read()) != -1) {
                System.out.print((char) content);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

面試官:字節(jié)流可以處理一切文件為什么還需要字符流呢?

輸出:

可讀取字節(jié)數(shù):20
忽略字節(jié)數(shù):3
剩余全量字節(jié):name is JavaBuild

3.2 OutputStream(字節(jié)輸出流)

outputstream作為所有字節(jié)輸出流的父類,主要則是將內(nèi)存或者說程序中的數(shù)據(jù)以字節(jié)流的方式導(dǎo)入到外部存儲中,如數(shù)據(jù)庫、文件以及其他本地磁盤等。它的使用方法相比較字節(jié)輸入流要少:

  1. write(int b):將特定字節(jié)寫入輸出流。
  2. write(byte b[ ]) : 將數(shù)組b 寫入到輸出流,等價于 write(b, 0, b.length) 。
  3. write(byte[] b, int off, int len) : 在write(byte b[ ]) 方法的基礎(chǔ)上增加了 off 參數(shù)(偏移量)和 len 參數(shù)(要讀取的最大字節(jié)數(shù))。
  4. flush():刷新此輸出流并強(qiáng)制寫出所有緩沖的輸出字節(jié)。
  5. close():關(guān)閉輸出流釋放相關(guān)的系統(tǒng)資源。

面試官:字節(jié)流可以處理一切文件為什么還需要字符流呢?

我們同樣以FileOutputStream為例進(jìn)行上述方法的測試:

public class Test {
    public static void main(String[] args) throws IOException {
        try (FileOutputStream output = new FileOutputStream("E://output.txt")) {
            byte[] array = "JavaBuild".getBytes();
            //將一個字節(jié)數(shù)組寫入本地E盤的外部文件output.txt中
            output.write(array);

            //換行方式1:Windows下的換行符為"\r\n"
            output.write("\r\n".getBytes());
            //換行方式2:推薦使用,具有良好的跨平臺性
            String newLine = System.getProperty("line.separator");
            output.write(newLine.getBytes());

            //輸出字節(jié),這里的數(shù)字會被轉(zhuǎn)為asicc碼中對應(yīng)的字符
            output.write(64);
            output.write(56);
            output.write(56);
            output.write(56);
            //關(guān)閉輸出流
            output.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

效果:
面試官:字節(jié)流可以處理一切文件為什么還需要字符流呢?

這里可以直接輸出單字節(jié)數(shù)據(jù),也可以輸出指定的字節(jié)數(shù)組。輸出字節(jié)時以int類型輸出,最終根據(jù)ASCII表轉(zhuǎn)為字符。如十進(jìn)制64的轉(zhuǎn)為@符號。

3.3 Reader(字符輸入流)

在講解字符流之前,我們來解釋一個面試問題: “為什么有了字節(jié)流了還需要使用更耗時的字符流”

確實,字節(jié)作為信息存儲的最小單元,我們可以通過字節(jié)流實現(xiàn)所有信息的輸入與輸出,但有時候會存在一些問題,比如中文輸入時的編碼問題,將上述3.1中的測試代碼稍微改一下,執(zhí)行結(jié)果如下,中文在控制臺輸出時亂碼了。當(dāng)然我們可以通過設(shè)置編碼來規(guī)避這個問題,但有時候不曉得編碼時,亂碼真的會帶來潛在風(fēng)險!
面試官:字節(jié)流可以處理一切文件為什么還需要字符流呢?

字符流與字節(jié)流的區(qū)別:

  • 字節(jié)流一般用來處理圖像、視頻、音頻、PPT、Word等類型的文件。字符流一般用于處理純文本類型的文件,如TXT文件等,但不能處理圖像視頻等非文本文件。
  • 字節(jié)流本身沒有緩沖區(qū),緩沖字節(jié)流相對于字節(jié)流,效率提升非常高。而字符流本身就帶有緩沖區(qū),緩沖字符流相對于效率提升不明顯。

說了這么多,我們現(xiàn)在來看一下Reader這個字符輸入流提供的主要方法吧,其實和InputStream差不多,只不過一個是以字節(jié)為單位的讀取,一個是以字符為單位。

  1. read() : 從輸入流讀取一個字符。
  2. read(char[] cbuf) : 從輸入流中讀取一些字符,并將它們存儲到字符數(shù)組 cbuf中,等價于 read(cbuf, 0, cbuf.length) 。
  3. read(char[] cbuf, int off, int len):在read(char[] cbuf) 方法的基礎(chǔ)上增加了 off 參數(shù)(偏移量)和 len 參數(shù)(要讀取的最大字符數(shù))。
  4. skip(long n):忽略輸入流中的 n 個字符 ,返回實際忽略的字符數(shù)。
  5. close() : 關(guān)閉輸入流并釋放相關(guān)的系統(tǒng)資源。

面試官:字節(jié)流可以處理一切文件為什么還需要字符流呢?

我們將上述3.1中的測試代碼稍作加工,采用FileReader流進(jìn)行輸入,打印結(jié)果:

面試官:字節(jié)流可以處理一切文件為什么還需要字符流呢?

可以看到即便有中文,輸出在控制臺也沒有亂碼,因為字符流默認(rèn)采用的是 Unicode 編碼。

那么字符流是如何實現(xiàn)txt文件讀取的呢?通過FileReader類的繼承關(guān)系我們可以看到它繼承了InputStreamReader,這是一個字節(jié)轉(zhuǎn)字符輸入流,所以說從根本上,字符流底層依賴的還是字節(jié)流!

// 字節(jié)流轉(zhuǎn)換為字符流的橋梁
public class InputStreamReader extends Reader {
}
// 用于讀取字符文件
public class FileReader extends InputStreamReader {
}

3.4 Writer(字符輸出流)

writer是將內(nèi)存或者說程序中的數(shù)據(jù)以字符流的方式導(dǎo)入到外部存儲中,如數(shù)據(jù)庫、文件以及其他本地磁盤等。
常用方法也和OutputStream相似:

  1. write(int c) : 寫入單個字符。
  2. write(char[] cbuf):寫入字符數(shù)組 cbuf,等價于write(cbuf, 0, cbuf.length)。
  3. write(char[] cbuf, int off, int len):在write(char[] cbuf) 方法的基礎(chǔ)上增加了 off 參數(shù)(偏移量)和 len 參數(shù)(要讀取的最大字符數(shù))。
  4. write(String str):寫入字符串,等價于 write(str, 0, str.length()) 。
  5. write(String str, int off, int len):在write(String str) 方法的基礎(chǔ)上增加了 off 參數(shù)(偏移量)和 len 參數(shù)(要讀取的最大字符數(shù))。
  6. append(CharSequence csq):將指定的字符序列附加到指定的 Writer 對象并返回該 Writer 對象。
  7. append(char c):將指定的字符附加到指定的 Writer 對象并返回該 Writer 對象。
  8. flush():刷新此輸出流并強(qiáng)制寫出所有緩沖的輸出字符。
  9. close():關(guān)閉輸出流釋放相關(guān)的系統(tǒng)資源。

我們同樣以FileWriter為例,去測試一下:

public class Test {
    public static void main(String[] args) throws IOException {
        try (FileWriter fw = new FileWriter("E:\\outwriter.txt")) {
           fw.write("大家好。!");
           fw.append("我是JavaBuild");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

面試官:字節(jié)流可以處理一切文件為什么還需要字符流呢?

結(jié)尾彩蛋

如果本篇博客對您有一定的幫助,大家記得 留言+點贊+收藏 呀。原創(chuàng)不易,轉(zhuǎn)載請聯(lián)系Build哥!
面試官:字節(jié)流可以處理一切文件為什么還需要字符流呢?

如果您想與Build哥的關(guān)系更近一步,還可以關(guān)注“JavaBuild888”,在這里除了看到《Java成長計劃》系列博文,還有提升工作效率的小筆記、讀書心得、大廠面經(jīng)、人生感悟等等,歡迎您的加入!
面試官:字節(jié)流可以處理一切文件為什么還需要字符流呢?

小編推薦閱讀

好特網(wǎng)發(fā)布此文僅為傳遞信息,不代表好特網(wǎng)認(rèn)同期限觀點或證實其描述。

相關(guān)視頻攻略

更多

掃二維碼進(jìn)入好特網(wǎng)手機(jī)版本!

掃二維碼進(jìn)入好特網(wǎng)微信公眾號!

本站所有軟件,都由網(wǎng)友上傳,如有侵犯你的版權(quán),請發(fā)郵件[email protected]

湘ICP備2022002427號-10 湘公網(wǎng)安備:43070202000427號© 2013~2025 haote.com 好特網(wǎng)