Java NIO

ByteBuffer

ByteBuffer

image

使用byte[]数组存储

  1. capacity
    数组长度

  2. limit
    第一个不可读取或写入的index

  3. mark
    标记当前position,通过reset返回到mark位置

  4. position
    下一个要读取或写入的index

  5. int remaining()
    获取剩余容量: limit-position

0<mark<position<limit<capacity

  1. 判断缓冲区是否只读
ByteBuffer byteBuffer = ByteBuffer.wrap(new byte[] {1, 2, 3, 4, 5, 6});
System.out.println(byteBuffer.isReadOnly());  // false
  1. 直接缓冲区
    image
ByteBuffer byteBuffer = ByteBuffer.wrap(new byte[] {1, 2, 3, 4, 5, 6});
System.out.println(byteBuffer.isDirect());  // false
  1. 还原缓冲区状态
public final Buffer clear() {
    position = 0;	
    limit = capacity;
    mark = -1;
    return this;
}

position=1; limit=capacity; mark=-1;

  1. 对缓冲区进行反转 flip()
    将指针设置为0,mark设置为-1,当前位置设置为不可读写的位置
    将限制设置为当前位置
public final Buffer flip() {
    limit = position;
    position = 0;
    mark = -1;
    return this;
}

//  测试
ByteBuffer buffer = ByteBuffer.allocateDirect(10);
buffer.put(new byte[] {1, 2, 3, 4, 5, 6}, 2, 3);

//        buffer.rewind();
buffer.flip();
while (buffer.hasRemaining())
{
    System.out.println(buffer.get());
}
// 3  4  5
  1. remind()
    将指针设置为0,mark设置为-1
public final Buffer rewind() {
    position = 0;
    mark = -1;
    return this;
}

//  测试
ByteBuffer buffer = ByteBuffer.allocateDirect(10);
buffer.put(new byte[] {1, 2, 3, 4, 5, 6}, 2, 3);

buffer.rewind();
while (buffer.hasRemaining())
{
    System.out.println(buffer.get());
}
// 3  4  5 0 0 0 0 0 0 0
  1. putLong()
    将8个字节put到缓冲区中,如putLong(1) --> 1 2 3 4 5 6 7 8
    高位/低位 array() 字节顺序

  2. asCharBuffer 视图转换
    两个字节转为一个字符 e.g

ByteBuffer buffer = ByteBuffer.allocateDirect(10);
buffer.put(new byte[] {0, 98, 0, 99, 0, 100, 0, 101, 0, 102});  // StandardCharsets.UTF-16BE

buffer.rewind();

CharBuffer charBuffer = buffer.asCharBuffer();
while (charBuffer.hasRemaining())
{
    System.out.println(charBuffer.get());
}
// b c d e f

直接缓冲区和堆缓冲区

  • 直接缓冲区的内存分配和释放的成本要高于非直接缓冲区。因此不要频繁的创建直接缓冲区
  1. slice()
    内存共享缓冲区

  2. 压缩缓冲区

image

 byte[] bytes = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
 ByteBuffer buffer = ByteBuffer.wrap(bytes);

 buffer.position(2);
 buffer.compact();
 System.out.println("current position:" + buffer.position());  // 8
 buffer.rewind();

 while (buffer.hasRemaining())
 {
     System.out.println(buffer.get());
 }

3
4
5
6
7
8
9
10
9
10
  1. duplicate 复制缓冲区
    新缓冲区的内容一样,内存共享

Channel

通道概述

通道:数据传输的通路

NIO是将数据放在缓冲区进行管理,再利用通道进行数据传输到目的地

channel

image

jdk1.8 通道子接口

  • AsynchronousChannel
  • AsynchronousByteChannel
  • ReadableByteChannel
  • ScatteringByteChannel
  • WriteableByteChannel
  • GatheringByteChannel
  • ByteChannel
  • SeekableByteChannel
  • NetworkChannel
  • MulticastChannel
  • InterruptipleChannel
  1. AsynchronousChannel
    使通道支持异步IO操作
    (1)Future operation()
    Future用来判断IO操作是否完成,或者等待完成
    (2)回调的方式
    void operation(... A attachment, CompletionHandler<V, supper A> handler)

  2. ReadableByteChannel
    只允许一个线程read,第二个会被阻塞
    1) 将通道当前位置读到ByteBuffer中
    2) read(ByteBuffer )是同步的

  3. ScatteringByteChannel
    从通道中读取字节到多个缓冲区

public long read(ByteBuffer[] dsts) throws IOException;
  1. GatheringByteChannel
    将多个缓冲区的内容写到通道中

  2. ByteChannel
    整合ReadableByteChannel和WriteableChannel

  3. SeekableByteChannel
    允许position改变

  4. NetworkChannel
    与Socket进行关联
    bind(SocketAddress)

FilChannel

Charset charset = Charset.forName("UTF-8");//Java.nio.charset.Charset处理了字符转换问题。它通过构造CharsetEncoder和CharsetDecoder将字符序列转换成字节和逆转换。
CharsetDecoder decoder = charset.newDecoder();

System.out.println(System.getProperty("user.dir"));
FileInputStream fis = new FileInputStream("nio/src/main/resources/aa.txt");

FileChannel fileChannel = fis.getChannel();
ByteBuffer byteBuffer = ByteBuffer.allocate(4);
CharBuffer charBuffer = CharBuffer.allocate(4);
int i = 0;
while (fileChannel.isOpen() && fileChannel.read(byteBuffer) != -1)
{
    System.out.print("第" + (++i) + "次填充byte缓冲区,长度为:" + byteBuffer.position());
    byteBuffer.flip();
    decoder.decode(byteBuffer, charBuffer, true);
    System.out.println("  char decode use length=" + byteBuffer.position());

    charBuffer.flip();
    while (charBuffer.hasRemaining())
    {
        System.out.println("    转换为char缓冲区,charBuffer[" + charBuffer.position() + "]=" + charBuffer.get());
    }
    charBuffer.clear();
    System.out.println();
    byteBuffer.compact();  // 压缩,把未使用的压缩到前面
}
# 输入 aa.txt
大型社死现场
# 输出
第1次填充byte缓冲区,长度为:4  char decode use length=3
    转换为char缓冲区,charBuffer[0]=大

第2次填充byte缓冲区,长度为:4  char decode use length=3
    转换为char缓冲区,charBuffer[0]=型

第3次填充byte缓冲区,长度为:4  char decode use length=3
    转换为char缓冲区,charBuffer[0]=社

第4次填充byte缓冲区,长度为:4  char decode use length=3
    转换为char缓冲区,charBuffer[0]=死

第5次填充byte缓冲区,长度为:4  char decode use length=3
    转换为char缓冲区,charBuffer[0]=现

第6次填充byte缓冲区,长度为:3  char decode use length=3
    转换为char缓冲区,charBuffer[0]=场

分析:第一次读取四个字节:[1,2,3,4],在字符解码时,使用了三个字节转码为 ‘大’,使用压缩方法将剩余的未使用的字节压缩到开头,然后继续从后面读取,以此反复操作。

由此可知:中文一个字符占用三个字节,数字、英文字母占用一个字节

posted @ 2021-07-29 17:40  fight139  阅读(39)  评论(0编辑  收藏  举报