NIO的BufferByteBuffer
1 NIO中的Buffer
1.1 ByteBuffer详解(最常用的)
1.1.1 ByteBuffer 是抽象类,他的主要实现为 HeapByteBuffer 和 MappedByteBuffer
1. HeapByteBuffer 堆ByteBuffer JVM内的堆内存 ---> 读写操作 效率低 会收到GC影响
2. MappedByteBuffer(DirectByteBuffer) OS内存 ---> 读写操作 效率高 不会收到GC影响 。 不主动析构,会造成内存的泄露
3. 什么是内存溢出,什么是内存泄漏 ?
内存溢出:你真正操作的数据量集已经超出了我实际的内存 oom 100m的内存,实际操作的过程中 真实内存为120m 一次性的读取到内存
内存泄露:100m的内存,但实际处理的数据为80m,内存就不够了 20m的内存就给泄露了
内存泄露的原因:1、没有主动析构,主动释放内存 2、内存碎片
1.1.2 获取方式
1. ByteBuffer.allocate(10);//一旦分配空间,不可以动态调整
2. encode()
1.1.3 核心结构
ByteBuffer是一个类似数组的结构,整个结构中包含三个主要的状态
1. Capacity
buffer的容量,类似于数组的size
2. Position
buffer当前缓存的下标,在读取操作时记录读到了那个位置,在写操作时记录写到了那个位置。从0开始,每读取一次,下标 1
3. Limit
读写限制,在读操作时,设置了你能读多少字节的数据,在写操作时,设置了你还能写多少字节的数据
所谓的读写模式,本质上就是这几个状态的变化。主要有Position和Limit联合决定了Buffer的读写数据区域。
1.1.4 代码演示
public class TestNio {
@Test
public void testStatus() {
ByteBuffer allocate = ByteBuffer.allocate(10);
System.out.println("allocate.capacity() = " allocate.capacity()); // 10
System.out.println("allocate.position() = " allocate.position()); // 0
System.out.println("allocate.limit() = " allocate.limit()); // 10
}
@Test
public void testStatus1() throws IOException {
// 写模式
ByteBuffer buffer = ByteBuffer.allocate(10);
byte[] bytes = {'a', 'b', 'c', 'd'};
buffer.put(bytes);
System.out.println("buffer.capacity() = " buffer.capacity()); // 10
System.out.println("buffer.position() = " buffer.position()); // 4
System.out.println("buffer.limit() = " buffer.limit()); // 10
}
@Test
public void testStatus3() throws IOException {
// 写模式
ByteBuffer buffer = ByteBuffer.allocate(10);
byte[] bytes = {'a', 'b', 'c', 'd'};
buffer.put(bytes);
// 转换成读模式
buffer.flip();
System.out.println("buffer.capacity() = " buffer.capacity()); // 存储的容量 10
System.out.println("buffer.position() = " buffer.position()); // 0
System.out.println("buffer.limit() = " buffer.limit()); // 4
}
// flip把buffer转换成读模式为啥position为0呢 flip的源码一看就知道了
public final Buffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
@Test
public void testStatus4() throws IOException {
// 写模式
ByteBuffer buffer = ByteBuffer.allocate(10);
byte[] bytes = {'a', 'b', 'c', 'd'};
buffer.put(bytes);
// 写模式
buffer.clear();
// buffer.compact(); 写模式 把之前没读完的数据往移动,继续写入后面的数据
System.out.println("buffer.capacity() = " buffer.capacity()); // 10
System.out.println("buffer.position() = " buffer.position()); // 0
System.out.println("buffer.limit() = " buffer.limit()); // 10
}
//clear转成写模式为啥position值为0,为啥我把50行注释掉 position为4呢 clear源码
public final Buffer clear() {
position = 0;
limit = capacity;
mark = -1;
return this;
}
// 看完就是知道 调用clear方法会把position置为0也就是从头开始写入,但是buffer里面的数据不会消失,还是可以读取到达
// 如果写入的话会把之前的数据给覆盖掉而且。
@Test
public void testStatus5() {
// 创建一个buffer默认是写模式
ByteBuffer buffer = ByteBuffer.allocate(10);
// 往里面存储数据
buffer.put(new byte[]{'a', 'b', 'c', 'd'});
// 读模式
buffer.flip();
System.out.println("buffer.get() = " (char) buffer.get());
System.out.println("buffer.get() = " (char) buffer.get());
System.out.println("buffer.capacity() = " buffer.capacity()); // 10
System.out.println("buffer.position() = " buffer.position()); // 2
System.out.println("buffer.limit() = " buffer.limit()); // 4
System.out.println("==============================");
// 变写模式了 写模式 把之前没读完的数据往移动,继续写入后面的数据
buffer.compact();
System.out.println("buffer.capacity() = " buffer.capacity()); // 10
System.out.println("buffer.position() = " buffer.position()); // 2
System.out.println("buffer.limit() = " buffer.limit()); // 4
}
}
1.1.5 总结
最后总结一下
写入Buffer数据之前要设置写模式
1. 写模式
1. 新创建的Buffer自动是写模式
2. 调用了clear,compact方法
读取Buffer数据之前要设置读模式
2. 读模式
1. 调用flip方法
1.2 核心API
-
buffer中写入数据[写模式 创建一个bytebuffer ,clear(),compact()]
1. channel的read方法 channel.read(buffer) 2. buffer的put方法 buffer.put(byte) buffer.put((byte)'a').. buffer.put(byte[])
-
从buffer中读出数据
1. channel的write方法 2. buffer的get方法 //每调用一次get方法会影响,position的位置。 3. rewind方法(手风琴),可以将postion重置成0 ,用于复读数据。 4. mark&reset方法,通过mark方法进行标记(position),通过reset方法跳回标记,从新执行. 5. get(i) 方法,获取特定position上的数据,但是不会对position的位置产生影响。
1.2.1 代码演示 rewind()方法
public static void main(String[] args) {
ByteBuffer buffer = ByteBuffer.allocate(10);
buffer.put(new byte[]{'a', 'b', 'c', 'd'});
// 读模式
buffer.flip();
while (buffer.hasRemaining()) {
System.out.println("buffer.get() = " (char) buffer.get());
}
System.out.println("================");
buffer.rewind(); // 把position的值设置为0
while (buffer.hasRemaining()) {
System.out.println("buffer.get() = " (char) buffer.get());
}
}
1.2.2 代码演示 mark&reset方法
public static void main(String[] args) {
ByteBuffer buffer = ByteBuffer.allocate(10);
buffer.put(new byte[]{'a', 'b', 'c', 'd'});
buffer.flip();
System.out.println("buffer.get() = " (char) buffer.get());// a
System.out.println("buffer.get() = " (char) buffer.get());// b
buffer.mark(); // 打一个标记
System.out.println("buffer.get() = " (char) buffer.get());// c
System.out.println("buffer.get() = " (char) buffer.get());// d
buffer.reset(); // 回到打标记position位置
System.out.println("buffer.get() = " (char) buffer.get());// c
System.out.println("buffer.get() = " (char) buffer.get());// d
}
// 把position的值赋值给了 mark
public final Buffer mark() {
mark = position;
return this;
}
// reset源码 你在那里调用的mark方法,他就回到哪里
public final Buffer reset() {
int m = mark;
if (m < 0)
throw new InvalidMarkException();
position = m;
return this;
}
1.3 字符串的操作
1.3.1 字符串存储到Buffer中
-
代码演示
// 默认是写模式 ByteBuffer buffer = ByteBuffer.allocate(10); buffer.put("dongdong".getBytes()); // 转换成读模式 buffer.flip(); while (buffer.hasRemaining()) { System.out.println("buffer.get() = " (char)buffer.get()); } // 又转换成写模式 buffer.clear(); // ByteBuffer buffer = Charset.forName("UTF-8").encode("dongdong"); 1、encode方法自动 把字符串按照字符集编码后,存储在ByteBuffer. 2、自动把ByteBuffer设置成读模式,且不能手工调用flip方法。 ByteBuffer buffer = StandardCharsets.UTF_8.encode("dongdong"); while (buffer.hasRemaining()) { System.out.println("buffer.get() = " (char) buffer.get()); } buffer.clear(); 1、encode方法自动 把字符串按照字符集编码后,存储在ByteBuffer. 2、自动把ByteBuffer设置成读模式,且不能手工调用flip方法。 ByteBuffer buffer = ByteBuffer.wrap("dongdong".getBytes()); while (buffer.hasRemaining()) { System.out.println("buffer.get() = " (char) buffer.get()); } buffer.clear();
1.3.2 问题 为什么 ByteBuffer buffer = StandardCharsets.UTF_8.encode(“dongdong”) 时候 在调用 buffer.flip()方法读不到
ByteBuffer buffer = StandardCharsets.UTF_8.encode("dongdong");
System.out.println("buffer.capacity() = " buffer.capacity()); // 8
System.out.println("buffer.position() = " buffer.position()); // 0
System.out.println("buffer.limit() = " buffer.limit()); // 8
buffer.flip();
System.out.println("buffer.capacity() = " buffer.capacity()); // 8
System.out.println("buffer.position() = " buffer.position()); // 0
System.out.println("buffer.limit() = " buffer.limit()); // 0
while (buffer.hasRemaining()) {
System.out.println("buffer.get() = " (char) buffer.get());
}
buffer.clear();
// flip()方法的源码看就知道了
public final Buffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
// 在我没有调用的时候我此时的 capacity 唯我传入的值的长度为8,而我的position 是我写入或者读取的下标 此时我还没有写入 所以为0 而我的limit的值是我此时能读或者能写入多少,此时我及没读又没写所以和我的传入值得长度一样为8
// 在我调用buffer.flip()方法得时候 通过源码可以看出 上来就把position赋值给了limit都等于0 肯定是读取不到数据的。
// 所以在使用ByteBuffer buffer = StandardCharsets.UTF_8.encode("dongdong") 或者 ByteBuffer buffer = Charset.forName("UTF-8").encode("dongdong") 这些api的时候 一定记得此时已经是读模式了,不需要再转成读模式了切记~~
1.3.3 Buffer中的数据转换成字符串
-
代码演示
ByteBuffer buffer = ByteBuffer.allocate(10); buffer.put("咚".getBytes()); buffer.flip(); CharBuffer result = StandardCharsets.UTF_8.decode(buffer); System.out.println("result.toString() = " result.toString());
1.4 粘包与半包
1.4.1 概念
- 粘包:在传输数据的过程中,在一条消息中读取到了另一条的部分数据。
- 半包:在传输数据的过程中,只接收到了部分数据,而非完整的数据。
1.4.2 代码
- \n 作为分割符,进行行的区分。
- compact方法进行处理,把第一次没有读取完的数据,向前移动和后面的内容进行整合。
public static void main(String[] args) {
ByteBuffer buffer = ByteBuffer.allocate(50);
buffer.put("Hi DongDong\nl love y".getBytes());
doLineSplit(buffer);
// buffer.put("ou\nDo you like me?\n".getBytes());
}
// byteBuffer接收的数据 \n
private static void doLineSplit(ByteBuffer buffer) {
buffer.flip();
for (int i = 0; i < buffer.limit(); i ) {
if (buffer.get(i) == '\n') {
// 这里为什么要这样定义长度呢?
// 是为了让buffer的空间不那么大造成资源的浪费
int len = i 1 - buffer.position();
ByteBuffer target = ByteBuffer.allocate(len);
for (int j = 0; j < len; j ) {
// 这里不get的话上面的取的得position的只就为0
target.put(buffer.get());
}
// 读模式
target.flip();
System.out.println("StandardCharsets.UTF_8.decode(target).toString() = " StandardCharsets.UTF_8.decode(target).toString());
}
}
// 写模式,把之前没读完的数据往移动,继续写入后面的数据
buffer.compact();
}
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhgjefek
系列文章
更多
同类精品
更多
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01 -
怎样阻止微信小程序自动打开
PHP中文网 06-13