Java NIO使用及原理分析

本文详细介绍了Java NIO中的核心对象——缓冲区、通道和选择器的使用方法,包括它们的基本概念、属性及其操作流程。通过示例展示了如何在实际应用中运用这些对象进行高效的数据读写。
转载自:http://blog.youkuaiyun.com/wuxianglong/article/details/6604817

在NIO中有几个核心对象需要掌握:缓冲区(Buffer)、通道(Channel)、选择器(Selector)。

缓冲区Buffer

       缓冲区实际上是一个容器对象,更直接的说,其实就是一个数组,在NIO库中,所有数据都是用缓冲区处理的。在读取数据时,它是直接读到缓冲区中的; 在写入数据时,它也是写入到缓冲区中的;任何时候访问 NIO 中的数据,都是将它放到缓冲区中。而在面向流I/O系统中,所有数据都是直接写入或者直接将数据读取到Stream对象中。
       在NIO中,所有的缓冲区类型都继承于抽象类Buffer,最常用的就是ByteBuffer,对于Java中的基本类型,基本都有一个具体Buffer类型与之相对应,它们之间的继承关系如下图所示:
     
       Buffer对象最重要的属性有下面三个,它们一起合作完成对缓冲区内部状态的变化跟踪:
       position:指定了下一个将要被写入或者读取的元素索引,它的值由get()/put()方法自动更新,在新创建一个Buffer对象时,position被初始化为0。
       limit:指定还有多少数据需要取出(在从缓冲区写入通道时),或者还有多少空间可以放入数据(在从通道读入缓冲区时)。
      capacity:指定了可以存储在缓冲区中的最大数据容量,实际上,它指定了底层数组的大小,或者至少是指定了准许我们使用的底层数组的容量。
      以上个属性值之间有一些相对大小的关系:0 <= position <= limit <= capacity。如果创建一个新的容量大小为10的ByteBuffer对象,在初始化的时候,position设置为0,limit和 capacity被设置为10,在以后使用ByteBuffer对象过程中,capacity的值不会再发生变化,而其它两个将会随着使用而变化。
     如果向缓冲区中写入4个数据后,则此时position的值为4,即下一个将要被写入的字节索引为4,而limit仍然是10,如下图所示:
    
     下一步把读取的数据写入到输出通道中,相当于从缓冲区中读取数据,在此之前,必须调用flip()方法,该方法将会完成两件事情:
     1. 把limit设置为当前的position值
     2. 把position设置为0
     由于position被设置为0,所以可以保证在下一步输出时读取到的是缓冲区中的第一个字节,而limit被设置为当前的position,可以保证读取的数据正好是之前写入到缓冲区中的数据,如下图所示:   
     现在调用get()方法从缓冲区中读取数据写入到输出通道,这会导致position的增加而limit保持不变,但position不会超过limit的值,所以在读取我们之前写入到缓冲区中的4个自己之后,position和limit的值都为4,如下图所示:
     
     在从缓冲区中读取数据完毕后,limit的值仍然保持在我们调用flip()方法时的值,调用clear()方法能够把所有的状态变化设置为初始化时的值,如下图所示:
 
简单示例:
import java.nio.IntBuffer;

public class IntBufferTest {

    public static void main(String[] args) {
        // 分配新的int缓冲区,参数为缓冲区容量  
        // 新缓冲区的当前位置将为零,其limit将为其capacity。它将具有一个底层实现数组,其数组偏移量将为零。  
        IntBuffer buffer = IntBuffer.allocate(10);
        System.out.print("初始化: ");
        System.out.print("position: " + buffer.position() + ", ");
        System.out.print("limit: " + buffer.limit() + ", ");
        System.out.println("capacity: " + buffer.capacity());

        for (int i = 0; i < 5; ++i) {
            // 将给定整数写入此缓冲区的当前位置,当前位置递增  
            buffer.put(i);
        }
        System.out.print("put值后: ");
        System.out.print("position: " + buffer.position() + ", ");
        System.out.print("limit: " + buffer.limit() + ", ");
        System.out.println("capacity: " + buffer.capacity());

        // 重设此缓冲区,将limit为当前位置position,然后将当前位置position设置为0  
        buffer.flip();
        System.out.print("flip()后: ");
        System.out.print("position: " + buffer.position() + ", ");
        System.out.print("limit: " + buffer.limit() + ", ");
        System.out.println("capacity: " + buffer.capacity());

        // 查看在当前位置和限制位置之间是否有元素  
        while (buffer.hasRemaining()) {
            // 读取此缓冲区当前位置的整数,然后当前位置递增  
            int j = buffer.get();
            System.out.print(j + "  ");
        }
        System.out.println();

        buffer.clear();
        System.out.print("clear()后: ");
        System.out.print("position: " + buffer.position() + ", ");
        System.out.print("limit: " + buffer.limit() + ", ");
        System.out.println("capacity: " + buffer.capacity());
    }
}
运行结果:
初始化: position: 0, limit: 10, capacity: 10
put值后: position: 5, limit: 10, capacity: 10
flip()后: position: 0, limit: 5, capacity: 10
0  1  2  3  4  
clear()后: position: 0, limit: 10, capacity: 10

通道Channel

       通道是一个对象,通过它可以读取和写入数据,当然了所有数据都通过Buffer对象来处理。我们永远不会将字节直接写入通道中,相反是将数据写入包含一个或者多个字节的缓冲区。同样不会直接从通道中读取字节,而是将数据从通道读入缓冲区,再从缓冲区获取这个字节。
       在NIO中,提供了多种通道对象,而所有的通道对象都实现了Channel接口。它们之间的继承关系如下图所示:
      
使用NIO读取数据
      在前面我们说过,任何时候读取数据,都不是直接从通道读取,而是从通道读取到缓冲区。所以使用NIO读取数据可以分为下面三个步骤:
      1. 从FileInputStream获取Channel
      2. 创建Buffer
      3. 将数据从Channel读取到Buffer中
     下面是一个简单的使用NIO从文件中读取数据的例子:
    static void read() throws Exception {
        FileInputStream fin = new FileInputStream("build.gradle");

        FileChannel fc = fin.getChannel(); // 获取通道  

        ByteBuffer buffer = ByteBuffer.allocate(1024);// 创建缓冲区  

        for (; fc.read(buffer) > 0/* 读取数据到缓冲区 */;) {
            buffer.flip();

            while (buffer.remaining() > 0) {
                byte b = buffer.get();
                System.out.print(((char) b));
            }
            buffer.clear();
        }
        fin.close();
    }
使用NIO写入数据
      使用NIO写入数据与读取数据的过程类似,同样数据不是直接写入通道,而是写入缓冲区,可以分为下面三个步骤:
       1. 从FileInputStream获取Channel
       2. 创建Buffer
       3. 将数据从Channel写入到Buffer中
      下面是一个简单的使用NIO向文件中写入数据的例子:
    static void write() throws Exception {
        FileOutputStream fout = new FileOutputStream("c:\\test.txt");

        FileChannel fc = fout.getChannel();

        ByteBuffer buffer = ByteBuffer.allocate(1024);
        for (int i = 0; i < 10; ++i) {
            buffer.put((byte) i);
        }
        buffer.flip();

        fc.write(buffer);
        
        fout.close();
    }


MATLAB主动噪声和振动控制算法——对较大的次级路径变化具有鲁棒性内容概要:本文主要介绍了一种在MATLAB环境下实现的主动噪声和振动控制算法,该算法针对较大的次级路径变化具有较强的鲁棒性。文中详细阐述了算法的设计原理与实现方法,重点解决了传统控制系统中因次级路径动态变化导致性能下降的问题。通过引入自适应机制和鲁棒控制策略,提升了系统在复杂环境下的稳定性和控制精度,适用于需要高精度噪声与振动抑制的实际工程场景。此外,文档还列举了多个MATLAB仿真实例及相关科研技术服务内容,涵盖信号处理、智能优化、机器学习等多个交叉领域。; 适合人群:具备一定MATLAB编程基础和控制系统理论知识的科研人员及工程技术人员,尤其适合从事噪声与振动控制、信号处理、自动化等相关领域的研究生和工程师。; 使用场景及目标:①应用于汽车、航空航天、精密仪器等对噪声和振动敏感的工业领域;②用于提升现有主动控制系统对参数变化的适应能力;③为相关科研项目提供算法验证与仿真平台支持; 阅读建议:建议读者结合提供的MATLAB代码进行仿真实验,深入理解算法在不同次级路径条件下的响应特性,并可通过调整控制参数进一步探究其鲁棒性边界。同时可参考文档中列出的相关技术案例拓展应用场景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值