欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java基礎(chǔ)教程之字符流文件讀寫

 更新時間:2018年07月03日 14:26:43   作者:Single_Yam  
這篇文章主要給大家介紹了關(guān)于Java基礎(chǔ)教程之字符流文件讀寫的相關(guān)資料,,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

前言

上篇文章,我們介紹了 Java 的文件字節(jié)流框架中的相關(guān)內(nèi)容,而我們本篇文章將著重于文件字符流的相關(guān)內(nèi)容。

首先需要明確一點的是,字節(jié)流處理文件的時候是基于字節(jié)的,而字符流處理文件則是基于一個個字符為基本單元的。

但實際上,字符流操作的本質(zhì)就是「字節(jié)流操作」+「編碼」兩個過程的封裝,你想是不是,無論你是寫一個字符到文件,你需要將字符編碼成二進(jìn)制,然后以字節(jié)為基本單位寫入文件,或是你讀一個字符到內(nèi)存,你需要以字節(jié)為基本單位讀出,然后轉(zhuǎn)碼成字符。

理解這一點很重要,這將決定你對字符流整體上的理解是怎樣的,下面我們一起看看相關(guān) API 的設(shè)計。

基類 Reader/Writer

在正式學(xué)習(xí)字符流基類之前,我們需要知道 Java 中是如何表示一個字符的。

首先,Java 中的默認(rèn)字符編碼為:UTF-8,而我們知道 UTF-8 編碼的字符使用 1 到 4 個字節(jié)進(jìn)行存儲,越常用的字符使用越少的字節(jié)數(shù)。

而 char 類型被定義為兩個字節(jié)大小,也就是說,對于通常的字符來說,一個 char 即可存儲一個字符,但對于一些增補字符集來說,往往會使用兩個 char 來表示一個字符。

Reader 作為讀字符流的基類,它提供了最基本的字符讀取操作,我們一起看看。

先看看它的構(gòu)造器:

protected Object lock;
protected Reader() {
 this.lock = this;
}

protected Reader(Object lock) {
 if (lock == null) {
 throw new NullPointerException();
 }
 this.lock = lock;
}

Reader 是一個抽象類,所以毋庸置疑的是,這些構(gòu)造器是給子類調(diào)用的,用于初始化 lock 鎖對象,這一點我們后續(xù)會詳細(xì)解釋。

public int read() throws IOException {
 char cb[] = new char[1];
 if (read(cb, 0, 1) == -1)
 return -1;
 else
 return cb[0];
}

public int read(char cbuf[]) throws IOException {
 return read(cbuf, 0, cbuf.length);
}

abstract public int read(char cbuf[], int off, int len)

基本的讀字符操作都在這了,第一個方法用于讀取一個字符出來,如果已經(jīng)讀到了文件末尾,將返回 -1,同樣的以 int 作為返回值類型接收,為什么不用 char?原因是一樣的,都是由于 -1 這個值的解釋不確定性。

第二個方法和第三個方法是類似的,從文件中讀取指定長度的字符放置到目標(biāo)數(shù)組當(dāng)中。第三個方法是抽象方法,需要子類自行實現(xiàn),而第二個方法卻又是基于它的。

還有一些方法也是類似的:

  • public long skip(long n):跳過 n 個字符
  • public boolean ready():下一個字符是否可讀
  • public boolean markSupported():見 reset 方法
  • public void mark(int readAheadLimit):見 reset 方法
  • public void reset():用于實現(xiàn)重復(fù)讀操作
  • abstract public void close():關(guān)閉流

這些個方法其實都見名知意,并且和我們的 InputStream 大體上都差不多,都沒有什么核心的實現(xiàn),這里不再贅述,你大致知道它內(nèi)部有些個什么東西即可。

Writer 是寫的字符流,它用于將一個或多個字符寫入到文件中,當(dāng)然具體的 write 方法依然是一個抽象的方法,待子類來實現(xiàn),所以我們這里亦不再贅述了。

適配器 InpustStramReader/OutputStreamWriter

適配器字符流繼承自基類 Reader 或 Writer,它們算是字符流體系中非常重要的成員了。主要的作用就是,將一個字節(jié)流轉(zhuǎn)換成一個字符流,我們先以讀適配器為例。

首先就是它最核心的成員:

private final StreamDecoder sd;

StreamDecoder 是一個解碼器,用于將字節(jié)的各種操作轉(zhuǎn)換成字符的相應(yīng)操作,關(guān)于它我們會在后續(xù)的介紹中不間斷的提到它,這里不做統(tǒng)一的解釋。

然后就是構(gòu)造器:

public InputStreamReader(InputStream in) {
 super(in);
 try {
 sd = StreamDecoder.forInputStreamReader(in, this, (String)null); 
 } catch (UnsupportedEncodingException e) {
 throw new Error(e);
 }
}

public InputStreamReader(InputStream in, String charsetName) 
 throws UnsupportedEncodingException
{
 super(in);
 if (charsetName == null)
 throw new NullPointerException("charsetName");
 sd = StreamDecoder.forInputStreamReader(in, this, charsetName);
}

這兩個構(gòu)造器的目的都是為了初始化這個解碼器,都調(diào)用的方法 forInputStreamReader,只是參數(shù)不同而已。我們不妨看看這個方法的實現(xiàn):

這是一個典型的靜態(tài)工廠模式,三個參數(shù),var0 和 var1 沒什么好說的,分別代表的是字節(jié)流實例和適配器實例。

而參數(shù) var2 其實代表的是一種字符編碼的名稱,如果為 null,那么將使用系統(tǒng)默認(rèn)的字符編碼:UTF-8 。

最終我們能夠得到一個解碼器實例。

接著介紹的所有方法幾乎都是依賴的這個解碼器而實現(xiàn)的。

public String getEncoding() {
 return sd.getEncoding();
}

public int read() throws IOException {
 return sd.read();
}

public int read(char cbuf[], int offset, int length){
 return sd.read(cbuf, offset, length);
}

public void close() throws IOException {
 sd.close();
}

解碼器中相關(guān)的方法的實現(xiàn)代碼還是相對復(fù)雜的,這里我們不做深入的研究,但大體上的實現(xiàn)思路就是:「字節(jié)流讀取 + 解碼」的過程。

當(dāng)然了,OutputStreamWriter 中必然也存在一個相反的 StreamEncoder 實例用于編碼字符。

除了這一點外,其余的操作并沒有什么不同,或是通過字符數(shù)組向文件中寫入,或是通過字符串向文件中寫入,又或是通過 int 的低 16 位向文件中寫入。

文件字符流 FileReader/Writer

文件的字符流可以說非常簡單了,除了構(gòu)造器,就不存在任何其他方法了,完全依賴文件字節(jié)流。

我們以 FileReader 為例,

FileReader 繼承自 InputStreamReader,有且僅有以下三個構(gòu)造器:

public FileReader(String fileName) throws FileNotFoundException {
 super(new FileInputStream(fileName));
}

public FileReader(File file) throws FileNotFoundException {
 super(new FileInputStream(file));
}

public FileReader(FileDescriptor fd) {
 super(new FileInputStream(fd));
}

理論上來說,所有的字符流都應(yīng)當(dāng)以我們的適配器為基類,因為只有它提供了字符到字節(jié)之間的轉(zhuǎn)換,無論你是寫或是讀都離不開它。

而我們的 FileReader 并沒有擴(kuò)展任何一個自己的方法,父類 InputStreamReader 中預(yù)實現(xiàn)的字符操作方法對他來說已經(jīng)足夠,只需要傳入一個對應(yīng)的字節(jié)流實例即可。

FileWriter 也是一樣的,這里不再贅述了。

字符數(shù)組流 CharArrayReader/Writer

字符數(shù)組和字節(jié)數(shù)組流是類似的,都是用于解決那種不確定文件大小,而需要讀取其中大量內(nèi)容的情況。

由于它們內(nèi)部提供動態(tài)擴(kuò)容機(jī)制,所以既可以完全容納目標(biāo)文件,也可以控制數(shù)組大小,不至于分配過大內(nèi)存而浪費了大量內(nèi)存空間。

先以 CharArrayReader 為例

protected char buf[];

public CharArrayReader(char buf[]) {
 this.buf = buf;
 this.pos = 0;
 this.count = buf.length;
}

public CharArrayReader(char buf[], int offset, int length){
 //....
}

構(gòu)造器核心任務(wù)就是初始化一個字符數(shù)組到內(nèi)部的 buf 屬性中,以后所有對該字符數(shù)組流實例的讀操作都基于 buf 這個字符數(shù)組。

關(guān)于 CharArrayReader 的其他方法以及 CharArrayWriter,這里不再贅述了,和上篇的字節(jié)數(shù)組流基本類似。

除此之外,這里還涉及一個 StringReader 和 StringWriter,其實本質(zhì)上和字符數(shù)組流是一樣的,畢竟 String 的本質(zhì)就是 char 數(shù)組。

緩沖數(shù)組流 BufferedReader/Writer

同樣的,BufferedReader/Writer 作為一種緩沖流,也是裝飾者流,用于提供緩沖功能。大體上類似于我們的字節(jié)緩沖流,這里我們簡單介紹下。

private Reader in;
private char cb[];
private static int defaultCharBufferSize = 8192;

public BufferedReader(Reader in, int sz){..}

public BufferedReader(Reader in) {
 this(in, defaultCharBufferSize);
}

cb 是一個字符數(shù)組,用于緩存從文件流中讀取出來的部分字符,你可以在構(gòu)造器中初始化這個數(shù)組的長度,否則將使用默認(rèn)值 8192 。

public int read() throws IOException {..}

public int read(char cbuf[], int off, int len){...}

關(guān)于 read,它依賴成員屬性 in 的讀方法,而 in 作為一個 Reader 類型,內(nèi)部往往又依賴的某個 InputStream 實例的讀方法。

所以說,幾乎所有的字符流都離不開某個字節(jié)流實例。

關(guān)于 BufferedWriter,這里也不再贅述了,大體上都是類似的,只不過一個是讀一個是寫而已,都圍繞著內(nèi)部的字符數(shù)組進(jìn)行。

標(biāo)準(zhǔn)打印輸出流

打印輸出流主要有兩種,PrintStream 和 PrintWriter,前者是字節(jié)流,后者是字符流。

這兩個流算是對各自類別下的流做了一個集成,內(nèi)部封裝有豐富的方法,但實現(xiàn)也稍顯復(fù)雜,我們先來看這個 PrintStream 字節(jié)流:

主要的構(gòu)造器有這么幾個:

  • public PrintStream(OutputStream out)
  • public PrintStream(OutputStream out, boolean autoFlush)
  • public PrintStream(OutputStream out, boolean autoFlush, String encoding)
  • public PrintStream(String fileName)

顯然,簡單的構(gòu)造器會依賴復(fù)雜的構(gòu)造器,這已經(jīng)算是 jdk 設(shè)計「老套路」了。區(qū)別于其他字節(jié)流的一點是,PrintStream 提供了一個標(biāo)志 autoFlush,用于指定是否自動刷新緩存。

接著就是 PrintStream 的寫方法:

  • public void write(int b)
  • public void write(byte buf[], int off, int len)

除此之外,PrintStream 還封裝了大量的 print 的方法,寫入不同類型的內(nèi)容到文件中,例如:

  • public void print(boolean b)
  • public void print(char c)
  • public void print(int i)
  • public void print(long l)
  • public void print(float f)
  • 等等

當(dāng)然,這些方法并不會真正的將數(shù)值的二進(jìn)制寫入文件,而只是將它們所對應(yīng)的字符串寫入文件,例如:

print(123);

最終寫入文件的不是 123 所對應(yīng)的二進(jìn)制表述,而僅僅是 123 這個字符串,這就是打印流。

PrintStream 使用的緩沖字符流實現(xiàn)所有的打印操作,如果指明了自動刷新,則遇到換行符號「\n」會自動刷新緩沖區(qū)。

所以說,PrintStream 集成了字節(jié)流和字符流中所有的輸出方法,其中 write 方法是用于字節(jié)流操作,print 方法用于字符流操作,這一點需要明確。

至于 PrintWriter,它就是全字符流,完全針對字符進(jìn)行操作,無論是 write 方法也好,print 方法也好,都是字符流操作。

總結(jié)一下,我們花了三篇文章講解了 Java 中的字節(jié)流和字符流操作,字節(jié)流基于字節(jié)完成磁盤和內(nèi)存之間的數(shù)據(jù)傳輸,最典型的就是文件字符流,它的實現(xiàn)都是本地方法。有了基本的字節(jié)傳輸能力后,我們還能夠通過緩沖來提高效率。

而字符流的最基本實現(xiàn)就是,InputStreamReader 和 OutputStreamWriter,理論上它倆就已經(jīng)能夠完成基本的字符流操作了,但也僅僅局限于最基本的操作,而構(gòu)造它們的實例所必需的就是「一個字節(jié)流實例」+「一種編碼格式」。

所以,字符流和字節(jié)流的關(guān)系也就如上述的等式一樣,你寫一個字符到磁盤文件中所必需的步驟就是,按照指定編碼格式編碼該字符,然后使用字節(jié)流將編碼后的字符二進(jìn)制寫入文件中,讀操作是相反的。

文章中的所有代碼、圖片、文件都云存儲在我的 GitHub 上:

(https://github.com/SingleYam/overview_java)

大家也可以選擇通過本地下載。

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。

相關(guān)文章

  • MyBatis實現(xiàn)配置加載的步驟

    MyBatis實現(xiàn)配置加載的步驟

    本文主要介紹了MyBatis實現(xiàn)配置加載的步驟,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-05-05
  • 無感NullPointerException的值相等判斷方法

    無感NullPointerException的值相等判斷方法

    當(dāng)我們需要去判斷一個?入?yún)?查庫?返回的開關(guān)變量(通常是個Integer類型的)時,常常會寫如下的if-else判斷語句。但又會為在生產(chǎn)環(huán)境看到的「NullPointerException」感到困擾,遇到這個問題如何處理呢,下面小編通過本文給大家詳細(xì)講解,需要的朋友參考下吧
    2023-02-02
  • Spring循環(huán)依賴的三種方式(推薦)

    Spring循環(huán)依賴的三種方式(推薦)

    本篇文章主要介紹了Spring循環(huán)依賴的三種方式,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-01-01
  • 使用SpringBoot中的Schedule定時發(fā)送郵件的方法

    使用SpringBoot中的Schedule定時發(fā)送郵件的方法

    在SpringBoot中,你可以使用@Scheduled注解來創(chuàng)建定時任務(wù),@Scheduled注解可以應(yīng)用于方法上,表示這個方法是一個定時任務(wù),可以根據(jù)指定的時間間隔或固定時間執(zhí)行,本文就給大家介紹一下如何使用SpringBoot中的Schedule定時發(fā)送郵件,需要的朋友可以參考下
    2023-08-08
  • spring?boot使用@Async注解解決異步多線程入庫的問題

    spring?boot使用@Async注解解決異步多線程入庫的問題

    最近在寫項目是需要添加異步操作來提高效率,所以下面這篇文章主要給大家介紹了關(guān)于spring?boot使用@Async注解解決異步多線程入庫問題的相關(guān)資料,文中通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-05-05
  • 詳解Java設(shè)計模式編程中的Flyweight享元模式的開發(fā)結(jié)構(gòu)

    詳解Java設(shè)計模式編程中的Flyweight享元模式的開發(fā)結(jié)構(gòu)

    這篇文章主要介紹了Java設(shè)計模式編程中的Flyweight享元模式的開發(fā)結(jié)構(gòu),享元模式能夠最大限度地重用現(xiàn)有的同類對象,需要的朋友可以參考下
    2016-04-04
  • Java項目如何打包成Jar的實現(xiàn)步驟

    Java項目如何打包成Jar的實現(xiàn)步驟

    一般情況下我們都是使用Java項目直接部署發(fā)布,有時需要我們將寫好的項目打成jar包,方便后期調(diào)用,本文主要介紹了Java項目如何打包成Jar,感興趣的可以了解一下
    2023-11-11
  • 徹底解決tomcat中文亂碼問題方案

    徹底解決tomcat中文亂碼問題方案

    這篇文章主要介紹了徹底解決tomcat中文亂碼問題方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • java中Timer定時器的使用和啟動方式

    java中Timer定時器的使用和啟動方式

    這篇文章主要介紹了java中Timer定時器的使用和啟動方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • Java Socket編程實例(一)- TCP基本使用

    Java Socket編程實例(一)- TCP基本使用

    這篇文章主要講解Java Socket編程中TCP的基本使用,希望能給大家做一個參考。
    2016-06-06

最新評論