`
airu
  • 浏览: 270775 次
  • 性别: Icon_minigender_1
  • 来自: 云南
社区版块
存档分类
最新评论

Java-NIO-缓冲区基础

 
阅读更多
NIO中,有自己设计的一套缓冲区系统。
使用I/O,离不开缓冲区,高效的缓冲区,往往起到事半功倍的效果。
下面就看看NIO中的缓冲区是如何设计的。
首先,我们是针对概念来说明缓冲区的属性。缓冲区的四个属性:
1、容量(Capacity)
这是一个缓冲区被创建后的固定属性,不可改变。代表该缓冲区的最大容量。
2、上界(Limit)
这个表明当前缓冲区中的元素计数。
3、位置(Position)
这是下一个要存取的元素索引(下标)。使用get,put方法
4、标记(Mark)
一个用来备忘的设置。例如调用mark()则使得mark = position。在调用reset()时,position = mark。

下面是这四个属性的大小比较
0< =mark <= position <= limit <= capacity

那么这几个属性扮演什么样的角色呢?
我们通过函数来一一说明。有了缓冲区,我们还得有各种各样的针对缓冲区的操作。
package java.nio;
public abstract class Buffer {
public final int capacity();
public final int position();
public final Buffer position(int newPositio);
public final int limit()
public final Buffer limit(int newLimit);
public final Buffer mark();
public final Buffer reset();
public final Buffer clear();
public final Buffer flip();
public final Buffer rewind();
public final int remaining();
public final boolean hasRemaining();
public abstract boolean isReadOnly(); 
}

这里注意,上面的代码只是为了说明Buffer的操作。实际中可能很多方法有实现。
注意这些API的设计中,例如, mark()返回的是一个Buffer,clear()返回的也是一个Buffer,这样设计,是为了级联操纵。例如
buffer.mark().clear().position(0);

当然如果太长了,也是不好看的。
这里还有一个方法需要注意,isReadOnly()这说明,并非所有的缓冲区都是可以写的。所以如果这个方法返回true,那么,这个缓冲区就不能写了。

接下来开始说具体方法:
1、存取。
存取操作是通过get和put方法实现的。
public abstract class ByteBuffer
extends Buffer implements Comparable {
// This is a partial API listing 
public abstract byte get();
public abstract byte get(int index); 
public abstract ByteBuffer put(byte b); 
public abstract ByteBuffer put(int index, byte b); 
}

这里需要注意的是,如果存取中的index参数超出限制,那么,是会抛出IndexOutOfBoundsException。如果是put超出,那么将抛出BufferOverflowException异常。

2、填充。
填充主要使用put方法。这里的put要注意的是,如果是ByteBuffer不能这样使用buffer.put('H')
因为我们put的并不是char类型,而必须是byte,所以要强制转换 buffer.put((byte)'H');
还有,put可以使用索引下标。例如:buffer.put(0,(byte)'H')。如果不适用下标索引,那么put到的都是放在position位置。

3、翻转:
在缓冲区填充满以后,我们需要通知使用者,这要如何做呢?
实际上,limit的引入,就是解决这个问题的,使用者将从position开始,到limit的元素作为读取元素。我们可以这样做。buffer.limit(buffer.position()).position(0);
当然,这里还有一个函数可以更方便  buffer.flip();
这里还有一个函数和flip类似,rewind,这个函数不影响limit,只是把position重置为0,意思就是重读。如果连续两次flip,那么我们得到的就是一个position和limit都为0的缓冲区,对这样的缓冲区使用get,是会抛出BufferUnderflowException异常。

4、释放
缓冲区的基本操作都有了。我们可以循环读取缓冲区的数据,但是如何知道到了上界呢?
函数 hasRemaining()  和 remaining()就是这样的函数。我们可以这样使用:
int i = 0;
while(buffer.hasRemaining()){
    array[i++] = buffer.get();
    ...
}
or
int count = buffer.remaining();
for(int i = 0 ; i < count; i++){
    array[i] = buffer.get();
}

当然这样的效率都不会很高。
注意:缓冲区并不保证并发。
当我们使用完缓冲区,就可以清空了。使用 clear函数。
clear函数并不改变元素。只是将 limit = capacity; position=0而已。

5、压缩
compact函数。
这个压缩很就是把已经读过的数据抛弃,使用后面的数据覆盖(移动至索引0),并且把limit设置为capacity。
压缩类似于先进先出的FIFO队列。压缩完成后,注意position移动到了未处理数据之后,等待继续填充。position也就是移动的元素的个数。
这里比较不好理解。如果画个图,可能就容易理解了。
压缩前的缓冲区



调用 buffer.compact();



这里需要分清楚的就是,之前我们是在读取数据,当读了部分数据以后,调用压缩函数,此时,就变成了填充数据,如果要继续读,那么需要调用flip函数。

6、标记
mark函数做标记,reset函数恢复标记。如果没有调用mark,也就是说mark是undefined,那么会抛出InvalidMarkException异常。
注意一些函数调用会清除标志,例如rewind,clear,flip
看到这里,觉得还是蛮复杂。相互之间的耦合度比较高。所以最好自己写代码弄清楚。

7、比较
这里缓冲区的比较,实际上是比较当前位置,到上界的元素是否相同。
注意除了equals还有compareTo函数。compareTo函数必须是两个相同的缓冲区,否则会抛出异常ClassCastException,而equals只是返回false。

8,批量移动

对于之前的单个元素赋值,可能觉得略显繁琐。这里提供了一些批量移动操作
public abstract class CharBuffer
extends Buffer implements CharSequence, Comparable {

// This is a partial API listing
public CharBuffer get(char [] dst);
public CharBuffer get(char [] dst, int offset, int length);
public final CharBuffer put(char[] src);
public CharBuffer put(char [] src, int offset, int length);
public CharBuffer put(CharBuffer src);
public final CharBuffer put(String src);
public CharBuffer put(String src, int start, int end);
}


这里提供的函数get,就可以获取我们所需的缓冲区中的内容。
buffer.get(myArray)
等价于
buffer.get(myArray,0,myArray.length)

但是,需要注意的是,如果数组元素太大,而我们又没有指定大小,意味着缓冲区要填充满数组,但是缓冲区太小,这样,就会抛出BufferUnderflowException异常。为了不抛出这样的异常,我们可以这样做:

char [] bigArray = new char[1000];
int length = buffer.remaining();
buffer.get(bigArray,0,length);
...


如果说我们的数组很小,而缓冲区很大,那么我们可以这么做:
char[] smallArray  = new char[10];
while(buffer.hasRemaining()){
    int length = Math.min(buffer.remaining(),smallArray.length);
    buffer.get(smallArray,0,length);
    ...
}


对于put函数,也是类似。
buffer.put(myArray)  等价于 buffer.put(myArray,0,myArray.length)
当然,如果我们往缓冲区put大于容量的数组,也会抛出BufferOverflowException异常来的。所以,我们可以类似的这样做:
while(srcBuffer.hasRemaining()){
  dstBuffer.put(srcBuffer.get());
}


对于put来说,我们还可以存入string,虽然string和char不同,但是为了方便
我们可以
buffer.put(myString)  等价于 buffer.put(myString,0,myString.length)






  • 大小: 9.8 KB
  • 大小: 10.1 KB
0
0
分享到:
评论

相关推荐

    Java-NIO非阻塞服务器示例.docx

    ByteBuffer是NIO包中的一个缓冲区机制,用于存储IO操作的数据。ByteBuffer可以分配固定大小的缓冲区,用于存储数据,以便高效地进行IO操作。 六、SelectorServer类 SelectorServer类是一个示例服务器端类,用于...

    JAVA-NIO-DEMO

    2. **缓冲区(Buffers)**:缓冲区是数据操作的基本单位,所有对数据的操作都是通过缓冲区进行的。它们提供了一种在通道与应用程序之间存储数据的方式。 3. **选择器(Selectors)**:选择器允许单个线程监控多个...

    Java-NIO-Netty框架学习

    3. **高性能的缓冲区**:Netty使用ByteBuf作为缓冲区,相较于Java原生的ByteBuffer,提供了更丰富的操作接口和更优秀的性能。 4. **强大的编码解码器**:Netty提供了一系列预定义的编解码器,用于处理各种常见协议...

    Java NIO实战开发多人聊天室

    01-Java NIO-课程简介....17-Java NIO-Buffer-缓冲区分片.mp4 18-Java NIO-Buffer-只读缓冲区.mp4 19-Java NIO-Buffer-直接缓冲区.mp4 21-Java NIO-Selector-概述.mp4 23-Java NIO-Selector-示例代码(客户端).mp4 24

    httpcore-nio-4.3.jar包

    NIO的核心概念包括通道(Channel)、缓冲区(Buffer)和选择器(Selector)。通道可以读写数据,缓冲区用于临时存储数据,选择器则用于监听多个通道的事件,实现了多路复用,从而实现非阻塞I/O。 3. **HttpCore NIO...

    java-nio.rar_NIO_java nio

    传统的Java I/O基于字节流和字符流,而NIO则提供了通道(Channels)和缓冲区(Buffers)的概念,以及非阻塞I/O操作的能力。本资料"java-nio.rar"主要探讨的是如何使用Java NIO实现异步连接池,这在高并发场景下尤其...

    java-NIO-demo

    Java NIO提供了多种类型的缓冲区,如ByteBuffer、CharBuffer、IntBuffer等,对应不同的数据类型。 选择器(Selector)是NIO中的多路复用机制。通过一个选择器,我们可以监控多个通道的状态,例如是否有数据可读、可...

    JAVA-NIO程序设计完整实例

    - **缓冲区(Buffer)**: 缓冲区是NIO中的另一个关键组件,用于存储数据。Java提供了多种类型的Buffer,如ByteBuffer、CharBuffer、IntBuffer等,它们都继承自Buffer抽象类。 - **选择器(Selector)**: 选择器...

    java-nio.rar_java nio_nio 对象实例化

    传统的Java IO基于流和缓冲区,而NIO则引入了通道(Channel)和选择器(Selector)的概念,使得多路复用变得更加便捷。 在标题中提到的“java-nio.rar_java nio_nio 对象实例化”,我们可以理解为这个压缩包中包含...

    Java-NIO-Programming-Cookbook(含源码)

    通过这本书和提供的源码,你可以深入了解Java NIO的工作原理,学习如何有效地利用直接缓冲区进行高性能的I/O操作,并通过实践来优化自己的代码。在实际项目中,合理运用这些知识可以显著提升Java应用的I/O性能。

    java-NIO 指南

    而NIO基于通道和缓冲区,是面向缓冲区的,可以是非阻塞的。 - IO适合简单的数据传输,而NIO更适合处理大量的并发连接,如服务器端的网络编程。 总的来说,Java NIO提供了一种更加灵活、高效的I/O模型,尤其适用于...

    java-NIO-入门教程.docx

    缓冲区可以是直接缓冲区或非直接缓冲区,直接缓冲区是使用本机内存的缓冲区,非直接缓冲区是使用 Java 堆中的缓冲区。 通道是 NIO 中的另一个核心对象,是对原 I/O 包中的流的模拟。所有的 I/O 操作都需要通过通道...

    无涯教程(LearnFk)-Java-Nio教程离线版.pdf

    在Java NIO中有几个核心概念,包括缓冲区(Buffers)、通道(Channels)、选择器(Selectors)以及NIO中使用的字符编码和解码器。 缓冲区(Buffers):在Java NIO中,数据被读入或写出到通道时,数据必须先进入缓冲...

    yubo-java-nio:java nio的学习项目

    yubo-java-nioNIO 直接缓冲区 VS 非直接缓冲区直接缓冲区1、直接缓冲区最适合I/O 2、创建成本比非直接缓冲区高 3、直接缓冲区使用的内存是通过调用原生的、操作系统特定的代码来分配的 4、内存存储区域不受限制垃圾...

    talent-nio3.0.0备份

    在Java中,NIO主要由java.nio包及其子包组成,包含通道(Channels)、缓冲区(Buffers)和选择器(Selectors)等核心组件。 【标签】"源码"和"工具"表明这个压缩包可能包含了项目的源代码以及一些辅助开发或分析的...

    Java语言基础教程-Java NIO流篇2

    缓冲区是NIO的核心,它允许我们批量处理数据,提高了效率。 接下来,我们来看NIO的另一重要概念——通道(Channel)。通道是连接到I/O设备(如文件、网络套接字等)的桥梁,它可以读取或写入数据。通道是双向的,...

    Java-NIO与IO的区别和比较.doc

    NIO的主要特点包括非阻塞I/O、字符转换、缓冲区以及通道等。 1. **非阻塞I/O**:传统的Java IO基于流,通常是阻塞的,意味着在进行读写操作时,如果数据未准备好,线程会被阻塞直到数据可用。而NIO的非阻塞模式允许...

Global site tag (gtag.js) - Google Analytics