`

NIO中ByteBuffer的一些注意事项

 
阅读更多

         缓冲区即可以用来输入也可以用来输出,这一点和流不同,流只能向一个方向传递数据。

          ByteBuffer是Java的NIO中普遍使用的用于接收和发送消息的缓冲区。在读写数据时,它具有内部状态来跟踪缓冲区的当前位置。

 

          1.缓冲区的内部状态:

          capacity:      缓冲区的元素总数(不可修改)。

          position:       下一个要读写的元素位置(从0开始)。

          limit:             第一个不可读写的位置。

          mark:            用户选定的position的前一个位置或0。

注:position和limit之间的距离指示了可读/存的字节数。

       boolean hasRemaining():当缓冲区至少还有一个元素时,返回true。

       int remaining():position和limit之间字节个数。

所以,他们满足的关系是这样的:0<=mark<=position<=limit<=capacity

      reset():将position的值还原成上传调用mark()方法后的position的值。

 

      2.接下来说说创建buffer的两种模式的区别:

创建ByteBuffer可以使用allocate或者wrap,就像下面这样:

      ByteBuffer allocate(int capacity)

      ByteBuffer allocateDirect(int capacity)

      ByteBuffer wrap(int capacity)

      ByteBuffer wrap(byte[] array,int offset,int length)

注:创建的缓冲区都是定长的,大小无法改变。若发现刚创建的缓冲区容量太小,只能重新创建一个合适的。特别注意一下 ByteBuffer wrap(byte[] array,int offset,int length),这样创建的ByteBuffer的capacity和array的大小是一样的,position是offset,limit是offset+length,position之前和limit之后的数据依然可以访问到。

像这样:

public class Singleton {
	public static void main(String args[]){
		byte arr[]=new byte[100];
		ByteBuffer buffer=ByteBuffer.wrap(arr,3,25);
		System.out.println("Capacity is:  "+buffer.capacity());
		System.out.println("Position is: "+buffer.position());
		System.out.println("limit is: "+buffer.limit());
	}
}
//结果:
Capacity is:  100
Position is: 3
limit is: 28

 

 

       接下来说一下allocate和wrap的区别:

        wrap只是简单地创建一个具有指向被包装数组的引用的缓冲区,该数组成为后援数组。对后援数组中的数据做的任何修改都将改变缓冲区中的数据,反之亦然。

 

public static void main(String args[]){  
        byte arr[]=new byte[100];  
        
        //将arr数组全部置为1
        Arrays.fill(arr, (byte)1);
        ByteBuffer buffer=ByteBuffer.wrap(arr,3,25);
        
        //对后援数组中的数据做的任何修改都将改变缓冲区中的数据
        arr[0]=(byte)2;
        buffer.position(0);
        
        System.out.println(buffer.get());
        //在缓冲区上调用array()方法即可获得后援数组的引用。
        System.out.println(Arrays.toString(buffer.array()));
	}
//运行结果:
2
[2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  1, ....]//总共100个元素

 

         allocate则是创建了自己的后援数组,在缓冲区上调用array()方法也可获得后援数组的引用。通过调用arrayOffset()方法,甚至可以获取缓冲区中第一个元素在后援数组的偏移量。但是使用wrap创建的ByteBuffer,调用arrayOffset永远是0。

 

public static void main(String args[]){  
        
        ByteBuffer buffer=ByteBuffer.allocate(100);
        
        //对后援素组的修改也可以反映到buffer上
        byte arr[]=buffer.array();
        arr[1]=(byte)'a';
        buffer.getInt(); //不会影响arrayoffset,why?
        
        System.out.println(Arrays.toString(buffer.array()));
        System.out.println(buffer.arrayOffset());
	}
//运行结果:
[0, 97, 0, 0, 0, 0, 0, 0,...]//总共100个元素
0

 

      ByteBuffer allocateDirect(int capacity)创建的叫直接缓冲区,I/O速度更快。可以通过isDirect()方法查看一个缓冲区是否是直接缓冲区。由于直接缓冲区是没有后援数组的,所以在其上面调用array()或arrayOffset()都会抛出UnsupportedOperationException异常。注意有些平台或JVM可能不支持这个创建直接缓冲区。

 

        3.下来我们看看ByteBuffer的get和put方法。

put和get方法有很多重载模式,具体可以参考API。但是切记无论是put还是get,position的值都会递增。看一下如下代码就一目了然了:

public class Singleton {
	public static void main(String args[]){
		ByteBuffer buffer=ByteBuffer.allocate(10);
		System.out.printf("position is %d,  capacity id %d, limit is   %d\n",buffer.position(),buffer.capacity(),buffer.limit());
		
		buffer.put("a".getBytes());
		System.out.printf("position is %d,  capacity id %d, limit is   %d\n",buffer.position(),buffer.capacity(),buffer.limit());
		
		buffer.putInt(2);
		System.out.printf("position is %d,  capacity id %d, limit is   %d\n",buffer.position(),buffer.capacity(),buffer.limit());
	

		System.out.println(“it is: ”+buffer.getChar());
		System.out.printf("position is %d,  capacity id %d, limit is   %d\n",buffer.position(),buffer.capacity(),buffer.limit());
	}
}

//结果:
position is 0,  capacity id 10, limit is   10
position is 1,  capacity id 10, limit is   10
position is 5,  capacity id 10, limit is   10
it is:
position is 7,  capacity id 10, limit is   10

  可见不是我们所想的那样,一getChar就可以得到char。ByteBuffer很灵活,需要通过设置position来读取。无论是get还是set,position都会增加

当然了以byte为单位,int(4字节),char(2字节)。

    要想获得我们具体的值可以这样:

public class Singleton {
	public static void main(String args[]){
		ByteBuffer buffer=ByteBuffer.allocate(10);
		System.out.printf("position is %d,  capacity id %d, limit is   %d\n",buffer.position(),buffer.capacity(),buffer.limit());
		
		buffer.put("a".getBytes());
		System.out.printf("position is %d,  capacity id %d, limit is   %d\n",buffer.position(),buffer.capacity(),buffer.limit());
		
		buffer.putInt(2);
		System.out.printf("position is %d,  capacity id %d, limit is   %d\n",buffer.position(),buffer.capacity(),buffer.limit());
	
		buffer.position(0);
		System.out.println("byte is: "+(char)buffer.get());
		System.out.println("int is: "+buffer.getInt());
	}
}

//结果:
position is 0,  capacity id 10, limit is   10
position is 1,  capacity id 10, limit is   10
position is 5,  capacity id 10, limit is   10
byte is: a
int is: 2

 

       以上说的是基于相对位置的get和put,接下来我们看一下基于绝对位置的get和put:

      byte get(int index)

      ByteBuffer put(int index,byte b)

       绝对位置形式的get和put不会改变position的值。基于绝对位置的get()和put()以指定的索引位置为参数,从该位置读取或者向该位置写入数据,不会改变position的值。

 

      除此之外,还可以指定大端存储还是小端存储。

       ByteOrder order()

       ByteBuffer order(ByteOrder order)

 

           4.ByteBuffer准备了几个方法利于我们设置position和limit。

         clear()        将position设置为0,limit设置为capacity,mark未定义。

         flip()           将posiion设置为0,limit设置为position,mark未定义。

         rewind()     将position设置为0,limit没有改变,mark未定义。

        

         5.看一下,ByteBuffer提供的一些函数:

public static void main(String args[]){  
        
        ByteBuffer buffer=ByteBuffer.allocate(100);
        
        //对后援素组的修改也可以反映到buffer上
        byte arr[]=buffer.array();
       Arrays.fill(arr, (byte)2);
        
       ByteBuffer newBuffer=buffer.duplicate();
       arr[1]=3;
       
       
       byte arr1[]=new byte[100];
       buffer.position(0);//若这没有标识position为0,则会抛出java.nio.BufferUnderflowException,是从position到limit复制的
       buffer.get(arr1);
       arr1[0]=7;//不会影响到ByteBuffer的后援数组
       
       
        System.out.println(Arrays.toString(newBuffer.array()));
        System.out.println(buffer.position());
        System.out.println(newBuffer.position());
        System.out.println(Arrays.toString(arr1));
	}
//运行结果:
[2, 3, 2, 2, 2, 2, 2, 2....//100个
100
0
[7, 3, 2, 2, 2, 2, 2,.....//100个

 

 

public static void main(String args[]){
		ByteBuffer buffer=ByteBuffer.allocate(10);
		
		byte arr[]=buffer.array();
		for(int i=0;i<arr.length;i++){
			arr[i]=(byte)i;
		}
	

		buffer.position(2);
		buffer.limit(6);
		
		arr[3]=(byte)66;   //这个是为了证明:buffer和newBuffer共享后援数组
		
		//slice返回原ByteBuffer的一个镜像,所有改变互相可见。position和limit独立
		//返回的ByteBuffer的position永远为0,limit为原ByteBuffer的(limit-position)
		//返回的ByteBuffer的capacity为(limit-position)
		//但是调用ByteBuffer.array返回的是同一个完整的数组,就是原ByteBuffer的
		//后援数组
		ByteBuffer newBuffer=buffer.slice();
		
		System.out.println(buffer.position());
		System.out.println(buffer.limit());
		System.out.println(buffer.capacity());
		
		System.out.println(newBuffer.position());
		System.out.println(newBuffer.limit());
		System.out.println(newBuffer.capacity());
		
		System.out.println(Arrays.toString(buffer.array()));
		System.out.println(Arrays.toString(newBuffer.array()));
		
		System.out.println(buffer.get());
		System.out.println(newBuffer.get());
		
		//System.out.println(newBuffer.capacity(8));   capacity是无法这样设置的。
	}

 



 

  • 大小: 225.7 KB
分享到:
评论

相关推荐

    NIO处理大文件

    9. 注意事项: 虽然NIO提供了强大的性能优化,但它的API相对复杂,使用时需要注意内存管理,合理设置缓冲区大小,以及正确处理异常。 总之,NIO是Java处理大文件的一个强大工具,通过非阻塞I/O、通道、缓冲区和...

    JAVA NIO 异步通信模板服务端

    ### 注意事项 - **线程安全**:NIO操作通常是线程不安全的,所以在多线程环境下,需要对共享的Buffer和Channel进行适当的同步控制。 - **内存管理**:Buffer的容量应合理设置,避免频繁扩容或浪费内存。 - **异常...

    JAVA IO-NIO 详解

    注意事项** - **释放资源**: 使用完毕后需正确释放资源,避免内存泄漏。 - **性能考量**: 对于大文件,可能会影响性能,需要根据实际情况评估。 #### 七、Selector使用 **1. 创建Selector** - **Selector**: ...

    Java NIO实战之聊天室功能详解

    Java NIO实战之聊天室功能详解主要介绍了Java NIO实战之聊天室功能,结合实例形式详细分析了Java NIO聊天室具体的服务端、客户端相关实现方法与操作注意事项。 Java NIO概述 Java NIO(New I/O)是一种Java API,...

    java大文件读取-乔乐共享

    #### 四、注意事项 1. **内存映射文件**: - 在使用内存映射文件时需要注意文件大小不能超过系统限制。 - 使用完毕后记得释放映射区域,否则可能导致内存泄漏。 2. **分块读取**: - 缓存大小的选择很重要,过...

    java 显示文件中的内容

    本文将深入探讨如何使用Java读取并显示文本文件的内容,并提供相关的编程技巧和注意事项。 首先,让我们从基础开始。在Java中,我们通常使用`java.io`包中的`BufferedReader`类来读取文本文件。以下是一个简单的...

    【IT十八掌徐培成】Java基础第26天-08.DirectByteBuffer2.zip

    6. **使用注意事项**: - **内存泄漏**:由于直接缓冲区不参与垃圾回收,使用后必须确保释放,否则可能导致内存泄漏。 - **内存碎片**:长期使用直接缓冲区可能导致内存碎片,影响系统性能。 - **容量限制**:...

    io操作复制文件

    六、复制大文件时的注意事项 1. 使用缓冲区:如上述示例所示,使用缓冲区可以减少磁盘I/O操作,提高性能。 2. 错误处理:务必捕获并处理可能发生的IOException。 3. 文件权限:确保程序具有读取源文件和写入目标文件...

    netty5.0 jar包 官方例子_改 中文手册

    5. **最佳实践**:提供在实际项目中使用Netty时的一些最佳实践和注意事项。 通过阅读《51CTO下载-Netty 5用户指南.pdf》和实际运行官方例子,你可以更深入地理解Netty的工作原理,并能够有效地将其应用到你的网络...

    netty-netty-4.1.32.final-remark.zip

    "remark" 暗示这个压缩包可能包含了一些注解或说明,可能是对这个特定版本的一些关键特性、优化或注意事项的解释。 描述中的 "tony老师netty" 可能是指一位名叫 Tony 的教师或专家,他与 Netty 相关的教学或讲解...

    Java堆外内存的使用Java开发Java经验技巧共5页

    4. **注意事项**: - 直接内存虽快,但也有局限,比如容量有限,过度使用可能导致内存溢出。 - 直接内存不受JVM的垃圾收集器管理,可能导致内存泄露,开发者需手动管理。 - 直接内存分配和释放的性能优于堆内,但...

    NDKImageProcesser:使用NDK的图像处理器

    **优化与注意事项** - **内存管理**:处理大图像时要注意内存分配和释放,避免内存泄漏。 - **线程安全**:如果在多个线程中处理图像,需要确保代码的线程安全性。 - **性能监控**:使用NDK可能会增加应用程序的复杂...

    MINA文档

    通过学习这些资料,开发者不仅可以了解MINA的基本概念,还能掌握实际开发中的技巧和注意事项,提升网络应用的开发效率和质量。对于想要深入研究MINA框架或者正在使用MINA开发项目的开发者来说,这些资源无疑是一份...

    在Java中int和byte[]的相互转换

    ### 注意事项 - 在处理字节序问题时,需要注意Java默认使用的是大端字节序。如果你需要与使用小端字节序的系统通信,你需要在转换过程中调整字节的顺序。 - 当转换大型整数(如long)时,上述方法不再适用,因为...

    基于Android的多线程下载,单纯的下载功能

    在Android中,可以使用`java.nio`包中的类来实现文件的读写操作,如`FileInputStream`、`FileOutputStream`和`ByteBuffer`。以下是一个简化的多线程下载任务类: ```java public class DownloadTask implements ...

Global site tag (gtag.js) - Google Analytics