zoukankan      html  css  js  c++  java
  • java基础之NIO

    以下内容摘自 这个教程 ,对于异步编程和文件锁部分暂时还未接触。

    1. 为什么要使用 NIO

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

    2. 核心对象

    缓冲区通道 是 NIO 中的核心对象,几乎在每一个 I/O 操作中都要使用它们。

    通道是对原 I/O 包中的流的模拟。到任何目的地(或来自任何地方)的所有数据都必须通过一个 Channel 对象。一个 Buffer 实质上是一个容器对象。发送给一个通道的所有对象必须首先放到缓冲区中;同样地,从通道中读取的任何数据都要读到缓冲区中。

    2.1 缓冲区

    Buffer 是一个对象,它包含一些要写入或者刚读出的数据。在 NIO 中加入 Buffer 对象,体现了新库与原 I/O 的一个重要区别。在面向流的 I/O 中,我们将数据直接写入或者将数据直接读到 Stream 对象中。

    在 NIO 库中,所有数据都是用缓冲区处理的。在读取数据时,它是直接读到缓冲区中的。在写入数据时,它是直接写入到缓冲区中的。在任何时候访问 NIO 中的数据,我们都是将它放到缓冲区中。

    缓冲区实质上是一个数组。通常它是一个字节数组,但是也可以使用其他类型的数组。但是一个缓冲区不仅仅是一个数组。缓冲区提供了对数据的结构化访问,而且还可以跟踪系统的 读/写 进程。

    下面是一个简单的小例子,通过 buffer.put 存储数据,通过 buffer.get 将里面的数据打印出来。这里的数据来源是自定义的数据,一般情况下可以从文件、网络中获取数据。

    import java.nio.FloatBuffer;
    
    public class UseFolatBuffer {
    	public static void main(String[] args){
    		FloatBuffer buffer = FloatBuffer.allocate(10);
    		
    		for(int i=0;i<buffer.capacity();i++){
    			float f = (float)Math.sin( (((float)i)/10)*(2*Math.PI) );
    			buffer.put(f);
    		}
    		
    		// 从写模式切换到读模式
    		buffer.flip();
    		
    		while(buffer.hasRemaining()){
    			float f = buffer.get();
    			System.out.println(f);
    		}
    	}
    }
    

    2.2 通道

    Channel(通道)是一个对象,可以通过它读取和写入数据。拿 NIO 与原来的 I/O 做个比较,通道就像是流。

    正如前面提到的,所有数据都通过 Buffer 对象来处理。我们永远不会将字节直接写入通道中,相反,我们是直接将数据写入包含一个或者多个字节的缓冲区。同样,我们也不会直接从通道中读取字节,而是将数据从通道读入缓冲区,再从缓冲区获取这个字节。

    3. 基本操作

    3.1 从文件中读取

    从文件中读取数据时,主要由这三个步骤。首先从 FileInputStream 获取 Channel。其次,就是要创建一个 Buffer 用于存储数据了。最后,通过 Channel 将数据读到 Buffer 以便我们利用。还是看一下代码:

    import java.io.FileInputStream;
    import java.io.IOException;
    import java.nio.ByteBuffer;
    import java.nio.channels.FileChannel;
    
    public class ReadFromFile {
    	public static void main(String[] args)throws IOException{
    		FileInputStream fn = new FileInputStream("a.txt");
    		// 获取文件和缓冲区的通道
    		FileChannel fc = fn.getChannel();
    		
    		// 创建缓冲区
    		ByteBuffer buffer = ByteBuffer.allocate(1024);
    		// 将文件中的数据读到缓冲区中
    		fc.read(buffer);
    		// 从写模式切换到读模式
    		buffer.flip();
    		while(buffer.remaining()>0){
    			// 获取缓冲区中的数据
    			byte b = buffer.get();
    			System.out.print((char)b);
    		}
    		fn.close();
    	}
    }
    

    3.2 写入文件

    将数据写入到文件中的过程和将文件中的数据读取出来是类似的。一样是获取通道、创建缓冲区、写入数据这一系列操作。

    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.nio.ByteBuffer;
    import java.nio.channels.FileChannel;
    
    public class WriteToFile {
    	
    	static private final byte message[] = { 83, 111, 109, 101, 32,
                98, 121, 116, 101, 115, 46 };
    	
    	public static void main(String[] args)throws IOException{
    		FileOutputStream fos = new FileOutputStream("b.txt");
    		// 获取通道
    		FileChannel fc = fos.getChannel();
    		// 创建缓冲区
    		ByteBuffer buf = ByteBuffer.allocate(1024);
    		
    		// 往缓冲区中添加数据
    		for(int i=0;i<message.length;i++)
    			buf.put(message[i]);
    		
    		buf.flip();
    		// 将数据通过通道写到文件中
    		fc.write(buf);
    		fos.close();
    	}
    }
    

    4. 缓冲区内部细节

    这一部分主要介绍一下 NIO 中一个重要的缓冲区组件:状态变量。

    状态变量是缓冲区「内部统计机制」的关键。每一个 读/写 操作都会改变缓冲区的状态。通过记录和跟踪这些变化,缓冲区就可以更好的管理自己的资源。在缓冲区内部,使用三个值指定其在任意时刻的状态:

    • position
      position 的英文意思是位置的意思,它表示下一个字节将存放到数据的哪一个位置上。例如,我们已经从通道中读取了三个字节到缓冲区中了,那么缓冲区中的 position 将会设置为 3,指向数组中的第四个元素。

    • limit
      limit 变量表明还有多少数据需取出(在从缓冲区写入通道时),或者还有多少空间可以放入数据(在从通道中读入缓冲区时)。

    • capacity
      capacity 表明可以存储在缓冲区中的最大数据容量。实际上,它指定了底层数组的大小。

    下面通过图片说明下每次执行一个方法之后这三个变量的变化。
    我们首先观察一个新创建的缓冲区。我们假设这个缓冲区的总容量 为8个字节。 Buffer 的状态如下所示:
    buffer

    回想一下 ,limit 决不能大于 capacity,此例中这两个值都被设置为 8。我们通过将它们指向数组的尾部之后(如果有第8个槽,则是第8个槽所在的位置)来说明这点。

    position 设置为0。如果我们读一些数据到缓冲区中,那么下一个读取的数据就进入 slot 0 。如果我们从缓冲区写一些数据,从缓冲区读取的下一个字节就来自 slot 0 。 position 设置如下所示:

    由于 capacity 不会改变,所以我们在下面的讨论中可以忽略它。

    第一次读取
    现在我们可以开始在新创建的缓冲区上进行读/写操作。首先从输入通道中读一些数据到缓冲区中。第一次读取得到三个字节。它们被放到数组中从 position 开始的位置,这时 position 被设置为 0。读完之后,position 就增加到 3,如下所示:

    limit 没有改变。

    第二次读取
    在第二次读取时,我们从输入通道读取另外两个字节到缓冲区中。这两个字节储存在由 position 所指定的位置上, position 因而增加 2:

    limit 没有改变。

    flip
    现在我们要将数据写到输出通道中。在这之前,我们必须调用 flip() 方法。这个方法做两件非常重要的事:

    • 它将 limit 设置为当前 position。
    • 它将 position 设置为 0。

    前一小节中的图显示了在 flip 之前缓冲区的情况。下面是在 flip 之后的缓冲区:

    我们现在可以将数据从缓冲区写入通道了。 position 被设置为 0,这意味着我们得到的下一个字节是第一个字节。 limit 已被设置为原来的 position,这意味着它包括以前读到的所有字节,并且一个字节也不多。

    第一次写入
    在第一次写入时,我们从缓冲区中取四个字节并将它们写入输出通道。这使得 position 增加到 4,而 limit 不变,如下所示:

    第二次写入
    我们只剩下一个字节可写了。 limit在我们调用 flip() 时被设置为 5,并且 position 不能超过 limit。所以最后一次写入操作从缓冲区取出一个字节并将它写入输出通道。这使得 position 增加到 5,并保持 limit 不变,如下所示:

    clear
    最后一步是调用缓冲区的 clear() 方法。这个方法重设缓冲区以便接收更多的字节。 Clear 做两种非常重要的事情:

    • 它将 limit 设置为与 capacity 相同。
    • 它设置 position 为 0。

    下图显示了在调用 clear() 后缓冲区的状态:

    缓冲区现在可以接收新的数据了。本文仅仅是对原教程的一些整理,可能不够详细,整体的介绍流程也不够好,还是建议大家去看一下 原教程

  • 相关阅读:
    考虑浏览器兼容的文件上传(IE8不支持FormData)
    IDEA tomcat 部署WEB项目
    如何在springcloud分布式系统中实现分布式锁?
    ABAP DEMO33 选择周的搜索帮助
    ABAP函数篇1 日期函数
    ABAP函数篇2 测试DATE_CONVERT_TO_FACTORYDATE
    增强篇7 判断标准屏幕能否做屏幕增强
    增强篇6 CMOD增强删除
    ABAP DEMO 年月的搜索帮助
    HoloLens开发手记-配置开发环境 Install the tools
  • 原文地址:https://www.cnblogs.com/firepation/p/9493580.html
Copyright © 2011-2022 走看看