Java NIO流

       对于JavaNIO还是不是很了解,之前认为NIO的N是non-block IO的意思,非阻塞;但是原来是New IO的意思。这个新表示的是和原来的BIO而言是一种新的IO吧。NIO的主要特性就是缓冲区,通道,和选择器(这个可能不是)。

      Java在JDK1.4版本呢,引入了NIO这个新的api。Sun公司官方说明NIO的特性如下:

1. 为所有的原始类型提供了(Buffer)缓存支持;

2. 字符集编码解码解决方案

3. Channel(通道):一个新的原始I/O抽象;

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

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

      对于上面的特性,我是不大了解的。不过,不影响,我们继续介绍。

      NIO创建的目的是,实现高速的I/O,而无需编写自定义的本机代码。

      那NIO大概是怎么做到的呢?它将最耗时的I/O操作转回操作系统,因而可以极大的提高速度。而最耗时的I/O操作是填充和提取缓冲区。

      原来的io和现在NIO最重要的区别就是,数据打包和传输的方式。以前是以流,现在是以块的方式处理数据。

      之前用流的方式呢,只能一次一个字节的处理数据。一个输入流产生一个字节的数据,而一个输出流则消耗一个字节的数据。这样的好处是,创建过滤器特别容易,可以创建多个过滤器,每个过滤器处理只处理一部分的数据。坏处就是,比较慢。

      而NIO用块的方式呢,可以一次一个块的处理数据。每一步的操作都在产生或者消耗一个块,好处是相对于流快得多,坏处是,不够优雅和简单。

缓冲区

      然后开始介绍缓冲区,缓冲区就是上面介绍到的NIO的特性第一条,为所有的原始数据都提供了Buffer缓存的支持。它主要是将所有的原始数据放在数组中,以块的形式处理。

      而每种缓冲区的类都有四个属性:容量(Capacity),上界(Limit),位置(Position),以及标记(Mark),用于提供关于其所包含的数据元素的信息。

      容量:缓冲区能够容纳的数据元素的最大数量,缓冲区创建时确定,永远不能被改变;

      上界:缓冲区第一个不能被读或写的元素,或者说,缓冲区现存元素的计数。

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

      标记:一个备忘的位置。调用mark()来设定mark=position。调用reset()来设定position = mark;标记在设定前是未定义的undefined。

      缓冲区的分类有,ByteBuffer(字节缓冲区),CharBuffer(字符缓冲区),ShortBuffer(短整型缓冲区),IntBuffer(整型缓冲区),LongBuffer(长整型缓冲区),FloatBuffer(单精度浮点缓冲区),DoubleBuffer(双精度浮点缓冲区),就是没有布尔缓冲区。

      他们都是抽象类,所以不能实例化,然后他们都继承了Buffer类,所以都有存get(),取set()方法,也都可以通过各自的静态方法allocation,创建缓冲区。该方法是通过warpping将现有的数组包装到缓冲区中来为缓冲区的内容分配空间,或者通过创建现有字节缓冲区的视图来创建。

      下面这是一个简单的实例,从上到下,创建一个整型的缓冲区,然后将现有的数组放到该缓冲区中。可以通过put改变数组中的数据,并且由于缓冲区中的数据对数组是可见的,所以改变缓冲区也会改变数据,可以认为是传引用。flip(),由于get()每调用一次,position位置都会改变,本来pos会等于3的,而用flip可以让pos变为0;clear()效果也一样。而duplicate()可以复制一个缓冲区,一模一样,也是传引用,修改哪个都会影响到另一个。

import java.nio.IntBuffer;
import java.util.Arrays;

/**
 * Created by liuyanling on 2017/6/30.
 */
public class BufferTest {
    public static void main(String[] args) {
        //创建缓冲区,并指定大小
        IntBuffer  intBuffer = IntBuffer.allocate(10);

        //给缓冲区赋值,建立数组
        int[] intArray = new int[]{3,5,7};
        intBuffer = intBuffer.wrap(intArray);


        //修改元素
        intBuffer.put(0,9);

        //打印缓冲区数据
        for(int i=0;i<intBuffer.limit();i++) {
           System.out.print(intBuffer.get()+"\t");
           //System.out.print(intBuffer+"\t"); //
        }

        System.out.println("\n缓冲区的数据对数组是可见的,修改视图,数组中的数据也会变;传引用");
        //打印原始数组
        Arrays.stream(intArray).forEach(temp-> System.out.print(temp+"\t"));


        //intBuffer.flip();//get()会使pos改变,对缓冲区进行反转,将limit=pos;pos=0; (将当前位置给limit,然后变为0)
        intBuffer.clear();
        System.out.println(intBuffer);

        IntBuffer intBuffer2 = intBuffer.duplicate();
        System.out.println(intBuffer2);

        intBuffer2.put(0,11);

        //0 <= mark <= position <= limit <= capacity

        //打印缓冲区数据
        for(int i=0;i<intBuffer.limit();i++) {
            System.out.print(intBuffer.get()+"\t");
        }

        System.out.println("\n复制的缓冲区对原来的缓冲区也是可见的;传引用");
        //打印原始数组
        for(int i=0;i<intBuffer2.limit();i++) {
            System.out.print(intBuffer2.get()+"\t");
        }
    }
}
      结果是这样

9	5	7	
缓冲区的数据对数组是可见的,修改视图,数组中的数据也会变;传引用
9	5	7	java.nio.HeapIntBuffer[pos=0 lim=3 cap=3]
java.nio.HeapIntBuffer[pos=0 lim=3 cap=3]
11	5	7	
复制的缓冲区对原来的缓冲区也是可见的;传引用
11	5	7	
      未完待续!



     


### 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(); } } ``` 这段程序创建了一个能够同时处理多个客户端请求的服务端应用程序。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值