Java-NIO
-
NIO是以块的方式处理数据,但是IO是以最基础的字节流的形式一次一个字节地去写入和读出的。所以在效率上的话,肯定是NIO效率比IO效率会高出很多。
-
NIO不在是和IO一样用OutputStream和InputStream 输入流的形式来进行处理数据的,但是又是基于这种流的形式,而是采用了通道和缓冲区的形式来进行处理数据的。
-
NIO的通道是可以双向的,但是IO中的流只能是单向的
-
Channel,国内大多翻译成“通道”。
-
Channel和IO中的Stream(流)是差不多一个等级的。
-
Stream是单向的,譬如:InputStream, OutputStream。
-
Channel是双向的,既可以用来进行读操作,又可以用来进行写操作。
-
Java NIO中的FileChannel是一个连接到文件的通道。可以通过文件通道读写文件。
3.Buffer
-
Buffer是一个缓冲区实质上是一个数组,用于和NIO通道FileChannel进行交互。
-
在 I/O 中,将数据直接写入或者将数据直接读到 Stream 对象中。
-
在 NIO 库中,所有数据都是用Buffer 缓冲区处理的。
-
在读取数据和写入数据时,它是直接读到或写入缓冲区中的。
-
任何时候访问 NIO 中的数据,都是将它放到缓冲区中。
-
-
向Buffer中写数据
-
从Channel写到Buffer (fileChannel.read(buf))
-
通过Buffer的put()方法 (buf.put(…))
-
-
从Buffer中读取数据
-
从Buffer读取到Channel (channel.write(buf))
-
使用get()方法从Buffer中读取数据 (buf.get())
把Buffer简单地理解为一组基本数据类型的元素列表,它通过几个变量来保存这个数据的当前位置状态
-
-
java.nio.Buffer类是一个抽象类,不能被实例化。Buffer类的直接子类,如ByteBuffer等也是抽象类,所以也不能被实例化。
但是ByteBuffer类提供了静态工厂方法来获得ByteBuffer的实例:
- ByteBuffer:字节缓冲流对象
- capacity (总的大小)
- position(添加数据position增加,还可以归0)
- limit (position归o,limit值是position原来的位置的值)
- clear方法:源代码中并没有真正把数据清除,position=0,limit=capacity,mark=-1
-
案例
import java.nio.ByteBuffer; public class FileWriterWithNIO { public void writerWithNIO() { //创建了一个11个byte的数组的缓冲区 ByteBuffer buffer = ByteBuffer.allocate(11); System.out.println("==刚创建ByteBuffer对象=="); System.out.printf("capacity:%s,limit:%s,position:%s\n", buffer.capacity(), buffer.limit(), buffer.position()); System.out.println("==ByteBuffer对象写入数据=="); //buffer写入数据 buffer.put("hello".getBytes()); System.out.printf("capacity:%s,limit:%s,position:%s\n", buffer.capacity(), buffer.limit(), buffer.position()); System.out.println("==调用buffer.flip()=="); //position归0,limit的值就position原来的值 buffer.flip(); System.out.printf("capacity:%s,limit:%s,position:%s\n", buffer.capacity(), buffer.limit(), buffer.position()); byte[] array = buffer.array(); String s = new String(array, 0, buffer.limit()); System.out.println(s); } public static void main(String[] args) { new FileWriterWithNIO().writerWithNIO(); } } /* ==刚创建ByteBuffer对象== capacity:11,limit:11,position:0 ==ByteBuffer对象写入数据== capacity:11,limit:11,position:5 ==调用buffer.flip()== capacity:11,limit:5,position:0 hello */
import java.nio.ByteBuffer; public class FileReaderWithNIO { public void readFileSystem() { // 创建了一个11个byte的数组的缓冲区 ByteBuffer buffer = ByteBuffer.allocate(11); buffer.put("hello".getBytes()); System.out.println("capacity:" + buffer.capacity() + ",limit:" + buffer.limit() + ",position:" + buffer.position()); // 读缓冲区里的数据 // 将position的位置设置为零 buffer.flip(); for (int i = 0; i < buffer.limit(); i++) { System.out.print((char) buffer.get(i)); } } public static void main(String[] args) { new FileReaderWithNIO().readFileSystem(); } } /* capacity:11,limit:11,position:5 hello */
4.Channel与Buffer一起使用
-
FileChannel和Buffer一起用的步骤
-
使用FileChannel之前,必须先打开它。(但是,我们无法直接打开一个FileChannel,需要通过使用一个InputStream、OutputStream或RandomAccessFile来获取一个FileChannel实例)
-
FileChannel类里的方法:
- 文件复制: transferTo (offset,读数据通道的大下,目标通道)
-
-
用完FileChannel后必须将其关闭
-
案例
public class ReadWriterWithNIO { public void readWriterWithNIO(File src) { FileInputStream fis = null; FileChannel fileChannel = null; try { fis = new FileInputStream(src); //使用FileChannel之前,必须先打开它,FileChannel个流不能直接用要套在其它流上用 fileChannel = fis.getChannel(); //创建一个大小为2的数组缓冲区 ByteBuffer buf = ByteBuffer.allocate(2); //从该通道读取到给定缓冲区的字节数 int bytesRead = fileChannel.read(buf); System.out.println("读到多少个字节数=" + bytesRead); while (bytesRead != -1) { buf.flip(); while (buf.hasRemaining()) { System.out.print((char) buf.get()); } //清空缓冲区 buf.clear(); //从该通道读取到给定缓冲区 bytesRead = fileChannel.read(buf); } } catch (IOException e) { e.printStackTrace(); } finally { try { if (fileChannel != null) { fileChannel.close(); } } catch (IOException e) { e.printStackTrace(); } } try { if (fis != null) { fis.close(); } } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { new ReadWriterWithNIO().readWriterWithNIO(new File("aa.txt")); } }
-
利用NIO进行文件拷贝(也可拷贝部分数据)的示例:
public class TestFileCopyWithNIO { public static void main(String[] args) throws IOException { saveToFileSystem(new File("writerdata.txt"),new File("writerdata_copy.txt")); } public static void saveToFileSystem(File src, File target) throws IOException { //实例化文件输入流 FileInputStream fis = new FileInputStream(src); //实例化文件输出流 FileOutputStream fos = new FileOutputStream(target); //输入流通道 FileChannel inChannel = fis.getChannel(); //输出流通道 FileChannel outChannel = fos.getChannel(); //将字节从输入流通道的传输到输出流通道。 inChannel.transferTo(0, inChannel.size(), outChannel); //关闭通道和流 inChannel.close(); outChannel.close(); fis.close(); fos.close(); } }
5.事件及nio的非阻塞读取
-
Java NIO的事件选择器允许一个单独的线程来监视多个输入通道
-
可以注册多个通道使用一个选择器,然后使用一个单独的线程来“选择”通道:
-
这些通道里已经有可以处理的输入,或者选择已准备写入的通道。
-
这种选择机制,使得一个单独的线程很容易来管理多个通道
-
-
由于有了事件选择器,因此NIO可以以非阻塞的方式读取数据
-
NIO的这些特性在网络通讯方面非常有用。
6.NIO和IO的区别
-
Java NIO和IO的主要区别
-
面向Stream和面向Buffer
-
Java NIO和IO之间最大的区别是IO是面向流(Stream)的,NIO是面向块(buffer)的。
-
面向流意味着从流中一次可以读取一个或多个字节,拿到读取的这些做什么你说了算,这里没有任何缓存(这里指的是使用流没有任何缓存,接收或者发送的数据是缓存到操作系统中的,流就像一根水管从操作系统的缓存中读取数据)而且只能顺序从流中读取数据,如果需要跳过一些字节或者再读取已经读过的字节,你必须将从流中读取的数据先缓存起来。
-
面向块的处理方式有些不同,数据是先被读/写到buffer中的,根据需要你可以控制读取什么位置的数据。这在处理的过程中给用户多了一些灵活性,你需要额外做的工作是检查你需要的数据是否已经全部到了buffer中,你还需要保证当有更多的数据进入buffer中时,buffer中未处理的数据不会被覆盖。
-
-
阻塞IO和非阻塞IO
-
所有的Java IO流都是阻塞的,这意味着,当一条线程执行read()或者write()方法时,这条线程会一直阻塞知道读取到了一些数据或者要写出去的数据已经全部写出,在这期间这条线程不能做任何其他的事情
-
java NIO的非阻塞模式(Java NIO有阻塞模式和非阻塞模式,阻塞模式的NIO除了使用Buffer存储数据外和IO基本没有区别)允许一条线程从channel中读取数据,通过返回值来判断buffer中是否有数据,如果没有数据,NIO不会阻塞,因为不阻塞这条线程就可以去做其他的事情,过一段时间再回来判断一下有没有数据NIO的写也是一样的,一条线程将buffer中的数据写入channel,它不会等待数据全部写完才会返回,而是调用完write()方法就会继续向下执行
-
-
Selectors
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!