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交互
     * <p>
     * //实现文件的复制    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
        }

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

没有更多推荐了,返回首页