Java NIO流

上篇博客中了解Java IO,这篇博客重点介绍Java NIO,着重学习 NIO 中的流。



Java NIO 是 Java New I/O 的简称。其特性如下:

1、为所有的原始类型提供(buffer)缓存支持。

2、字符集编码解码解决方案。

3、Channel 一个新的原始I/O抽象。

4、支持锁和内存映射文件的文件访问接口。

5、提供多路(non-blocking)非阻塞式的高伸缩性网络 I/O。

NIO创建的目的 是为了让Java程序员可以实现高速而无需编写自定义的本机代码。NIO将最耗时的I/O操作(即填充和提取缓冲区)转移回操作系统,因而可以极大的提高速度。


在JDK1.4中原来的I/O包和NIO已经很好的集成了,Java.io.* 已经以NIO为基础重新实现了,所以它现在可以利用NIO的一些特性。

缓冲区是包在一个对象内的基本数据元素数组。buffer类相比一个简单数组的优点是它将关于数据的数据内容和信息包含在一个单一的对象中。buffer 类以及它专有的子类定义了一个用于处理数据缓冲区的API 。所有的缓冲区都具有四个属性来提供关于其所包含的数据元素的信息

1、容量(Capacity)

缓冲区能够容纳的数据元素的最大数量。这一容量在缓冲区创建时被设定,并且永远不能被改变。代表缓冲区的最大容量(一般新建一个缓冲区的时候,limit的值和capacity的值默认是相等的)

2、上界(Limit)

缓冲区的第一个不能被读或写的元素。缓冲区中现存元素的计数。所有对Buffer读写操作都会以limit变量的值作为上限。

3、位置(Position)

下一个要被读或写的元素的索引。位置会自动由相应的get()和put()函数更新。

4、标记(Mark)

一个备忘位置。调用Mark()来设定 mark = postion。调用reset()设定position = mark。标记在设定前是未定义的(undefined)。代表对缓冲区进行读写时,当前游标的位置。


flip、rewind、clear这三个方法便是用来设置这些值的。
flip方法
反转缓冲区。首先将限制设置为当前位置,然后将位置设置为 0。如果已定义了标记,则丢弃该标记。 常与compact方法一起使用。通常情况下,在准备从缓冲区中读取数据时调用flip方法。
public final Buffer flip() {
 limit = position;
 position = 0;
 mark = -1;
 return this;
}
rewind方法
rewind() 使缓冲区为重新读取已包含的数据做好准备:它使限制保持不变,将位置设置为 0。(也就是相当于选取当前缓冲去总的全部有效数据)和clear()类似,只是不改动限制.

public final Buffer rewind() {
 position = 0;
 mark = -1;
 return this;
}
clear方法
clear方法将缓冲区清空,一般是在重新写缓冲区时调用。

public final Buffer clear()
{
    position = 0; //重置当前读写位置
    limit = capacity; 
    mark = -1;  //取消标记
    return this;
}

缓冲区的分类:(注:非布尔类型)

1、字节缓冲区 ByteBuffer

2、字符缓冲区 CharBuffer

3、双精浮点型(double)缓冲区 DoubleBuffer

4、单精浮点型(float)缓冲区 FloatBuffer

5、整型(int)缓冲区 IntBuffer

6、长整型(long)缓冲区 LongBuffer

7、短整型(short)缓冲区 ShortBuffer

上述各类型的缓冲区都提供了读和写的方法 get 和 put 方法,也提供了一些批量的get 和 put 方法。而且缓冲区可以通过allocation创建,此方法通过wrapping 将一个现有(数据类型)数组包装到缓冲区中来为缓冲区内容分配空间,或者通过创建现有字节缓冲区的视图来创建。

缓冲区的操作:

1、存 put

2、取 get

复制缓冲区

可以创建描述从外部存储到数组中的数据元素的缓冲区对象。但是缓冲区不限于管理数组中的外部数据。它们也能管理其他缓冲区中的外部对象。Duplicate()函数创建了一个与原始缓冲区相似的新缓冲区。两个缓冲区共享数据元素,拥有同样的容量,但每个缓冲区拥有各自的位置,上界和标记属性。对一个缓冲区内的数据元素所做的改变会反映在另一个缓冲区上。这一副本缓冲区具有与原始缓冲区同样的数据视图。如果原始的缓冲区为只读,或者为直接缓冲区,新的缓冲区将继承这些属性。需要注意的是,复制一个缓冲区会创建一个新的buffer对象,但并不复制数据。原始缓冲区和副本都会操作同样的数据元素。









### Java NIO 的使用教程及示例 Java NIO 是一种高效的输入/输出模型,它通过通道(Channel)、缓冲区(Buffer)以及选择器(Selector)来实现高性能的数据读写操作。以下是关于 Java NIO 的详细介绍及其实际应用。 #### 1. 基本概念 Java NIO 提供了一种全新的方式来进行文件和网络数据的操作。其核心组件包括: - **Channel**: 数据传输的核心接口,类似于传统 IO 中的 Stream。常见的 Channel 实现有 `FileChannel` 和 `SocketChannel` 等[^1]。 - **Buffer**: 数据容器,在 NIO 中所有的数据都必须先放入 Buffer 中才能被 Channel 处理。常用的 Buffer 类型有 `ByteBuffer`, `CharBuffer`, `IntBuffer` 等。 - **Selector**: 负责监控多个 Channel 上的事件(如连接打开、数据到达等),并提供多路复用的功能[^3]。 这些组件共同构成了 Java NIO 的基础架构。 #### 2. 文件读写的简单示例 下面是一个简单的例子,展示如何利用 FileChannel 进行文件的读取与写入操作。 ```java import java.nio.file.*; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; public class FileReadWriteExample { public static void main(String[] args) throws Exception { Path path = Paths.get("example.txt"); try (FileChannel channel = FileChannel.open(path, StandardOpenOption.READ)) { ByteBuffer buffer = ByteBuffer.allocate(1024); int bytesRead = channel.read(buffer); // 将数据从 Channel 读到 Buffer while (bytesRead != -1) { // 当没有更多数据可读时返回 -1 buffer.flip(); // 切换为读模式 while (buffer.hasRemaining()) { System.out.print((char) buffer.get()); // 输出字符 } buffer.clear(); // 清空 Buffer 准备下一次读取 bytesRead = channel.read(buffer); } } // 写入文件的例子 String content = "This is a test."; ByteBuffer writeBuffer = ByteBuffer.wrap(content.getBytes()); try (FileChannel outChannel = FileChannel.open(path, StandardOpenOption.WRITE, StandardOpenOption.CREATE)) { outChannel.write(writeBuffer); // 将 Buffer 写入 Channel } } } ``` 上述代码展示了如何使用 `FileChannel` 来完成文件的内容读取和写入过程。 #### 3. 非阻塞服务器端编程实例 另一个重要的场景是非阻塞式的网络通信。这里给出一个基于 Selector 的非阻塞 TCP Server 示例。 ```java import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.Iterator; import java.util.Set; public class NonBlockingServer { private final int port; public NonBlockingServer(int port) { this.port = port; } public void start() throws IOException { Selector selector = Selector.open(); ServerSocketChannel serverChannel = ServerSocketChannel.open(); serverChannel.configureBlocking(false); InetSocketAddress address = new InetSocketAddress(port); serverChannel.socket().bind(address); serverChannel.register(selector, SelectionKey.OP_ACCEPT); System.out.println("Listening on port " + port); while (true) { selector.select(); Set<SelectionKey> selectedKeys = selector.selectedKeys(); Iterator<SelectionKey> keyIterator = selectedKeys.iterator(); while (keyIterator.hasNext()) { SelectionKey key = keyIterator.next(); if (key.isAcceptable()) { handleAccept(key, selector); } else if (key.isReadable()) { handleMessage(key); } keyIterator.remove(); } } } private void handleAccept(SelectionKey key, Selector selector) throws IOException { SocketChannel clientChannel = ((ServerSocketChannel) key.channel()).accept(); clientChannel.configureBlocking(false); clientChannel.register(selector, SelectionKey.OP_READ); System.out.println("Client connected."); } private void handleMessage(SelectionKey key) throws IOException { SocketChannel socketChannel = (SocketChannel) key.channel(); ByteBuffer readBuffer = ByteBuffer.allocate(1024); int numRead = -1; try { numRead = socketChannel.read(readBuffer); } catch (IOException e) { System.err.println("Error reading from client: " + e.getMessage()); key.cancel(); return; } if (numRead == -1) { closeConnection(socketChannel, key); } else { processMessageAndRespond(socketChannel, readBuffer); } } private void closeConnection(SocketChannel socketChannel, SelectionKey key) throws IOException { System.out.println("Closing connection with client..."); key.cancel(); socketChannel.close(); } private void processMessageAndRespond(SocketChannel socketChannel, ByteBuffer buffer) throws IOException { buffer.flip(); CharBuffer charBuffer = Charset.defaultCharset().decode(buffer); System.out.println("Received message: " + charBuffer.toString()); String response = "Echo: " + charBuffer.toString(); byte[] responseBytes = response.getBytes(Charset.defaultCharset()); ByteBuffer outputBuffer = ByteBuffer.wrap(responseBytes); socketChannel.write(outputBuffer); buffer.compact(); } public static void main(String[] args) throws IOException { new NonBlockingServer(8080).start(); } } ``` 这段程序创建了一个能够同时处理多个客户端请求的服务端应用程序。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值