Java NIO

NIO(New IO)即从 JDK 1.4 开始,Java 提供了一系列改进的输入/输出处理的新功能,新增的许多用于处理输入/输出的类,这些类都被放在java.nio 包以及子包下,并且对原 java.io 包里的许多类都以 NIO 为基础进行了该项,新增了满足 NIO 的功能。

本文包含:

  1. java 新IO 概述
  2. 使用Buffer
  3. 使用 Channel
  4. 字符集和 Charset
  5. 文件锁

1. java 新IO 概述

新IO 采样内存映射文件的方式来处理输入/输出,速度更快。
Java 中新 IO 的包如下:
在这里插入图片描述
Channel 和 Buffer 是新IO 中的两个核心对象,Channel 是对传统的输入/输出系统的模拟,在新IO 系统中所有数据都必须通过 Channel 传输:Channel 与传统的 InputStream、OutputStream 的模拟 ,在新 IO 系统中所有的数据都需要通过通道传输;Channel 与传统的 InputStream、OutputStream 最大的区别是它提供了一个map() 方法,通过该map() 方法可以直接将“一块数据”映射到内存中。如果说传统的输入/输出系统是面向流的处理,那么新 IO 则是面向块的处理。
在这里插入图片描述

2. 使用Buffer

从内部结构上来看,Buffer 就像一个数组,它可以保存多个类型相同的数据。Buffer 是一个抽象类,其最常用的子类是 ByteBuffer,它可以在底层字节数组上进行 get/set 操作。处理 ByteBuffer 之外,对应于其它基本数据类型都有其对应的Buffer 类:CharBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer、DoubleBuffer。
这些 Buffer 类都没有提供构造器,通过使用如下方法来得到一个 Buffer 对象。
在这里插入图片描述
在 Buffer 中有三个重要的概念:容量、界限和位置
在这里插入图片描述
除此之外,Buffer 里还支持一个可选的标记,Buffer 允许直接将 position 定位到 mark 处。这些值满足如下关系:
在这里插入图片描述
下图显示了某个Buffer 读入了一些数据后的示意图。
在这里插入图片描述
在这里插入图片描述
Buffer 中包含两个重要的方法,即 flip() 和 clear(),flip() 为从 Buffer 中取出数据做好准备,而 clear() 为再次向Buffer中装入数据做好准备。
此外,Buffer还包含如下一些常用方法:
在这里插入图片描述
此外,Buffer的所有子类还提供了两个重要的方法:put() 和 get() 方法,用于向Buffer 中放入数据和从Buffer 中取出数据。当使用put()和 get() 方法放入、取出数据时,Buffer 既支持对单个数据的访问,也支持对批量数据的访问(以数组作为参数)
当使用put() 和 get() 来访问Bufffer 中的数据时,分为相对和绝对两种。
在这里插入图片描述

3. 使用 Channel

Channel 类似于传统的流对象,但与传统的流对象有两个主要区别。

  1. Channel 可以直接将指定文件的部分或全部映射成Buffer,
  2. 程序不能直接访问Channel 中的数据,包括读取、写入都不行,Channel 只能与 Buffer 进行交互。也就是说,如果要从Channel 中取得数据,就必须先用Buffer 从 Channel 中取得一些数据,然后让程序从Buffer 中取出这些数据;如果要将程序中的数据写入 Channel,一样先让程序将数据放入Buffer 中,程序再将 buffer 里的数据写入 Channel 中。
    在这里插入图片描述
    所有的 Channel 都不应该通过构造器来直接创造,而是通过传统的节点 InputStream、OutputStream 的 getChannel() 方法来返回对应的 Channel,不同的节点流获得的 Channel 不一样。
    Channel 中最常用的三类方法是 map()、read() 和 write(),其中map() 方法用于将 Channel 对应的部分或者全部数据映射成 ByteBuffer;而 read() 或 write() 方法都以一系列重载形式,这些方法用于从Buffer 中读取数据或者向Buffer 中写入数据。
    在这里插入图片描述

4. 字符集和 Charset

编码:把明文的字符序列转换成计算机所能理解的二进制序列
解码:把二进制序列转换成普通人能看懂的明文字符串
在这里插入图片描述
下面是常用的字符串别名:
在这里插入图片描述
可以使用 System 类的 getProperties() 方法来访问本地系统的文件编码格式,文件编码格式的属性名为 file.encoding。
一旦知道了字符集的别名之后,程序就可以调用 Charset 的 forName() 方法来创建对应的Charset 对象,forName() 方法的参数就是相应字符集的别名。例如如下代码:
在这里插入图片描述
获得了Charset 对象之后,就可以通过该对象的newDecoder() 、newEncoder() 方法分别返回 CharsetDecoder 和 CharsetEncoder 对象,代表该 Charset 的解码器和编码器。调用 CharsetDecoder 的 decode() 方法就可以将 ByteBuffer(字节序列)转换成CharBuffer(字符序列),调用CharsetEncoder 的 encode() 方法就可以将 CharBuffer 或 String(字符序列)转换成 ByteBuffer(字节序列)。
Charset 类也提供了如下三个方法:
在这里插入图片描述
在这里插入图片描述
在 String 类里提供了一个 getBytes(String charset) 方法,该方法返回 byte[],该方法也是使用指定的字符集将字符串转为字节序列。

5. 文件锁

文件锁可以有效阻止多个进程并发修改同一个文件。

文件锁控制文件的全部或部分字节的访问,在NIO 中,Java 提供了 FileLock 来支持文件锁定功能,在 FileChannel 中提供的 lock()/tryLock() 方法可以获得文件锁 FileLock 对象,从而锁定文件。

lock() 和 tryLock() 方法存在区别:
当 lock() 试图锁定某个文件时,如果无法得到文件锁,程序将一直被阻塞;
而 tryLock() 是尝试锁定文件,它将直接返回而不是阻塞,如果获得了文件锁,该方法则返回该文件锁,否则返回null。

如果FileChannel 只想锁定文件的部分内容,而不是全部内容,则可以使用如下的 lock() 或 tryLock() 方法。
在这里插入图片描述
当参数 shared 为 true 时,表明该锁是一个共享锁,它将允许多个进程来读取该文件,但阻止其它进程获得对文件的排他锁。当 shared 为 false 时,表明该锁是一个排他锁,它将锁住对该文件的读写。程序可以通过调用 FileLock 的 isShared 来判断它获得的锁是否为共享锁。

直接使用 lock() 或 tryLock() 方法获得的文件是排他锁。
处理完文件后通过 FileLock 的 release() 方法释放文件锁。下面程序示范了使用 FileLock 锁定文件的实例。
在这里插入图片描述
上面程序中的第一行粗体字代码用于对指定文件加锁,接着调用 Thread.sleep(10000)暂停了 10 秒后才释放文件锁(如程序第二行粗体字代码所示),因此在这10 秒之内,其他程序无法对 a.txt 文件进行修改。
文件锁虽然可以用于控制并发访问,但对于高并发访问的情形,还是推荐使用数据库来保存程序信息,而不是使用文件。
关于文件锁还有以下几点说明:
在这里插入图片描述在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值