zoukankan      html  css  js  c++  java
  • 27 Java学习之NIO Buffer(二)(待补充)

    一. Buffer介绍

    Buffer,故名思意,缓冲区,实际上是一个容器,是一个连续数组。Channel提供从文件、网络读取数据的渠道,但是读取或写入的数据都必须经由Buffer。具体看下面这张图就理解了:

      上面的图描述了从一个客户端向服务端发送数据,然后服务端接收数据的过程。客户端发送数据时,必须先将数据存入Buffer中,然后将Buffer中的内容写入通道。服务端这边接收数据必须通过Channel将数据读入到Buffer中,然后再从Buffer中取出数据来处理。

    Buffer本质上就是一块内存区,可以用来写入数据,并在稍后读取出来。这块内存被NIO Buffer包裹起来,对外提供一系列的读写方便开发的接口。

      在NIO中,Buffer是一个顶层父类,它是一个抽象类,常用的Buffer的子类有:

    • ByteBuffer
    • IntBuffer
    • CharBuffer
    • LongBuffer
    • DoubleBuffer
    • FloatBuffer
    • ShortBuffer

      如果是对于文件读写,上面几种Buffer都可能会用到。但是对于网络读写来说,用的最多的是ByteBuffer。

    字节缓冲区和其他缓冲区类型最明显的不同在于,它们可能成为通道所执行I/O的源头或目标,如果对NIO有了解的朋友们一定知道,通道只接收ByteBuffer作为参数。如我们所知道的,操作系统在内存区域进行I/O操作,这些内存区域,就操作系统方面而言,是相连的字节序列。于是,毫无疑问,只有字节缓冲区有资格参与I/O操作

    二. Buffer的属性

    • capacity(容量):buffer本质是一个数组,在初始化时有固定的大小,这个值就是容量。容量不可改变,一旦缓冲区满了,需要将其清空才能将继续进行读写操作。       
         ByteBuffer bf = ByteBuffer.allocate(10);  //创建了一个最大容量为10的字节缓冲区
    • position(位置):表示当前的位置,初始化时为0,当一个基本数据类型的数据写入buffer时,position会向前移动到下一个可插入数据的Buffer单元。position最大值可以是                              capacity-1。
    • limit(限制):在缓冲区写模式下,limit表示你最多能往Buffer里写多少数据,大小等于capacity;在缓冲区读模式下,limit表示能从缓冲区内读取到多少数据,因此,当切换Buffer到读模式时,limit会被设置成写模式下的position值。可以认为是缓冲区中实际元素的数量
    • mark(标记):一个备忘位置,调用mark()方法的话,mark值将存储当前position的值,等下次调用reset()方法时,会设定position的值为之前的标记值

                              

     1 package com.test.a;
     2 
     3 import java.io.FileInputStream;
     4 import java.io.FileNotFoundException;
     5 import java.io.FileOutputStream;
     6 import java.io.IOException;
     7 import java.nio.ByteBuffer;
     8 import java.nio.channels.FileChannel;
     9 
    10 public class Test {
    11     public static void main(String[] args) throws IOException{
    12         FileInputStream fileInputStream=new FileInputStream("C:\Users\hermioner\Desktop\in.txt");
    13         FileChannel fileChannel=fileInputStream.getChannel();
    14         
    15         FileOutputStream fileOutputStream=new FileOutputStream("C:\Users\hermioner\Desktop\out.txt");
    16         FileChannel fileChannel2=fileOutputStream.getChannel();
    17         
    18         //初始化缓冲区
    19         ByteBuffer buffer=ByteBuffer.allocate(20);
    20         System.out.println("通道文件的大小:"+fileChannel.size());
    21         System.out.println("缓冲区初始化时当前位置:"+buffer.position());
    22         System.out.println("缓冲区初始化时可写的限制:"+buffer.limit());
    23         System.out.println("------------循环开始---------");
    24         
    25         //判断通道内数据是否读取完成
    26         while(-1 != fileChannel.read(buffer))
    27         {
    28             System.out.println("缓冲区写模式下当前位置:"+buffer.position());
    29             System.out.println("缓冲区写模式下的限制:"+buffer.limit());
    30             
    31             //将缓冲区从写模式切换到读模式
    32             buffer.flip();
    33             System.out.println("缓冲区读模式下当前位置:"+buffer.position());
    34             System.out.println("缓冲区读模式下的限制:"+buffer.limit());
    35             
    36             //判断缓冲区内是否还有数据可读取
    37             while(buffer.hasRemaining()) {
    38                 fileChannel2.write(buffer);
    39             }
    40             buffer.clear();
    41         }
    42         fileChannel.close();
    43         fileChannel2.close();
    44         fileOutputStream.close();
    45         fileInputStream.close();
    46 
    47     }
    48 }
    View Code
    1 通道文件的大小:13
    2 缓冲区初始化时当前位置:0
    3 缓冲区初始化时可写的限制:20
    4 ------------循环开始---------
    5 缓冲区写模式下当前位置:13
    6 缓冲区写模式下的限制:20
    7 缓冲区读模式下当前位置:0
    8 缓冲区读模式下的限制:13
    View Code

    执行结果,out.txt文件中的内容和in.txt文件中的内容一样样,且为:

    hello,

    world

    说明:可以看到,在缓冲区写模式下,limit的大小始终等于capacity;而在读模式下,limit等于模式切换前position的大小

    position <= limit <= capacity

    再举个例子,观察四个值得变化:

    (1)创建一个容量大小为10的字节缓冲区

    ByteBuffer bf = ByteBuffer.allocate(10);

    此时:mark = -1; position = 0; limit =10 ; capacity = 10;

    (2)往缓冲区中put五个字节

    bf.put((byte)'H').put((byte)'e').put((byte)'l').put((byte)'l').put((byte)'0');

    注意这里一个字符是占用两个字节的,但是英文字符只占用一个字节,所以这样是可以实现存储效果的。

    此时:此时:mark = -1; position = 5; limit = 10; capacity = 10;

    (3)调用flip方法,切换为读就绪状态

    bf.flip();

    此时:mark = -1; position = 0; limit = 5; capacity = 10;

    (4)读取两个元素

    System.out.println("" + (char) bf.get() + (char) bf.get());

    此时:mark = -1; position = 2; limit = 5; capacity = 10;

    (5)标记此时的position位置

    bf.mark();

    此时:mark = 2; position = 2; limit = 5; capacity = 10;

    (6)读取两个元素后,恢复到之前mark的位置处

    System.out.println("" + (char) bf.get() + (char) bf.get());
    bf.reset();

    执行完第二行代码:mark = 2; position = 2; limit = 5; capacity = 10;

     

    (7)调用compact方法,释放已读取数据的空间,准备重新填充缓存区

    bf.compact();

    此时:mark = 2; position = 3; limit = 10; capacity = 10;

    注意观察数组中元素的变化,实际上进行了数组拷贝,抛弃了已读字节元素,保留了未读字节元素;

    三. Buffer中的重要方法

    3.1 flip

    buffer.flip()该方法是用于将缓冲区从写模式切换到读模式,这是一种固定写法

    public final Buffer flip() {
        limit = position;
        position = 0;
        mark = -1;
        return this;
        }

    3.2 remaind方法

    public final Buffer rewind() {
        position = 0;
        mark = -1;
        return this;
        }

    将position的位置设置为0,表示可以重新读取Buffer中的所有数据,limit保持不变。

    3.3 clear方法

     public final Buffer clear() {
      position = 0;
      limit = capacity;
       mark = -1;
        return this;
    }
    • 一旦完成对buffer中数据的读取,需要让buffer做好再次被写入的准备,这时候可以调用clear方法来完成。
    • clear方法将position设置为0,limit设置为容量的值,也就意味着buffer被清空了,但是这个清空的概念是写入数据可以从缓冲区的指定位置开始,但buffer里面的数据并没有        删除。
    • 如果buffer里面还有数据没有被读取,这个时候调用clear方法会导致那些数据被“遗忘”,因为没有标记告诉你哪些是读取过哪些没有被读取。

    3.4 向buffer中写入数据

    • 通过channel写入;
    • 通过buffer的put方法写入:
    buffer.put("channel".getBytes());

    3.5 读取buffer中的数据

    • 通过channel读取;
    • 通过buffer的get方法读取:
    byte b = buffer.get();

    四. 利用Buffer读写数据,通常遵循四个步骤

    • 把数据写入buffer;

    • 调用flip;

    • 从Buffer中读取数据;

    • 调用buffer.clear()或者buffer.compact()。

    当写入数据到buffer中时,buffer会记录已经写入的数据大小。当需要读数据时,通过 flip() 方法把buffer从写模式调整为读模式;在读模式下,可以读取所有已经写入的数据。

    当读取完数据后,需要清空buffer,以满足后续写入操作。清空buffer有两种方式:调用 clear()compact() 方法。clear会清空整个buffer,compact则只清空已读取的数据,未被读取的数据会被移动到buffer的开始位置,写入位置则近跟着未读数据之后。

    参考文献:

    https://www.cnblogs.com/dolphin0520/p/3919162.html

    https://www.cnblogs.com/dongguacai/p/5812831.html

    https://www.cnblogs.com/chenpi/p/6475510.html

  • 相关阅读:
    mysql 性能优化方案 (转)
    MYSQL 优化常用方法
    php高级研发或架构师必了解---很多问题面试中常问到!
    MetaMask/metamask-extension/mascara 的运行实现
    简单使用metamascara
    nodejs stream 手册学习
    nodejs-stream部分
    metamask中的import account的代码实现
    Signature Request Warnings & eth_sign学习
    gulp学习-metamask前端使用
  • 原文地址:https://www.cnblogs.com/Hermioner/p/9788081.html
Copyright © 2011-2022 走看看