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

目录
相关文章
|
10天前
|
人工智能 Java 物联网
JAVA网络编程的未来:URL与URLConnection的无限可能,你准备好了吗?
随着技术的发展和互联网的普及,JAVA网络编程迎来新的机遇。本文通过案例分析,探讨URL与URLConnection在智能API调用和实时数据流处理中的关键作用,展望其未来趋势和潜力。
33 7
|
1天前
|
Java 大数据 API
14天Java基础学习——第1天:Java入门和环境搭建
本文介绍了Java的基础知识,包括Java的简介、历史和应用领域。详细讲解了如何安装JDK并配置环境变量,以及如何使用IntelliJ IDEA创建和运行Java项目。通过示例代码“HelloWorld.java”,展示了从编写到运行的全过程。适合初学者快速入门Java编程。
|
2天前
|
机器学习/深度学习 自然语言处理 前端开发
前端神经网络入门:Brain.js - 详细介绍和对比不同的实现 - CNN、RNN、DNN、FFNN -无需准备环境打开浏览器即可测试运行-支持WebGPU加速
本文介绍了如何使用 JavaScript 神经网络库 **Brain.js** 实现不同类型的神经网络,包括前馈神经网络(FFNN)、深度神经网络(DNN)和循环神经网络(RNN)。通过简单的示例和代码,帮助前端开发者快速入门并理解神经网络的基本概念。文章还对比了各类神经网络的特点和适用场景,并简要介绍了卷积神经网络(CNN)的替代方案。
|
10天前
|
安全 Java API
深入探索Java网络编程中的HttpURLConnection:从基础到进阶
本文介绍了Java网络编程中HttpURLConnection的高级特性,包括灵活使用不同HTTP方法、处理重定向、管理Cookie、优化安全性以及处理大文件上传和下载。通过解答五个常见问题,帮助开发者提升网络编程的效率和安全性。
|
10天前
|
JSON 安全 算法
JAVA网络编程中的URL与URLConnection:那些你不知道的秘密!
在Java网络编程中,URL与URLConnection是连接网络资源的两大基石。本文通过问题解答形式,揭示了它们的深层秘密,包括特殊字符处理、请求头设置、响应体读取、支持的HTTP方法及性能优化技巧,帮助你掌握高效、安全的网络编程技能。
38 9
|
8天前
|
存储 安全 Java
🌟Java零基础-反序列化:从入门到精通
【10月更文挑战第21天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
33 5
|
5天前
|
安全 Java 调度
Java中的多线程编程入门
【10月更文挑战第29天】在Java的世界中,多线程就像是一场精心编排的交响乐。每个线程都是乐团中的一个乐手,他们各自演奏着自己的部分,却又和谐地共同完成整场演出。本文将带你走进Java多线程的世界,让你从零基础到能够编写基本的多线程程序。
17 1
|
10天前
|
JSON Java API
JAVA网络编程新纪元:URL与URLConnection的神级运用,你真的会了吗?
本文深入探讨了Java网络编程中URL和URLConnection的高级应用,通过示例代码展示了如何解析URL、发送GET请求并读取响应内容。文章挑战了传统认知,帮助读者更好地理解和运用这两个基础组件,提升网络编程能力。
31 5
|
6月前
|
安全 Java
从零开始学习 Java:简单易懂的入门指南之不可变集合、方法引用(二十六)
从零开始学习 Java:简单易懂的入门指南之不可变集合、方法引用(二十六)

热门文章

最新文章