Java NIO(New IO)是一个可以替代标准Java IO API的IO API(从Java 1.4开始),Java NIO提供了与标准IO不同的IO工作方式。很多小伙伴可能和我一样,对于习惯了用IO来操作文件之后,对这个日渐流行的新东西会有不少的疑惑,那么阅读本文吧,和我一起打开NIO的大门,学习NIO操作文件。

一、NIO与IO的区别

下面有一个概要的区别图,图下面会有区别的描述。

IO                NIO
    面向流            面向缓冲
    阻塞IO            非阻塞IO
       无                 选择器

Java IO中最为核心的一个概念是流(Steam),面向流的编程。流是信息的载体。IO中的一个流要么是输入流,要么是输出流,不可能同时是输入流和输出流。Java IO的各种流是阻塞的。这意味着,当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。

NIO是面向缓冲,数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。 非阻塞写也是如此。

二、NIO的核心组件

NIO的核心包含三个重要组件:

1、Channel通道

Channel可以理解为IO的Stream流,每次读取数据,都是从通道中读取,写数据也是写入到通道,直接对接Buffer缓冲区。常见的通道有FileChannel,是文件的通道,用来读取文件,本文着重将这个通道。还有DataChannel,通过UDP读写网络数据,SocketChannel通过TCP读写网络数据,ServerSocketChannel监听新进来的TCP连接,对每一个新进来的TCP连接,都会建立一个SocketChannel。

通道可以实现双向读写,比如说用RandomAccessFile类获取文件读写,调用RandomAccessFile.getChannel()方法,获取的就是读写双向的通道。代码如下两行。

RandomAccessFile randomAccessFile = new RandomAccessFile(path.toFile(),"rw");
FileChannel fileChannel = randomAccessFile.getChannel();

下面这两种情况下获取的通道,只能单向的操作,请看代码。

FileOutputStream fos = new FileOutputStream(path.toFile());
FileChannel fileChannel1 = fos.getChannel();
FileInputStream fis = new FileInputStream(path.toFile());
FileChannel fileChannel2 = fis.getChannel();

这里分别通过字节输出流和字节输入流获取了通道,这两个通道都分别有write()和read()方法,但字节输出流创建的通道调用读取的方法时候就会报错,同理,输入流调用写的方法也会报错,什么原因呢?

原来是打开文件的权限不同导致的。从 FileInputStream 对象的getChannel( )方法获取的 FileChannel 对象是只读的,虽然FileChannel 实现了 ByteChannel 接口,看起来是双向的。但是在这样的通道上调用 write( )方法将抛出未经检查的NonWritableChannelException 异常,因为 FileInputStream 对象总是以 read-only 的权限打开文件。

2、Buffer缓冲区

缓冲区:缓冲区实质上是一个数组。最常用的缓冲区类型是ByteBuffer,对应Java的基本类型都有一种缓冲区区:

缓冲区类型:

  • ByteBuffer
  • CharBuffer
  • ShortBuffer
  • IntBuffer
  • LongBuffer
  • FloatBuffer
  • DoubleBuffer

类似IO中的BufferedInputStream等,NIO操作文件的核心方式就是Channel+Buffer,下面直接上一段代码,看一下缓冲区如何和通道结合起来使用。

    // 1. 获取数据源 和 目标传输地的输入输出流(此处以数据源 = 文件为例)FileInputStream fin = new FileInputStream(infile);FileOutputStream fout = new FileOutputStream(outfile);// 2. 获取数据源的输入输出通道FileChannel fcin = fin.getChannel();FileChannel fcout = fout.getChannel();// 3. 创建 缓冲区 对象:Buffer(共有2种方法)// 方法1:使用allocate()静态方法ByteBuffer buff = ByteBuffer.allocate(256);// 上述方法创建1个容量为256字节的ByteBuffer// 注:若发现创建的缓冲区容量太小,则重新创建一个大小合适的缓冲区// 方法2:通过包装一个已有的数组来创建// 注:通过包装的方法创建的缓冲区保留了被包装数组内保存的数据ByteBuffer buff = ByteBuffer.wrap(byteArray);// 额外:若需将1个字符串存入ByteBuffer,则如下String sendString="你好,服务器. ";ByteBuffer sendBuff = ByteBuffer.wrap(sendString.getBytes("UTF-16"));// 4. 从通道读取数据到缓冲区// 注:若 以读取到该通道数据的末尾,则返回-1fcin.read(buff);// 5. 传出数据准备:将缓存区的读模式 转换->> 写模式buff.flip();// 6. 从缓冲区中读取数据写入到通道fcout.write(buff);// 7. 重置缓冲区// 目的:重用现在的缓冲区,即 不必为了每次读写都创建新的缓冲区,在再次读取之前要重置缓冲区// 注:不会改变缓冲区的数据,只是重置缓冲区的主要索引值buff.clear();

上面方法中,在声明缓冲区的时候,罗列了不同的方式,后面读写操作都是对接的缓冲区。那么中间的flip()和clear()到底是干什么的呢?下面讲解一下Buffer缓冲区的内部原理方便大家理解。

Buffer缓冲区分为三个重要的变量:

Position:

在从通道读取时,您将所读取的数据放到底层的数组中。 position 变量跟踪已经写了多少数据。更准确地说,它指定了下一个字节将放到数组的哪一个元素中。因此,如果您从通道中读三个字节到缓冲区中,那么缓冲区的 position 将会设置为3,指向数组中第四个元素。

同样,在写入通道时,您是从缓冲区中获取数据。 position 值跟踪从缓冲区中获取了多少数据。更准确地说,它指定下一个字节来自数组的哪一个元素。因此如果从缓冲区写了5个字节到通道中,那么缓冲区的 position 将被设置为5,指向数组的第六个元素。

Limit:

limit 变量表明还有多少数据需要取出(在从缓冲区写入通道时),或者还有多少空间可以放入数据(在从通道读入缓冲区时)。

position 总是小于或者等于 limit

Capacity:

缓冲区的 capacity 表明可以储存在缓冲区中的最大数据容量。实际上,它指定了底层数组的大小 ― 或者至少是指定了准许我们使用的底层数组的容量。limit 决不能大于 capacity

看了上面对三个变量的描述,在看一下buffer缓冲区操作的三个方法的代码:

public final Buffer flip() {   limit = position;    position = 0;   mark = -1;   return this;   } 

flip():此方法将position的值赋给了limit,将position置为0,常用在读取通道中的数据到Buffer之后,要进行写操作之前调用,此时,缓冲区的数据的长度在position的位置,将position的值赋给limit,position置为0,可以一个字节不多一个字节不少的进行写的操作,保证数据的可靠性。

public final Buffer clear() {   position = 0;     //设置为0limit = capacity;    //极限和容量相同mark = -1;   //取消标记return this;
} 

clear():此方法相当于给Buffer复位,但不会清除Buffer中的数据,调用这个方法使缓冲区为为新的通道读取或写入做准备。如果是写入,新的写入会覆盖旧的同一个位置的内容。

public final Buffer rewind() {position = 0;mark = -1;return this;}

rewind() :使缓冲区为重新读取已包含的数据做好准备:它使限制保持不变,将位置设置为 0。和clear()类似,只是不改动限制。

以上三个方法都不对buffer内的数据作修改。

3、Selector选择器(略)

三、文件复制操作实战

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
/****@desc 复制文件*/
public class Test {public static void main(String[] args) throws IOException {// 定义源文件 & 目标文件String infile = "C:\\copy.sql";String outfile = "C:\\copy.txt";// 1. 获取数据源 和 目标传输地的输入输出流FileInputStream fin = new FileInputStream(infile);FileOutputStream fout = new FileOutputStream(outfile);// 2. 获取数据源的输入输出通道FileChannel fcin = fin.getChannel();FileChannel fcout = fout.getChannel();// 3. 创建缓冲区对象,容量初始化为1024ByteBuffer buff = ByteBuffer.allocate(1024);while (true) {// 4. 从通道读取数据 & 写入到缓冲区int r = fcin.read(buff);// 返回-1代表已读取到该通道数据的末尾,循环可以结束if (r == -1) {break;}// 5. 传出数据准备:调用flip()方法  buff.flip();// 6. 从 Buffer 中读取数据 & 传出数据到目标的输入通道fcout.write(buff);// 7. 复位缓冲区buff.clear();}}}

以上是对NIO操作文件的一些原理和实战的讲解,对NIO感兴趣的可以看看,如有疑问,下方留言我会随时回复哦。

惊!一文看懂Java NIO读写文件相关推荐

  1. java rest 序列化_一文看懂Java序列化

    一文看懂Java序列化 简介 首先我们看一下wiki上面对于序列化的解释. 序列化(serialization)在计算机科学的数据处理中,是指将数据结构或对象状态转换成可取用格式(例如存成文件,存于缓 ...

  2. 10分钟看懂 Java NIO 底层原理

    写在前面 很多的小伙伴,被java IO 模型,搞得有点儿晕,一会儿是4种模型,一会儿又变成了5种模型. 很多的小伙伴,也被nio这个名词搞晕了,一会儿java 的nio 不叫 非阻塞io,一会儿ja ...

  3. 【Java基础】10分钟看懂Java NIO

    一.IO概述 IO的操作方式通常分为几种:同步阻塞BIO.同步非阻塞NIO.异步非阳塞AIO 1.在JDK1.4之前,我们建立网络连接的时候采用的是 BIO 模式. 2.Java NIO(New IO ...

  4. 一文看懂Java锁机制

     作者:VectorJin https://juejin.cn/post/6844904036026548237 背景知识 指令流水线 CPU的基本工作是执行存储的指令序列,即程序.程序的执行过程实际 ...

  5. 一文看懂Java设计模式

    一.概述 这段时间系统看了一下设计模式的相关内容,也分享一下相关内容: 七个设计原则 创建型模式(5种) 结构型模式(7种) 行为型模式(11种) 总体来说设计模式分为三大类: 创建型模式,共五种: ...

  6. 一文看懂Java微服务架构,WEB2.0,垂直架构,分布式架构,微服务架构

    Java微服务架构 目录: 了解开发环境&生成环境 WEB1.0 & WEB2.0 垂直架构 分布式架构 微服务架构 1.了解开发环境&生产环境 1.1 开发环境 平时在写代码 ...

  7. java 序列化 uid_一文看懂Java序列化之serialVersionUID

    serialVersionUID适用于Java的序列化机制.简单来说,Java的序列化机制是通过判断类的serialVersionUID来验证版本一致性的.在进行反序列化时,JVM会把传来的字节流中的 ...

  8. 一文看懂Java虚拟机——JVM基础概念整理

    1 基础概念 2 垃圾回收 3 虚拟机调优

  9. 一文看懂JUC之AQS机制

     作者:VectorJin juejin.cn/post/6844904041760161806 为了解决原子性的问题,Java加入了锁机制,同时保证了可见性和顺序性.JDK1.5的并发包中新增了Lo ...

最新文章

  1. 微信小程序开发中如何实现侧边栏的滑动效果?
  2. 【python】Python遍历dict的key最高效的方法是什么?
  3. python测试网络连通性_python 判断网络连通的实现方法
  4. hdu 1874畅通工程续(基础Floyd)
  5. AAAI 2020 开源论文 | 语义感知BERT(SemBERT)
  6. 最新发布!斯坦福 CS224n 出作业视频详细讲解啦!
  7. 电脑出现qtwebengineprocess.exe停止报警_FANUC报警号,不用再翻书本了。
  8. java正则匹配英文句号_Scala 正则表达式 0411
  9. 信息科学 计算机 区别,电子信息科学技术和计算机科学技术有什么区别啊
  10. 持续集成部署Jenkins工作笔记0001---持续集成、持续部署、持续交付的概念
  11. ASP.NET 生命周期(原文翻译)
  12. Android FrameWork——PackageManager框架
  13. videojs暂停时显示大按钮_服务失败时进行故障恢复,Windows 7系统如何打开服务管理程序...
  14. java传递json_JAVA中使用JSON进行数据传递示例
  15. Linux 磁盘无损扩容
  16. Nginx神来之笔,提高并发原来如此简单
  17. 2021年焊工(初级)考试题及焊工(初级)考试报名
  18. cuda之thread,block,gird详解
  19. USRP B210驱动安装
  20. ios10怎么设置电池颜色_苹果手机电池颜色变黄了怎么调

热门文章

  1. DNS提示错误无法上网怎么办?苹果电脑如何修改DNS?
  2. 震动硅谷!43 岁知名技术大佬当街遇刺身亡,马斯克怒批暴力犯罪
  3. 改进蚁群算法 改进flod算法对路径进行双向平滑度优化,提高路径的平滑度
  4. qW3xT.2,解决挖矿病毒。
  5. 数据科学与机器学习案例之汽车目标客户销售策略研究
  6. C#操纵Word的坑
  7. 车载服务器作用,穿针引线谈铁路机车车载系统服务器
  8. Java实现第九届蓝桥杯螺旋折线
  9. Beta阶段基于NABCD评论作品
  10. 项目中的用户鉴权是如何实现的?