Java NIO三大組件與ByteBuffer深入理解及使用
1、三大組件
1.1 Channel & Buffer
channel 有一點(diǎn)類似于 stream,它就是讀寫數(shù)據(jù)的雙向通道,可以從 channel 將數(shù)據(jù)讀入 buffer,也可以將 buffer 的數(shù)據(jù)寫入 channel,而之前的 stream 要么是輸入,要么是輸出,channel 比 stream 更為底層
常見的 Channel 有
- FileChannel
- DatagramChannel
- SocketChannel
- ServerSocketChannel
buffer 則用來緩沖讀寫數(shù)據(jù),常見的 buffer 有
- ByteBuffer
- MappedByteBuffer
- DirectByteBuffer
- HeapByteBuffer
- ShortBuffer
- IntBuffer
- LongBuffer
- FloatBuffer
- DoubleBuffer
- CharBuffer
1.2 Selector
selector 單從字面意思不好理解,需要結(jié)合服務(wù)器的設(shè)計(jì)演化來理解它的用途
多線程版設(shè)計(jì)
多線程版缺點(diǎn)
- 內(nèi)存占用高
- 線程上下文切換成本高
- 只適合連接數(shù)少的場(chǎng)景
線程池版設(shè)計(jì)
線程池版缺點(diǎn)
- 阻塞模式下,線程僅能處理一個(gè) socket 連接
- 僅適合短連接場(chǎng)景
selector 版設(shè)計(jì)
selector 的作用就是配合一個(gè)線程來管理多個(gè) channel,獲取這些 channel 上發(fā)生的事件,這些 channel 工作在非阻塞模式下,不會(huì)讓線程吊死在一個(gè) channel 上。適合連接數(shù)特別多,但流量低的場(chǎng)景(low traffic)
調(diào)用 selector 的 select() 會(huì)阻塞直到 channel 發(fā)生了讀寫就緒事件,這些事件發(fā)生,select 方法就會(huì)返回這些事件交給 thread 來處理
2、ByteBuffer
有一普通文本文件 data.txt,內(nèi)容為
1234567890abcd
使用 FileChannel 來讀取文件內(nèi)容
package org.example.demo1; import lombok.extern.slf4j.Slf4j; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; @Slf4j public class ChannelDemo1 { public static void main(String[] args) { try (FileChannel channel = new FileInputStream("data.txt").getChannel()) { ByteBuffer buffer = ByteBuffer.allocate(10); do { // 向 buffer 寫入 int len = channel.read(buffer); log.debug("讀到字節(jié)數(shù):{}", len); if (len == -1) { break; } // 切換 buffer 讀模式 buffer.flip(); while(buffer.hasRemaining()) { byte b = buffer.get(); log.debug("實(shí)際字節(jié){}", (char)b); } // 切換 buffer 寫模式 buffer.clear(); } while (true); } catch (IOException e) { e.printStackTrace(); } } }
輸出
15:03:39.467 [main] DEBUG org.example.demo1.ChannelDemo1 - 讀到字節(jié)數(shù):10
15:03:39.475 [main] DEBUG org.example.demo1.ChannelDemo1 - 實(shí)際字節(jié)1
15:03:39.475 [main] DEBUG org.example.demo1.ChannelDemo1 - 實(shí)際字節(jié)2
15:03:39.476 [main] DEBUG org.example.demo1.ChannelDemo1 - 實(shí)際字節(jié)3
15:03:39.476 [main] DEBUG org.example.demo1.ChannelDemo1 - 實(shí)際字節(jié)4
15:03:39.476 [main] DEBUG org.example.demo1.ChannelDemo1 - 實(shí)際字節(jié)5
15:03:39.476 [main] DEBUG org.example.demo1.ChannelDemo1 - 實(shí)際字節(jié)6
15:03:39.476 [main] DEBUG org.example.demo1.ChannelDemo1 - 實(shí)際字節(jié)7
15:03:39.476 [main] DEBUG org.example.demo1.ChannelDemo1 - 實(shí)際字節(jié)8
15:03:39.476 [main] DEBUG org.example.demo1.ChannelDemo1 - 實(shí)際字節(jié)9
15:03:39.476 [main] DEBUG org.example.demo1.ChannelDemo1 - 實(shí)際字節(jié)0
15:03:39.476 [main] DEBUG org.example.demo1.ChannelDemo1 - 讀到字節(jié)數(shù):4
15:03:39.477 [main] DEBUG org.example.demo1.ChannelDemo1 - 實(shí)際字節(jié)a
15:03:39.477 [main] DEBUG org.example.demo1.ChannelDemo1 - 實(shí)際字節(jié)b
15:03:39.477 [main] DEBUG org.example.demo1.ChannelDemo1 - 實(shí)際字節(jié)c
15:03:39.477 [main] DEBUG org.example.demo1.ChannelDemo1 - 實(shí)際字節(jié)d
15:03:39.477 [main] DEBUG org.example.demo1.ChannelDemo1 - 讀到字節(jié)數(shù):-1
2.1 ByteBuffer 正確使用姿勢(shì)
- 向 buffer 寫入數(shù)據(jù),例如調(diào)用 channel.read(buffer)
- 調(diào)用 flip() 切換至讀模式
- 從 buffer 讀取數(shù)據(jù),例如調(diào)用 buffer.get()
- 調(diào)用 clear() 或 compact() 切換至寫模式
- 重復(fù) 1~4 步驟
2.2 ByteBuffer 結(jié)構(gòu)
ByteBuffer 有以下重要屬性
- capacity
- position
- limit
一開始
寫模式下,position 是寫入位置,limit 等于容量,下圖表示寫入了 4 個(gè)字節(jié)后的狀態(tài)
flip 動(dòng)作發(fā)生后,position 切換為讀取位置,limit 切換為讀取限制
讀取 4 個(gè)字節(jié)后,狀態(tài)
clear 動(dòng)作發(fā)生后,狀態(tài)
compact 方法,是把未讀完的部分向前壓縮,然后切換至寫模式
調(diào)試工具類
package org.example.utils; import io.netty.util.internal.StringUtil; import java.nio.ByteBuffer; import static io.netty.util.internal.MathUtil.isOutOfBounds; import static io.netty.util.internal.StringUtil.NEWLINE; public class ByteBufferUtil { private static final char[] BYTE2CHAR = new char[256]; private static final char[] HEXDUMP_TABLE = new char[256 * 4]; private static final String[] HEXPADDING = new String[16]; private static final String[] HEXDUMP_ROWPREFIXES = new String[65536 >>> 4]; private static final String[] BYTE2HEX = new String[256]; private static final String[] BYTEPADDING = new String[16]; static { final char[] DIGITS = "0123456789abcdef".toCharArray(); for (int i = 0; i < 256; i++) { HEXDUMP_TABLE[i << 1] = DIGITS[i >>> 4 & 0x0F]; HEXDUMP_TABLE[(i << 1) + 1] = DIGITS[i & 0x0F]; } int i; // Generate the lookup table for hex dump paddings for (i = 0; i < HEXPADDING.length; i++) { int padding = HEXPADDING.length - i; StringBuilder buf = new StringBuilder(padding * 3); for (int j = 0; j < padding; j++) { buf.append(" "); } HEXPADDING[i] = buf.toString(); } // Generate the lookup table for the start-offset header in each row (up to 64KiB). for (i = 0; i < HEXDUMP_ROWPREFIXES.length; i++) { StringBuilder buf = new StringBuilder(12); buf.append(NEWLINE); buf.append(Long.toHexString(i << 4 & 0xFFFFFFFFL | 0x100000000L)); buf.setCharAt(buf.length() - 9, '|'); buf.append('|'); HEXDUMP_ROWPREFIXES[i] = buf.toString(); } // Generate the lookup table for byte-to-hex-dump conversion for (i = 0; i < BYTE2HEX.length; i++) { BYTE2HEX[i] = ' ' + StringUtil.byteToHexStringPadded(i); } // Generate the lookup table for byte dump paddings for (i = 0; i < BYTEPADDING.length; i++) { int padding = BYTEPADDING.length - i; StringBuilder buf = new StringBuilder(padding); for (int j = 0; j < padding; j++) { buf.append(' '); } BYTEPADDING[i] = buf.toString(); } // Generate the lookup table for byte-to-char conversion for (i = 0; i < BYTE2CHAR.length; i++) { if (i <= 0x1f || i >= 0x7f) { BYTE2CHAR[i] = '.'; } else { BYTE2CHAR[i] = (char) i; } } } /** * 打印所有內(nèi)容 * @param buffer */ public static void debugAll(ByteBuffer buffer) { int oldlimit = buffer.limit(); buffer.limit(buffer.capacity()); StringBuilder origin = new StringBuilder(256); appendPrettyHexDump(origin, buffer, 0, buffer.capacity()); System.out.println("+--------+-------------------- all ------------------------+----------------+"); System.out.printf("position: [%d], limit: [%d]\n", buffer.position(), oldlimit); System.out.println(origin); buffer.limit(oldlimit); } /** * 打印可讀取內(nèi)容 * @param buffer */ public static void debugRead(ByteBuffer buffer) { StringBuilder builder = new StringBuilder(256); appendPrettyHexDump(builder, buffer, buffer.position(), buffer.limit() - buffer.position()); System.out.println("+--------+-------------------- read -----------------------+----------------+"); System.out.printf("position: [%d], limit: [%d]\n", buffer.position(), buffer.limit()); System.out.println(builder); } private static void appendPrettyHexDump(StringBuilder dump, ByteBuffer buf, int offset, int length) { if (isOutOfBounds(offset, length, buf.capacity())) { throw new IndexOutOfBoundsException( "expected: " + "0 <= offset(" + offset + ") <= offset + length(" + length + ") <= " + "buf.capacity(" + buf.capacity() + ')'); } if (length == 0) { return; } dump.append( " +-------------------------------------------------+" + NEWLINE + " | 0 1 2 3 4 5 6 7 8 9 a b c d e f |" + NEWLINE + "+--------+-------------------------------------------------+----------------+"); final int startIndex = offset; final int fullRows = length >>> 4; final int remainder = length & 0xF; // Dump the rows which have 16 bytes. for (int row = 0; row < fullRows; row++) { int rowStartIndex = (row << 4) + startIndex; // Per-row prefix. appendHexDumpRowPrefix(dump, row, rowStartIndex); // Hex dump int rowEndIndex = rowStartIndex + 16; for (int j = rowStartIndex; j < rowEndIndex; j++) { dump.append(BYTE2HEX[getUnsignedByte(buf, j)]); } dump.append(" |"); // ASCII dump for (int j = rowStartIndex; j < rowEndIndex; j++) { dump.append(BYTE2CHAR[getUnsignedByte(buf, j)]); } dump.append('|'); } // Dump the last row which has less than 16 bytes. if (remainder != 0) { int rowStartIndex = (fullRows << 4) + startIndex; appendHexDumpRowPrefix(dump, fullRows, rowStartIndex); // Hex dump int rowEndIndex = rowStartIndex + remainder; for (int j = rowStartIndex; j < rowEndIndex; j++) { dump.append(BYTE2HEX[getUnsignedByte(buf, j)]); } dump.append(HEXPADDING[remainder]); dump.append(" |"); // Ascii dump for (int j = rowStartIndex; j < rowEndIndex; j++) { dump.append(BYTE2CHAR[getUnsignedByte(buf, j)]); } dump.append(BYTEPADDING[remainder]); dump.append('|'); } dump.append(NEWLINE + "+--------+-------------------------------------------------+----------------+"); } private static void appendHexDumpRowPrefix(StringBuilder dump, int row, int rowStartIndex) { if (row < HEXDUMP_ROWPREFIXES.length) { dump.append(HEXDUMP_ROWPREFIXES[row]); } else { dump.append(NEWLINE); dump.append(Long.toHexString(rowStartIndex & 0xFFFFFFFFL | 0x100000000L)); dump.setCharAt(dump.length() - 9, '|'); dump.append('|'); } } public static short getUnsignedByte(ByteBuffer buffer, int index) { return (short) (buffer.get(index) & 0xFF); } }
測(cè)試如下:
package org.example.demo1; import java.nio.ByteBuffer; import static org.example.utils.ByteBufferUtil.debugAll; public class TestByteBufferReadWrite { public static void main(String[] args){ ByteBuffer byteBuffer = ByteBuffer.allocate(10); byteBuffer.put((byte) 0x61);// a debugAll(byteBuffer); byteBuffer.put(new byte[]{0x62,0x63,0x64}); debugAll(byteBuffer); byteBuffer.get(); debugAll(byteBuffer); //切換為讀的狀態(tài) byteBuffer.flip(); byteBuffer.get(); debugAll(byteBuffer); byteBuffer.compact(); debugAll(byteBuffer); } }
運(yùn)行結(jié)果如下:
18:12:55.063 [main] DEBUG io.netty.util.internal.logging.InternalLoggerFactory - Using SLF4J as the default logging framework
+--------+-------------------- all ------------------------+----------------+
position: [1], limit: [10]
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 61 00 00 00 00 00 00 00 00 00 |a......... |
+--------+-------------------------------------------------+----------------+
+--------+-------------------- all ------------------------+----------------+
position: [4], limit: [10]
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 61 62 63 64 00 00 00 00 00 00 |abcd...... |
+--------+-------------------------------------------------+----------------+
+--------+-------------------- all ------------------------+----------------+
position: [5], limit: [10]
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 61 62 63 64 00 00 00 00 00 00 |abcd...... |
+--------+-------------------------------------------------+----------------+
+--------+-------------------- all ------------------------+----------------+
position: [1], limit: [5]
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 61 62 63 64 00 00 00 00 00 00 |abcd...... |
+--------+-------------------------------------------------+----------------+
+--------+-------------------- all ------------------------+----------------+
position: [4], limit: [10]
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 62 63 64 00 00 00 00 00 00 00 |bcd....... |
+--------+-------------------------------------------------+----------------+
Process finished with exit code 0
2.3 ByteBuffer 常見方法
分配空間
可以使用 allocate 方法為 ByteBuffer 分配空間,其它 buffer 類也有該方法
Bytebuffer buf = ByteBuffer.allocate(16);
例子:
package org.example.demo1; import java.nio.ByteBuffer; public class TestByteBufferAllocate { public static void main(String[] args){ System.out.println(ByteBuffer.allocate(16).getClass()); System.out.println(ByteBuffer.allocateDirect(16).getClass()); } }
運(yùn)行結(jié)果如下:
注意:
class java.nio.HeapByteBuffer -java 堆內(nèi)存,讀寫效率低,受到GC的影響 class java.nio.DirectByteBuffer -直接內(nèi)存,讀寫效率高(少一次拷貝),不會(huì)受GC影響,分配的效率低
向 buffer 寫入數(shù)據(jù)
有兩種辦法
- 調(diào)用 channel 的 read 方法
- 調(diào)用 buffer 自己的 put 方法
int readBytes = channel.read(buf);
和
buf.put((byte)127);
從 buffer 讀取數(shù)據(jù)
同樣有兩種辦法
- 調(diào)用channel的write方法
- 調(diào)用buffer自己的get方法
int writeBytes = channel.write(buf);
和
byte b = buf.get();
get 方法會(huì)讓 position 讀指針向后走,如果想重復(fù)讀取數(shù)據(jù)
可以調(diào)用 rewind 方法將 position 重新置為 0
package org.example.demo1; import java.nio.ByteBuffer; import static org.example.utils.ByteBufferUtil.debugAll; public class TestByteBufferRead { public static void main(String[] args){ ByteBuffer buffer = ByteBuffer.allocate(10); buffer.put(new byte[]{'a','b','c','d'}); buffer.flip(); //rewind 從頭開始讀 buffer.get(new byte[4]); debugAll(buffer); System.out.println("===============================rewind================================"); buffer.rewind(); System.out.println((char)buffer.get()); } }
調(diào)用結(jié)果:
+--------+-------------------- all ------------------------+----------------+
position: [4], limit: [4]
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 61 62 63 64 00 00 00 00 00 00 |abcd...... |
+--------+-------------------------------------------------+----------------+
===============================rewind================================
a
或者調(diào)用 get(int i) 方法獲取索引 i 的內(nèi)容,它不會(huì)移動(dòng)讀指針
package org.example.demo1; import java.nio.ByteBuffer; import static org.example.utils.ByteBufferUtil.debugAll; public class TestByteBufferRead { public static void main(String[] args){ ByteBuffer buffer = ByteBuffer.allocate(10); buffer.put(new byte[]{'a','b','c','d'}); buffer.flip(); //get(i) 不會(huì)改變讀索引的位置 System.out.println((char) buffer.get(3)); debugAll(buffer); } }
調(diào)用結(jié)果:
mark 和 reset
mark 是在讀取時(shí),做一個(gè)標(biāo)記,即使 position 改變,只要調(diào)用 reset 就能回到 mark 的位置
package org.example.demo1; import java.nio.ByteBuffer; import static org.example.utils.ByteBufferUtil.debugAll; public class TestByteBufferRead { public static void main(String[] args){ ByteBuffer buffer = ByteBuffer.allocate(10); buffer.put(new byte[]{'a','b','c','d'}); buffer.flip(); //mark & reset //mark 做一個(gè)標(biāo)記,記錄position位置,reset 是將position重置到mark的位置 System.out.println((char) buffer.get()); System.out.println((char) buffer.get()); buffer.mark();//加標(biāo)記,索引2的位置 System.out.println((char) buffer.get()); System.out.println((char) buffer.get()); buffer.reset();//將position重置到索引2 System.out.println((char) buffer.get()); System.out.println((char) buffer.get()); } }
測(cè)試結(jié)果:
a
b
c
d
c
d
注意
rewind 和 flip 都會(huì)清除 mark 位置
字符串與ByteBuffer互轉(zhuǎn)
package org.example.demo1; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import static org.example.utils.ByteBufferUtil.debugAll; public class TestByteBufferString { public static void main(String[] args){ ByteBuffer buffer = ByteBuffer.allocate(16); buffer.put("hello".getBytes()); debugAll(buffer); buffer.flip(); CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer); String charBufferstr = charBuffer.toString(); System.out.println(charBufferstr); //2.Charset ByteBuffer buffer2 = StandardCharsets.UTF_8.encode("hello"); debugAll(buffer2); CharBuffer charBuffer1 = StandardCharsets.UTF_8.decode(buffer2); String buffer1 = charBuffer1.toString(); System.out.println(buffer1); //3.wrap ByteBuffer buffer3 = ByteBuffer.wrap("hello".getBytes()); debugAll(buffer3); CharBuffer charBuffer3 = StandardCharsets.UTF_8.decode(buffer3); String bufferstr3 = charBuffer3.toString(); System.out.println(bufferstr3); } }
輸出:
+--------+-------------------- all ------------------------+----------------+
position: [5], limit: [16]
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 68 65 6c 6c 6f 00 00 00 00 00 00 00 00 00 00 00 |hello...........|
+--------+-------------------------------------------------+----------------+
hello
+--------+-------------------- all ------------------------+----------------+
position: [0], limit: [5]
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 68 65 6c 6c 6f |hello |
+--------+-------------------------------------------------+----------------+
+--------+-------------------- all ------------------------+----------------+
position: [0], limit: [5]
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 68 65 6c 6c 6f |hello |
+--------+-------------------------------------------------+----------------+
hello
Buffer的線程安全
Buffer是非線程安全的
2.4 Scattering Reads
分散讀取,有一個(gè)文本文件parts.txt
onetwothree
使用如下方式讀取,可以將數(shù)據(jù)填充至多個(gè) buffer
package org.example.demo1; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import static org.example.utils.ByteBufferUtil.debugAll; public class TestByteBufferReads { public static void main(String[] args){ try (RandomAccessFile file = new RandomAccessFile("parts.txt", "r")) { FileChannel channel = file.getChannel(); ByteBuffer a = ByteBuffer.allocate(3); ByteBuffer b = ByteBuffer.allocate(3); ByteBuffer c = ByteBuffer.allocate(5); channel.read(new ByteBuffer[]{a, b, c}); a.flip(); b.flip(); c.flip(); debugAll(a); debugAll(b); debugAll(c); } catch (IOException e) { e.printStackTrace(); } } }
結(jié)果:
+--------+-------------------- all ------------------------+----------------+
position: [0], limit: [3]
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 6f 6e 65 |one |
+--------+-------------------------------------------------+----------------+
+--------+-------------------- all ------------------------+----------------+
position: [0], limit: [3]
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 74 77 6f |two |
+--------+-------------------------------------------------+----------------+
+--------+-------------------- all ------------------------+----------------+
position: [0], limit: [5]
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 74 68 72 65 65 |three |
+--------+-------------------------------------------------+----------------+
2.5 Gathering Writes
使用如下方式寫入,可以將多個(gè) buffer 的數(shù)據(jù)填充至 channel
package org.example.demo1; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; public class TestGatheringWrites { public static void main(String[] args){ ByteBuffer b1 = StandardCharsets.UTF_8.encode("hello"); ByteBuffer b2 = StandardCharsets.UTF_8.encode("world"); ByteBuffer b3 = StandardCharsets.UTF_8.encode("你好"); try(FileChannel channel = new RandomAccessFile("words2.txt","rw").getChannel()){ channel.write(new ByteBuffer[]{b1,b2,b3}); }catch (IOException ex){ } } }
輸出結(jié)果:
2.6 黏包半包現(xiàn)象
網(wǎng)絡(luò)上有多條數(shù)據(jù)發(fā)送給服務(wù)端,數(shù)據(jù)之間使用 \n 進(jìn)行分隔 但由于某種原因這些數(shù)據(jù)在接收時(shí),被進(jìn)行了重新組合,例如原始數(shù)據(jù)有3條為
- Hello,world\n
- I'm zhangsan\n
- How are you?\n
變成了下面的兩個(gè) byteBuffer (黏包,半包)
- Hello,world\nI'm zhangsan\nHo
- w are you?\n
現(xiàn)在要求你編寫程序,將錯(cuò)亂的數(shù)據(jù)恢復(fù)成原始的按 \n 分隔的數(shù)據(jù)
public static void main(String[] args) { ByteBuffer source = ByteBuffer.allocate(32); // 11 24 source.put("Hello,world\nI'm zhangsan\nHo".getBytes()); split(source); source.put("w are you?\nhaha!\n".getBytes()); split(source); } private static void split(ByteBuffer source) { source.flip(); int oldLimit = source.limit(); for (int i = 0; i < oldLimit; i++) { if (source.get(i) == '\n') { System.out.println(i); ByteBuffer target = ByteBuffer.allocate(i + 1 - source.position()); // 0 ~ limit source.limit(i + 1); target.put(source); // 從source 讀,向 target 寫 debugAll(target); source.limit(oldLimit); } } source.compact(); }
到此這篇關(guān)于Java NIO三大組件與ByteBuffer深入理解及使用的文章就介紹到這了,更多相關(guān)Java NIO三大組件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Springboot如何實(shí)現(xiàn)代理服務(wù)器
這篇文章主要介紹了Springboot如何實(shí)現(xiàn)代理服務(wù)器問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-06-06Spring如何基于Proxy及cglib實(shí)現(xiàn)動(dòng)態(tài)代理
這篇文章主要介紹了Spring如何基于Proxy及cglib實(shí)現(xiàn)動(dòng)態(tài)代理,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06Java基于Netty實(shí)現(xiàn)Http server的實(shí)戰(zhàn)
本文主要介紹了Java基于Netty實(shí)現(xiàn)Http server的實(shí)戰(zhàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02java final 和instanceof 關(guān)鍵字的區(qū)別
這篇文章介紹了java final 和instanceof 關(guān)鍵字的區(qū)別,有需要的朋友可以參考一下2013-09-09