国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

白話Java I/O模型

VincentFF / 2601人閱讀

摘要:因為所有的數據從最底層講是字節,那么就可以使用字節流這個概念去指代數據動態轉移這個過程。而數據的轉移,就是把一堆字節流從運往。創建內存中的中轉區域,然后將上面的文件的字節流直接接入到這個。然后再從把字節流輸出到對應的。

I/O的很多操作和使用,其實并不是一個非常直觀的概念,特別是打開文件、創建buffer。這對于終端用戶來講是個非常奇葩和奇怪的過程。我只是想要從一個文件里讀取內容,從過程上來講,我只需要知道:

讀取的source文件

寫入的目的地

那我干嘛要去關心神馬打開文件、創建stream和buffer?!

所以,要理解I/O這一套東西以及它所涉及的stream、buffer,你必須先理解計算機的底層是如何工作的。如果沒有這一步的底層基礎理論做支撐,所有的I/O操作將無法變得直觀。

為理解I/O所需要用到的底層知識并不算多,就幾點:

計算機的對數據的操作一定要經過內存。無論你是計算出來的數據、從硬盤中的文件讀取的數據、從網絡中讀取到的數據,都必須要經過內存。source data先到內存,然后內存再到destination。

既然涉及到內存,就存在一個“有限”的問題。所有的程序都是往內存跑,無疑這一個非常寶貴的資源。那么你的“傳輸數據”任務,并沒有那么高的優先級可以任意地去占有這個資源。你有且只能獲取一部分,特別地,還應該是相對較小的一部分區域,來供你做數據傳輸。

所有在計算機中的數據,無論是文字、圖片、聲音都是0、1這樣的bits。所以,最為通用的傳輸數據方式必然是面向字節的,也就是圍繞字節這個概念來做的。

面對以上這些現實,你不得不在programming時考慮上述問題。因為你不是終端用戶只需要一個簡單的接口。你是細節的操作者,必須對以上限制做出具體的可操作性的回應。

因為你只能使用一小部分的內存空間做數據轉移,所以這就必然需要一個buffer的概念去指代內存這部分的小空間。所以你要創建buffer并為它分配大小,然后所有的數據轉移都通過這塊小小的中轉站。(這就像是一個城市的快遞中心,所有全國各地發往這個地區的快件,都必須通過這個中轉站來做統一調配。)這是對計算機的體系架構——所有數據都必須通過內存——所作出的回應。

因為所有的數據從最底層講是字節(bit),那么就可以使用字節流這個概念去指代數據動態轉移這個過程。而數據的轉移,就是把一堆字節流從source運往destination。但由于上面的原因,這個過程無法直接完成,所以你必須把字節流從:source --> buffer --> destination,或者destination --> buffer --> source

由于傳輸的都是字節流,所以你需要一個工具把這個stream給開墾出來,所以你需要有一個File式的對象,從上面可以取得一個Channel或者Stream,也即是把file轉換為字節流的池子,以便直接把文件的字節流拿給buffer。它們就像是data的礦源,通過buffer這輛采礦的小車,不斷地把礦石(data)從礦源(source)運到外面(destination)。

有了這部分的知識,我們再來看“Java中的NIO是如何讀取文件的”就不會變得怪異了。

RandomAccessFile aFile = new RandomAccessFile("test.txt", "rw");
FileChannel inChannel = aFile.getChannel();

//create buffer with capacity of 48 bytes
ByteBuffer buf = ByteBuffer.allocate(48);

int bytesRead = inChannel.read(buf); //read into buffer.
while (bytesRead != -1) {

  buf.flip();  //make buffer ready for read

  while(buf.hasRemaining()){
      System.out.print((char) buf.get()); // read 1 byte at a time
  }

  buf.clear(); //make buffer ready for writing
  bytesRead = inChannel.read(buf);
}
aFile.close();

讓我們將之前討論的過程復現一遍:

首先創建RandomAccessFile對象,來提供真實文件test.txt在Java中對應的對象(可以理解為一個bean)。這個對象將提供各種服務來配合Java內部各種機制的操作。無疑,提供一個Channel是它的本質工作之一。

創建內存中的中轉區域buffer,然后將上面的文件Channel的字節流直接接入到這個buffer。

然后再從buffer把字節流輸出到System.out.print對應的std io

接下來可以深入更多的細節。

由于buffer是被重復利用的部分,所以這涉及到清理buffer的概念buf.clear()。那你可能會說,為什么不可以自動地清理buffer?因為這里的buffer是一個底層的基礎服務。對于上層的應用來講,有些場景是需要清理buffer,有些場景是需要復用buffer的內容。你怎么可以一概而論地認為所有應用場景都是只需要消費一次buffer中的數據呢?所以,作為底層設施來講,你必須提供足夠的靈活性,讓developer自己決定是否需要清理buffer。

再來比較奇怪的是flip()這部分,為什么需要對buffer做flip操作呢?這就涉及到內存中buffer的管理問題。

這里的管理方式其實很常見,在內存中,基本上都是以數組的形式提供堆棧結構來管理數據。那么,這就涉及到對這個數組的操作問題。你要有一個表征position的指針來指導數據的寫入方向。

對“寫數據”這個過程來講,數據的position指針是從起始點,index為0的點,逐步增大index來寫數據的。只要一直在做“寫”操作,這個指針就會在buffer中不斷地往index增大的方向移動。

顯然,當你需要讀數據時,你是需要從這個buffer的起始點再開始。所以,你需要一個操作把position指針復位到起始位置,然后從這個地方開始不斷地往下讀。(一個值得思考的問題是:為什么不引入兩個position指針,一個用來讀,一個用來寫。這樣不是就不用把“讀”的position指針復位了嗎?)

如果多走一步,為了驗證flip()是否真的在讓position指針復原,你還可以使用以下代碼:

System.out.println("Read " + bytesRead);

// switch the buffer from writing mode into reading mode
buf.flip();

while (buf.hasRemaining()) {
    System.out.println((char) buf.get());
}

System.out.println("---------------------------");

// reset the pointer back to original point again
buf.flip();

while (buf.hasRemaining()) {
    System.out.println((char) buf.get());
}

可以看到,從buffer中又一次獲取到了同樣的信息。

從這個例子可以看到很多深層次的東西。例如,為什么你必須了解底層的內存運作機制、操作系統的運轉機制?因為這些細節決定了你該以什么樣的方式去設計你的編程模式,也決定了你應該如何去理解編程語言中提供的一些機制,或者為什么一個庫應該會這樣設計。這是一切具體行動的現實。

“具備什么功能”是這底層基礎設施提供的封裝好的API。但你要做的是programming的工作,不得不利用底層的基礎設施去構建新的服務和產品。如果沒辦法理解這些底層機制,你就沒辦法真正地去構建東西。很多的問題,其實可以被繞過又或是不可能被實現,不在于邏輯有問題,而是單純的信息差,你并不知道這個構建出的抽閑概念下面隱藏的真實東西。

如果能夠理解內存的利用方式,那么,“分片、以stack的形式來做操作”的模式將成為你本能的一部分。進而,涉及到的position移動或者flip()的指針回調問題,就會成為你的直觀。

當然,積累這部分的基礎知識是非??菰锖头ξ兜?。但如同所有的基本功,它們不會在短期內為你提供足夠的回報,但卻會為你將來形成正確的“直覺”和“直觀”做出巨大的貢獻。

任何的抽象概念都具備直觀,只不過這個直觀所依賴的基礎不同。抽象如“概率論基礎”,其“形象”的直觀,其實是數學系本科所學的“經典概率論”,否則你會迷失在“測度論”的細節里。但這個“直觀”對于其它專業的人來講,并不直觀,甚至是異常復雜。而這個就是所謂的牢固的前提基礎知識。進一步,如果你想要學好“概率論基礎”這樣高度抽象的topic,你必須先夯實“經典概率論”這個基礎,必須先建立對它的深刻認知。否則,你對“概率論基礎”的理解根本無從談起。

同樣的,如果你希望理解類似于編程語言中I/O庫的設計、理解各類緩存中間件、消息隊列中間件的設計,你必須要先建立“計算機如何運作”這個前提基礎。只有你熟悉了計算機的運作方式,能夠以計算機底層的習慣去思考問題、處理問題,你才能夠看到各種組件設計的直觀,才會看到各種莫名其妙的“繞路”到底是在解決什么、是為了什么。

所以,這樣一個學習過程是任何抽象技能所避免不了的。你必須通過反復的閱讀和練習來掌握第一層的基礎概念,熟悉到讓這一層的抽象變成你腦海中的一個條件反射式的直觀。再這個新建立的直觀本能基礎上,你才可以去理解更高層次的抽象。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/72616.html

相關文章

  • JAVA NIO 一步步構建I/O多路復用的請求模型

    摘要:為解決這問題,我們發現元兇處在一線程一請求上,如果一個線程能同時處理多個請求,那么在高并發下性能上會大大改善。這樣一個線程可以同時發起多個調用,并且不需要同步等待數據就緒。表示當前就緒的事件類型。 JAVA NIO 一步步構建I/O多路復用的請求模型 摘要:本文屬于原創,歡迎轉載,轉載請保留出處:https://github.com/jasonGeng88/blog 文章一:JAVA ...

    X_AirDu 評論0 收藏0
  • NIO網絡相關基礎知識

    摘要:操作系統是能夠獲取到事件操作完成的事件,基于回調函數機制和操作系統的操作控制實現事件檢測機制。 前面的文章NIO基礎知識介紹了Java NIO的一些基本的類及功能說明,Java NIO是用來替換java 傳統IO的,NIO的一些新的特性在網絡交互方面會更加的明顯。 Java 傳統IO的弊端 ????基于JVM來實現每個通道的輪詢檢查通道狀態的方法是可行的,但仍然是有問題的,檢查每個通道...

    1fe1se 評論0 收藏0
  • 白話原型和原型鏈

    摘要:二原型和原型鏈其實,原型這個名字本身就很容易產生誤解,原型在百度詞條中的釋義是指原來的類型或模型。對象的原型是通過創建的對象,其原型是而的原型是,從而兩個原型對象和有了繼承關系三和這是容易混淆的兩個屬性。 關于原型和原型鏈的介紹,網上數不勝數,但能講清楚這兩個概念的很少,大多數都是介紹各種對象、屬性之間如何指來指去,最后的結果就是箭頭滿天飛,大腦一團糟。本文將從這兩個概念的命名入手,用...

    lijy91 評論0 收藏0
  • I/O模型Java NIO源碼分析

    摘要:最近在學習網絡編程和相關的知識,了解到是模式的網絡框架,但是提供了不同的來支持不同模式的網絡通信處理,包括同步異步阻塞和非阻塞。因為的版本使用的的模式,而則希望使用模式,而且版本沒有將的部分配置項暴露出來,比如說和。 ??最近在學習Java網絡編程和Netty相關的知識,了解到Netty是NIO模式的網絡框架,但是提供了不同的Channel來支持不同模式的網絡通信處理,包括同步、異步、...

    yuanxin 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<