6. 彤哥說netty系列之Java NIO核心組件之Buffer

<p align="right">——日拱一卒,不期而至!</p>html

nio

你好,我是彤哥,本篇是netty系列的第六篇。java

簡介

上一章咱們一塊兒學習了Java NIO的核心組件Channel,它能夠看做是實體與實體之間的鏈接,並且須要與Buffer交互,這一章咱們就來學習一下Buffer的特性。編程

概念

Buffer用於與Channel交互時使用,經過上一章的學習咱們知道,數據從Channel讀取到Buffer,或者從Buffer寫入Channel。數組

nio

Buffer本質上是一個內存塊,能夠向裏面寫入數據,或者從裏面讀取數據,在Java中它被包裝成了Buffer對象,並提供了一系列的方法用於操做這個內存塊。網絡

屬性

爲了更好地理解Buffer的數據結構,咱們必須熟悉它的三個經常使用屬性:數據結構

  • capacity:容量
  • position:當前位置
  • limit:限制長度

在讀模式和寫模式下,position和limit的位置有所不一樣,見下圖:架構

nio

capacity

Buffer做爲一個存儲塊,是有固定大小的,這個固定大小咱們稱做「容量」。app

當Buffer寫滿以後,須要先清空或者讀取數據,才能繼續寫入新的數據。dom

position

寫模式下,position從0開始,每寫入一個單位的數據,position前進一位,position最大可到達(capacity-1)的位置。性能

當Buffer從寫模式切換爲讀模式時,position將重置爲0。讀取數據時,一樣地,position每讀取一個單位,前進一位,此時,position最大可到達limit的位置(實際最大可讀取的位置是(limit-1))。

limit

寫模式下,limit最大值等於capacity。

讀模式下,limit最大值等於切換爲讀模式時position的值,本文來源工從號彤哥讀源碼。

> 這裏可能有點繞,position相似於數組的下標,是從0開始的,limit表示最大能夠讀取或者寫入的長度,capacity表示最大的容量,limit和capacity不是下標,相似於數組的長度,因此跟position比較須要-1。在寫模式下,position指向的是下一個待寫入的位置;在讀模式下,position指向的是下一個待讀取的位置。

類型

Java NIO自帶的Buffer類型有:

  • ByteBuffer
  • MappedByteBuffer
  • CharBuffer
  • ShortBuffer
  • IntBuffer
  • LongBuffer
  • FloatBuffer
  • DoubleBuffer

與基本類型同樣,每一種Buffer的基本單位長度不同罷了。

其中,MappedByteBuffer是一種特殊的ByteBuffer,它使用內存映射的方式加載物理文件,並不會耗費同等大小的物理內存,是一種直接操做堆外內存的方式,讀寫性能比較高。

基本用法

上面咱們學習了Buffer的數據結構以及經常使用的Buffer類型,它們怎麼使用呢?常見的用法主要有四種:

  • 將數據寫入Buffer
  • 切換爲讀模式flip()
  • 從Buffer中讀取數據
  • 清空數據並切換爲寫模式clear()或者compact()

來個栗子

nio

public class FileChannelTest {
    public static void main(String[] args) throws IOException {
        // 從文件獲取一個FileChannel
        FileChannel fileChannel = new RandomAccessFile("D:\\object.txt", "rw").getChannel();
        // 分配一個Byte類型的Buffer
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        // 將FileChannel中的數據讀出到buffer中,-1表示讀取完畢
        // buffer默認爲寫模式,本文來源工從號彤哥讀源碼
        // read()方法是相對channel而言的,相對buffer就是寫
        while ((fileChannel.read(buffer)) != -1) {
            // buffer切換爲讀模式
            buffer.flip();
            // buffer中是否有未讀數據
            while (buffer.hasRemaining()) {
                // 讀取數據
                System.out.print((char)buffer.get());
            }
            // 清空buffer,爲下一次寫入數據作準備
            // clear()會將buffer再次切換爲寫模式
            buffer.clear();
        }
    }
}

allocate()

要獲取一個Buffer對象,必須先分配它,每一個Buffer類都有一個allocate()方法用於分配Buffer對象。

如下示例分配了一個容量爲1024的ByteBuffer對象:

ByteBuffer buffer = ByteBuffer.allocate(1024);

下面是分配了一個容量爲48的CharBuffer的對象:

CharBuffer buf = CharBuffer.allocate(48);

將數據寫入Buffer

將數據寫入Buffer有兩種形式:

  • 從Channel讀出數據並寫入Buffer,也叫從Channel讀入Buffer
  • 調用Buffer本身的put()方法寫入數據

從Channel讀入Buffer的示例以下:

int bytesRead = inChannel.read(buf); //讀入Buffer

Buffer本身put()寫入數據的示例以下:

buf.put(127);

固然,put()有不少不一樣的類型,好比在特定位置寫入,寫入不一樣類型的數據等等,能夠在IDEA中按F12查看。

flip()

flip()方法用於將Buffer從寫模式切換爲讀模式,position將切換到0位置,且limit將切換到剛纔position的位置。

也就是說,position變成了可讀數據的首位,limit表示能夠讀取的最大數據長度。

從Buffer中讀取數據

從Buffer中讀取數據也有兩種形式:

  • 從Buffer讀取數據,並寫入Channel,也叫做從Buffer寫入Channel
  • 調用Buffer本身的get()方法讀取數據

從Buffer寫入Channel的示例以下:

// 本文來源工從號彤哥讀源碼
int bytesWritten = inChannel.write(buf);

調用Buffer本身的get()方法讀取數據的示例以下:

byte aByte = buf.get();

固然,get()有不少不一樣的類型,好比從特定的位置讀取,讀取不一樣類型的數據等等,能夠在IDEA中按F12查看。

rewind()

rewind()方法會重置position爲0,但limit保持不變,所以能夠用來從新讀取數據。一般是在從新讀取數據以前調用。

clear()

clear()方法用於清空整個Buffer,並將Buffer從讀模式切換回寫模式,且position歸位到0位置。

compact()

compact()方法用於清空已讀取的數據,並將未讀取的數據移至Buffer的頭部,position的位置移動到從頭開始計算的未讀取的數據的下一個位置,它也會將Buffer從讀模式切換回寫模式。

mark() 和 reset()

mark()方法用於標記給定位置,而後能夠在以後經過reset()方法從新回到mark的位置,示例以下:

buffer.mark();

//屢次調用buffer.get(),例如在解析過程當中。

buffer.reset(); //將位置從新設置爲標記。

總結

今天咱們學習了Java NIO核心組件Buffer,它常常跟Channel聯合起來使用。講到這裏咱們一直在使用FileChannel在舉例,那麼它們到底跟網絡編程有什麼關係呢?請聽下回分解。

參考

http://tutorials.jenkov.com/java-nio/channels.html

最後,也歡迎來個人工從號彤哥讀源碼系統地學習源碼&架構的知識。

nio

相關文章
相關標籤/搜索