Java NIO

1. JavaNIO简介

  • Java NIO(New IO / Non Blocking IO)是从Java 1.4版本开始引入的一个新的IO API,可以替代标准的Java IO API。
  • NIO与原来的IO有同样的作用和目的,但是使用的方式完全不同,NIO支持面向缓冲区的、基于通道的IO操作。NIO将以更加高效的方式进行文件的读写操作。
IO NIO
面向流 (Stream Oriented) 面向缓冲区 (Buffer Oriented)
阻塞IO (Blocking IO) 非阻塞IO (Non Blocking IO)
无选择器 选择器 (Selector)
单向传输 (输入与输出要分别建立输入流对象与输出流对象) 双向传输 (一个缓冲区承担输入与输出)
  • Java NIO系统的核心在于:通道(Channel)、缓冲区(Buffer)。
  • 通道(Channel):表示打开到 IO 设备(例如:文件、套接字)的连接。若需要使用 NIO 系统,需要获取用于连接 IO 设备的通道以及用于容纳数据的缓冲区,然后操作缓冲区,对数据进行处理。
  • 简言之,Channel 负责传输, Buffer 负责存储。

2. 缓冲区(Buffer)和通道(Channel)

2.1 缓冲区(Buffer)

  • 缓冲区(Buffer):一个用于特定基本数据类型的容器。由 java.nio 包定义的,所有缓冲区都是 Buffer 抽象类的子类。
  • Java NIO 中的 Buffer 主要用于与 NIO 通道进行交互,数据是从通道读入缓冲区,从缓冲区写入通道中的。
  • Buffer 的两种模式:
    • 写模式:新建一个Buffer、调用clear()方法、调用compact()方法
    • 读模式:调用flip()方法、ByteBuffer buffer = Charset.forName("UTF-8").encode("JavaNIO");中的encode()方法自动将ByteBuffer设置成读模式
  • Buffer 就像一个数组,可以保存多个相同类型的数据。根据数据类型不同(boolean除外) ,有以下 Buffer 常用子类:
    • ByteBuffer
    • CharBuffer
    • ShortBuffer
    • IntBuffer
    • LongBuffer
    • FloatBuffer
    • DoubleBuffer
  • 上述 Buffer 类都采用相似的方法管理数据,只是各自管理的数据类型不同而已。都是通过如下方法获取一个 Buffer对象:static XxxBuffer allocate(int capacity)(创建一个容量为 capacity 的 XxxBuffer 对象),一旦分配空间,就不可以动态调整。
  • Buffer 中的重要概念:
    • 容量(capacity):表示 Buffer 最大数据容量,缓冲区容量不能为负,并且创建后不能更改。
    • 限制(limit):表示第一个不应该读取或写入的数据的索引,即位于 limit 后的数据不可读取或写入。缓冲区的限制不能为负,并且不能大于其容量。
    • 位置(position):下一个要读取或写入的数据的索引。缓冲区的位置不能为负,并且不能大于其限制。
    • 标记(mark)与重置(reset):标记是一个索引,通过 Buffer 中的 mark() 方法指定 Buffer 中一个特定的 position,之后可以通过调用 reset() 方法恢复到这个 position。

0 ≤ mark ≤ position ≤ limit ≤ capacity

所谓的 Buffer 的读写模式,本质上就是 position 和 limit 联合作用于读写数据区域实现的。

ByteBuffer

  • Buffer 的常用方法如下:
方 法 描 述
Buffer clear() 清空(逻辑清空,已存储数据不会被清除,再次写入新数据会将旧数据覆盖)缓冲区并返回对缓冲区的引用
Buffer compact() 设置写模式,此时未读取的数据前置
Buffer flip() 将缓冲区的界限设置为当前位置,并将当前位置充值为 0
int capacity() 返回 Buffer 的 capacity 大小
boolean hasRemaining() 判断缓冲区中是否还有元素
int limit() 返回 Buffer 的界限 (limit) 的位置
Buffer limit(int n) 将设置缓冲区界限为 n,并返回一个具有新 limit 的缓冲区对象
Buffer mark() 对缓冲区设置标记
int position() 返回缓冲区的当前位置 position
Buffer position(int n) 将设置缓冲区的当前位置为 n,并返回修改后的 Buffer 对象
int remaining() 返回 position 和 limit 之间的元素个数
Buffer reset() 将 position 转到以前设置的 mark 所在的位置
Buffer rewind() 将 position 重置为 0(便于复读数据),取消设置的 mark
  • Buffer 所有子类提供了两个用于数据操作的方法:get() 与 put() 方法
    • 获取 Buffer 中的数据:
      • get():读取单个字节 (会移动 position)
      • get(byte[] dst):批量读取多个字节到 dst 中 (会移动 position)
      • get(int index):读取指定索引位置的字节 (不会移动 position)
    • 将数据放入 Buffer 中:
      • put(byte b):将给定单个字节写入缓冲区的当前位置 (会移动 position)
      • put(byte[] src):将 src 中的字节写入缓冲区的当前位置 (会移动 position)
      • put(int index, byte b):将指定字节写入缓冲区的索引位置 (不会移动 position)
package com.java.nio;

import org.junit.Test;
import java.nio.Buffer;
import java.nio.ByteBuffer;

public class BufferTest {
   
	//非直接缓冲区:通过allocate()方法分配非直接缓冲区,将缓冲区建立在JVM的内存中。
    //直接缓冲区:通过allocateDirect()方法分配直接缓冲区,将缓冲区建立在物理内存中,可以提高效率。
    @Test
    public void test3() {
   
        //分配直接缓冲区
        ByteBuffer buffer = ByteBuffer.allocateDirect(1024); //等价于:new DirectByteBuffer(1024);
        //判断该缓冲区是直接缓冲区还是非直接缓冲区
        System.out.println(buffer.isDirect()); //true
    }
    
    @Test
    public void test2() {
   
        String str = "abcde";
        ByteBuffer buffer = ByteBuffer.allocate(1024); //一旦分配空间,就不可以动态调整。
        buffer.put(str.getBytes());

        buffer.flip();

        byte[] dst = new byte[buffer.limit()];
        buffer.get(dst, 0, 2);

        System.out.println(new String(dst, 0, 2)); //ab
        System.out.println(buffer.position()); //2

        buffer.mark();

        buffer.get(dst, 2, 2);
        System.out.println(new String(dst, 0, 4)); //abcd
        System.out.println(buffer.position()); //4

        buffer.reset();

        System.out.println(buffer.position()); //2

        //判断缓冲区中是否还有剩余数据
        if (buffer.hasRemaining()) {
   
            //获取缓冲区中可以操作的数据的数量
            System.out.println(buffer.remaining()); //3
        }
    }

    @Test
    public void test1() {
   
        //1.分配一个指定大小的缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(1024); //底层实现:new byte[1024];

        System.out.println("----------allocate----------");
        System.out.println(buffer); //java.nio.HeapByteBuffer[pos=0 lim=1024 cap=1024]
        System.out.println("position = " + buffer.position()); //position = 0
        System.out.println("limit = " + buffer.limit()); //limit = 1024
        System.out.println("capacity = " + buffer.capacity()); //capacity = 1024

        //2.利用put方法将数据存入缓冲区
        String str = "abcde";
        ByteBuffer buffer2 = buffer.put(str.getBytes());

        System.out.println("----------put----------");
        System.out.println(buffer); //java.nio.HeapByteBuffer[pos=5 lim=1024 cap=1024]
        System.out.println(buffer2); //java.nio.HeapByteBuffer[pos=5 lim=1024 cap=1024]

        //3.利用flip方法切换到读取数据模式
        Buffer buffer3 = buffer.flip();

        System.out.println("----------flip----------");
        System.out.println(buffer); //java.nio.HeapByteBuffer[pos=0 lim=5 cap=1024]
        System.out.println(buffer3); //java.nio.HeapByteBuffer[pos=0 lim=5 cap=1024]

        //4.利用get方法读取缓冲区的数据
        byte[] dst = new byte[buffer.limit()];
        ByteBuffer buffer4 = buffer.get(dst);

        System.out.println("----------get----------");
        System.out.println(buffer); //java.nio.HeapByteBuffer[pos=5 lim=5 cap=1024]
        System.out.println(buffer4); //java.nio.HeapByteBuffer[pos=5 lim=5 cap=1024]
        System.out.println(new String(dst, 0, dst.length)); //abcde

        //5.rewind()方法:可重复读
        Buffer buffer5 = buffer.rewind();

        System.out.println("----------rewind----------");
        System.out.println(buffer); //java.nio.HeapByteBuffer[pos=0 lim=5 cap=1024]
        System.out.println(buffer5); //java.nio.HeapByteBuffer[pos=0 lim=5 cap=1024]

        //6.clear()方法:清空缓冲区,但是缓冲区中的数据依然存在,处于“被遗忘状态”。
        Buffer buffer6 = buffer.clear();

        System.out.println("----------clear----------");
        System.out.println(buffer); //java.nio.HeapByteBuffer[pos=0 lim=1024 cap=1024]
        System.out.println(buffer6); //java.nio.HeapByteBuffer[pos=0 lim=1024 cap=1024]
    }
}
package java.nio;

public abstract class Buffer {
   

    // The characteristics of Spliterators that traverse and split elements maintained in Buffers.
    static final int SPLITERATOR_CHARACTERISTICS = Spliterator.SIZED | Spliterator.SUBSIZED | Spliterator.ORDERED;

    // Invariants: mark <= position <= limit <= capacity
    private int mark = -1;
    private int position = 0;
    private int limit;
    private int capacity;

    // Used only by direct buffers
    // NOTE: hoisted here for speed in JNI GetDirectBufferAddress
    long address;

    // Creates a new buffer with the given mark, position, limit, and capacity, after checking invariants.
    Buffer(int mark, int pos, int lim, int cap) {
          
        if (cap < 0)
            throw new IllegalArgumentException("Negative capacity: " + cap);
        this.capacity = cap;
        limit(lim);
        position(pos);
        if (mark >= 0) {
   
            if (mark > pos)
                throw new IllegalArgumentException("mark > position: (" + mark + " > " + pos + ")");
            this.mark = mark;
        }
    }
    
    //Flips this buffer. The limit is set to the current position and then the position is set to zero. If the mark is defined then it is discarded. 
    public final Buffer flip() {
   
        limit = position;
        position = 0;
        mark = -1</
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值