NIO之Buffer

Buffer


 

Buffer

Mark<=Position <=Limt<=Capacity

状态变量

  • position:

在从通道读取时,将所读取的数据放到底层的数组中。 position 变量跟踪已经写了多少数据。它指定了下一个字节将放到数组的哪一个元素中。因此,如果从通道中读三个字节到缓冲区中,那么缓冲区的position 将会设置为3,指向数组中第四个元素。

  • mark:

一个备忘标记位置调用,mark()函数设置mark=positon,调用reset()设置position=mark。

  • limit

limit 变量表明还有多少数据需要取出(在从缓冲区写入通道时),或者还有多少空间可以放入数据(在从通道读入缓冲区时)。

  • capacity

缓冲区的 capacity 表明可以储存在缓冲区中的最大数据容量。实际上,它指定了底层数组的大小或者指定了准许使用的底层数组的容量。

图形演示

初始:一个新创建的缓冲区。假设这个缓冲区的 总容量 为8个节。 Buffer 的状态如下所示:

  • 缓冲区:写入5个字节。Buffer 的状态如下所示:

  • flip():它将设置limit为它将设置position为0;它将设置position为0;可以读缓冲区的内容了:

  • 写入通道

第一次写入时,从缓冲区中取四个字节并将它们写入输出通道。这使得 position 增加到
4,而 limit 不变:

再次写入,只剩下一个字节可写, limit在调用 flip() 时被设置为
5,并且 position 不能超过 limit。所以最后一次写入操作从缓冲区取出一个字节并将它写入输出通道。这使得 position 增加到
5,并保持 limit 不变。

  • clear

它将limit设置为与capacity 相同;它设置 position为0;可以再次往缓冲区写数据了。

缓冲区操作

  • 分配


  • /**
    * 分配缓冲区
    */
    @Test
    public void allocateBuffer(){
    ByteBuffer bf=ByteBuffer.allocate(1024);
    System.out.println("position:"+bf.position());
    System.out.println("limit:"+bf.limit());
    System.out.println("capacity:"+bf.capacity());
    }

    运行结果:


  • 包装


  • /**
    * 将原有数组包装成一个缓冲区。
    */
    public void wrapBuffer(){
    int [] ints={1,2,3,4,5};
    IntBuffer ib=IntBuffer.wrap(ints);
    ib.put(1,8);
    System.out.println(Arrays.toString(ints));
    }

    运行结果:

数组和缓冲区共用一分数据。

  • 分片


  • @Test
    public void sliceBuffer() {
    IntBuffer ib = IntBuffer.allocate(10);
    for(int i=0;i<10;i++){
    ib.put(i);
    }
    ib.position(3);
    ib.limit(6);
    IntBuffer sliceBuf=ib.slice();
    System.out.println("before slice:"+Arrays.toString(sliceBuf.array()));
    for(int j=0;j<sliceBuf.capacity();j++){
    sliceBuf.put(j,sliceBuf.get()*3);
    }
    System.out.println("after slice:"+Arrays.toString(sliceBuf.array()));
    System.out.println("old buffer"+Arrays.toString(sliceBuf.array()));
    }

    运行结果:

原缓冲区和新的缓冲区分片共享同一个底层数据数组,并且对缓冲区分片的新缓冲区修改只影响子缓冲区。

  • 压缩


  • @Test
    public void compactBuffer() {
    CharBuffer cb = CharBuffer.allocate(15);
    for (int i = 65; i < 75; i++) {
    cb.put((char) i);
    }
    cb.flip();
    System.out.println("original buffer:"+Arrays.toString(cb.array()));
    System.out.println("original positon:"+cb.position());
    System.out.println("original limit:"+cb.limit());
    for(int j=0;j<6;j++){
    cb.get();
    }
    System.out.println("after get position:"+cb.position());
    cb.compact();
    System.out.println("after compact position:"+cb.position());
    System.out.println("after compact limit:"+cb.limit());
    System.out.println("after compact buffer:"+Arrays.toString(cb.array()));
    cb.flip();
    System.out.println("after flip position:"+cb.position());
    System.out.println("after flip limit:"+cb.limit());
    }

    运行结果:

未读的元素移动到下表0开始,position为最后一个未读元素的下一个下标,limit在这个过程中没变化,如果想读取这部分元素,执行一次翻转。

  • 比较

  两个缓冲区相等的充分必要条件:

  1. 两个缓冲区类型必须相同

  2. 两个缓冲区剩余元素数量必须相同,两个缓冲区容量可以不同

  3.  两个缓冲区通过get()取得的元素序列必须相同


@Test
public void compareBuffer(){
//cb1和cb2的capacity不相等
CharBuffer cb1=CharBuffer.allocate(10);
CharBuffer cb2=CharBuffer.allocate(8);
for (int i=65;i<75;i++){
cb1.put((char)i);
}
for (int j=69;j<75;j++){
cb2.put((char)j);
}
cb1.flip();
cb2.flip();
System.out.println("original cb1:"+Arrays.toString(cb1.array()));
System.out.println("original cb2:"+Arrays.toString(cb2.array()));
//cb1读取六个元素
for(int m=0;m<5;m++){
cb1.get();
}
//cb2读取一个元素
cb2.get();
System.out.println(cb1.equals(cb2));
}

运行结果:

只读缓冲区



  • public void readOnlyBuffer() {
    IntBuffer ib = IntBuffer.allocate(10);
    for (int i = 0; i < 10; i++) {
    ib.put(i);
    }
    System.out.println("original buffer:"+Arrays.toString(ib.array()));
    IntBuffer readOnlyBuffer=ib.asReadOnlyBuffer();
    readOnlyBuffer.flip();
    //以下语句会接收异常:ReadOnlyBufferException。
    //System.out.println("readonly buffer:"+Arrays.toString(readOnlyBuffer.array()));
    System.out.print("original readonly buffer:");
    for (int j=0;j<readOnlyBuffer.capacity();j++){
    System.out.print(readOnlyBuffer.get());

    }
    }

    只读缓冲区会和原缓冲区公用一分数据。


直接缓冲区

给定一个直接字节缓冲区,Java 虚拟机将尽最大努力直接对它执行本机 I/O
操作。也就是说,它会在每一次调用底层操作系统的本机 I/O
操作之前(或之后),尝试避免将缓冲区的内容拷贝到一个中间缓冲区中(或者从一个中间缓冲区中拷贝数据)


/**
* 直接缓冲区
* @throws Exception
*/
@Test
public void directBuffer() throws Exception {
FileInputStream fi = new FileInputStream(this.sourcePath);
FileOutputStream fo = new FileOutputStream(this.destPath);
ByteBuffer directBuffer = ByteBuffer.allocateDirect(1024);
FileChannel fic = fi.getChannel();
FileChannel foc = fo.getChannel();
while (fic.read(directBuffer) != -1) {
directBuffer.flip();
foc.write(directBuffer);
directBuffer.clear();
}
fi.close();
fo.close();
}

内存映像

内存映射文件 I/O
是通过使文件中的数据出现为内存数组的内容来完成的。一般来说,只有文件中实际读取或者写入的部分才会送入(或者 映射 )到内存中。

只能通过FileChannel来创建。

代码实例:


public void mappedByteBuffer() throws  Exception{
FileInputStream fi = new FileInputStream(this.sourcePath);
//MappedByteBuffer是ByteBuffer的子类。
MappedByteBuffer mappedByteBuffer=fi.getChannel().map(FileChannel.MapMode.READ_ONLY,0l,1024l);

}

字节缓冲区

  • 字节顺序

  1. 大端字节

2.  小端字节

  • 取决于硬件设计,JVM默认是大端字节,IP协议使用大端的网络字节顺序。

  • JVM对字节顺序的支持:ByteOrder

获取本地字节顺序:ByteOrder.nativeOrder();


 /**
* 字节顺序
*/
@Test
public void byteOrder(){
System.out.println("My mac pro byte order:"+ByteOrder.nativeOrder());
}

运行结果:

视图缓冲区


/**
* 视图buffer
*/
@Test
public void viewBuffer() {
this.byteOrder();
ByteBuffer bb = ByteBuffer.allocate(16).order(ByteOrder.BIG_ENDIAN);
CharBuffer cb=bb.asCharBuffer();
bb.put((byte)0);
bb.put((byte)‘r‘);
bb.put((byte)0);
bb.put((byte)‘e‘);
bb.put((byte)0);
bb.put((byte)‘q‘);
bb.put((byte)0);
bb.put((byte)‘u‘);
bb.put((byte)0);
bb.put((byte)‘e‘);
bb.put((byte)0);
bb.put((byte)‘l‘);
bb.put((byte)0);
bb.put((byte)‘q‘);
bb.put((byte)0);
bb.put((byte)‘i‘);
System.out.println("original bb position:"+bb.position()+",limit:"+bb.limit()+",bb is:"+Arrays.toString(bb.array()));
System.out.println("view buffer cb position:"+cb.position()+",limit:"+cb.limit()+",bb is:"+cb.toString());
}

运行结果:

时间: 2024-10-19 03:07:55

NIO之Buffer的相关文章

《Java源码分析》:Java NIO 之 Buffer

<Java源码分析>:Java NIO 之 Buffer 在上篇博文中,我们介绍了Java NIO 中Channel 和Buffer的基本使用方法,这篇博文将从源码的角度来看下Buffer的内部实现. 在Java API文档中,对Buffer的说明摘入如下: Buffer:是一个用于特定基本数据类型的容器.这里的特定基本数据类型指的是:除boolean类型的其他基本上数据类型. 缓冲区是特定基本数据类型元素的线性有限序列.除内容外,缓冲区饿基本属性还包括三个重要的属性,如下: 1.capaci

NIO的Buffer类族和Channel

在NIO的实现中,Buffer是一个抽象类.JDK为每一种Java原生类型都创建了一个Buffer,如图所示. 除了ByteBuffer外,其他每一种Buffer都具有完全一样的操作,唯一的区别仅仅在于它们所对应的数据类型.因为ByteBuffer多用于绝大多数标准I/O操作的接口,因此它有些特殊的方法. 在NIO中和Buffer配合使用的还有Channel.Channel是一个双向通道,既可读,也可写.有点类似Stream,但Stream是单向的.应用程序中不能直接对Channel进行读写操作

一天一个类--NIO 之Buffer

java.nio  --- 定义了 Buffer 及其数据类型相关的子类.其中被 java.nio.channels 中的类用来进行 IO 操作的 ByteBuffer 的作用非常重要. java.nio.channels----定义了一系列处理 IO 的 Channel 接口以及这些接口在文件系统和网络通讯上的实现.通过 Selector 这个类,还提供了进行非阻塞 IO 操作的办法.这个包可以说是 NIO API 的核心. java.nio.channels.spi----定义了可用来实现

【Java nio】buffer

1 package com.slp.nio; 2 3 import org.junit.Test; 4 5 import java.nio.ByteBuffer; 6 7 /** 8 * Created by sanglp on 2017/3/1. 9 * 一.缓冲区:在Java nio中负责数据的存储.缓冲区就是数据,用于村塾不同数据类型的数据 10 * 根据数据类型的不同(boolean类型除外),提供了相应类型的缓冲区 11 * ByteBuffer 12 * CharBuffer 13

通俗编程——白话NIO之Buffer

Buffer简单介绍 Buffer意为缓冲区.其本质上就是是一块可写入数据,然后能够从中读取数据的内存区域.通过该种方式有助于降低系统开销和提高外设效率.对于缓冲区我们早有所了解,比方在C中标准I/O中的read,write直接调用系统的输入输出.而scanf和printf则借助缓冲区在适当的时候调用read.write操作.在NIO中,为了方便对缓冲区的操作.jAVA设计者将缓冲区封装为Buffer(实际上就是封装了基本数据元素的数组),并提供对应的方法对其操作. 在開始之前,首先须要明确下面

Java NIO -- 缓冲区(Buffer)的数据存取

缓冲区(Buffer): 一个用于特定基本数据类型的容器.由 java.nio 包定义的,所有缓冲区都是 Buffer 抽象类的子类.Java NIO 中的 Buffer 主要用于与 NIO 通道进行交互,数据是从通道读入缓冲区,从缓冲区写入通道中的. Buffer 就像一个数组,可以保存多个相同类型的数据.根据数据类型不同(boolean 除外) ,有以下 Buffer 常用子类:ByteBufferCharBuffer ShortBuffer IntBuffer LongBuffer Flo

Java NIO (3) Buffer

Java NIO Buffer Java NIO Buffers are used when interacting with NIO Channels. As you know, data is read from channels into buffers, and written from buffers into channels. A buffer is essentially a block of memory into which you can write data, which

NIO之Buffer的clear()、rewind()、flip()方法的区别

Java的NIO中有关Buffer的几种常用方法比如clear,rewind和flip到底有哪些区别.下面给大家这三种方法的源码,方便大家记忆. clear()方法用于写模式,其作用为情况Buffer中的内容,所谓清空是指写上限与Buffer的真实容量相同,即limit==capacity,同时将当前写位置置为最前端下标为0处.代码如下: public final Buffer clear() { position = 0; //设置当前下标为0 limit = capacity; //设置写越

NIO的Buffer的相关操作

1.Buffer的创建 Buffer的创建可以通过两种方式.使用静态方法allocate()从堆中分配缓冲区,或者是一个既有的数组中创建缓冲区: //从堆中分配 ByteBuffer buffer = ByteBuffer.allocate(1024); //从既有的数组中创建 byte array[] = new byte[1024]; ByteBuffer buffer = ByteBuffer.wrap(array); 2.重置和清空缓冲区 Buffer 还提供了一些用于重置和清空Buff

Java NIO:Buffer、Channel 和 Selector

Buffer 一个 Buffer 本质上是内存中的一块,我们可以将数据写入这块内存,之后从这块内存获取数据. java.nio 定义了以下几个 Buffer 的实现,这个图读者应该也在不少地方见过了吧. 其实核心是最后的 ByteBuffer,前面的一大串类只是包装了一下它而已,我们使用最多的通常也是 ByteBuffer. 我们应该将 Buffer 理解为一个数组,IntBuffer.CharBuffer.DoubleBuffer 等分别对应 int[].char[].double[] 等.