Java NIO(NEW IO)是从Java1.4开始引入的新版IO,用来替代标准的Java IO API
NIO于原来的IO有相同的功能,但是他们之间的使用方式是完全不同的,NIO是面向缓冲区,面向通道的的IO操作,NIO拥有更加高效的进行文件读写。
另外NIO在网络编程可以是一个无阻塞的IO交互,可以大大提升Socket
交互的效率。
IO | NIO |
---|---|
面向流(Stream Oriented) | 面向缓冲区(Buffer Oriented) |
阻塞IO(Block IO) | 无阻塞IO(Non Block IO) |
无 | Selectors(选择器) |
单向通道 | 双向通道 |
channel
在NIO中文件通道是不具备文件传输功能的,如果需要文件传输,必须依赖于缓冲区的存在。
通道常见实现类有:
FileChannel
为例
Java 针对支持通道的类提供了getChannel()方法
本地文件IO
FileInputStream/FileOutInputStream
RandomAccessFile
网络文件IO
Socket
ServerSocket
DatagramSocket
在JDK 1.7中NIO.2针对各个通道提供了静态方法open()
在JDK 1.7中NIO.2的Files工具类的newByteChannel()
/**
* 采用支持通道的类,创建对应的文件通道
*
**/
public void test6() throws FileNotFoundException {
FileInputStream fileInputStream = new FileInputStream(new File("D:\\1.txt"));
FileChannel channel = fileInputStream.getChannel();
}
/**
* 使用静态方法获得
*/
public void test3()throws IOException{
FileChannel open = FileChannel.open(Paths.get("src/psb (2).jpg"), StandardOpenOption.READ);
FileChannel channel = FileChannel.open(Paths.get("psb.jpg"), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW);
}
Buffer
缓冲区
缓冲区(buffer):在Java NIO 中负责数据存储。缓冲区就是数组。用于存储不同数据类型的数据
根据数据类型不同(Boolean除外),提供了相应类型的缓冲区:
在buffer缓冲区中有四个核心参数:
capacity
:缓冲区中最大存储数据的容量。一旦声明不能改变
limit
:界限,表示缓冲区中可以操作数据的大小。(limit后数据不能进行读写)
position
:位置,表示缓冲区中正在操作数据的位置
mark
:记录,表示记录当前position的位置。可以通过reset()恢复到mark的位置
mark <= position <= limit <= capacity
他们之间存在这样的关系
buffer
缓冲区分类
非直接缓冲区public class TestBuffer {
public static void main(String[] args) {
// 打开1024字节大小的缓冲区
ByteBuffer allocate = ByteBuffer.allocate(1024);
System.out.println("-------------allocate-----------");
System.out.println(allocate.position());
System.out.println(allocate.limit());
System.out.println(allocate.capacity());
// 利用put方法存入数据到缓冲区中
System.out.println("--------------put---------------");
allocate.put(new String("abc").getBytes());
System.out.println(allocate.position());
System.out.println(allocate.limit());
System.out.println(allocate.capacity());
// filp()方法进行读写模式转换
allocate.flip();
System.out.println("--------------get---------------");
// 将数据读取到字节数组中
byte[] bytes = new byte[allocate.limit()];
allocate.get(bytes);
System.out.println(new String(bytes,0,bytes.length));
// rewind():可重复读
allocate.rewind();
// clear:清空缓冲区,但是缓冲区中的数据依然存在,但是处于被遗忘状态
// 判断缓冲区中是否还有可以操作的数据
if (allocate.hasRemaining()) {
// 打印还有多少数据可以操作
System.out.println(allocate.remaining());
}
}
}
直接缓冲区allocateDirect()
工厂方法创建,此方法返回的缓冲区进行分配和取消分配所需成本通常高于非直接缓冲区。直接缓冲区的内容可以主流在常规的垃圾回收堆之外,因此,他们对应用程序内存需求量造成的影响可能并不明显。所以,建议讲直接缓冲区主要分配给那些易受基础系统的本机IO操作影响、持久的缓冲区。一般情况下,最好尽在直接缓冲区能在程序性能方面带来明显好处时分配他们。
直接缓冲区还可以通过FilChannel
的map()
方法将文件区域直接银蛇到内存中来创建。该方法返回MappedByteBuffer
。Java平台的实现有助于。
ByteBuffer allocate = ByteBuffer.allocate(1024);
分配了一个1024空间大小的缓冲区allocate.put(new String("abc").getBytes());
向缓冲区放入三个字节数的字符串allocate.flip();
利用filp()
方法进行读写反转Selector
选择器
NIO之所以能在网络编程上面有很大的提升,于这个关键元素有很大的关系
Selector
选择器,网络编程使用NIO的大哥!!!
服务器可以执行一个线程,运行Selector程序,进行监听操作。
新连接, 已经连接, 读取数据,写入数据
Selector常用方法:
public static Selector Open();
得到一个选择器对象
public int select(long timeout);
监听所有注册通道,存在IO流操作是,会将对应的信息SelectionKey存入到内部的集
合中,参数是一个超时时间
public Set selectionKeys();
返回当前Selector内部集合中保存的所有SelectionKey
SelectionKey
表示Selector和网络通道直接的关系
int OP_ACCEPT; 16 需要连接
int OP_CONNECT; 8 已经连接
int OP_READ; 1 读取操作
int OP_WRITE; 4 写入操作
SelectionKey
public abstract Selector selector();
得到与之关联的 Selector 对象
public abstract SelectableChannel channel();
得到与之关联的通道
public final Object attachment();
得到与之关联的共享数据
public abstract SelectionKey interestOps(int ops);
设置或改变监听事件
public final boolean isAcceptable();
是否可以 accept
public final boolean isReadable();
是否可以读
public final boolean isWritable();
是否可以写
采用无阻塞IO实现网络聊天室
import org.junit.Test;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
/**
* Title:TestBlockIO
* Description:TestBlockIO ,测试无阻塞NIO
*
* @author justLym
* @version 1.0.0
* @date 2019/11/23
**/
public class TestBlockIO {
@Test
public void client() throws IOException {
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",6888));
socketChannel.configureBlocking(false);
ByteBuffer buffer = ByteBuffer.allocate(1024);
buffer.put("黎亚明".getBytes());
buffer.flip();
socketChannel.write(buffer);
buffer.clear();
socketChannel.close();
}
@Test
public void server() throws IOException{
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.bind(new InetSocketAddress("127.0.0.1",6888));
ByteBuffer buffer = ByteBuffer.allocate(1024);
Selector selector = Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
while(selector.select()>0){
Iterator iterator = selector.selectedKeys().iterator();
while(iterator.hasNext()){
SelectionKey selectionKey = iterator.next();
if(selectionKey.isAcceptable()){
SocketChannel accept = serverSocketChannel.accept();
System.out.println(accept);
accept.configureBlocking(false);
accept.register(selector,SelectionKey.OP_READ);
}else if(selectionKey.isReadable()){
SocketChannel channel = (SocketChannel)selectionKey.channel();
channel.configureBlocking(false);
int len = 0;
while((len=channel.read(buffer))!=-1){
buffer.flip();
System.out.println(new String(buffer.array(),0,len));
buffer.clear();
}
}
iterator.remove();
}
}
serverSocketChannel.close();
selector.close();
}
}