Java网络编程从入门到精通(34):读写缓冲区中的数据---使用get和put方法按顺序读写单个数据

简介: 本文为原创,如需转载,请注明作者和出处,谢谢! 上一篇:Java网络编程从入门到精通(33):非阻塞I/O的缓冲区(Buffer)     对于缓冲区来说,最重要的操作就是读写操作。

本文为原创,如需转载,请注明作者和出处,谢谢!

上一篇:Java网络编程从入门到精通(33):非阻塞I/O的缓冲区(Buffer)

    对于缓冲区来说,最重要的操作就是读写操作。缓冲区提供了两种方法来读写缓冲区中的数据:getput方法和array方法。而getput方法可以有三种读写数据的方式:按顺序读写单个数据、在指定位置读写单个数据和读写数据块。除了上述的几种读写数据的方法外,CharBuffer类还提供了用于专门写字符串的putappend方法。在本文及后面的文章中将分别介绍这些读写缓冲区的方法。

虽然使用allocate方法创建的缓冲区并不是一次性地分配内存空间,但我们可以从用户地角度将一个缓冲区想象成一个长度为capacity的数组。当缓冲区创建后,和数组一样,缓冲区的大小(capacity值)将无法改变,也无法访问缓冲区外的数据。如下面的代码创建了一个大小为6的字节缓冲区。

ByteBuffer byteBuffer  =  ByteBuffer.allocate( 6 );

对于byteBuffer来说,只能访问属于这个缓冲区的六个字节的数据,如果超过了这个范围,将抛出一个BufferOverflowException异常,这是一个运行时错误,因为这个错误只能在程序运行时被发现。

既然缓冲区和数组类似,那么缓冲区也应该象数组一样可以标识当前的位置。缓冲区的position方法为我们提供了这个功能。position方法有两种重载形式,它们的定义如下:

public   final   int  position()
public   final  Buffer position( int  newPosition)

第一个重载形式用来获取缓冲区的当前位置。在创建缓冲区后,position的初始值是0,也就是缓冲区第一个元素的位置。当从缓冲区读取一个元素后,position的值加1。我们从这一点可以看出,position方法返回的位置就是当前可以读取的元素的位置。position的取值范围从0capacity – 1。如果position的值等于capacity,说明缓冲区当前已经没有数据可读了。

position方法的第二个重载形式可以设置缓冲区的当前位置。参数newPosition的取值范围是0 <= newPosition < capacity。如果newPosition的值超出这个范围,position方法就会抛出一个IllegalArgumentException异常。

在大多数情况下不需要直接控制缓冲区的位置。缓冲区类提供的用于读写数据的方法可以自动地设置缓冲区的当前位置。在缓冲区类中,getput方法用于读写缓冲区中的数据。getput方法的定义如下:

ByteBuffer类的getput方法:

public   abstract   byte  get()            
public   abstract  ByteBuffer put( byte  b)

IntBuffer类的getput方法:

public   abstract   int  get()            
public   abstract  IntBuffer put( int  i)

其他五个缓冲区类中的getput方法定义和上面的定义类似,只是get方法返回相应的数据类型,而put方法的参数是相应的数据类型,并且返回值的类型是相应的缓冲区类。

每当put方法向缓冲区写入一个数据后,缓冲区的当前位置都会加1。如果缓冲区的当前位置已经等于capacity,调用put方法就会抛出一个java.nio.BufferOverflowException异常。在缓冲区未初赋值的区域将被0填充。使用get方法可以得到缓冲区当前位置的数据,并使缓冲区的当前位置加1。和put方法一样,在缓冲区当前位置等于capacity时使用get方法也会抛出java.nio.BufferOverflowException异常。缓冲区的初始状态如图1所示。

图1 缓冲区的初始状态

从图1可以看出,在缓冲区创建之初,当前的位置和缓冲区中的数据都为0。当使用如下语句向缓冲区中写入数据后,缓冲区当前状态如图2所示。

 

byteBuffer.put(( byte ) 2 );
byteBuffer.put((
byte ) - 1 );

图2  缓冲区的当前状态

 

当缓冲区的当前位置如图3所示时,使用putget方法将会抛出上述的BufferOverflowException异常。

图3  当前位置处于缓冲区尾

如果要使用get方法得到缓冲区中的指定数据,必须将缓冲区的当前位置移动到指定的位置,我们可以使用position方法将当前位置移到缓冲区的任何位置。如下面的代码将图3所示的缓冲区的当前位置设为2,并用get方法获得位置2的数据:

byteBuffer.position( 2 );
System.out.println(byteBuffer.get());

上面的代码将输出3。缓冲区的当前位置为除了使用position方法,也可以使用rewind方法将缓冲区的当前位置设为0rewind方法的定义如下:

public   final  Buffer rewind()

在图2所示的缓冲区状态下调用rewind方法,就会得到如图4的缓冲区状态。

图4  调用rewind方法后的缓冲区状态

接下来让我们执行如下语句:

System.out.println(byteBuffer.get());

缓冲区的状态将如图5所示。

图5  调用get方法后的缓冲区状态

缓冲区除了positioncapacity外,还提供了一个标识来限制缓冲区可访问的范围。这个标识就是limitlimitposition一样,在缓冲区类中也提供了两个重载方法。用于获得和设置limit的值。limit方法的定义如下:


public   final   int  limit()
public   final  Buffer limit( int  newLimit)

在初始状态下,缓冲区的limitcapacity值相同。但limitcapacity的区别是limit可以通过limit方法进行设置,而capacity在创建缓冲区时就已经指定了,并且不能改变。(在上面所讲的position方法的newPosition参数的取值范围时曾说是0 <= newPosition < capacity,其实严格地说,应是0 <= newPosition < limitlimit的其他性质和capacity一样。如在图5所示的缓冲区状态中将limit的值设为2,就变成了图6所示的状态。

图6  将limit设为2的缓冲区状态

在这时position的值等于limit,就不能访问缓冲区的当前数据,也就是说不能使用getput方法。否则将抛出BufferOverflowException异常。由于使用allocate创建的缓冲区并不是一次性地分配内存空间,因此,可以将缓冲区的capacity设为很大的值,如10M。缓冲区过大可能在某些环境中会使系统性能降低(如在PDA或智能插秧机中),因此,可以使用limit方法根据具体的情况来限定缓冲区的大小。当然,limit还可以表示缓冲区中实际的数据量,这将在后面讲解。下面的代码演示了如何使用limit方法来枚举缓冲区中的数据:

while (byteBuffer.position()  <  byteBuffer.limit())
    System.out.println(byteBuffer.get());

我们还可以用fliphasRemaining方法来重写上面的代码。flip方法将limit设为缓冲区的当前位置。当limit等于position时,hasRemaining方法返回false,而则返回true fliphasRemaining方法的定义如下:


public   final  Buffer flip()
public   final   boolean  hasRemaining()

    下面的代码演示了如何使用hasRemaining方法来枚举缓冲区中的数据:

while (byteBuffer.hasRemaining())
    System.out.println(byteBuffer.get());

如果从缓冲区的第一个位置依次使用put方法向缓冲区写数据,当写完数据后,再使用flip方法。这样limit的值就等于缓冲区中实际的数据量了。在网络中传递数据时,可以使用这种方法来设置数据的结束位置。

为了回顾上面所讲内容,下面的代码总结了创建缓冲区、读写缓冲区中的数据、设置缓冲区的limitposition的方法。

   package net;
  
  
import  java.nio. * ;
  
  
public   class  GetPutData
  {
      
public   static   void  main(String[] args)
      {
          
//  创建缓冲区的四种方式
          IntBuffer intBuffer  =  IntBuffer.allocate( 10 );
          ByteBuffer byteBuffer 
=  ByteBuffer.allocateDirect( 10 );
          CharBuffer charBuffer 
=  CharBuffer.wrap( " abcdefg " );
          DoubleBuffer doubleBuffer 
=  DoubleBuffer.wrap( new   double [] {  1.1 2.2  });
          
          
//  向缓冲区中写入数据
          intBuffer.put( 1000 );
          intBuffer.put(
2000 );
          
          System.out.println(
" intBuffer的当前位置: "   +  intBuffer.position());
          
          intBuffer.position(
1 );   //  将缓冲区的当前位置设为1
          System.out.println(intBuffer.get());   //  输出缓冲区的当前数据
          
          intBuffer.rewind();  
//  将缓冲区的当前位置设为0
          System.out.println(intBuffer.get());   //  输出缓冲区的当前数据
          
          byteBuffer.put((
byte ) 20 );
          byteBuffer.put((
byte ) 33 );
          byteBuffer.flip();   
//  将limit设为position,在这里是2
          byteBuffer.rewind(); 
          
while (byteBuffer.hasRemaining())   //  枚举byteBuffer中的数据
              System.out.print(byteBuffer.get()  +   "   " );
          
          
while (charBuffer.hasRemaining())   //  枚举charBuffer中的数据
              System.out.print(charBuffer.get()  +   "   " );
  
          
//  枚举doubleBuffer中的数据
           while (doubleBuffer.position()  <  doubleBuffer.limit())
              System.out.print(doubleBuffer.get() 
+   "   " );
  
      }
  }

运行结果:  

intBuffer的当前位置: 2
2000
1000
20   33  a b c d e f g  1.1   2.2

注意:如果必须使用缓冲区的大小来读取缓冲区的数据,尽量不要使用capacity,而要使用limit。如尽量不要写成如下的代码:

while (byteBuffer.position()  <  byteBuffer.capacity())
    System.out.println(byteBuffer.get());

这是因为当limit比capacity小时,上面的代码将会抛出一个BufferUnderflowException异常。



国内最棒的Google Android技术社区(eoeandroid),欢迎访问!

《银河系列原创教程》发布

《Java Web开发速学宝典》出版,欢迎定购

目录
打赏
0
0
0
0
11
分享
相关文章
Java中的异常处理方法
本文深入剖析Java异常处理机制,介绍可检查异常、运行时异常和错误的区别与处理方式。通过最佳实践方法,如使用合适的异常类型、声明精确异常、try-with-resources语句块、记录异常信息等,帮助开发者提高代码的可靠性、可读性和可维护性。良好的异常处理能保证程序稳定运行,避免资源泄漏和潜在问题。
|
6天前
|
Java代码结构解析:类、方法、主函数(1分钟解剖室)
### Java代码结构简介 掌握Java代码结构如同拥有程序世界的建筑蓝图,类、方法和主函数构成“黄金三角”。类是独立的容器,承载成员变量和方法;方法实现特定功能,参数控制输入环境;主函数是程序入口。常见错误包括类名与文件名不匹配、忘记static修饰符和花括号未闭合。通过实战案例学习电商系统、游戏角色控制和物联网设备监控,理解类的作用、方法类型和主函数任务,避免典型错误,逐步提升编程能力。 **脑图速记法**:类如太空站,方法即舱段;main是发射台,static不能换;文件名对仗,括号要成双;参数是坐标,void不返航。
26 5
神经架构搜索:自动化设计神经网络的方法
在人工智能(AI)和深度学习(Deep Learning)快速发展的背景下,神经网络架构的设计已成为一个日益复杂而关键的任务。传统上,研究人员和工程师需要通过经验和反复试验来手动设计神经网络,耗费大量时间和计算资源。随着模型规模的不断扩大,这种方法显得愈加低效和不够灵活。为了解决这一挑战,神经架构搜索(Neural Architecture Search,NAS)应运而生,成为自动化设计神经网络的重要工具。
|
25天前
|
java.time常用方法汇总
`java.time` API 是从 Java 8 开始引入的时间日期处理库,旨在替代老旧的 `java.util.Date` 和 `Calendar`。它提供了更简洁、强大和灵活的方式处理日期、时间、时区及时间间隔,支持全球化和时间计算需求。API 包含获取当前时间、创建指定时间、解析和格式化字符串、进行加减运算、比较时间、获取年月日时分秒、计算时间间隔、时区转换以及判断闰年等功能。示例代码展示了如何使用这些功能,极大简化了开发中的时间处理任务。
Java网络编程:高级应用与安全性探讨
Java网络编程:高级应用与安全性探讨
Java中的网络编程:Socket编程详解
Java中的网络编程:Socket编程详解
深入了解Java中的网络编程与Socket通信
深入了解Java中的网络编程与Socket通信

热门文章

最新文章

AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等