NIO采用内存映射文件的方式处理输入输出,NIO将文件或文件的一段区域映射到内存中,这样就可以像访问内存一样来访问文件了(这种方式模拟了操作系统上的虚拟内存的概念),通过这种方式来进行输入输出比传统的输入输出要快得多.

Java中与NIO相关的包如下:

java.nio包:主要包含各种与Buffer相关的类.

java.nio.channels包:与Channle和Selector相关的类.

java.nio.charset包:主要包含与字符集相关的类

java.nio.channels.spi包:与Channel相关的服务提供编程接口

java.nio.charset.spi包:包含与字符集想的服务提供者编程接口

NIO中的新特点:Channel(通道)和Buffer(缓冲),Channle与传统的InputStream,OutputStream最大的区别在于它提供了一个map()方法,通过该方法可以海子街将"一块数据"映射到内存中.如果说传统的输入/输出系统是面向流的处理,则NIO则是面向块的处理.

Buffer可以被理解成一个容器,它的本质是一个数组,发送到Channel中的所有对象都必须首先放到Buffer中,而从Channel中读取的数据也必须先放到Buffer中.

除了ByteBuffer之外,它们都采用相同或相似的方法来管理数据,只是各自管理的数据类型不同而已.

NIO还提供了加工Unicode字符串映射成字节序列以及逆映射操作的Charset类, 也提供了用于支持费阻塞式输入输出的Selector类.

15.9.2使用Buffer

Buffer就像是一个数组,可以保存多个类型相同的数据.Buffer是一个抽象类,最常用的子类是ByteBuffer,它可以在底层字节数组上进行get/set操作. 也有CharBuffer, ShortBuffer, IntBuffer, LongBuffer, FloatBuffer, DoubleBuffer.

这些实现类都没提供构造器,使用如下方式是构造:

ByteBuffer byteBuffer = ByteBuffer.allocate(160);

或者

ByteBuffer byteBuffer = ByteBuffer.allocateDirect(160);//ByteBuffer 独有的创建方法,称为直接BUffer

直接Buffer的创建成本比普通的Buffer创建成本高,但效率也高,所以只适用于长生存期的Buffer,不适用于短生存期,一次用完就丢弃的Buffer.

但实际使用较多的是ByteBuffer和CharBuffer,其他Buffer子类则较少用到.其中ByteBuffer还有一个子类: MappedByteBuffer,它用于表示Channel将磁盘文件的部分或全部内容映射到内存中后得到的结果,通常MappedByteBuffer对象由Channel的map()方法返回.

Buffer中三个重要的概念: 容量(capacity),界限(limit),位置(position)

Buffer的主要作用就是装入数据,然后输出数据. 开始时,Buffer的Position为0,limit为capacity, 程序可通过put()方法向Buffer中放入一些数据(或者从Channel中获取一些数据),每放入一些数据,Buffer的position相应地向后移动一些位置.

当Buffer装入数据结束后,调用Buffer的flip()方法,该方法将limit设置为position所在位置,并将position设为0,这就使得Buffer的读写指针又移动到了开始的位置.也就是说,Buffer调用filp()方法之后,Buffer为输出数据做好准备;当Buffer输出数据结束后,Buffer调用clear()方法,clear方法不是清空Buffer的数据,它仅仅将position指为0,将limit置为capacity,这样为再次向Buffe热衷装入数据做好准备.

Buffer包含的一些常用方法:

这些方法大概可以分类

1.标记功能: mark()和reset()

2.position操作: position() position(int newPs) rewind() reset()

3.limit操作: limit() limit(int newLt)

4.剩余元素操作: hasRemaining() remaining()

5.容器大小: capacity()

重点:

put()方法:用于向Buffer中放入数据

get()方法:用于从Buffer中取出数据

这两个方法,既支持对单个数据的访问,也支持对批量数据的访问.

而且,分为相对和绝对两种.

相对(relative):从Buffer的当前position处开始读取或写入数据, 然后将位置(position)的值按处理的元素的个数增加.

绝对(Absolute):直接根据索引向Buffer中读取或写入数据,使用绝对方式访问Buffer里的数据时,并不会影响位置(position)的值.

    public static void bufferTest(){
CharBuffer buff = CharBuffer.allocate(); // ①
System.out.println("capacity: " + buff.capacity());
System.out.println("limit: " + buff.limit());
System.out.println("position: " + buff.position());
// 放入元素
buff.put('a');
buff.put('b');
buff.put('c'); // ②
System.out.println("加入三个元素后,position = "
+ buff.position());
// 调用flip()方法
buff.flip(); // ③
System.out.println("执行flip()后,limit = " + buff.limit());
System.out.println("position = " + buff.position());
// 取出第一个元素
System.out.println("第一个元素(position=0):" + buff.get()); // ④
System.out.println("取出一个元素后,position = "
+ buff.position());
// 调用clear方法
buff.clear(); // ⑤
System.out.println("执行clear()后,limit = " + buff.limit());
System.out.println("执行clear()后,position = "
+ buff.position());
System.out.println("执行clear()后,缓冲区内容并没有被清除:"
+ "第三个元素为:" + buff.get()); // ⑥
System.out.println("执行绝对读取后,position = "
+ buff.position());
}

15.9.3 使用Channel

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

1. Channel可以直接将指定文件的部分或全部直接映射成Buffer.

2. 程序不能直接访问Channel中的数据,包括读取, 写入,都不行,Channel中取出一些数据,然后让程序从Buffer中取出这些数据:如果要将程序中的数据写入Channel, 一样先让程序将数据放入Buffer中,程序再将Buffer里的数据写入Channel中.

Channle接口的实现类很多,这里只介绍FileChannel的用法.

所有的Channle都不应该通过构造器来直接创建,而是通过传统的节点InputStream,OutputStream的getChannle()方法来返回对应的Channle.

Channle中最常用的三类方法是map(),read(),write(),其中map()方法用于将Channle对应的部分或全部数据映射成ByteBuffer,而read()和write()方法都有一系列重载形式,这些方法用于从Buffer中读取数据或者向Buffer中写入数据.

public static void fileChannelTest(){
File f = new File(".project");
try(
// 创建FileInputStream,以该文件输入流创建FileChannel
FileChannel inChannel = new FileInputStream(f).getChannel();
// 以文件输出流创建FileBuffer,用以控制输出
FileChannel outChannel = new FileOutputStream("aw.txt")
.getChannel())
{
// 将FileChannel里的全部数据映射成ByteBuffer
MappedByteBuffer buffer = inChannel.map(FileChannel
.MapMode.READ_ONLY , , f.length()); // ①
// 直接将buffer里的数据全部输出
outChannel.write(buffer); // ②
// 再次调用buffer的clear()方法,复原limit、position的位置
buffer.clear(); // 使用GBK的字符集来创建解码器
Charset charset = Charset.forName("UTF-8");
// 创建解码器(CharsetDecoder)对象
CharsetDecoder decoder = charset.newDecoder();
// 使用解码器将ByteBuffer转换成CharBuffer
CharBuffer charBuffer = decoder.decode(buffer);
// CharBuffer的toString方法可以获取对应的字符串
System.out.println(charBuffer);
}
catch (IOException ex)
{
ex.printStackTrace();
}
}

下面给一个出问题的例子,这个是

RandomAccessFile生成的FileChannle,可是与疯狂讲义里说的有偏差,主要问题是当插入的数据超出FileChannle的范围时,FileChannle不会自动扩大,反而变成等待状态,
所访问的文件直接挂掉,一点那个文件,eclipse也挂掉,最后Mac都无法关机,只能强制关机。也不知道哪里没用对。
public static void randomFileChannelTest() throws FileNotFoundException, IOException{
File f = new File("write.txt");
try(
// 创建一个RandomAccessFile对象 FileChannel randomChannel = new RandomAccessFile(f, "rw").getChannel();
)
{
// 将Channel中所有数据映射成ByteBuffer
randomChannel.position();
System.out.println(f.length());
System.out.println(randomChannel.size());
ByteBuffer buffer = randomChannel.map(FileChannel.MapMode.READ_ONLY, ,);
System.out.println(buffer.limit());
System.out.println(buffer.capacity());
// 使用GBK的字符集来创建解码器
Charset charset = Charset.forName("UTF-8");
// 创建解码器(CharsetDecoder)对象
CharsetDecoder decoder = charset.newDecoder();
// 使用解码器将ByteBuffer转换成CharBuffer
CharBuffer charBuffer = decoder.decode(buffer);
// CharBuffer的toString方法可以获取对应的字符串
System.out.println(charBuffer);
// 把Channel的记录指针移动到最后
randomChannel.position();
System.out.println("randomChannel.position():"+randomChannel.position());
// 将buffer中所有数据输出
buffer.flip();
randomChannel.write(buffer);
buffer.clear();
randomChannel.close();
}catch(Exception e){
e.printStackTrace();
}
}

编码解码(字符集和Charset),(在String类里也提供了一个getBytes(String charset)方法,该方法返回byte[],也是使用指定的字符集将字符串转换成字节序列,跟这个Charset的功能类似).

public class CharsetTransform
{
public static void main(String[] args)
throws Exception
{
// 创建简体中文对应的Charset
Charset cn = Charset.forName("GBK");
// 获取cn对象对应的编码器和解码器
CharsetEncoder cnEncoder = cn.newEncoder();
CharsetDecoder cnDecoder = cn.newDecoder();
// 创建一个CharBuffer对象
CharBuffer cbuff = CharBuffer.allocate();
cbuff.put('孙');
cbuff.put('悟');
cbuff.put('空');
cbuff.flip();
// 将CharBuffer中的字符序列转换成字节序列
ByteBuffer bbuff = cnEncoder.encode(cbuff);
// 循环访问ByteBuffer中的每个字节
for (int i = ; i < bbuff.capacity() ; i++)
{
System.out.print(bbuff.get(i) + " ");
}
// 将ByteBuffer的数据解码成字符序列
System.out.println("\n" + cnDecoder.decode(bbuff));
}
}

15.9.5 文件锁

最新文章

  1. C++程序内存泄漏检测方法
  2. 微信公众号 扫码支付 模式二 demo
  3. RequireJS源码初探
  4. sdut 2411:Pixel density(第三届山东省省赛原题,字符串处理)
  5. PetaPoco 增删改查
  6. java实现版本号的比较
  7. Android MenuItem 设置文本颜色-TextColor设置
  8. hdu 6121---Build a tree(深搜+思维)
  9. nmon 使用
  10. sudoku--设想
  11. Python 斐波那契数列练习
  12. Android Studio精彩案例(三)《模仿微信ViewPage+Fragment实现方式二》
  13. 关于PHP的 PHP-FPM进程CPU 100%的一些原因分析和解决方案
  14. MySQL会发生死锁吗?
  15. POI (Apache POI)
  16. BZOJ3235 [Ahoi2013]好方的蛇 【单调栈 + dp】
  17. 从浏览器输入URL到显示页面到底发生了什么?
  18. 【刷题】LOJ 6014 「网络流 24 题」最长 k 可重区间集
  19. POI读取Excel数据保存到数据库,并反馈给用户处理信息(导入带模板的数据)
  20. MD5—加密,加盐

热门文章

  1. python接口自动化-有token的接口项目使用unittest框架设计
  2. 大数据学习——linux系统的网卡配置步骤
  3. 【转载】CentOS6.5升级手动安装GCC4.8.2
  4. python3--产生偏移和元素:enumerate
  5. python中的“坑”—持续更新
  6. POJ 3680: Intervals【最小费用最大流】
  7. 【数学】codeforces A. Success Rate
  8. bitcms-比特内容管理系统 3.1版源码发布
  9. [APIO2012] 派遣 dispatching
  10. Codeforces 513G1 513G2 Inversions problem [概率dp]