关于NIO
简介
javaNIO 是java1.4引入的新的IO API,可以替代java io API ,NIO和IO有同样的作用和目的,但是使用方式不同。NIO支持面向缓冲区的,基本的通道IO操作,NIO将以更加高效的方式进行文件的读写操作。
NIO和传统的IO的区别
NIO | IO |
---|---|
面向缓冲区(Buffer) | 面向流(stream) |
非阻塞 | 阻塞 |
选择器 | 无 |
- IO可以看作是水流,是单向的,NIO是通道连接,通道没有数据,通道负责链接,数据在缓冲区中。通过缓冲区传输数据,而且是双向的。
关于本地通信
通道和缓冲区
NIO系统的核心就是通道(channel)和缓冲区(Buffer),通道表示IO设备的连接,若使用NIO系统需要获取链接IO设备的通道和用于容纳数据的缓冲区,然后造作缓冲区,对数据进行处理。
缓冲区的数据存取
缓冲区用于数据的存取。底层是数组实现。
根据数据类型不同提供了,相应类型的缓冲区。
我们以最常见的ByteBuffer为例
/ ** capacity 缓冲区的最大存储数据容量,大小不变 limit 界限,缓冲区中可以操作数据的大小(limit后) position
* 位置,缓冲区中正在操作的位置
*
*/ mark 标记,用于记录position的位置,用reset()恢复到mark的位置
public class TestBuffer {
public static void main(String[] args) {
String string = "abcde";
// 分配指定大小的缓冲区
ByteBuffer bb = ByteBuffer.allocate(1024);
// 存取缓冲区数据
bb.put(string.getBytes());
bb.flip();// 切换成读取数据的模式
byte[] bytes = new byte[bb.limit()];
bb.get(bytes);// 读取缓冲区的数据
System.out.println(new String(bytes, 0, bytes.length));
bb.rewind();// 可以重复 读
bb.clear();// 清空缓冲区,数据没有被清空依然存在,,处于被遗忘状态,不知道数据的多少
}
}
直接缓冲区与非直接缓冲区(Buffer)
非直接缓冲区:通过allocate()方法分配的缓冲区,将缓冲区建立在JVM的内存中
直接缓冲区:通过allocateDirect()方法分配的缓冲区,将缓冲区建立在操作系统的物理内存中可以提高效率,但是不安全,消耗大,数据写到内存映射文件中后,不能控制。
通道(Channel)
一、通道(Channel):用于源节点与目标节点的连接。在 Java NIO 中负责缓冲区中数据的传输。Channel 本身不存储数据,因此需要配合缓冲区进行传输。
二、通道的主要实现类
java.nio.channels.Channel 接口:
|–FileChannel
|–SocketChannel
|–ServerSocketChannel
|–DatagramChannel
三、获取通道
Java 针对支持通道的类提供了 getChannel() 方法
本地 IO:
FileInputStream/FileOutputStream
RandomAccessFile
网络IO:
Socket
ServerSocket
DatagramSocket在 JDK 1.7 中的 NIO.2 针对各个通道提供了静态方法 open()
- 在 JDK 1.7 中的 NIO.2 的 Files 工具类的 newByteChannel()
四、通道之间的数据传输
- transferFrom()
- transferTo()
五、分散(Scatter)与聚集(Gather)
- 分散读取(Scattering Reads):将通道中的数据分散到多个缓冲区中
- 聚集写入(Gathering Writes):将多个缓冲区中的数据聚集到通道中
六、字符集:Charset
- 编码:字符串 -> 字节数组
- 解码:字节数组 -> 字符串
package jnio;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import org.junit.Test;
public class TestChannel {
@Test
// 使用直接缓冲区完成文件的复制(映射文件)
public void test2() throws IOException {
// 获取管道
FileChannel inChannel = FileChannel.open(Paths.get("1.jpg"),
StandardOpenOption.READ);
FileChannel outChannel = FileChannel.open(Paths.get("3.jpg"),
StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW,
StandardOpenOption.READ);
// 内存映射文件
MappedByteBuffer inMappedByteBuffer = inChannel.map(MapMode.READ_ONLY,
0, inChannel.size());
MappedByteBuffer outMappedByteBuffer = outChannel.map(
MapMode.READ_WRITE, 0, inChannel.size());
// 直接对内存映射文件进行读取
byte[] dst = new byte[inMappedByteBuffer.limit()];
inMappedByteBuffer.get(dst);
outMappedByteBuffer.put(dst);
inChannel.close();
outChannel.close();
}
@Test
// 使用通道完成文件的复制(非直接缓冲区)
public void test1() {
FileInputStream fis = null;
FileOutputStream fos = null;
// 获取通道
FileChannel inchannel = null;
FileChannel outchannel = null;
try {
fis = new FileInputStream("H:\\BaiduYunDownload\\nio.zip");
fos = new FileOutputStream("H:\\BaiduYunDownload\\nio4.zip");
// 获取通道
inchannel = fis.getChannel();
outchannel = fos.getChannel();
// 分配缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
// 将通道中的数据写入缓冲区
while (inchannel.read(buffer) != -1) {
buffer.flip();
outchannel.write(buffer);
buffer.clear();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (outchannel != null) {
try {
outchannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (inchannel != null) {
try {
inchannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Test
public void test3() throws IOException{
FileChannel inChannel = FileChannel.open(Paths.get("H:\\BaiduYunDownload\\nio.zip"),
StandardOpenOption.READ);
FileChannel outChannel = FileChannel.open(Paths.get("H:\\BaiduYunDownload\\nio2.zip"),
StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW,
StandardOpenOption.READ);
inChannel.transferTo(0, inChannel.size(), outChannel);
inChannel.close();
outChannel.close();
}
}
分散读取和聚集写入
分散读取(Scattering Reads)将通道中的数据分散到多个缓冲区中,按顺序存储
聚集写入(Gathering Writes):将多个缓冲区中的数据聚集到通道中,按顺序写入
@Test
//分散读取和聚集写入
public void test4() throws Exception{
RandomAccessFile raf1 = new RandomAccessFile("1.jpg", "rw");
//获取通道
FileChannel fileChannel = raf1.getChannel();
//分配缓冲区大小
ByteBuffer buf1 = ByteBuffer.allocate(100);
ByteBuffer buf2 = ByteBuffer.allocate(1024);
ByteBuffer[] buffers = {buf1,buf2};
//读取
fileChannel.read(buffers);
System.out.println(new String(buffers[0].toString()));
//写入
for (int i = 0; i < buffers.length; i++) {
buffers[i].flip();
}
RandomAccessFile raf2 = new RandomAccessFile("5.jpg","rw");
FileChannel fileChannel2 = raf2.getChannel();
fileChannel2.write(buffers);
}
编码和解码
public void test5() throws IOException{
Charset charset = Charset.forName("GBK");
//获取编码器与解码器
CharsetEncoder encoder = charset.newEncoder();
CharsetDecoder decoder = charset.newDecoder();
CharBuffer buffer = CharBuffer.allocate(1024);
buffer.put("赵建银");
buffer.flip();
//编码
ByteBuffer buffer2 = encoder.encode(buffer);
buffer2.flip();
CharBuffer buffer3 = decoder.decode(buffer2);
System.out.println(buffer3.toString());
}