NIO

## NIO概述 ##

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

JDK之后的NIO:也叫做NIO2(BIO)

  Path:路径(与平台无关)

  Paths:有一个静态方法返回路径(返回Path的静态方法)

    public static Path get(URI uri);

  Files:提供静态方法(操作文件的工具类)

    public static long copy(Path source, OutputStream out)

    将文件中的所有字节复制到输出流。

    public static Path write(Path path, Iterable lines, Charset cs, OpenOption... options)

    将文本行写入文件。

## NIO与IO区别 ##

| IO |NIO|

| - |

| 面向流(Stream Oriented) | 面向缓冲区(Buffer Oriented) |

| 阻塞IO(Blocking IO) | 非阻塞IO(Non Blocking IO) |

| (无) | 选择器(Selectors) |

## 通道和缓冲区 ##

Java NIO系统的核心在于:通道(Channel)和(Buffer)。通道表示打开到 IO 设备(例如:套接字)的连接。若需要使用 NIO 系统,需用于连接 IO 设备的通道以及用于容纳数据区。然后操作缓冲区,对数据进行处理

**简而言之, Channel 负责传输, Buffer 负责存储**

### NIO缓冲区(Buffer) ###

- 缓冲区(Buffer) :一个用于特定基本数据类型的容器。由 java.nio 包定义的,所有缓冲区都是 Buffer 抽象类的子类

- Java NIO 中的 Buffer 主要用于与 NIO 通道进行交互,数据是从通道读入缓冲区,从缓冲区写入通道中的

Buffer 就像一个数组,可以保存多个相同类型的数据。根据数据类型不同(boolean 除外) ,有以下 Buffer 常用子类:

* ByteBuffer

* CharBuffer

* ShortBuffer

* IntBuffer

* LongBuffer

* FloatBuffer

* DoubleBuffer

上述 Buffer 类 他们都采用相似的方法进行管理数据,只是各自管理的数据类型不同而已。

都是通过如下方法获取一个 Buffer对象:

static XxxBuffer allocate(int capacity) : 创建一个容量为 capacity 的 XxxBuffer 对象

Buffer 中的重要概念:

> 容量 (capacity) : 表示 Buffer 最大数据容量,缓冲区容量不能为负,并且创

建后不能更改。

> 限制 (limit): 第一个不应该读取或写入的数据的索引,即位于 limit 后的数据

不可读写。缓冲区的限制不能为负,并且不能大于其容量。

> 位置 (position): 下一个要读取或写入的数据的索引。缓冲区的位置不能为

负,并且不能大于其限制

> 标记 (mark)与重置 (reset): 标记是一个索引,通过 Buffer 中的 mark() 方法

指定 Buffer 中一个特定的 position,之后可以通过调用 reset() 方法恢复到这

个 position

>并且  0 < mark <= position <=limit <= capacity

1. 分配一个缓冲区

要获得一个Buffer对象,你必须首先分配它,通过allocate()分配了一个10字节大小的缓冲区。

```java

ByteBuffer buf = ByteBuffer.allocate(10);

```

![allocate](image/aa.png)

2. 将数据写入缓冲区

将数据写入缓冲区有两种方式:

  1.利用 put() 存入数据到缓冲区中

```java

String str = "abcde";

buf.put(str.getBytes());

```

![allocate](image/bb.png)

  2.将数据从 Channel写入Buffer

```java

int bytesRead = inChannel.read(buf);

//读入缓冲区。

```

3. filpp()

该flip()方法将a Buffer从写入模式切换到读取模式。调用flip()将position返回设置为0,并将其设置为limit 刚才的位置。

换句话说,position现在标记了读取位置,并limit标记了多少字节,字符等被写入缓冲区 - 可以读取的字节数,字节数等限制。

```java

buf.flip();

```

4. 从缓冲区读取数据

有两种方法可以从Buffer中读取数据。

1.将数据从缓冲区读入通道。

```java

//从缓冲区读入通道。

int bytesWritten = inChannel.write(buf);

```

2.使用其中一个get()方法自己从缓冲区中读取数据。

```java

byte[] dst = new byte[buf.limit()];

buf.get(dst);

System.out.println(new String(dst, 0, dst.length));

```

```java

```

5. rewind()

Buffer.rewind() 让 position 返回到0,这样你就可以重新读取缓冲区中的所有数据。在limit保持不变,因此仍然标记多少个元素(字节,字符等),可以从被读取Buffer。

6. clear()和compact()

一旦你完成了读取数据,Buffer 准备好再次写入。你可以通过调用clear()或调用compact()。

如果调用clear(),则position将设置回0并且limit会变成capacity。, 换句话说,缓冲区被清除,但是缓冲区中的数据未被清除。, 只有markers告诉您可以将数据写入缓冲区的位置。

如果在调用clear()时缓冲区中存在未读取的数据,那么数据将处于“forgotten”,这意味着不再有任何标记,指示已读取的数据以及尚未读取的数据。

如果Buffer中仍有未读数据,并且想稍后read,需要先写一些内容,调用compact()而不是clear()。

compact()将所有未读数据复制到缓冲区的开始处。, 然后它将position设置在最后一个未读元素之后。, 极限属性仍然设置为容量,就像clear()一样。, 现在缓冲区已准备好写入,但不会覆盖未读数据。

7. mark()和reset()

可以通过调用Buffer.mark()方法在Buffer中标记给定的位置。然后可以通过调用该Buffer.reset() 方法将位置重新设置回标记的位置。

```java

        String str = "abcde";

ByteBuffer buf = ByteBuffer.allocate(1024);

buf.put(str.getBytes());

buf.flip();

byte[] dst = new byte[buf.limit()];

buf.get(dst, 0, 2);

System.out.println(new String(dst, 0, 2));

System.out.println(buf.position());

//mark() : 标记

buf.mark();

buf.get(dst, 2, 2);

System.out.println(new String(dst, 2, 2));

System.out.println(buf.position());

//reset() : 恢复到 mark 的位置

buf.reset();

System.out.println(buf.position());

```

8. equals()和compareTo()

可以使用equals()和compareTo()来比较两个缓冲区

equals()

它们是相同的类型(byte,char,int等)

它们在缓冲区中具有相同数量的剩余字节,字符等。

所有剩余的字节,字符等是相等的。

正如你所看到的,equals只比较缓冲区的一部分,而不是它内部的每一个元素。, 实际上,它只是比较缓冲区中的其余元素。

compareTo()

该compareTo()方法比较两个缓冲区的其余元素(字节,字符等),用于例如排序例程。在下列情况下,缓冲区被认为比另一个缓冲区“小”

第一个元素等于另一个缓冲区中的对应元素,小于另一个缓冲区中的元素。

所有的元素都是相等的,但第一个缓冲区在第二个缓冲区之前耗尽元素(元素较少)。

### NIO通道(Channel) ###

你可能感兴趣的:(NIO)