Java NIO 中的 Buffer 缓冲区详解(下)

简介: Java NIO 中的 Buffer 缓冲区详解

2、向 buffer 中写数据


写数据到 Buffer 有两种方式:


(1)从 channel 写到 Buffer


(2)通过 Buffer 的 put 方法写到 Buffer 里。


从 Channel 写到 Buffer 的例子


int byteRead = channel.read(buf); // read into buffer


通过 put 方法写入 buffer 的例子:


buf.put(100);


put 的方法有很多版本,允许你不同的方式把数据写入到 buffer 中,例如,写到一个指定的位置,或者把字节数组写入到 Buffer .


3、flip() 方法


flip 方法将 Buffer 从写模式切换到读模式。调用 flip() 方法将会 position 设置为 0 , 并且将 limt 设置为之前 position 的值。换句话说,position 现在用于标记读的位置,limit 表示之前写进了多少个 byte, char 等(现在能读取多少个 byte, char 等)。


从 Buffer 中读取数据


从 Buffer 中读取数据到 Channel 中:


(1) 从 Buffer 中读取数据到 Channel


(2)使用 get 方法从 Buffer 中读取数据


从 Buffer 中读取数据到 Channnel 到例子:


// read form buffer into channel 
int bytesWritten = inChannel.write(buf);


使用 get() 方法从 Buffer 中读取数据的例子:


byte aByte = buf.get();


get 方法中有很多版本,允许你以不同的方式 Buffer 中读取数据。例如,从指定 position 读取,或者从 Buffer 中读取到字节数组。


Buffer 几个方法


1、rewind() 方法


Buffer.rewind() 将 position 返回0, 所以你可以重读 Buffer 中的所有数据。limit 保持不变,仍然表示能从 Buffer 中读取到多少个元素(byte, char 等)。


2、clear() 与 compact() 方法


一旦读完 Buffer 中的数据,需要让 Buffer 准备好再次被写入。可以通过 clear() 或 compact() 方法来完成


如果调用的是 cleanr () 方法,position 兼备设置为 0 , limit 被设置成 capactiy 的值。换句话说,Buffer 被清空了。 Buffer 中的数据并未清除,只是这些标记高数我们从哪里开始往 Buffer 中写数据。


如果 Buffer 中有些数未读的数据,调用 clear() 方法,数据将 “被遗忘”,意味着不在有任何标记会告诉你那些数据被读过,那些还没有。


如果 Buffer 中依然有未读的数据,且后续还需要这些数据,但是此时想要先写这些数据,那么使用 compact() 方法。


compact() 方法将所有未读的数据拷贝到 Buffer 起始处。然后将 position 设置到最后一个未读元素正后面。 limit 属性依然像 clear() 方法一样。设置成 capacity. 现在 Buffer 准备好写数据了,但是不会覆盖未读的数据。


3、mark() 与 reset() 方法


通过调用 Buffer.mark() 方法,可以标记 Buffer 中的一个特定 position . 之后可以通过调用 Buffer.reset() 方法恢复到这个 position 例如:


buffer.mark();
// call buffer.get() a couple of times, e.g. during parsing  
buffer.reset(); // set position back to mark


缓冲区操作


1、缓冲区分片


在 NIO 中除了可以分配或者包装一个缓冲区对象外,还可以更具现有的缓冲区对象来创建一个子缓冲区,即现有缓冲区上切出一片来作为一个新的缓冲区,但现有的缓冲区与创建的子缓冲区在底层数组层面上是数据共享的,也就是说,子缓冲区相当于是现有缓冲区的一个视图窗口。调用 slice() 方法可以创建一个子缓冲区。


// 缓冲区分片
@Test
public void b01() {
    ByteBuffer buffer = ByteBuffer.allocate(10);
    // 放入数据
    for (int i = 0; i < buffer.capacity(); i++) {
        buffer.put((byte) i);
    }
    // 创建子缓冲区
    buffer.position(3);
    buffer.limit(7);
    ByteBuffer slice = buffer.slice();
    // 改变子缓冲区中的内容
    for (int i = 0; i < slice.capacity(); i++) {
        byte b = slice.get(i);
        b *= 10;
        slice.put(i, b);
    }
    // 复位
    buffer.position(0);
    buffer.limit(buffer.capacity());
    while (buffer.remaining() > 0) {
        System.out.println(buffer.get());
    }
}


输出结果如下:


image.png


2、只读缓冲区


只读缓冲区非常简单,可以读取他们,但是不能向他们写入数据。可以通过调用缓冲区的 asReadOnlyBufer() 方法,将任何常规缓冲区转换为只读缓冲区,这个方法返回一个与原缓冲区完全相同的缓冲区,并与原缓冲区共享数据,只不过它是只读的。如果原缓冲区的内容发生了变化,只读缓冲区的内容随之发生变化:


// 只读缓冲区
@Test
public void b02() {
    ByteBuffer buffer = ByteBuffer.allocate(10);
    // 放入数据
    for (int i = 0; i < buffer.capacity(); i++) {
        buffer.put((byte) i);
    }
    // 创建一个只读缓冲区
    ByteBuffer readOnlyBuf = buffer.asReadOnlyBuffer();
    for (int i = 0; i < buffer.capacity(); i++) {
        byte b = buffer.get(i);
        b *= 10;
        buffer.put(i, b);
    }
    readOnlyBuf.position(0);
    readOnlyBuf.limit(readOnlyBuf.capacity());
    while (readOnlyBuf.remaining() > 0) {
        System.out.println(readOnlyBuf.get());
    }
}


3、直接缓冲区


直接缓冲区为了加快 I/O 速度,使用一种特殊的方式为其分配内存的缓冲区, JDK 文档的描述为:给定一个直接字节缓冲区,Java 虚拟机将尽最大努力直接对它执行本机 I/O 操作之前(或之后),尝试避免将缓冲区的内容拷贝到一个中间缓冲区中或者从一个中间缓冲区中拷贝数据。要分配直接缓冲区,需要调 allocatieDirect() 方法,而不是 alloacte() 方法,使用方式与普通缓冲区并无区别。


// 直接缓冲区,文件拷贝
@Test
public void b03() throws IOException {
    String filePath = "/xx/01.txt";
    FileInputStream inputStream = new FileInputStream(filePath);
    FileChannel fileInChannel = inputStream.getChannel();
    String outPath = "/xx/02.txt";
    FileOutputStream outputStream = new FileOutputStream(outPath);
    FileChannel fileOutChannel1 = outputStream.getChannel();
    //  使用 allocateDirect , 而不是 allocate
    ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
    while (true) {
        buffer.clear();
        int r = fileInChannel.read(buffer);
        if (r == -1) {
            break;
        }
        buffer.flip();
        fileOutChannel1.write(buffer);
    }
    fileInChannel.close();
    fileOutChannel1.close();
}


4、内存映射文件 I/O


内存映射文件 I/O 是一种读和写文件数据的方法,它可以比常规的基于流或者通道的 I/O 快得多。内存映射 I/O 是通过使文件中的数据出现为内存数组的内容来完成的,这起初听起来师傅不过就是为了将整个文件读取到内容中,但是事实上并不是这样的。一般来说只有文件实际读取或者写入的部分才会映射到内存中。


// 内存映射文件 I/O 
@Test
public void b04() throws IOException {
    String filePath = "/xxx/01.txt";
    RandomAccessFile randomAccessFile = new RandomAccessFile(filePath, "rw");
    FileChannel fileChannel = randomAccessFile.getChannel();
    MappedByteBuffer mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, 1024);
    mappedByteBuffer.put(0, (byte) 97);
    mappedByteBuffer.put(1023, (byte) 122);
    fileChannel.close();
}


相关文章
|
3天前
|
监控 Java API
探索Java NIO:究竟在哪些领域能大显身手?揭秘原理、应用场景与官方示例代码
Java NIO(New IO)自Java SE 1.4引入,提供比传统IO更高效、灵活的操作,支持非阻塞IO和选择器特性,适用于高并发、高吞吐量场景。NIO的核心概念包括通道(Channel)、缓冲区(Buffer)和选择器(Selector),能实现多路复用和异步操作。其应用场景涵盖网络通信、文件操作、进程间通信及数据库操作等。NIO的优势在于提高并发性和性能,简化编程;但学习成本较高,且与传统IO存在不兼容性。尽管如此,NIO在构建高性能框架如Netty、Mina和Jetty中仍广泛应用。
14 3
|
10天前
|
存储 监控 Java
Java的NIO体系
通过本文的介绍,希望您能够深入理解Java NIO体系的核心组件、工作原理及其在高性能应用中的实际应用,并能够在实际开发中灵活运用这些知识,构建高效的Java应用程序。
26 5
|
5月前
|
Java 大数据
解析Java中的NIO与传统IO的区别与应用
解析Java中的NIO与传统IO的区别与应用
|
1月前
|
消息中间件 缓存 Java
java nio,netty,kafka 中经常提到“零拷贝”到底是什么?
零拷贝技术 Zero-Copy 是指计算机执行操作时,可以直接从源(如文件或网络套接字)将数据传输到目标缓冲区, 而不需要 CPU 先将数据从某处内存复制到另一个特定区域,从而减少上下文切换以及 CPU 的拷贝时间。
java nio,netty,kafka 中经常提到“零拷贝”到底是什么?
|
2月前
|
Java
让星星⭐月亮告诉你,Java NIO之Buffer详解 属性capacity/position/limit/mark 方法put(X)/get()/flip()/compact()/clear()
这段代码演示了Java NIO中`ByteBuffer`的基本操作,包括分配、写入、翻转、读取、压缩和清空缓冲区。通过示例展示了`position`、`limit`和`mark`属性的变化过程,帮助理解缓冲区的工作原理。
38 2
|
3月前
|
存储 网络协议 Java
Java NIO 开发
本文介绍了Java NIO(New IO)及其主要组件,包括Channel、Buffer和Selector,并对比了NIO与传统IO的优势。文章详细讲解了FileChannel、SocketChannel、ServerSocketChannel、DatagramChannel及Pipe.SinkChannel和Pipe.SourceChannel等Channel实现类,并提供了示例代码。通过这些示例,读者可以了解如何使用不同类型的通道进行数据读写操作。
Java NIO 开发
|
2月前
|
缓存 Java
java文件读取 while ((len = reader.read(buffer)) != -1){}的理解
本文解释了Java中使用`InputStreamReader`和`read(buffer)`方法循环读取文件内容的机制,强调了如何正确理解读取循环和处理读取到的数据,以及如何处理字符编码和换行符。
57 0
|
4月前
|
Java
"揭秘Java IO三大模式:BIO、NIO、AIO背后的秘密!为何AIO成为高并发时代的宠儿,你的选择对了吗?"
【8月更文挑战第19天】在Java的IO编程中,BIO、NIO与AIO代表了三种不同的IO处理机制。BIO采用同步阻塞模型,每个连接需单独线程处理,适用于连接少且稳定的场景。NIO引入了非阻塞性质,利用Channel、Buffer与Selector实现多路复用,提升了效率与吞吐量。AIO则是真正的异步IO,在JDK 7中引入,通过回调或Future机制在IO操作完成后通知应用,适合高并发场景。选择合适的模型对构建高效网络应用至关重要。
98 2
|
4月前
|
网络协议 C# 开发者
WPF与Socket编程的完美邂逅:打造流畅网络通信体验——从客户端到服务器端,手把手教你实现基于Socket的实时数据交换
【8月更文挑战第31天】网络通信在现代应用中至关重要,Socket编程作为其实现基础,即便在主要用于桌面应用的Windows Presentation Foundation(WPF)中也发挥着重要作用。本文通过最佳实践,详细介绍如何在WPF应用中利用Socket实现网络通信,包括创建WPF项目、设计用户界面、实现Socket通信逻辑及搭建简单服务器端的全过程。具体步骤涵盖从UI设计到前后端交互的各个环节,并附有详尽示例代码,助力WPF开发者掌握这一关键技术,拓展应用程序的功能与实用性。
155 0
|
5月前
|
安全 Java Linux
(七)Java网络编程-IO模型篇之从BIO、NIO、AIO到内核select、epoll剖析!
IO(Input/Output)方面的基本知识,相信大家都不陌生,毕竟这也是在学习编程基础时就已经接触过的内容,但最初的IO教学大多数是停留在最基本的BIO,而并未对于NIO、AIO、多路复用等的高级内容进行详细讲述,但这些却是大部分高性能技术的底层核心,因此本文则准备围绕着IO知识进行展开。
191 1