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

来源:https://www.cnblogs.com/tong-yuan/archive/2019/12/03/11980067.html
-Advertisement-
Play Games

——日拱一卒,不期而至! 你好,我是彤哥,本篇是netty系列的第六篇。 簡介 上一章我們一起學習了Java NIO的核心組件Channel,它可以看作是實體與實體之間的連接,而且需要與Buffer交互,這一章我們就來學習一下Buffer的特性。 概念 Buffer用於與Channel交互時使用,通 ...


——日拱一卒,不期而至!

nio

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

簡介

上一章我們一起學習了Java NIO的核心組件Channel,它可以看作是實體與實體之間的連接,而且需要與Buffer交互,這一章我們就來學習一下Buffer的特性。

概念

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

nio

Buffer本質上是一個記憶體塊,可以向裡面寫入數據,或者從裡面讀取數據,在Java中它被包裝成了Buffer對象,並提供了一系列的方法用於操作這個記憶體塊。

屬性

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

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

在讀模式和寫模式下,position和limit的位置有所不同,見下圖:

nio

capacity

Buffer作為一個存儲塊,是有固定大小的,這個固定大小我們稱作“容量”。

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

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


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 之前已經說過了SpringAOP中的幾種通知類型以及如何創建簡單的通知 "見地址" 一、什麼是切入點 通過之前的例子中,我們可以創建ProxyFactory的方式來創建通知,然後獲取目標類中的方法。通過不同類型的通知,能對這些方法做不同的事。但是,這種方式會對整個類中的所有方法都有作用,但是很多時間 ...
  • 命令模式,正如模式的名字一樣,該模式中的不同操作都可以當做不同的命令來執行,可以使用隊列來執行一系列的命令,也可以單獨執行某個命令。該模式重點是將不同的操作封裝為不同的命令對象,將操作的調用者與執行者進行解耦。 命令模式中的Command對象(即每一個命令,或者說命令對象)用於封裝在完成某項操作或觸 ...
  • pip install redis 五大數據類型使用 String類型 List類型 Hash類型 Set類型 Zset類型 全局key操作 ...
  • LinkedList實現原理(JDK1.8) LinkedList底層採用雙向鏈表,如果對鏈表這種結構比較熟悉的話,那LinkedList的實現原理看明白就相當容易。 鏈表通過“指針”將一組零散的記憶體塊串聯起來使用,每一個元素(節點)通過指針指向它的下一個元素,最後一個節點的下一個指向為null,而 ...
  • Java抽象類: 抽象類特點 :抽象類除了不能實例化對象之外,類的其它功能依然存在,成員變數、成員方法和構造方法的訪問方式和普通類一樣。 由於抽象類不能實例化對象,所以抽象類必須被extends [抽象類]方式 繼承才能被使用。 抽象類表示的是一種繼承關係。 (總結就是:抽象類裡面設計跟普通類一樣, ...
  • lua中json和table的互轉,是我們在平時開發過程中經常用到的。比如: 在用lua編寫的伺服器中,如果客戶端發送json格式的數據,那麼在lua處理業務邏輯的時候,必然需要轉換成lua自己的數據結構,如table。此時,就會用到table和json格式的互轉。 在用lua編寫的伺服器中,如果我 ...
  • 先定義消息類型 orders.proto 在GOPATH創建目錄和編譯這個消息類型輸出到該目錄,包名是message 編寫go文件進行序列化和反序列化剛纔生成的包里的類型結構體數據 ...
  • swap(a,b) 用於交換a,b兩個變數的值; max(a,b) 返回a,b中的最大值; min(a,b) 返回a,b中的最小值; abs(x) 返回x的絕對值,x必須是整數; ...
一周排行
    -Advertisement-
    Play Games
  • Timer是什麼 Timer 是一種用於創建定期粒度行為的機制。 與標準的 .NET System.Threading.Timer 類相似,Orleans 的 Timer 允許在一段時間後執行特定的操作,或者在特定的時間間隔內重覆執行操作。 它在分散式系統中具有重要作用,特別是在處理需要周期性執行的 ...
  • 前言 相信很多做WPF開發的小伙伴都遇到過表格類的需求,雖然現有的Grid控制項也能實現,但是使用起來的體驗感並不好,比如要實現一個Excel中的表格效果,估計你能想到的第一個方法就是套Border控制項,用這種方法你需要控制每個Border的邊框,並且在一堆Bordr中找到Grid.Row,Grid. ...
  • .NET C#程式啟動閃退,目錄導致的問題 這是第2次踩這個坑了,很小的編程細節,容易忽略,所以寫個博客,分享給大家。 1.第一次坑:是windows 系統把程式運行成服務,找不到配置文件,原因是以服務運行它的工作目錄是在C:\Windows\System32 2.本次坑:WPF桌面程式通過註冊表設 ...
  • 在分散式系統中,數據的持久化是至關重要的一環。 Orleans 7 引入了強大的持久化功能,使得在分散式環境下管理數據變得更加輕鬆和可靠。 本文將介紹什麼是 Orleans 7 的持久化,如何設置它以及相應的代碼示例。 什麼是 Orleans 7 的持久化? Orleans 7 的持久化是指將 Or ...
  • 前言 .NET Feature Management 是一個用於管理應用程式功能的庫,它可以幫助開發人員在應用程式中輕鬆地添加、移除和管理功能。使用 Feature Management,開發人員可以根據不同用戶、環境或其他條件來動態地控制應用程式中的功能。這使得開發人員可以更靈活地管理應用程式的功 ...
  • 在 WPF 應用程式中,拖放操作是實現用戶交互的重要組成部分。通過拖放操作,用戶可以輕鬆地將數據從一個位置移動到另一個位置,或者將控制項從一個容器移動到另一個容器。然而,WPF 中預設的拖放操作可能並不是那麼好用。為瞭解決這個問題,我們可以自定義一個 Panel 來實現更簡單的拖拽操作。 自定義 Pa ...
  • 在實際使用中,由於涉及到不同編程語言之間互相調用,導致C++ 中的OpenCV與C#中的OpenCvSharp 圖像數據在不同編程語言之間難以有效傳遞。在本文中我們將結合OpenCvSharp源碼實現原理,探究兩種數據之間的通信方式。 ...
  • 一、前言 這是一篇搭建許可權管理系統的系列文章。 隨著網路的發展,信息安全對應任何企業來說都越發的重要,而本系列文章將和大家一起一步一步搭建一個全新的許可權管理系統。 說明:由於搭建一個全新的項目過於繁瑣,所有作者將挑選核心代碼和核心思路進行分享。 二、技術選擇 三、開始設計 1、自主搭建vue前端和. ...
  • Csharper中的表達式樹 這節課來瞭解一下表示式樹是什麼? 在C#中,表達式樹是一種數據結構,它可以表示一些代碼塊,如Lambda表達式或查詢表達式。表達式樹使你能夠查看和操作數據,就像你可以查看和操作代碼一樣。它們通常用於創建動態查詢和解析表達式。 一、認識表達式樹 為什麼要這樣說?它和委托有 ...
  • 在使用Django等框架來操作MySQL時,實際上底層還是通過Python來操作的,首先需要安裝一個驅動程式,在Python3中,驅動程式有多種選擇,比如有pymysql以及mysqlclient等。使用pip命令安裝mysqlclient失敗應如何解決? 安裝的python版本說明 機器同時安裝了 ...