1、

Channel  通道,可以将指定文件的部分或全部直接映射成Buffer。

不能直接读写Channel中的数据,Channel只能与ByteBuffer交互。

读数据时,把Channel中的数据映射到ByteBuffer中取出数据使用。

写数据时,把数据放到Buffer中,再把ByteBuffer中的数据写到Channel中。

Channel是一个接口,常用的实现类有:

  • FileChannel    用于文件读写
  • DatagramChannel    用于UDP通信的Channel
  • ServerSocketChannel、SocketChannel    用于TCP通信的Channel

这里只介绍FileChannel,用于UDP、TCP通信的Channel在写网络编程时再介绍。

2、Channel常用方法:

  • 字节流对象.getChannel()      //获取对应类型的Channel对象。只有字节流对象才有getChannel()方法。
  • Channel对象.read(ByteBuffer  buffer)      //从Channel对应的输入流中读取数据到buffer中,buffer只能是ByteBuffer类型,不能是其他Buffer类型
  • Channel对象.write(ByteBuffer buffer)    //将buffer中的数据写到channel对应的流中
  • Channel对象.position()    //返会Channel中记录指针的位置,返回值是long型
  • Channel对象.position(long  index)     //将Channel中的记录指针调整到指定的位置
  • Channel对象.map(映射模式,起始下标,长度)    //将文件的部分/全部映射到一个MappedByteBuffer对象中,返回该MappedByteBuffer对象。

3、

示例:读文件,一次读完

         //创建Channel
File file=new File("./1.txt");
FileInputStream in=new FileInputStream(file);
FileChannel channel=in.getChannel(); //通过文件输入流对象获取Channel //创建Buffer
ByteBuffer byteBuffer=ByteBuffer.allocate(1024); //将Channel中的数据读取到Buffer中。Channel只能与ByteBuffer交互,不能与其它Buffer交互
channel.read(byteBuffer); //调用ByteBuffer的flip()方法,调整指针,做好数据使用准备
byteBuffer.flip(); //对ByteBuffer进行解码,转换为CharBuffer。因为ByteBuffer不能直接转换为String,通过toString()转换得到String不是文件内容。要把ByteBuffer转换为CharBuffer。
Charset charset=Charset.forName("GBK"); //创建Charset对象。Windows创建的文本文件默认以GBK编码,即默认的编码字符集为GBK。
//这里使用的编码字符集要与文件的编码字符集对应。如果我们在创建文件时指定字符集为UTF-8,或者在资源管理器中将文件的编码修改为UTF-8,则此处使用UTF-8.
CharsetDecoder decoder=charset.newDecoder(); //创建解码器
CharBuffer charBuffer=decoder.decode(byteBuffer); //使用解码器对ByteBuffer解码,得到CharBuffer //此处CharBuffer不能调用flip(),调用了反而没有数据 //可以通过get()获取char,也可以通过get(char[] arr)读取到一个char[]中,或者使用toString()转换为String
System.out.println(charBuffer.get()); //获取第一个字符
System.out.println(charBuffer.toString()); //把CharBuffer剩余部分转换为String输出。注意是剩余部分的数据。 //sout输出一个对象时,会自动调用这个对象的toString()方法,将对象转换为String输出。
//所以也可以写为 System.out.println(charBuffer); byteBuffer.clear();
charBuffer.clear();
channel.close();
in.close();

示例:读文件,循环读取

         //创建Channel
FileInputStream in=new FileInputStream("./1.txt");
FileChannel channel=in.getChannel(); //创建Buffer
ByteBuffer byteBuffer=ByteBuffer.allocate(102);
CharBuffer charBuffer; //创建解码器
CharsetDecoder decoder=Charset.forName("GBK").newDecoder(); //循环读取数据
while (channel.read(byteBuffer)!=-1){ //read()后,Channel中的指针会自动后移。没数据可读时返回-1。
byteBuffer.flip(); //做好数据使用准备
charBuffer=decoder.decode(byteBuffer); //解码
System.out.println(charBuffer);
byteBuffer.clear(); //清空,准备下次使用。必须清空byteBuffer。
/*
因为channel.read(byteBuffer)的机制是把channel的数据读取到byteBuffer中,返回byteBuffer中的内容长度,不是返回从channel中读取的数据长度。
如果不清空byteBuffer,第一次循环后,记录指针指到byteBuffer末尾,再次执行channel.read(byteBuffer)时,因为byteBuffer是满的,没有剩余空间,
不会从Channel中读取新数据,而返回的byteBuffer的内容长度不等于-1,会再次执行循环(使用第一次byteBuffer中的数据)。
会一直使用第一次读取到的数据,陷入死循环。
*/
charBuffer.clear(); //这个可缺省,因为下一次的值会自动覆盖上一次的。
} channel.close();
in.close();

示例:写文件

         //创建Channel
FileOutputStream out=new FileOutputStream("./2.txt");
FileChannel channel=out.getChannel(); //创建Buffer
ByteBuffer buffer=ByteBuffer.allocate(1024); //将要写的数据放入Buffer中
buffer.put("hello world!".getBytes()); //要调整好指针,标明可用数据。否则Buffer中的可用数据为空
buffer.flip(); //将Buffer中的数据写入Channel。会同步写入到文件中。
channel.write(buffer); buffer.clear();
channel.close();
out.close();

上面的三个例子,都使用了Buffer,本质和传统IO流的缓冲是一样的,读写速度都很快,但都没有使用通道映射。

4、

示例:使用通道映射读写文件

 File inFile=new File("./1.txt");
FileChannel inChannel=new FileInputStream(inFile).getChannel();
FileChannel outChannel=new FileOutputStream("./2.txt").getChannel(); /*
把文件输入流的Channel映射到Buffer中,输入流的Channel只能映射为只读。
映射的是整个文件。把inFile单独作为一个对象,就是为了获取文件长度
*/
MappedByteBuffer buffer=inChannel.map(FileChannel.MapMode.READ_ONLY,0,inFile.length()); //把Buffer中的内容写到输出流的Channel中,会同步写到输出文件中。这就实现了文件复制。
outChannel.write(buffer); inChannel.close();
outChannel.close();

示例:使用通道映射读文件

 //创建Channel
File inFile=new File("./1.txt");
FileChannel inChannel=new FileInputStream(inFile).getChannel(); //映射到Buffer中
MappedByteBuffer buffer=inChannel.map(FileChannel.MapMode.READ_ONLY,0,inFile.length()); //创建解码器。MappedByteBuffer是ByteBuffer的子类,要转换为CharBuffer使用。
CharsetDecoder decoder=Charset.forName("GBK").newDecoder(); //通道映射得到的MappedByteBuffer,使用MappedByteBuffer中的数据之前不必flip()调整指针,指针已经调整好了。
//当然 buffer.flip(); 写上也行 //转换为CharBuffer
CharBuffer charBuffer=decoder.decode(buffer); //使用charBuffer中的数据
System.out.println(charBuffer); buffer.clear();
charBuffer.clear();
inChannel.close();

使用通道映射是最快的。但如果映射的文件很大,比如1,2个G,一次性映射整个文件会占用很大的内存,反而会引起性能的下降,此时可以使用循环依次映射读取。

也可以只使用缓冲、不使用通道映射,利用循环依次读取,但是速度要慢些。

如果不用把数据(内容)转化为String,就不必使用解码器。

5、

RandomAccessFile类也可以使用通道映射:

      //创建Channel
File file = new File("./1.txt");
RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw"); //使用RandomAccessFile可以指定文件打开方式
FileChannel channel = randomAccessFile.getChannel(); /*
将Channel映射到Buffer中。
以r只读打开,只能映射为只读;以rw读写方式打开,不管映射模式指定为只读、还是读写,都会映射为读写。
就是说,以rw方式打开的文件,进行通道映射后,Channel既可以读,又可以写(会同步到文件中)
*/
MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, file.length()); channel.position(file.length()); //将记录指针移到Channel末尾
channel.write(buffer); //将buffer写到Channel末尾,即复制内容追加到末尾 channel.close();
randomAccessFile.close();

使用RandomAccessFile类进行通道映射的优点:

可以指定文件打开方式,以读写方式打开进行映射后,Channel既可以读,又可以写,适用于要同时进行读写的文件。

6、注意点

使用ByteBuffer中的数据之前,要先flip()调整好指针位置。

如果后续还要使用ByteBuffer,要先调用clear()将ByteBuffer清空后再使用(在循环读取数据时,往往要用到)。

File不用关闭,但File对应的流要关闭。

RandomAccessFile、Channel和流很相似,都需要关闭。

Buffer只是一个容器,不是流,不用关闭。

Java NIO中的Channel接口的更多相关文章

  1. java NIO中的buffer和channel

    缓冲区(Buffer):一,在 Java NIO 中负责数据的存取.缓冲区就是数组.用于存储不同数据类型的数据 根据数据类型不同(boolean 除外),提供了相应类型的缓冲区:ByteBufferC ...

  2. Java NIO学习笔记---Channel

    Java NIO 的核心组成部分: 1.Channels 2.Buffers 3.Selectors 我们首先来学习Channels(java.nio.channels): 通道 1)通道基础 通道( ...

  3. Java NIO中核心组成和IO区别

    1.Java NIO核心组件 Java NIO中有很多类和组件,包括Channel,Buffer 和 Selector 构成了核心的API.其它组件如Pipe和FileLock是与三个核心组件共同使用 ...

  4. Java NIO中的Buffer 详解

    Java NIO中的Buffer用于和NIO通道进行交互.如你所知,数据是从通道读入缓冲区,从缓冲区写入到通道中的.缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存.这块内存被包装成NIO ...

  5. Java NIO中的缓冲区Buffer(一)缓冲区基础

    什么是缓冲区(Buffer) 定义 简单地说就是一块存储区域,哈哈哈,可能太简单了,或者可以换种说法,从代码的角度来讲(可以查看JDK中Buffer.ByteBuffer.DoubleBuffer等的 ...

  6. 转载Java NIO中的Files类的使用

    Java NIO中的Files类(java.nio.file.Files)提供了多种操作文件系统中文件的方法. Files.exists() Files.exits()方法用来检查给定的Path在文件 ...

  7. 5. 彤哥说netty系列之Java NIO核心组件之Channel

    你好,我是彤哥,本篇是netty系列的第五篇. 简介 上一章我们一起学习了如何使用Java原生NIO实现群聊系统,这章我们一起来看看Java NIO的核心组件之一--Channel. 思维转变 首先, ...

  8. java 8中抽象类与接口的异同

    1.java 8中抽象类与接口的异同 相同点: 1)都是抽象类型: 2)都可以有实现方法(以前接口不行): 3)都可以不需要实现类或者继承者去实现所有方法,(以前不行,现在接口中默认方法不需要实现者实 ...

  9. Java NIO中的通道Channel(二)分散/聚集 Scatter/Gather

    什么是Scatter/Gather scatter/gather指的在多个缓冲区上实现一个简单的I/O操作,比如从通道中读取数据到多个缓冲区,或从多个缓冲区中写入数据到通道: scatter(分散): ...

随机推荐

  1. Spring加载配置文件的几种方法(org.springframework.beans.factory.BeanDefinitionStoreException)

    一:Spring中的几种容器都支持使用xml装配bean,包括:XmlBeanFactory ,ClassPathXmlApplicationContext ,FileSystemXmlApplica ...

  2. 记一次开发过程中,iview遇到的一些坑以及解决办法

    写在开头:本次项目采用的是vue2.0+iview3.0,最近公司没啥事,来总结一下开发过程中遇到的问题. 1.Modal关闭问题 需求背景:modal框里面是个form表单,点击确定之后,先验证fo ...

  3. 利用js阻止表单提交

    (1) return false <form name="loginForm" action="login.aspx" method="post ...

  4. MOTCF 没时间解释了 条件竞争漏洞

    moctf 没时间解释了 条件竞争漏洞 题目链接 条件竞争: 在本题目中,上传文件的时候服务器无条件的接收任何类型的文件,但是你上传之后服务器会给你的文件内容修改为too slow. 比如你上传了一句 ...

  5. 菜鸟之路——git学习及GitHub的使用

    首先,感谢廖雪峰老师的git教程 https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000 ...

  6. [NOI2010][bzoj2005] 能量采集 [欧拉函数+分块前缀和优化]

    题面: 传送门 思路: 稍微转化一下,可以发现,每个植物到原点连线上植物的数量,等于gcd(x,y)-1,其中xy是植物的横纵坐标 那么我们实际上就是要求2*sigma(gcd(x,y))-n*m了 ...

  7. BZOJ3998 [TJOI2015]弦论 【后缀自动机】

    题目 对于一个给定长度为N的字符串,求它的第K小子串是什么. 输入格式 第一行是一个仅由小写英文字母构成的字符串S 第二行为两个整数T和K,T为0则表示不同位置的相同子串算作一个.T=1则表示不同位置 ...

  8. 企鹅的游戏(penguin)

    企鹅的游戏(penguin) 题目描述 Shiva养了一只小企鹅.小企鹅很聪明,她总是帮Shiva和他的好朋友想出很多很好玩的游 戏.其中有一个游戏特别经典,Shiva和他的小伙伴们百玩不厌. 游戏规 ...

  9. 笔记:CS231n+assignment1(作业一)

    CS231n的课后作业非常的好,这里记录一下自己对作业一些笔记. 一.第一个是KNN的代码,这里的trick是计算距离的三种方法,核心的话还是python和machine learning中非常实用的 ...

  10. 如何用jquery获得td里边的内容

    jQuery 中使用 text() 或者 html() 函数可以获取td的内容: $("td").text();   // 或者 $("td").html(); ...