NIO

一.NIo的简介

Java NIO (New IO,Non-Blocking IO)是从Java 1.4版本开始引入的一套新的IO API,可以替代标准的Java IO API。NIO与原来的IO有同样的作用和目的,但是使用的方式完全不同,NIO支持面向缓冲区的、基于通道的IO操作。NIO将以更加高效的方式进行文件的读写操作。Non-Blocking应该是最好的理解

随着 JDK 7 的发布,Java对NIO进行了极大的扩展,增强了对文件处理和文件系统特性的支持,以至于我们称他们为 NIO.2。因为 NIO 提供的一些功能,NIO已经成为文件处理中越来越重要的部分。

二.NIO和传统IO的区别

IO: 面向流 单向的 面向缓冲区:通道可以是单双向的
阻塞IO

NIO:面向缓冲区(Buffer Oriented):通道可以是单向的,也可以是双向的
非阻塞IO(Non Blocking IO)
选择器(Selectors)

三.NIO的核心

NIO的核心在于:通道和缓冲区(Buffer),通道表示IO源daoIO设备(例如:文件,套接字)的连接,若需要时使用NIO需要获取IO设备中的通道以及用于容纳的数据缓冲区,对数据进行处理
缓冲区底层就是数组
简而言之:Channel 负责传输,Buffer负责存储

四.Buffer常用的属性

 容量 (capacity) :表示 Buffer 最大数据容量,一旦声明后,不能更改。通过Buffer中的capacity()获取。缓冲区capacity不能为负。

 限制 (limit):第一个不应该读取或写入的数据的索引,即位于 limit
后的数据不可读写。通过Buffer中的limit()获取。缓冲区的limit不能为负,并且不能大于其capacity。

 位置 (position):当前要读取或写入数据的索引。通过Buffer中的position()获取。缓冲区的position不能为负,并且不能大于其limit。

 标记 (mark):标记是一个索引,通过 Buffer 中的 mark() 方法将mark标记为当前position位置。
之后可以通过调用 reset() 方法将 position恢复到标记的mark处。

 标记、位置、限制、容量遵守以下不变式:
0 <= mark <= position <= limit <= capacity

package com.nyist.NIO;

/** * Created with IntelliJ IDEA. * User: @子玉 [2375900377] * Date: 2018-03-30 * Time: 11:37 * Description: */

import org.junit.Test;

import java.nio.ByteBuffer;
import java.nio.channels.ByteChannel;

/** * 一.Nio中传输中的两个重要的概念: * Buffer:缓冲区 负责数据的存储(读写) * channel :通道 代表了数据源与IO节点 (文件,网络socket) 之间的链接,负责传输Buffer * * 二。 java.nio.Buffer * |--------ByteBuffer * |--------CharBuffer * * |--------IntBuffer * |--------DoubleBuffer * |--------ShortBuffer * |--------LongBuffer * |--------DoubleBuffer 底层对应类型的数组 xxxBuffer的常用属性 capacity :容量 limit : 限制 默认的时候与capation 的值一样 position : 位置 mark : 标记 */
 class BufferTest {

    @Test
    public void test1(){

        ByteBuffer byteBuffer = ByteBuffer.allocate(10);//底层为10 的byte[]数组
        System.out.println(byteBuffer.capacity());
        System.out.println(byteBuffer.limit());
        System.out.println(byteBuffer.position());
//        System.out.println(byteBuffer.mark());
        byteBuffer.put("ddd".getBytes());
        System.out.println("***************");
        System.out.println(byteBuffer.capacity());
        System.out.println(byteBuffer.limit());
        System.out.println(byteBuffer.position());//每一次put都会将值移动一次

        System.out.println("**********flip()********");

        byteBuffer.flip();//将存入数据模式变成取出数据模式 已经存入的数据posstion又变成0,从头继续读

        System.out.println(byteBuffer.capacity());
        System.out.println(byteBuffer.limit());
        System.out.println("----"+byteBuffer.position());

        System.out.println("**********get()********");

        System.out.println((char)byteBuffer.get()); //每get一次posstion+1
        System.out.println((char)byteBuffer.get());
        System.out.println(byteBuffer.capacity());
        System.out.println(byteBuffer.limit());
        System.out.println(byteBuffer.position());

        byteBuffer.rewind();//重置position
        byteBuffer.clear();//清空 回到用户最初的状态 10,10,0

        System.out.println((char)byteBuffer.get());

    }

}

五.Channel 的介绍

5.1channel 表示IO源与目标节点打开的链接

channel本身不能存数据,只能与buffer交互
主要实现类:

二、主要实现类:
* java.nio.channels.Channel:
* |—-FileChannel:适用于本地数据传输
*
* |—-SocketChannel:适用于TCP中的网络传输的客户端
* |—-ServerSocketChannel:适用于TCP中的网络传输的服务器端
* |—-DatagramChannel:适用于UDP中的网络传输
*
* |—-Pipe.SinkChannel:
* |—-Pipe.SourceChannel:
*
* 三、实例化Channel:
* 方式一: 调用getChannel()
* FileInputStream—>FileChannel
* FileOutpuStream—>FileChannel
* RandomAccessFile—>FileChannel
* ————-
* Socket—>SocketChannel
* ServerSocket—>ServerSocketChannel
* DatagramSocket—>DatagramChannel
*
* 方式二: jdk7.0以上才可以使用
* 调用XxxChannel的静态方法:open(),得到XxxChannel实例。
*
* 方式三:jdk7.0以上才可以使用
* Files.newByteBuffer(),返回一个字节通道
*
* 四、Channel特点:既可以是单向的,也可以是双向的。
* |————-ServerSocketChannel TCP 监听TCP读写网络中
* |————-DatagramChannel 适用于UDP 读写网络中的数据通道
*

非直接缓存区代码实例

 /** * channleb只能与buffer交互 * 

* //实现文件的复制 FileChannel +ByteBuffer(非直接缓冲区) */ @Test public void test1() throws Exception { //1.提供相应的输入输出流 FileInputStream fis = new FileInputStream("D:\\nexus.war"); FileOutputStream fos = new FileOutputStream("D:\\nexus.war2.tar"); //创建相应的Channel 通过我们的流去创建对应的通道,然后通过通道继续读写数据 FileChannel inchannel = fis.getChannel(); FileChannel outchannel = fos.getChannel(); //3.提供缓冲区 ByteBuffer byteBuffer = ByteBuffer.allocate(1024); while ((inchannel.read(byteBuffer)) != -1) { byteBuffer.flip();//切换为读数据的模式 //如果这里不切换的话,缓存区会满 outchannel.write(byteBuffer); byteBuffer.clear();//清空,读完当前的缓存区然后再继续 } fis.close(); fos.close(); inchannel.close(); outchannel.close(); }

直接缓存区代码实例:

//使用FileChannel + MappedByteBuffer(直接缓冲区)-->物理内存映射文件
    @Test
    public void test2() throws Exception {


        long start = System.currentTimeMillis();


        //1.创建Channel
//      FileChannel inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ);
//      FileChannel outChannel = FileChannel.open(Paths.get("3.jpg"), StandardOpenOption.READ,StandardOpenOption.WRITE,StandardOpenOption.CREATE);
        FileChannel inChannel = FileChannel.open(Paths.get("D:\\nexus.war"), StandardOpenOption.READ);
        FileChannel outChannel = FileChannel.open(Paths.get("D:\\nexus2.war"), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);

        //2.创建得到直接缓冲区
        MappedByteBuffer inMappedBuffer = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
        MappedByteBuffer outMappedBuffer = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());

        //3.数据的读写
        byte[] dst = new byte[inMappedBuffer.limit()];
        inMappedBuffer.get(dst);//将数据写入到dst中
        outMappedBuffer.put(dst);//从dst中将数据取出

        //4.关闭资源
        inChannel.close();
        outChannel.close();


        long end = System.currentTimeMillis();
        System.out.println("直接缓冲区:" + (end - start));//1573-1575
        }

以上是我近期的一些整理,希望可以跟大家相互交流学习。

你可能感兴趣的:(java)