Java?NIO下ByteBuffer的常用方法學習
前言
咱就說,基礎(chǔ)不牢地動山搖,以前欠下的債遲早都要補回來,開始安排 Netty, 從 NIO 開始學起, 學習嘛, 肯定是先從案例開始學起
今日任務:
初步學習 NIO 中的 ByteBuffer
文件讀取案例
一個小小的案例, 讀取 test.txt 文件的內(nèi)容, 四步走:
- 獲取文件流
- 準備緩沖區(qū)
- 寫入緩沖區(qū)
- 遍歷讀取
代碼展示
private static void getFileContent(){ // 輸入輸出流 try(FileChannel channel = new FileInputStream("C:\Users\My\Desktop\learn\demo\demo\test.txt").getChannel()){ // 準備緩沖區(qū) ByteBuffer buffer = ByteBuffer.allocate(10); // 循環(huán)遍歷 while(true){ // 從 channel 讀取數(shù)據(jù), 向 buffer 寫入 int len = channel.read(buffer); if (len == -1){ System.out.println("沒有內(nèi)容了"); break; } // 打印 buffer 內(nèi)容 // 切換至 讀模式 buffer.flip(); // 遍歷查看是否還有剩余未讀數(shù)據(jù) while(buffer.hasRemaining()){ byte b = buffer.get(); System.out.println(b); } // 切換為寫模式 buffer.clear(); } }catch (Exception e){ System.out.println("文件未找到"); } }
打印結(jié)果
test.txt文件內(nèi)容如下所示
接下來執(zhí)行上述代碼, 結(jié)果如下
可以看到打印出來的結(jié)果是 ASCII 編碼表的值,所以我們把輸出轉(zhuǎn)為 char 類型, 結(jié)果如下
Buffer
Buffer繼承自O(shè)bject類,是基本類型元素的線性有限序列(容器)。除內(nèi)容外,Buffer的基本屬性有:Limit—限制,Capacity—容量,Position—位置。對著三個變量操作,可以完成幾乎所有對Buffer的代碼操作。
今天開始學習 Netty ,那就繞不過 NIO, 在 NIO 包下的 Buffer 有以下七種實現(xiàn):
- ByteBuffer
- ShortBuffer
- FloatBuffer
- CharBuffer
- DoubleBuffer
- IntBuffer
- LongBuffer
實際上就是八種數(shù)據(jù)類型的相關(guān) Buffer, 但是我只聽說過 ByteBuffer, 也是使用最廣泛的, 至于為什么我也不知道, 希望以后有機會能過來填坑, 有大佬知道的話也可以評論區(qū)說一下
ByteBuffer常用方法
在 java 中 ByteBuffer 的常用方法有以下七種:
- put: 寫入
- get: 讀數(shù)據(jù)
- flip: 切換讀模式
- rewind: 重新重頭開始讀
- mark: 記錄當前下標
- reset: 回到 mark 位置
- clear: 切換寫模式
- compact: 切換寫模式, 同時將未讀數(shù)據(jù)移動到首部
工具方法 selectAll()
為了方便觀察當前 ByteBuffer 所處的位置, 容量等信息, 寫了下面這個方法
private static void selectAll(ByteBuffer buffer){ System.out.println(); int limit = buffer.limit(); System.out.print("pos = " + buffer.position() + " " + "lim = " + buffer.limit() + " " + "cap = " + buffer.capacity()); System.out.println(); for (int i = 0; i < limit; i++) { if (i < 10){ System.out.print(" | "); }else{ System.out.print(" | "); } System.out.print(i); } System.out.println(); System.out.println("--------------------------------------------------------------------------"); for (int i = 0; i < limit; i++) { byte b = buffer.get(i); System.out.print(" | "); System.out.print((char) b); if (b == 0){ System.out.print(0); } } System.out.println(); }
打印結(jié)果如下:
根據(jù)打印結(jié)果我們可以很直觀的看到當前 ByteBuffer 每個下標上的元素是什么, 同時也能看到當前指針所處的位置(Position)
初始化
以下測試用例的 ByteBuffer 采用同一個 ByteBuffer
ByteBuffer buffer = ByteBuffer.allocate(16);
buffer.put()
put 方法就是往 ByteBuffer 中寫入數(shù)據(jù), 具體操作如下述代碼所示
private static void bufferPut(ByteBuffer buffer){ buffer.put(new byte[]{'a', 'b', 'c', 'd'}); selectAll(buffer); buffer.put(new byte[]{'1'}); selectAll(buffer); }
執(zhí)行結(jié)果如下:
圖中和代碼一樣, 是分兩次打印當前 ByteBuffer 的內(nèi)容, pos 就是下一次寫入的下標
buffer.get()
get 方法是獲取元素的, 但是我們?nèi)绻趧倢懭胫缶腿プx取的話, 是什么都讀取不到的, 具體如下所示
可以看到, 我們讀取出來當前的元素為 0, 我們進入源碼看一下 get 方法
如圖所示, 我們通過 get 方法獲取到的元素都是當前 pos 的下一個坐標元素, 在之前的 put 方法中, 我們看到最后 pos 是指向了 5 是下一個 寫入下標
我們有兩種方法可以獲取到元素:
- 通過下標獲取
- 將寫入模式更改為讀模式
通過下標獲取: 直接輸入下標就可以獲取到了
buffer.flip()
flip 方法: 將讀模式轉(zhuǎn)換為寫模式
還是剛才的例子, 使用方法 buffer.flip() 之后, 可以看到 pos 變成了 1, lim 變成了 5 , cap 不變
這里 pos 代表的是下一個 get 的下標, lim 代表的是當前的長度
注意: 在調(diào)用 flip 進入讀模式之后,后續(xù)如果調(diào)用 get()
導致 position
大于等于了 limit
的值,程序會拋出 BufferUnderflowException
異常。這點從之前 get
的源碼也可以看出來。
buffer.rewind()
rewind 方法可以理解為下標歸零, 也可以理解為重新開始, 重頭開始. 代碼展示如下所示
我們點進源代碼也可以看到, 它實際上就是將 pos 賦值了 0, 將 mark 賦值為 -1
buffer.mark() & buffer.reset()
這里將 mark 方法和 reset 方法一起講, 他倆是相輔相成的一個關(guān)系, 二者缺一不可
mark 方法標記當前下標, reset 回到 mark 標記的下標
可以看到, 當我們執(zhí)行 mark 方法的時候記錄了當前的下標, 執(zhí)行 reset 方法的時候 pos 又更改為了 1
buffer.clear() & buffer.compact()
clear 方法和 compact 方法都是對當前 ByteBuffer 寫入 , clear 方法是從頭開始寫入, compact 方法是將未讀內(nèi)容移至頭部然后再寫入
可以看到, 在執(zhí)行完 clear 方法之后, 新增的元素是從下標 0 開始寫入的
可以看到, 在執(zhí)行完 compact 方法之后, 它先是將沒有讀取到的數(shù)據(jù)放置頭部, 在接下來 put 的時候?qū)竺娴膬?nèi)容進行了一個覆蓋
全部代碼
import java.io.FileInputStream; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; /** * @Author ningxuan * @Date 2022/10/31 21:28 */ public class ByteBufferTest { public static void main(String[] args) { // 讀文件案例 // getFileContent(); // 初始化 ByteBuffer ByteBuffer buffer = ByteBuffer.allocate(16); // 寫測試 bufferPut(buffer); // a , b , c , d, 1 bufferGet(buffer); buffer.flip(); // System.out.println((char) buffer.get()); // a pos == 0 // System.out.println((char)buffer.get()); // b pos == 1 // buffer.rewind(); // System.out.println((char)buffer.get()); // c pos == 0 // System.out.println((char)buffer.get()); // d pos == 1 // bufferMarkTest(buffer); bufferCompact(buffer); } private static void bufferCompact(ByteBuffer buffer){ selectAll(buffer); buffer.get(); buffer.get(); selectAll(buffer); buffer.compact(); selectAll(buffer); buffer.put(new byte[]{'e', 'h'}); selectAll(buffer); } private static void bufferClear(ByteBuffer buffer){ selectAll(buffer); System.out.println(); buffer.clear(); buffer.put(new byte[]{'e', 'h'}); selectAll(buffer); } private static void bufferMarkTest(ByteBuffer buffer){ byte b; b = buffer.get(); // a System.out.println((char) b); buffer.mark(); System.out.println("position = " + buffer.position()); b = buffer.get(); // b System.out.println((char) b); b = buffer.get(); // c System.out.println((char) b); buffer.reset(); System.out.println("position = " + buffer.position()); b = buffer.get(); // b System.out.println((char) b); b = buffer.get(); // c System.out.println((char) b); } private static void bufferGet(ByteBuffer buffer){ byte b = buffer.get(0); // System.out.println((char)b); b = buffer.get(1); // System.out.println((char)b); } private static void bufferPut(ByteBuffer buffer){ buffer.put(new byte[]{'a', 'b', 'c', 'd'}); // selectAll(buffer); buffer.put(new byte[]{'1'}); // selectAll(buffer); } private static void selectAll(ByteBuffer buffer){ System.out.println(); int limit = buffer.limit(); System.out.print("pos = " + buffer.position() + " " + "lim = " + buffer.limit() + " " + "cap = " + buffer.capacity()); System.out.println(); for (int i = 0; i < limit; i++) { if (i < 10){ System.out.print(" | "); }else{ System.out.print(" | "); } System.out.print(i); } System.out.println(); System.out.println("--------------------------------------------------------------------------"); for (int i = 0; i < limit; i++) { byte b = buffer.get(i); System.out.print(" | "); System.out.print((char) b); if (b == 0){ System.out.print(0); } } System.out.println(); } private static void getFileContent(){ // 輸入輸出流 try(FileChannel channel = new FileInputStream("C:\Users\My\Desktop\learn\demo\demo\test.txt").getChannel()){ // 準備緩沖區(qū) ByteBuffer buffer = ByteBuffer.allocate(10); // 循環(huán)遍歷 while(true){ // 從 channel 讀取數(shù)據(jù), 向 buffer 寫入 int len = channel.read(buffer); if (len == -1){ System.out.println("沒有內(nèi)容了"); break; } // 打印 buffer 內(nèi)容 // 切換至 讀模式 buffer.flip(); // 遍歷查看是否還有剩余未讀數(shù)據(jù) while(buffer.hasRemaining()){ byte b = buffer.get(); System.out.println((char) b); } // 切換為寫模式 buffer.clear(); } }catch (Exception e){ System.out.println("文件未找到"); } } }
以上就是Java NIO下ByteBuffer的常用方法學習的詳細內(nèi)容,更多關(guān)于Java ByteBuffer的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java使用fastjson對String、JSONObject、JSONArray相互轉(zhuǎn)換
這篇文章主要介紹了Java使用fastjson對String、JSONObject、JSONArray相互轉(zhuǎn)換,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11詳解spring boot實現(xiàn)多數(shù)據(jù)源代碼實戰(zhàn)
本篇文章主要介紹了詳解spring boot實現(xiàn)多數(shù)據(jù)源代碼實戰(zhàn),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-07-07IntelliJ IDEA中Scala、sbt、maven配置教程
這篇文章主要介紹了IntelliJ IDEA中Scala、sbt、maven配置教程,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-09-09Springboot使用@WebListener?作為web監(jiān)聽器的過程解析
這篇文章主要介紹了Springboot使用@WebListener作為web監(jiān)聽器的過程,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-08-08