上一篇讲了基本的缓冲区概念,以及NIO的缓冲区属性以及部分操作。
现在让我们在看看缓冲区的一些细节。
1、缓冲区的创建
之前也许你已经看到了。我们使用alocate方法创建一个缓冲区。下面是一个创建方法集合。
public abstract class CharBuffer
extends Buffer implements CharSequence, Comparable {
// This is a partial API listing
public static CharBuffer allocate(int capacity);
public static CharBuffer wrap (char [] array);
public static CharBuffer wrap (char [] array, int offset, int length);
public final boolean hasArray( )
public final char [] array( )
public final int arrayOffset( )
}
注意,这些都是静态方法。
wrap方法使用一个现有的数组作为缓冲区备份。这里很重要,因为,其实缓冲区都需要这样一个真正存放数据的地方,我们可以看看如果allocate方法
/**
* Allocates a new byte buffer.
*
* <p> The new buffer's position will be zero, its limit will be its
* capacity, and its mark will be undefined. It will have a {@link #array
* </code>backing array<code>}, and its {@link #arrayOffset </code>array
* offset<code>} will be zero.
*
* @param capacity
* The new buffer's capacity, in bytes
*
* @return The new byte buffer
*
* @throws IllegalArgumentException
* If the <tt>capacity</tt> is a negative integer
*/
public static ByteBuffer allocate(int capacity) {
if (capacity < 0)
throw new IllegalArgumentException();
return new HeapByteBuffer(capacity, capacity);
}
HeapByteBuffer又是什么呢?继续跟踪
HeapByteBuffer(int cap, int lim) { // package-private
super(-1, 0, lim, cap, new byte[cap], 0);
/*
hb = new byte[cap];
offset = 0;
*/
}
这里解释下,HeapByteBuffer是ByteBuffer的一个实现类。她直接使用父类的构造函数来初始化。super第一个参数是mark,-1表示undefined,然后是position,然后是limit,然后是capacity,接下来就是一个真正存放的数组,最后是数组的偏移。
这下可以理解了,缓冲区实际上是由缓冲区的几个属性、操作和一个存放数据的数组(内存)组成的。我们如果改变数组,或者是通过缓冲区改变数组,都会影响双方的。
例如 CharBuffer charBuffer = CharBuffer.wrap(myArray,12,42)
这样的操作,设置了初始的position和limit。我们可以使用clear函数,然后再开始填充缓冲区,这里的缓冲区,并不是从12到42,而是从0到myArray.length
这里,我们还应该看到一个函数,hasArray(),这说明并非所有的缓冲区都包含一个数组,那么什么样的缓冲区不包含数组呢?答案是直接缓冲区。通过allocate和wrap创建的缓冲区都是间接的。直接缓冲区我们待会儿说。但是无论是直接还是间接,事实上缓冲区都必须又一个存放数据的地方。
如果是直接缓冲区,我们是不能获取到数组的,所以array方法不能再直接缓冲区中调用,否则抛出UnsupportedOperationException异常。当然,如果是只读的缓冲区,我们也不能调用array方法或者是arrayOffset方法。
arrayOffset方法是返回作为备份数组的起始下标。这就说明,并非一个数组必须是所有元素都在缓冲区内,而是可以拆分的。但是这里还没讲到,所以,以上提到的缓冲区,如果使用arrayOffset方法,返回都是0.
为了方便,我们的CharBuffer提供了几个方法
public abstract class CharBuffer
extends Buffer implements CharSequence, Comparable {
// This is a partial API listing
public static CharBuffer wrap (CharSequence csq)
public static CharBuffer wrap (CharSequence csq, int start, int end)
}
我们可以这样用。
CharBuffer charBuffer = CharBuffer.wrap ("Hello World");
这对于字符集码和正则表达式处理都是很方便的。
2、复制缓冲区
这里的复制,仅仅是复制缓冲区,而不是数据。
public abstract class CharBuffer
extends Buffer implements CharSequence, Comparable {
// This is a partial API listing
public abstract CharBuffer duplicate( );
public abstract CharBuffer asReadOnlyBuffer( );
public abstract CharBuffer slice( );
}
这三个函数可以完成复制功能。前两个顾名思义,容易理解,最后一个slice,其实也很容易理解,分割缓冲区,这时,起决于position和limit,新的缓冲区将针对原来的数据中的position到limit之间的数据作为新的缓冲区数据。这时,arrayOffset方法返回的就是position的位置了。
例如:要创建一个映射到数组位置12-20(9个元素)的buffer对象:
char[] myBuffer = new char[100];
CharBuffer cb = CharBuffer.wrap(myBuffer);
cb.position(12).limit(21);
CharBuffer sliced = cb.slice();
3、字节缓冲区
虽然我们有很多缓冲区类型,除了boolean型都有一一对应。
但是,ByteBuffer作为一个基本的缓冲区,其作用非同寻常。我们的通道只和字节缓冲区打交道。并且,其他的缓冲区和字节缓冲区都可以转换。
说到这里,我们就不得不理解计算机如何存储数据了。
实际上,内存是按照字节为单位来存放数据的。现在最流行的便是8个位一个字节。我们熟悉的是int占用4个字节,这样,这四个字节如何排列,又成为一个问题。
这个问题已经是硬件问题了,因为不同的硬件有不同的表现。
对于intel处理器,一般都是小端(little endian)
而对于摩托罗拉,Sun的处理器,一般都是大端(big endian)
还有网络字节顺序实际上也是大端的。
那么这两者有什么区别呢?
假设 一个int值 0x01234567 (注意是16进制),内存从左到右增长。地址左小右大
那么他在大端的存放 就类似 : 0x01 0x23 0x45 0x67
那么他在小端的存放,就应该是: 0x67 0x45 0x23 0x01
所以我们的字节操作中有了顺序问题。
package java.nio;
public final class ByteOrder
{
public static final ByteOrder BIG_ENDIAN;
public static final ByteOrder LITTLE_ENDIAN;
public static ByteOrder nativeOrder();
public String toString();
}
在JVM中,实际上我们是使用大端的。
看看关于Order的操作。
public abstract class ByteBuffer extends Buffer
implements Comparable {
// This is a partial API listing
public final ByteOrder order();
public final ByteBuffer order(ByteOrder bo);
}
order返回系统使用的字节顺序。注意如果系统字节顺序和JVM不一致,可能性能上会有些许慢。
现在知道了字节顺序,那么字节缓冲区转换为其他缓冲区,也就清楚了。
比如,我们有一个4字节缓冲区 0x01 0x23 0x45 0x67
转换为一个IntBuffer,那么,根据字节顺序,如果是大端,直接就是 0x1234567
如果是小端顺序,那么读出来就是 0x67452301
注意:视图缓冲区一旦创建,字节顺序是不可改变的。视图缓冲区后面讲到。
4、直接缓冲区
前面说到,allocate和wrap创建的都是间接缓冲区。间接缓冲区,就是在操作系统和JVM之间其实有一层,我们如果对一个间接缓冲区使用,那么首先是要在操作系统层次创建一个临时缓冲区,然后copy过去,再操作,在删除临时缓冲区。这都很麻烦。
所以,直接缓冲区。也就是在操作系统,而不是JVM进程中创建缓冲区,这就绕过了JVM栈,所以依赖于具体的操作系统。事实上,在操作系统中创建缓冲区,比起在JVM中,更加消耗资源。(因为JVM中是已经划分好的内存,直接设置一下,而操作系统中要通过系统调用,个人理解。)
使用 allocateDirect方法就可以获得直接缓冲区。
public static ByteBuffer allocateDirect (int capacity)
public abstract boolean isDirect( );
isDirect对于直接缓冲区的非字节视图缓冲区,也可能返回true
5、视图缓冲区
可以这样理解,字节缓冲区是所有缓冲类型的基础。我们在通道中,网络中文件流中四处传递字节缓冲区。但是,一旦进入应用,我们就要通过视图缓冲区来解读具体的数据了。
字节缓冲区提供了很多这样的API
public abstract class ByteBuffer
extends Buffer implements Comparable {
// This is a partial API listing
public abstract CharBuffer asCharBuffer( );
public abstract ShortBuffer asShortBuffer( );
public abstract IntBuffer asIntBuffer( );
public abstract LongBuffer asLongBuffer( );
public abstract FloatBuffer asFloatBuffer( );
public abstract DoubleBuffer asDoubleBuffer( );
}
前面提到过从ByteBuffer到IntBuffer的转换。其实这种转换,是一种解释包装。
所以需要提供一个顺序,然后按照顺序和类型,转换成另外一种视图,而原始数据其实是不变的。
例如,我们可以这样;
ByteBuffer byteBuffer =
ByteBuffer.allocate(7).order(ByteOrder.BIG_ENDIAN);
CharBuffer charBuffer = byteBuffer.asCharBuffer();
注意,java中char是占两个字节的。我们的转换可以看下图:
新的charBuffer视图其实还是使用的原来的缓冲区的数据,只是这时的元素变成了2个字节的char了。
看一段代码:
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.CharBuffer;
public class CharBufferView {
public static void print(Buffer buffer){
System.out.println("pos="+buffer.position()+", limit="+
buffer.limit() + ", capacity="+buffer.capacity()
+":'" + buffer.toString()+"'");
}
public static void main(String[] args)
throws Exception{
ByteBuffer byteBuffer = ByteBuffer.allocate(8).order(ByteOrder.BIG_ENDIAN);
CharBuffer charBuffer = byteBuffer.asCharBuffer();
byteBuffer.put(0,(byte)0);
byteBuffer.put(1,(byte)'H');
byteBuffer.put(2,(byte)0);
byteBuffer.put(3,(byte)'e');
byteBuffer.put(4,(byte)0);
byteBuffer.put(5,(byte)'l');
byteBuffer.put(6,(byte)0);
byteBuffer.put(7,(byte)'l');
byteBuffer.put(8,(byte)0);
byteBuffer.put(9,(byte)'o');
CharBufferView.print(byteBuffer);
CharBufferView.print(charBuffer);
}
}
结果:
pos=0, limit=10, capacity=10:'java.nio.HeapByteBuffer[pos=0 lim=10 cap=10]'
pos=0, limit=5, capacity=5:'Hello'
可以看到,转换为视图后,limit和capacity都变了。
6、数据元素视图
上面的视图使用了一个视图类来转换。在ByteBuffer中,我们可以直接获取不同视图的元素。
public abstract class ByteBuffer
extends Buffer implements Comparable {
public abstract char getChar( );
public abstract char getChar (int index);
public abstract short getShort( );
public abstract short getShort (int index);
public abstract int getInt( );
public abstract int getInt (int index);
public abstract long getLong( );
public abstract long getLong (int index);
public abstract float getFloat( );
public abstract float getFloat (int index);
public abstract double getDouble( );
public abstract double getDouble (int index);
public abstract ByteBuffer putChar (char value);
public abstract ByteBuffer putChar (int index, char value);
public abstract ByteBuffer putShort (short value);
public abstract ByteBuffer putShort (int index, short value);
public abstract ByteBuffer putInt (int value);
public abstract ByteBuffer putInt (int index, int value);
public abstract ByteBuffer putLong (long value);
public abstract ByteBuffer putLong (int index, long value);
public abstract ByteBuffer putFloat (float value);
public abstract ByteBuffer putFloat (int index, float value);
public abstract ByteBuffer putDouble (double value);
public abstract ByteBuffer putDouble (int index, double value);
}
这些操作一看便知。前面也提过其实就是根据大端小端的字节顺序,和这些数据类型的长度来组织数据。
这里需要注意的是:如果get或者是put时,数据不足,或者空间不够,都会发生异常。
如果不是用putXX的方法,直接getXX,那么可能产生意想不到的问题。
7、无符号数存取
对于java来说,没有无符号数处理(除了char)。为此,书中给出一个程序,用来处理无符号数缓冲区。
其基本原理是,使用比要存取的数据类型更大的数据类型来存放这个数,同时使用与运算强制设置符号位为0,剩余的位就可以作为数据而不是符号位了。
public class Unsigned
{
public static short getUnsignedByte (ByteBuffer bb) {
return ((short)(bb.get( ) & 0xff));
}
public static void putUnsignedByte (ByteBuffer bb, int value) {
bb.put ((byte)(value & 0xff));
}
...
}
这里省略了其他部分。这样我们就可以看到带符号的
8、内存映射缓冲区
映射缓冲区是与文件存储的数据元素关联的字节缓冲区,它通过内存映射来访问。映射缓
冲区通常是直接存取内存的,只能通过 FileChannel 类创建。映射缓冲区的用法和直接缓冲
区类似,但是 MappedByteBuffer 对象具有许多文件存取独有的特征。

- 大小: 20.5 KB
分享到:
相关推荐
人脸识别项目实战
内容概要:本文详细描述了一个完整的Web应用程序的开发过程。该项目主要采用了Hono作为服务器框架,Prisma作为ORM工具,JWT用于认证鉴权,以及一系列现代化的最佳实践确保系统的健壮性和安全性。项目初期构建了基础架构,并设置了必要的依赖和工具。在后端方面涵盖了公共API接口的设计、CRUD增删改查逻辑、用户认证和授权等功能。此外还特别关注到了API的安全保护,如输入输出的校验,跨站请求伪造CSRF的防范,XSS防御等措施;为确保代码的质量引入了代码检测(比如ESLint搭配Prettier),并建立了完善的测试框架以保障后续开发阶段的功能正确。对于可能出现的问题预先定义了一组规范化的异常响应,并提供OpenAPI文档以方便开发者理解和调用。数据存储层面上利用了关系型与非关系型数据库各自的特性,实现了数据的有效组织,最后提供了实用的脚本,可用于种子数据插入以及执行必要的初始化工作。 适合人群:面向具有一定JavaScript/TypeScript开发经验,尤其是Node.js后台服务搭建经验的中级程序员和技术团队。 使用场景及目标:这份材料非常适合那些需要快速建立安全高效的RES
【资源介绍】 1、该资源包括项目的全部源码,下载可以直接使用! 2、本项目适合作为计算机、数学、电子信息等专业的课程设计、期末大作业和毕设项目,也可以作为小白实战演练和初期项目立项演示的重要参考借鉴资料。 3、本资源作为“学习资料”如果需要实现其他功能,需要能看懂代码,并且热爱钻研和多多调试实践。 掌静脉识别算法源码(门禁).zip掌静脉识别算法源码(门禁).zip掌静脉识别算法源码(门禁).zip掌静脉识别算法源码(门禁).zip掌静脉识别算法源码(门禁).zip掌静脉识别算法源码(门禁).zip掌静脉识别算法源码(门禁).zip掌静脉识别算法源码(门禁).zip掌静脉识别算法源码(门禁).zip掌静脉识别算法源码(门禁).zip掌静脉识别算法源码(门禁).zip掌静脉识别算法源码(门禁).zip掌静脉识别算法源码(门禁).zip掌静脉识别算法源码(门禁).zip
手势识别项目实战
(参考GUI)MATLAB BP的交通标志系统.zip
人脸识别项目实战
内容概要:本文详细介绍了 C++ 函数的基础概念及其实战技巧。内容涵盖了函数的基本结构(定义、声明、调用)、多种参数传递方式(值传递、引用传递、指针传递),各类函数类型(无参无返、有参无返、无参有返、有参有返),以及高级特性(函数重载、函数模板、递归函数)。此外,通过实际案例展示了函数的应用,如统计数组元素频次和实现冒泡排序算法。最后,总结了C++函数的重要性及未来的拓展方向。 适合人群:有一定编程基础的程序员,特别是想要深入了解C++编程特性的开发人员。 使用场景及目标:① 学习C++中函数的定义与调用,掌握参数传递方式;② 掌握不同类型的C++函数及其应用场景;③ 深入理解函数重载、函数模板和递归函数的高级特性;④ 提升实际编程能力,通过实例强化所学知识。 其他说明:文章以循序渐进的方式讲解C++函数的相关知识点,并提供了实际编码练习帮助理解。阅读过程中应当边思考边实践,动手实验有助于更好地吸收知识点。
Comsol光学仿真模型:包括纳米球 柱 Mie散射多级分解 ,Comsol光学仿真模型; 纳米球; 柱; Mie散射; 多级分解,Comsol光学仿真模型:纳米结构Mie散射多级分解
永磁同步电机全速域控制高频方波注入法、滑模观测器法SMO、加权切矢量控制Simulink仿真模型 低速域采用高频方波注入法HF,高速域采用滑膜观测器法SMO,期间采用加权形式切 送前方法 1、零低速域,来用无数字滤波器高频方波注入法, 2.中高速域采用改进的SMO滑模观测器,来用的是sigmoid函数,PLL锁相环 3、转速过渡区域采用加权切法 该仿真各个部分清晰分明,仿真波形效果良好内附详细控制方法资料lunwen 带有参考文献和说明文档,仿真模型 ,核心关键词: 1. 永磁同步电机; 2. 全速域控制; 3. 高频方波注入法; 4. 滑模观测器法SMO; 5. 加权切换矢量控制; 6. Simulink仿真模型; 7. 零低速域控制; 8. 中高速域控制; 9. 转速过渡区域控制; 10. 仿真波形效果; 11. 详细控制方法资料; 12. 参考文献和说明文档。,永磁同步电机多域控制策略的仿真研究
基于蜣螂优化算法的无人机三维路径规划【23年新算法应用】可直接运行 Matlab语言 主要内容:读取地形数据,利用蜣螂算法DBO优化三维路径,目标函数为总路径最短,同时不能撞到障碍物,效果如图所示,包括迭代曲线图、三维路径图、二维平面图等等 ,基于蜣螂优化算法;无人机;三维路径规划;总路径最短;障碍物避免;Matlab语言;迭代曲线图;三维路径图;二维平面图,蜣螂算法优化无人机三维路径规划:实时避障、路径最短新应用
清华大学2024年研究生复试上机考试题.zip
南京理工大学研究生入学考试2011年复试上机试题
手势识别项目实战
这里是3501的内容,用于复习资料
异步电动机变压变频调速系统,包含六千多字的文档、框架图、Simulink仿真模型,电力拖动、电机控制仿真设计 仿真模型+报告 开关闭环对比仿真都有,资料如图所见如所得 ,异步电动机;变压变频调速系统;六千字文档;框架图;Simulink仿真模型;电力拖动;电机控制仿真设计;开闭环对比仿真;资料如图。,异步电机控制仿真系统:六千字详解与图解
人脸识别项目实战
手势识别项目实战
人脸识别项目实战
c语言学习
c语言学习