`
coolxing
  • 浏览: 870226 次
  • 性别: Icon_minigender_1
  • 来自: 北京
博客专栏
9a45b66b-c585-3a35-8680-2e466b75e3f8
Java Concurre...
浏览量:96008
社区版块
存档分类
最新评论

NIO中缓冲区的API介绍

阅读更多

[转载请注明作者和出处,  如有谬误, 欢迎在评论中指正. ]

在java NIO中, 通道是IO传输发生时数据通过的入口, 而缓冲区是数据的来源或目标. 

Buffer是java NIO中定义的所有缓冲区类的基类.

 

Buffer的属性

1. 容量(capacity)

缓冲区能够容纳的数据元素的最大数量, capacity在创建缓冲区时指定, 之后无法改变.

2. 上界(limit)

读模式下, limit表示能读取的最后一个字节; 写模式下, limit等于capacity.

3. 位置(position)

position表示当前索引.

4. 标记(mark)

mark表示一个备忘position.

属性之间的关系:0 <= mark <= position <= limit <= capacity

 

Buffer中与属性相关的API

1. int capacity(). 该方法返回Buffer的容量.

2. clear(). 该方法并不改变Buffer中已有的任何数据, 只是将position设置为0, limit设置为capacity. 一般在向缓冲区写入数据之前调用.

3. flip(). 将Buffer从写模式反转成读模式. 该方法将limit设置为当前position, 然后将position设置为0. 一般在读取缓冲区数据之前调用.

4. boolean hasRemaining(). 当前position和limit之间是否有元素.

5. int limit(). 返回Buffer的limit.

6. limit(int newLimit). 设置Buffer的limit.

7. mark(). 标记当前position. 

8. int position(). 返回当前的position.

9. position(int newPosition). 设置position.

10. int remaining(). 返回position和limit之间的元素个数.

11. reset(). 将position设置为之前mark的位置. 如果mark无效, 将抛出InvalidMarkException异常.

12. rewind(). 将position设置为0, 并抛弃mark. 不改变limit的值. 一般用于重新读取缓冲区中的数据.

 

填充和释放缓冲区

由于Buffer的各个子类的填充和释放缓冲区的方法所需的参数不一致, 所以没有将这些方法抽象到Buffer类中. 以ByteBuffer为例, 其填充和释放缓冲区方法有:

1. byte get(). 相对get操作, 该方法返回当前position处的byte数据, 并将position加1. 如果当前position大于等于limit, get()方法将抛出BufferUnderflowException异常.

2. byte get(int index). 绝对get操作, 该方法返回index处的byte数据, 不position的值. 如果指定的index小于0, 或者大于等于limit, 将方法将抛出IndexOutOfBoundsException异常.

3. get(byte[] dst, int offset, int length). 相对批量get操作, 与多次get操作比较, 该方法具有更高的效率. 如果length大于缓冲区的剩余字节, 将抛出BufferUnderflowException异常. 如果缓冲区存有比数组能容纳的数量更多的数据, 可以使用如下的方式处理:

char[] smallArray = new char[10];
while (buffer.hasRemaining()) {
	int length = Math.min(buffer.remaining(), smallArray.length);
	buffer.get(smallArray, 0, length);
	processData(smallArray, length);
}

4. put(byte b). 相对put操作, 该方法将byte数据存入position处, 并使得position加1. 如果当前position大于等于limit, 该方法将抛出BufferOverflowException异常.

5. put(int index, byte b). 绝对put操作, 该方法将byte数据存入index处, 不改变position的值. 如果指定的index小于0, 或者大于等于limit, 将方法将抛出IndexOutOfBoundsException异常.

6. put(byte[] src, int offset, int length). 相对批量put操作, 与多次put操作比较, 该方法具有更高的效率. 如果length大于缓冲区的剩余字节, 将抛出BufferOverflowException异常.

7. compact(). 该方法将当前position和limit之间的数据拷贝到缓冲区开始部分, 并将position设置为拷贝的数据的个数, limit设置为capacity.

 

创建缓冲区

Buffer的子类如ByteBuffer, CharBuffer等都是抽象类, 提供静态方法创建缓冲区对象, 以ByteBuffer为例:

1. allocate(int capacity). 创建一个指定capacity的ByteBuffer对象. 该方法返回的ByteBuffer对象底层维护了一个byte数组, 数组的length为指定的capacity.

2. wrap(byte[] array). 使用指定的byte数组创建缓冲区. 调用put操作造成的改动会直接影响这个数组, 对数组的改动也会对该ByteBuffer对象可见.

3. wrap(byte[] array, int offset, int length). 与上个方法类似, 但是使用offset初始化ByteBuffer对象的position, 使用length+offset初始化ByteBuffer对象的limit.

ByteBuffer类提供了获取其底层实现数组的方法:

1. boolean hasArray(). 该方法的返回值表明是否可以获取到ByteBuffer对象的底层实现数组. 如果该方法返回true, 则可以安全的调用array()和arrayOffset()方法. 如果缓冲区为只读缓冲区, 则该方法返回false.

2. byte[] array(). 该方法返回ByteBuffer对象的底层实现数组.

3. int arrayOffset(). 该方法返回缓冲区数据在数组中存储的开始位置的偏移量.

 

直接缓冲区

通过缓冲区类的allocateDirect(int capacity)方法可以创建相应的直接缓冲区, 缓冲区对象的isDirect方法的返回值表明该缓冲区是否为直接缓冲区. 直接缓冲区具有更好的IO效率, 同时, 创建和销毁直接缓冲区需要更大的代价. 直接缓冲区使用的内存是通过调用本地操作系统的代码分配的, 而不是在JVM堆栈中分配的, 使用直接缓冲区可能引入平台依赖. 

总之, 使用直接缓冲区需要谨慎权衡: 如果IO效率不是系统的瓶颈, 最好不要使用直接缓冲区.

 

字节顺序

多字节数据的存储顺序在不同的平台上可能不同, 有的平台默认使用big-endian(大端字节顺序), 有的平台则默认使用little-endian(小端字节顺序). big-endian的数据的高位存储在低位地址上, little-endian正好与之相反, 平台的默认字节顺序一般是由硬件决定的.

在java中, 字节顺序由ByteOrder类封装. ByteOrder类只有2个对象(其构造函数是private的)--ByteOrder.BIG_ENDIAN和ByteOrder.LITTLE_ENDIAN, 分别代表大端和小端字节顺序. ByteOrder.nativeOrder()方法返回当前平台的字节顺序.

所有缓冲区类都具有ByteOrder order()方法, 返回缓冲区所使用的字节顺序. ByteBuffer之外的缓冲区类的字节顺序是一个只读属性(一旦创建就确定了, 且无法修改): 通过allocate或wrap方法创建的缓冲区的字节顺序和ByteOrder.nativeOrder()方法的返回值相同, 如果缓冲区是另一个缓冲区的视图, 则视图的字节顺序和原始缓冲区的自己顺序相同.

ByteBuffer类则比较特殊, 可以通过order(ByteOrder bo)方法随时改变ByteBuffer对象的字节顺序, 如果没有指定, ByteBuffer的默认字节顺序为ByteOrder.BIG_ENDIAN.

public static void main(String[] args) {
	// 默认字节顺序为ByteOrder.BIG_ENDIAN
	ByteBuffer bb = ByteBuffer.allocate(4);
	bb.putInt(0xabcd1234);
	bb.flip();
	System.out.println(Integer.toHexString(bb.getInt()));
	// 改变其字节顺序
	bb.order(ByteOrder.LITTLE_ENDIAN);
	bb.flip();
	System.out.println(Integer.toHexString(bb.getInt()));
}
 

缓冲区视图

缓冲区视图和原始缓冲区共享全部或部分数据, 但是具有独立的capacity, limit, position, mark属性.

所有缓冲区类都有以下3个API用于创建其视图, 以CharBuffer为例:

1. CharBuffer duplicate(). 创建一个缓冲区视图, 视图和原始缓冲区共享全部数据, 对一个缓冲区内的数据元素所做的改变会反映在另外一个缓冲区上, 并继承原始缓冲区的direct和readOnly属性. 但是具有独立的capacity, limit, position, mark.

public static void main(String[] args) {
	CharBuffer cb = CharBuffer.allocate(100);
	cb.put("xing");
	CharBuffer view = cb.duplicate();
	// 视图的capacity, limit, position等属性初始值和原始缓冲区相同
	printProperty(cb);
	printProperty(view);

	// 视图继承原始缓冲区的direct和readOnly属性
	System.out.println(view.isDirect());
	System.out.println(view.isReadOnly());
	
	// 但是视图的capacity, limit, position等属性是独立的
	view.put(" zhang");
	printProperty(cb);
	printProperty(view);
	
	// 视图和原始缓冲区共享数据, 修改了视图会影响到原始缓冲区
	cb.position(view.position());
	cb.flip();
	char[] dst = new char[cb.limit()];
	cb.get(dst, 0, dst.length);
	System.out.println(new String(dst));
}

private static void printProperty(CharBuffer cb) {
	System.out.println(cb.position() + "@" + cb.limit() + "@" + cb.capacity());
}

2. CharBuffer asReadOnlyBuffer(). 创建只读缓冲区视图. 只读缓冲区视图不允许使用put操作, 并且其isReadOnly()函数将会返回true. 其余和duplicate()方法返回的缓冲区视图完全相同.

3. CharBuffer slice(). slice方法返回的缓冲区视图和duplicate()方法返回的缓冲区视图区别在于, slice视图只和原始缓冲区共享部分数据元素, slice视图的初始属性值分别为: position是0, limit和capacity为原始缓冲区的limit-position. 其余部分和duplicate视图完全相同.

除了以上3个API, ByteBuffer类提供了其特有的创建视图缓冲区的API:

public abstract CharBuffer asCharBuffer();   
public abstract ShortBuffer asShortBuffer();  
public abstract IntBuffer asIntBuffer();  
public abstract LongBuffer asLongBuffer();   
public abstract FloatBuffer  asFloatBuffer();  
public abstract DoubleBuffer asDoubleBuffer();

由于char, short, int, long, float, double都是多字节数据, 因此ByteBuffer的字节顺序决定了多个字节以怎样的形式组织成一个数据.

 

1
2
分享到:
评论

相关推荐

    java NIO 视频教程

    标准的IO基于字节流和字符流进行操作的,而NIO是基于通道(Channel)和缓冲区(Buffer)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。 Java NIO: Non-blocking IO(非阻塞IO) Java NIO...

    java.nio API详解

    在JDK 1.4以前,Java的IO操作集中在java.io这个包中,是基于流的同步(blocking)API。...从JDK 1.4起,NIO API作为一个基于缓冲区,并能提供异步(non-blocking)IO操作的API被引入。本文对其进行深入的介绍。

    java.nio demo

    Java的IO操作集中在java.io这个包中,是基于流的同步(blocking)API。对于大多数应用来说,这样...从JDK 1.4起,NIO API作为一个基于缓冲区,并能提供异步(non-blocking)IO操作的API被引入。本文对其进行深入的介绍。

    《NIO与Socket编程技术指南》高洪岩.zip

    非常详细地讲解了NIO中的缓冲区、通道、选择器、编码,以及使用Socket技术实现TCP/IP和UDP编程,细化到了演示全部SocketOption的特性,这对理解基于NIO和Socket技术为基础所开发的NIO框架是非常有好处的,本书以案例...

    java nio.doc

     java.nio 包定义了缓冲区类,这些类用于所有 NIO API。java.nio.charset 包中定义了字符集 API,java.nio.channels 包中定义了信道和选择器 API。每个子包都具有自己的服务提供程序接口 (SPI) 子包,SPI 子包的...

    javasnmp源码-nio-learn:JavaNIO使用示例,NIO的使用,TCP,UDP的简单示例

    nio缓冲区buffer 简介 Buffer是数据的容器,在nio中负责数据的存取,java为不同数据类型提供了相对应的缓冲区类型 如:ByteBuffer、CharBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer 、DoubleBuffer 等...

    JAVA_API1.6文档(中文)

    java.nio 定义作为数据容器的缓冲区,并提供其他 NIO 包的概述。 java.nio.channels 定义了各种通道,这些通道表示到能够执行 I/O 操作的实体(如文件和套接字)的连接;定义了用于多路复用的、非阻塞 I/O 操作的...

    基于Nio的多人聊天Demo

    NIO即non-blocking IO,顾名思义是一种非阻塞模型。...3、Buffer,缓冲区。Buffer底层是一个数组,供Channel实现对数据的读写。Buffer的position、limit、capacity分别指当前索引、读/写上限索引、数组容量。

    Java 1.6 API 中文 New

    java.nio 定义作为数据容器的缓冲区,并提供其他 NIO 包的概述。 java.nio.channels 定义了各种通道,这些通道表示到能够执行 I/O 操作的实体(如文件和套接字)的连接;定义了用于多路复用的、非阻塞 I/O 操作的...

    JavaAPI1.6中文chm文档 part1

    java.nio 定义作为数据容器的缓冲区,并提供其他 NIO 包的概述。 java.nio.channels 定义了各种通道,这些通道表示到能够执行 I/O 操作的实体(如文件和套接字)的连接;定义了用于多路复用的、非阻塞 I/O 操作的...

    Java NIO 网络编程初探

    NIO支持面向缓冲区的、基于通道的IO操作。能够更加高效的进行IO操作。NIO同样拥有文件读写,网络通信等IO操作,今天我们来看看NIO中的TCP网络通信的使用方法。 2. Java NIO 三大核心 Java NIO 有三大核心要素:...

    Java流NIO

    NIO于原来的IO有相同的功能,但是他们之间的使用方式是完全不同的,NIO是面向缓冲区,面向通道的的IO操作,NIO拥有更加高效的进行文件读写。 另外NIO在网络编程可以是一个无阻塞的IO交互,可以大大提升Socket交互的...

    netty学习:bio,nio到netty各种使用案例,包括基础使用案例,各api使用方法,零拷贝,websocket,群聊,私聊,编码,解码,自定义协议,protobuf等使用案例,rpc服务器,客户端等等学习

    jdk原生nio的缓冲区使用 jdk原生的nio channel使用 jdk原生的nio网络编程 jdk原生的React器编程模型(使用选择器)聊天室 零拷贝使用案例文件上传 netty的演进模型(3中React器使用案例线程池的添加时机)。 ...

    java api最新7.0

    java.nio 定义作为数据容器的缓冲区,并提供其他 NIO 包的概述。 java.nio.channels 定义了各种通道,这些通道表示到能够执行 I/O 操作的实体(如文件和套接字)的连接;定义了用于多路复用的、非阻塞 I/O 操作的...

    复习 J2SE基本内容 IO NIO AIO 的区别

    IO 是面向流的,NIO 是面向缓冲区的 IO 流是阻塞的,NIO 流是不阻塞的 IO 没有选择器,NIO 有选择器     从编程模式上来看AIO相对于NIO的区别在于,NIO需要使用者线程不停的轮询IO对象,来确定是否有数据准备好...

    JavaAPI中文chm文档 part2

    java.nio 定义作为数据容器的缓冲区,并提供其他 NIO 包的概述。 java.nio.channels 定义了各种通道,这些通道表示到能够执行 I/O 操作的实体(如文件和套接字)的连接;定义了用于多路复用的、非阻塞 I/O 操作的...

    NIO初体验 ╥﹏╥

    文章目录NIO与IO的区别阻塞和非阻塞,同步和异步的概念Buffer(缓冲区)Channel(通道)Selector(选择器) NIO的三个主要组成部分:Buffer(缓冲区)、Channel(通道)、Selector(选择器) NIO与IO的区别 Java NIO...

    通俗易懂的NIO教程(含配套资料)

    本教程为授权出品教程 本Java视频主要讲授 Java NIO 的使用及原理。Java NIO 是 JDK 1.4 发布的一套全新的IO API(New IO ...NIO支持面向缓冲区的、基于通道的IO操作。并且,NIO将以更加高效的方式进行文件的读写操作。

    java jdk-api-1.6 中文 chmd

    java.nio 定义作为数据容器的缓冲区,并提供其他 NIO 包的概述。 java.nio.channels 定义了各种通道,这些通道表示到能够执行 I/O 操作的实体(如文件和套接字)的连接;定义了用于多路复用的、非阻塞 I/O 操作的...

Global site tag (gtag.js) - Google Analytics