日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

Java NIO之缓冲区

發(fā)布時間:2025/3/21 59 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java NIO之缓冲区 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1.簡介

Java NIO 相關類在 JDK 1.4 中被引入,用于提高 I/O 的效率。Java NIO 包含了很多東西,但核心的東西不外乎 Buffer、Channel 和 Selector。本文中,我們先來聊聊的 Buffer 的實現。Channel 和 Selector 將在隨后的文章中講到。

?2.繼承體系

Buffer 的繼承類比較多,用于存儲各種類型的數據。包括 ByteBuffer、CharBuffer、IntBuffer、FloatBuffer 等等。這其中,ByteBuffer 最為常用。所以接下來將會主要分析 ByteBuffer 的實現。Buffer 的繼承體系圖如下:

?3.屬性及相關操作

Buffer 本質就是一個數組,只不過在數組的基礎上進行適當的封裝,方便使用。 Buffer 中有幾個重要的屬性,通過這幾個屬性來顯示數據存儲的信息。這個屬性分別是:

屬性說明
capacity?容量Buffer 所能容納數據元素的最大數量,也就是底層數組的容量值。在創(chuàng)建時被指定,不可更改。
position 位置下一個被讀或被寫的位置
limit 上界可供讀寫的最大位置,用于限制?position,position < limit
mark 標記位置標記,用于記錄某一次的讀寫位置,可以通過 reset 重新回到這個位置

?3.1 ByteBuffer 初始化

ByteBuffer 可通過 allocate、allocateDirect 和 wrap 等方法初始化,這里以 allocate 為例:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public static ByteBuffer allocate(int capacity) {if (capacity < 0)throw new IllegalArgumentException();return new HeapByteBuffer(capacity, capacity); }HeapByteBuffer(int cap, int lim) {super(-1, 0, lim, cap, new byte[cap], 0); }ByteBuffer(int mark, int pos, int lim, int cap, byte[] hb, int offset) {super(mark, pos, lim, cap);this.hb = hb;this.offset = offset; }

上面是 allocate 創(chuàng)建 ByteBuffer 的過程,ByteBuffer 是抽象類,所以實際上創(chuàng)建的是其子類 HeapByteBuffer。HeapByteBuffer 在構造方法里調用父類構造方法,將一些參數值傳遞給父類。最后父類再做一次中轉,相關參數最終被傳送到 Buffer 的構造方法中了。我們再來看一下 Buffer 的源碼:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public abstract class Buffer {// Invariants: mark <= position <= limit <= capacityprivate int mark = -1;private int position = 0;private int limit;private int capacity;Buffer(int mark, int pos, int lim, int cap) { // package-privateif (cap < 0)throw new IllegalArgumentException("Negative capacity: " + cap);this.capacity = cap;limit(lim);position(pos);if (mark >= 0) {if (mark > pos)throw new IllegalArgumentException("mark > position: ("+ mark + " > " + pos + ")");this.mark = mark;}} }

Buffer 創(chuàng)建完成后,底層數組的結構信息如下:

上面的幾個屬性作為公共屬性,被放在了 Buffer 中,相關的操作方法也是封裝在 Buffer 中。那么接下來,我們來看看這些方法吧。

?3.2 ByteBuffer 讀寫操作

ByteBuffer 讀寫操作時通過 get 和 put 完成的,這兩個方法都有重載,我們只看其中一個。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 // 讀操作 public byte get() {return hb[ix(nextGetIndex())]; }final int nextGetIndex() {if (position >= limit)throw new BufferUnderflowException();return position++; }// 寫操作 public ByteBuffer put(byte x) {hb[ix(nextPutIndex())] = x;return this; }final int nextPutIndex() {if (position >= limit)throw new BufferOverflowException();return position++; }

讀寫操作都會修改 position 的值,每次讀寫的位置是當前 position 的下一個位置。通過修改 position,我們可以讀取指定位置的數據。當然,前提是 position < limit。Buffer 中提供了position(int)?方法用于修改 position 的值。

1 2 3 4 5 6 7 public final Buffer position(int newPosition) {if ((newPosition > limit) || (newPosition < 0))throw new IllegalArgumentException();position = newPosition;if (mark > position) mark = -1;return this; }

當我們向一個剛初始化好的 Buffer 中寫入一些數據時,數據存儲示意圖如下:

如果我們想讀取剛剛寫入的數據,就需要修改 position 的值。否則 position 將指向沒有存儲數據的空間上,讀取空白空間是沒意義的。如上圖,我們可以將 position 設置為 0,這樣就能從頭讀取剛剛寫入的數據。

僅修改 position 的值是不夠的,如果想正確讀取剛剛寫入的數據,還需修改 limit 的值,不然還是會讀取到空白空間上的內容。我們將 limit 指向數據區(qū)域的尾部,即可避免這個問題。修改 limit 的值通過 limit(int) 方法進行。

1 2 3 4 5 6 7 8 public final Buffer limit(int newLimit) {if ((newLimit > capacity) || (newLimit < 0))throw new IllegalArgumentException();limit = newLimit;if (position > limit) position = limit;if (mark > limit) mark = -1;return this; }

修改后,數據存儲示意圖如下:

上面為了正確讀取寫入的數據,需要兩步操作。Buffer 中提供了一個便利的方法,將這兩步操作合二為一,即 flip 方法。

1 2 3 4 5 6 7 8 public final Buffer flip() {// 1. 設置 limit 為當前位置limit = position;// 1. 設置 position 為0position = 0;mark = -1;return this; }

?3.3 ByteBuffer 標記

我們在讀取或寫入的過程中,可以在感興趣的位置打上一個標記,這樣我們可以通過這個標記再次回到這個位置。Buffer 中,打標記的方法是 mark,回到標記位置的方法時 reset。簡單看下源碼吧。

1 2 3 4 5 6 7 8 9 10 11 12 public final Buffer mark() {mark = position;return this; }public final Buffer reset() {int m = mark;if (m < 0)throw new InvalidMarkException();position = m;return this; }

打標記及回到標記位置的流程如下:

?4.DirectByteBuffer

在 ByteBuffer 初始化一節(jié)中,我介紹了 ByteBuffer 的 allocate 方法,該方法實際上創(chuàng)建的是 HeapByteBuffer 對象。除了 allocate 方法,ByteBuffer 還有一個方法 allocateDirect。這個方法創(chuàng)建的是 DirectByteBuffer 對象。兩者有什么區(qū)別呢?簡單的說,allocate 方法所請求的空間是在 JVM 堆內進行分配的,而 allocateDirect 請求的空間則是在 JVM 堆外的,這部分空間不被 JVM 所管理。那么堆內空間和堆空間在使用上有什么不同呢?用一個表格列舉一下吧。

空間類型優(yōu)點缺點
堆內空間分配速度快JVM 整理內存空間時,堆內空間的位置會被搬動,比較笨重
堆外空間1. 空間位置固定,不用擔心空間被 JVM 隨意搬動
2. 降低堆內空間的使用率
1. 分配速度慢
2. 回收策略比較復雜

DirectByteBuffer 牽涉的底層技術點比較多,想要弄懂,還需要好好打基礎才行。由于本人目前能力很有限,關于 DirectByteBuffer 只能簡單講講。待后續(xù)能力提高時,我會再來重寫這部分的內容。如果想了解這方面的內容,建議大家看看其他的文章。

?5.總結

Buffer 是 Java NIO 中一個重要的輔助類,使用比較頻繁。在不熟悉 Buffer 的情況下,有時候很容易因為忘記調用 flip 或其他方法導致程序出錯。不過好在 Buffer 的源碼不難理解,大家可以自己看看,這樣可以避免出現一些奇怪的錯誤。

好了,本文到這里就結束了,謝謝閱讀!

  • 本文鏈接:?https://www.tianxiaobo.com/2018/03/04/Java-NIO之緩沖區(qū)/

from:?http://www.tianxiaobo.com/2018/03/04/Java-NIO%E4%B9%8B%E7%BC%93%E5%86%B2%E5%8C%BA/?

總結

以上是生活随笔為你收集整理的Java NIO之缓冲区的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。