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开发速学宝典》出版,欢迎定购

目录
相关文章
|
4天前
|
Java Linux API
统计android设备的网络数据使用量
统计android设备的网络数据使用量
14 0
|
14小时前
|
存储 安全 Java
Java一分钟:缓冲流提升读写效率
【5月更文挑战第11天】Java I/O的缓冲流通过内存缓冲区提升读写性能,实现批量处理和预读写。注意避免缓冲区溢出、忘记刷新和关闭以及数据同步问题。示例展示了字节和字符缓冲流在文件复制中的应用,降低磁盘I/O次数,提高效率。熟练掌握缓冲流使用有助于优化Java程序的I/O性能。
8 2
|
1天前
|
数据采集 机器学习/深度学习 前端开发
【好书推荐3】Python网络爬虫入门到实战
【好书推荐3】Python网络爬虫入门到实战
9 0
|
1天前
|
网络协议 安全 Linux
网络入门基础
网络入门基础
4 0
|
1天前
|
Java 开发者
Java一分钟之-Java IO流:文件读写基础
【5月更文挑战第10天】本文介绍了Java IO流在文件读写中的应用,包括`FileInputStream`和`FileOutputStream`用于字节流操作,`BufferedReader`和`PrintWriter`用于字符流。通过代码示例展示了如何读取和写入文件,强调了常见问题如未关闭流、文件路径、编码、权限和异常处理,并提供了追加写入与读取的示例。理解这些基础知识和注意事项能帮助开发者编写更可靠的程序。
6 0
|
1天前
|
Windows
LabVIEW通过网络传输数据
LabVIEW通过网络传输数据
|
1天前
|
JavaScript 前端开发 UED
在 JavaScript 中,异步编程和回调函数是处理非阻塞操作(如网络请求、文件读写等)的重要工具
【5月更文挑战第10天】JavaScript中的异步编程和回调函数用于处理非阻塞操作,提高应用响应性和吞吐量。异步编程通过回调函数、Promises和async/await等方式实现,避免程序因等待操作完成而阻塞。回调函数是异步操作完成后调用的函数,常用于处理网络请求等。然而,回调函数存在嵌套问题和错误处理困难,因此出现了Promises和async/await等更优解决方案。
10 3
|
1天前
|
网络协议 算法 Java
【Java网络编程】网络编程概述、UDP通信(DatagramPacket 与 DatagramSocket)
【Java网络编程】网络编程概述、UDP通信(DatagramPacket 与 DatagramSocket)
14 3
|
2天前
|
网络协议 Dubbo Java
【网络编程】理解客户端和服务器并使用Java提供的api实现回显服务器
【网络编程】理解客户端和服务器并使用Java提供的api实现回显服务器
9 0
|
3天前
|
安全 算法 网络安全
网络安全与信息安全:保护您的数据和隐私
【5月更文挑战第8天】随着互联网的普及和技术的快速发展,网络安全和信息安全已经成为我们日常生活中不可或缺的一部分。本文将介绍网络安全漏洞、加密技术和安全意识等方面的知识,帮助您更好地保护自己的数据和隐私。