<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          臥槽,這也太上頭了吧!

          共 7303字,需瀏覽 15分鐘

           ·

          2021-11-15 09:25

          “老王,Java IO 也太上頭了吧?”新兵蛋子小二向頭頂很涼快的老王抱怨道,“你瞧,我就按照傳輸方式對(duì) IO 進(jìn)行了一個(gè)簡(jiǎn)單的分類,就能搞出來這么多的玩意!”

          好久沒搞過 IO 了,老王看到這幅思維導(dǎo)圖也是吃了一驚。想想也是,他當(dāng)初學(xué)習(xí) Java IO 的時(shí)候頭也大,烏央烏央的一片,全是類,估計(jì)是所有 Java 包里面類最多的,一會(huì)是 Input 一會(huì)是 Output,一會(huì)是 Reader 一會(huì)是 Writer,真不知道 Java 的設(shè)計(jì)者是怎么想的。

          看著肺都快要?dú)庹ǖ男《贤跎钌畹匚艘豢跉猓托牡貙?duì)小二說:“主要是 Java 的設(shè)計(jì)者考慮得比較多吧,所以 IO 給人一種很亂的感覺,我來給你梳理一下。”

          01、傳輸方式劃分

          就按照你的那副思維導(dǎo)圖來說吧。

          傳輸方式有兩種,字節(jié)和字符,那首先得搞明白字節(jié)和字符有什么區(qū)別,對(duì)吧?

          字節(jié)(byte)是計(jì)算機(jī)中用來表示存儲(chǔ)容量的一個(gè)計(jì)量單位,通常情況下,一個(gè)字節(jié)有 8 位(bit)。

          字符(char)可以是計(jì)算機(jī)中使用的字母、數(shù)字、和符號(hào),比如說 A 1 $ 這些。

          通常來說,一個(gè)字母或者一個(gè)字符占用一個(gè)字節(jié),一個(gè)漢字占用兩個(gè)字節(jié)。

          具體還要看字符編碼,比如說在 UTF-8 編碼下,一個(gè)英文字母(不分大小寫)為一個(gè)字節(jié),一個(gè)中文漢字為三個(gè)字節(jié);在 Unicode 編碼中,一個(gè)英文字母為一個(gè)字節(jié),一個(gè)中文漢字為兩個(gè)字節(jié)。

          PS:關(guān)于字符編碼,可以看前面的章節(jié):錕斤拷

          明白了字節(jié)與字符的區(qū)別,再來看字節(jié)流和字符流就會(huì)輕松多了。

          字節(jié)流用來處理二進(jìn)制文件,比如說圖片啊、MP3 啊、視頻啊。

          字符流用來處理文本文件,文本文件可以看作是一種特殊的二進(jìn)制文件,只不過經(jīng)過了編碼,便于人們閱讀。

          換句話說就是,字節(jié)流可以處理一切文件,而字符流只能處理文本。

          雖然 IO 類很多,但核心的就是 4 個(gè)抽象類:InputStream、OutputStream、Reader、Writer。

          抽象大法真好

          雖然 IO 類的方法也很多,但核心的也就 2 個(gè):read 和 write。

          InputStream 類

          • int read():讀取數(shù)據(jù)
          • int read(byte b[], int off, int len):從第 off 位置開始讀,讀取 len 長(zhǎng)度的字節(jié),然后放入數(shù)組 b 中
          • long skip(long n):跳過指定個(gè)數(shù)的字節(jié)
          • int available():返回可讀的字節(jié)數(shù)
          • void close():關(guān)閉流,釋放資源

          OutputStream 類

          • void write(int b):寫入一個(gè)字節(jié),雖然參數(shù)是一個(gè) int 類型,但只有低 8 位才會(huì)寫入,高 24 位會(huì)舍棄(這塊后面再講)
          • void write(byte b[], int off, int len):將數(shù)組 b 中的從 off 位置開始,長(zhǎng)度為 len 的字節(jié)寫入
          • void flush():強(qiáng)制刷新,將緩沖區(qū)的數(shù)據(jù)寫入
          • void close():關(guān)閉流

          Reader 類

          • int read():讀取單個(gè)字符
          • int read(char cbuf[], int off, int len):從第 off 位置開始讀,讀取 len 長(zhǎng)度的字符,然后放入數(shù)組 b 中
          • long skip(long n):跳過指定個(gè)數(shù)的字符
          • int ready():是否可以讀了
          • void close():關(guān)閉流,釋放資源

          Writer 類

          • void write(int c):寫入一個(gè)字符
          • void write( char cbuf[], int off, int len):將數(shù)組 cbuf 中的從 off 位置開始,長(zhǎng)度為 len 的字符寫入
          • void flush():強(qiáng)制刷新,將緩沖區(qū)的數(shù)據(jù)寫入
          • void close():關(guān)閉流

          理解了上面這些方法,基本上 IO 的靈魂也就全部掌握了。

          二、操作對(duì)象劃分

          小二,你細(xì)想一下,IO IO,不就是輸入輸出(Input/Output)嘛:

          • Input:將外部的數(shù)據(jù)讀入內(nèi)存,比如說把文件從硬盤讀取到內(nèi)存,從網(wǎng)絡(luò)讀取數(shù)據(jù)到內(nèi)存等等
          • Output:將內(nèi)存中的數(shù)據(jù)寫入到外部,比如說把數(shù)據(jù)從內(nèi)存寫入到文件,把數(shù)據(jù)從內(nèi)存輸出到網(wǎng)絡(luò)等等。

          所有的程序,在執(zhí)行的時(shí)候,都是在內(nèi)存上進(jìn)行的,一旦關(guān)機(jī),內(nèi)存中的數(shù)據(jù)就沒了,那如果想要持久化,就需要把內(nèi)存中的數(shù)據(jù)輸出到外部,比如說文件。

          文件操作算是 IO 中最典型的操作了,也是最頻繁的操作。那其實(shí)你可以換個(gè)角度來思考,比如說按照 IO 的操作對(duì)象來思考,IO 就可以分類為:文件、數(shù)組、管道、基本數(shù)據(jù)類型、緩沖、打印、對(duì)象序列化/反序列化,以及轉(zhuǎn)換等。

          1)文件

          文件流也就是直接操作文件的流,可以細(xì)分為字節(jié)流(FileInputStream 和 FileOuputStream)和字符流(FileReader 和 FileWriter)。

          FileInputStream 的例子:

          int?b;
          FileInputStream?fis1?=?new?FileInputStream("fis.txt");
          //?循環(huán)讀取
          while?((b?=?fis1.read())!=-1)?{
          ????System.out.println((char)b);
          }
          //?關(guān)閉資源
          fis1.close();

          FileOutputStream 的例子:

          FileOutputStream?fos?=?new?FileOutputStream("fos.txt");
          fos.write("沉默王二".getBytes());
          fos.close();

          FileReader 的例子:

          int?b?=?0;
          FileReader?fileReader?=?new?FileReader("read.txt");
          //?循環(huán)讀取
          while?((b?=?fileReader.read())!=-1)?{
          ????//?自動(dòng)提升類型提升為?int?類型,所以用?char?強(qiáng)轉(zhuǎn)
          ????System.out.println((char)b);
          }
          //?關(guān)閉流
          fileReader.close();

          FileWriter 的例子:

          FileWriter?fileWriter?=?new?FileWriter("fw.txt");
          char[]?chars?=?"沉默王二".toCharArray();
          fileWriter.write(chars,?0,?chars.length);
          fileWriter.close();

          當(dāng)掌握了文件的輸入輸出,其他的自然也就掌握了,都大差不差。

          2)數(shù)組

          通常來說,針對(duì)文件的讀寫操作,使用文件流配合緩沖流就夠用了,但為了提升效率,頻繁地讀寫文件并不是太好,那么就出現(xiàn)了數(shù)組流,有時(shí)候也稱為內(nèi)存流。

          ByteArrayInputStream 的例子:

          InputStream?is?=new?BufferedInputStream(
          ????????new?ByteArrayInputStream(
          ????????????????"沉默王二".getBytes(StandardCharsets.UTF_8)));
          //操作
          byte[]?flush?=new?byte[1024];
          int?len?=0;
          while(-1!=(len=is.read(flush))){
          ????System.out.println(new?String(flush,0,len));
          }
          //釋放資源
          is.close();

          ByteArrayOutputStream 的例子:

          ByteArrayOutputStream?bos?=new?ByteArrayOutputStream();
          byte[]?info?="沉默王二".getBytes();
          bos.write(info,?0,?info.length);
          //獲取數(shù)據(jù)
          byte[]?dest?=bos.toByteArray();
          //釋放資源
          bos.close();

          3)管道

          Java 中的管道和 Unix/Linux 中的管道不同,在 Unix/Linux 中,不同的進(jìn)程之間可以通過管道來通信,但 Java 中,通信的雙方必須在同一個(gè)進(jìn)程中,也就是在同一個(gè) JVM 中,管道為線程之間的通信提供了通信能力。

          一個(gè)線程通過 PipedOutputStream 寫入的數(shù)據(jù)可以被另外一個(gè)線程通過相關(guān)聯(lián)的 PipedInputStream 讀取出來。

          final?PipedOutputStream?pipedOutputStream?=?new?PipedOutputStream();
          final?PipedInputStream?pipedInputStream?=?new?PipedInputStream(pipedOutputStream);

          Thread?thread1?=?new?Thread(new?Runnable()?{
          ????@Override
          ????public?void?run()?{
          ????????try?{
          ????????????pipedOutputStream.write("沉默王二".getBytes(StandardCharsets.UTF_8));
          ????????????pipedOutputStream.close();
          ????????}?catch?(IOException?e)?{
          ????????????e.printStackTrace();
          ????????}
          ????}
          });

          Thread?thread2?=?new?Thread(new?Runnable()?{
          ????@Override
          ????public?void?run()?{
          ????????try?{
          ????????????byte[]?flush?=new?byte[1024];
          ????????????int?len?=0;
          ????????????while(-1!=(len=pipedInputStream.read(flush))){
          ????????????????System.out.println(new?String(flush,0,len));
          ????????????}

          ????????????pipedInputStream.close();
          ????????}?catch?(IOException?e)?{
          ????????????e.printStackTrace();
          ????????}

          ????}
          });
          thread1.start();
          thread2.start();

          4)基本數(shù)據(jù)類型

          基本數(shù)據(jù)類型輸入輸出流是一個(gè)字節(jié)流,該流不僅可以讀寫字節(jié)和字符,還可以讀寫基本數(shù)據(jù)類型。

          DataInputStream 提供了一系列可以讀基本數(shù)據(jù)類型的方法:

          DataInputStream?dis?=?new?DataInputStream(new?FileInputStream(“das.txt”))?;
          byte?b?=?dis.readByte()?;
          short?s?=?dis.readShort()?;
          int?i?=?dis.readInt();
          long?l?=?dis.readLong()?;
          float?f?=?dis.readFloat()?;
          double?d?=?dis.readDouble()?;
          boolean?bb?=?dis.readBoolean()?;
          char?ch?=?dis.readChar()?;

          DataOutputStream 提供了一系列可以寫基本數(shù)據(jù)類型的方法:

          DataOutputStream?das?=?new?DataOutputStream(new?FileOutputStream(“das.txt”));
          das.writeByte(10);
          das.writeShort(100);
          das.writeInt(1000);
          das.writeLong(10000L);
          das.writeFloat(12.34F);
          das.writeDouble(12.56);
          das.writeBoolean(true);
          das.writeChar('A');

          5)緩沖

          CPU 很快,它比內(nèi)存快 100 倍,比磁盤快百萬倍。那也就意味著,程序和內(nèi)存交互會(huì)很快,和硬盤交互相對(duì)就很慢,這樣就會(huì)導(dǎo)致性能問題。

          為了減少程序和硬盤的交互,提升程序的效率,就引入了緩沖流,也就是類名前綴帶有 Buffer 的那些,比如說 BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter。

          緩沖流在內(nèi)存中設(shè)置了一個(gè)緩沖區(qū),只有緩沖區(qū)存儲(chǔ)了足夠多的帶操作的數(shù)據(jù)后,才會(huì)和內(nèi)存或者硬盤進(jìn)行交互。簡(jiǎn)單來說,就是一次多讀/寫點(diǎn),少讀/寫幾次,這樣程序的性能就會(huì)提高。

          6)打印

          恐怕 Java 程序員一生當(dāng)中最常用的就是打印流了:System.out 其實(shí)返回的就是一個(gè) PrintStream 對(duì)象,可以用來打印各式各樣的對(duì)象。

          System.out.println("沉默王二是真的二!");

          PrintStream 最終輸出的是字節(jié)數(shù)據(jù),而 PrintWriter 則是擴(kuò)展了 Writer 接口,所以它的 print()/println() 方法最終輸出的是字符數(shù)據(jù)。使用上幾乎和 PrintStream 一模一樣。

          StringWriter?buffer?=?new?StringWriter();
          try?(PrintWriter?pw?=?new?PrintWriter(buffer))?{
          ????pw.println("沉默王二");
          }
          System.out.println(buffer.toString());

          7)對(duì)象序列化/反序列化

          序列化本質(zhì)上是將一個(gè) Java 對(duì)象轉(zhuǎn)成字節(jié)數(shù)組,然后可以將其保存到文件中,或者通過網(wǎng)絡(luò)傳輸?shù)竭h(yuǎn)程。

          ByteArrayOutputStream?buffer?=?new?ByteArrayOutputStream();
          try?(ObjectOutputStream?output?=?new?ObjectOutputStream(buffer))?{
          ????output.writeUTF("沉默王二");
          }
          System.out.println(Arrays.toString(buffer.toByteArray()));

          與其對(duì)應(yīng)的,有序列化,就有反序列化,也就是再將字節(jié)數(shù)組轉(zhuǎn)成 Java 對(duì)象的過程。

          try?(ObjectInputStream?input?=?new?ObjectInputStream(new?FileInputStream(
          ????????new?File("Person.txt"))))?{
          ????String?s?=?input.readUTF();
          }

          8)轉(zhuǎn)換

          InputStreamReader 是從字節(jié)流到字符流的橋連接,它使用指定的字符集讀取字節(jié)并將它們解碼為字符。

          InputStreamReader?isr?=?new?InputStreamReader(
          ????????new?FileInputStream("demo.txt"));
          char?[]cha?=?new?char[1024];
          int?len?=?isr.read(cha);
          System.out.println(new?String(cha,0,len));
          isr.close();

          OutputStreamWriter 將一個(gè)字符流的輸出對(duì)象變?yōu)樽止?jié)流的輸出對(duì)象,是字符流通向字節(jié)流的橋梁。

          File?f?=?new?File("test.txt")?;
          Writer?out?=?new?OutputStreamWriter(new?FileOutputStream(f))?;?//?字節(jié)流變?yōu)樽址??
          out.write("hello?world!!")?;????//?使用字符流輸出??
          out.close()?;

          “小二啊,你看,經(jīng)過我的梳理,是不是感覺 IO 也沒多少東西!針對(duì)不同的場(chǎng)景、不同的業(yè)務(wù),選擇對(duì)應(yīng)的 IO 流就可以了,用法上就是讀和寫。”老王一口氣講完這些,長(zhǎng)長(zhǎng)的舒了一口氣。

          此時(shí)此刻的小二,還沉浸在老王的滔滔不絕中。不僅感覺老王的肺活量是真的大,還感慨老王不愧是工作了十多年的“老油條”,一下子就把自己感覺頭大的 IO 給梳理得很清晰了。


          《Java 程序員進(jìn)階之路》專欄,風(fēng)趣幽默、通俗易懂,對(duì) Java 初學(xué)者極度友好和舒適??,內(nèi)容包括但不限于 Java 語法、Java 集合框架、Java IO、Java 并發(fā)編程、Java 虛擬機(jī)等核心知識(shí)點(diǎn)。目前已更新到第 68 篇。

          點(diǎn)擊上方名片,發(fā)送關(guān)鍵字「03」 就可以獲取離線版 PDF 了,15 萬+字,妥妥的良心。亮白與暗黑兩個(gè)版本都有喲,讓我們一起成為更好的 Java 工程師吧。

          以后不要再問二哥哪本書適合 Java 新手了,問就是二哥的《Java 程序員進(jìn)階之路》啊~

          鐵子們,《Java 程序員進(jìn)階之路》在 GitHub 上已經(jīng)收獲了 571 枚星標(biāo),小伙伴們趕緊去點(diǎn)點(diǎn)了,沖 600 star!

          https://github.com/itwanger/toBeBetterJavaer

          沒有什么使我停留——除了目的,縱然岸旁有玫瑰、有綠蔭、有寧靜的港灣,我是不系之舟

          推薦閱讀

          瀏覽 68
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲一区二区无码 | 黄色A级网站 | 午夜一级电影 | 欧美专区在线观看 | 狠操网|