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. 在F12 控制台输入,可执行jquery操作

    <!-- 控制台执行jquery -->var importJs=document.createElement('script') //在页面新建一个script标签importJs.se ...

  2. Maven使用入门

    Maven使用POM文件管理项目资源,pom.xml文件位于项目根目录下,结构如下: <?xml version="1.0" encoding="UTF-8&quo ...

  3. js中基础数据类型

    变量声明 undefined //未定义只声明   var age; alert(name);function fc(a1,a2,a3) { //alert(a1); //alert(a2); //a ...

  4. IOS开发学习笔记021-练习2

    只是简单练习一下,主要是学习里面的思想,处理问题的方法. 不过还有一个问题没想到解决方法. 那就是动态生成的按钮如何绑定按钮事件,请哪位大神指点一下啊.(知道怎么办了,原来是方法addTarget) ...

  5. Windows核心编程小结1

    这本书绝对经典,看看定会增加不少知识.当然这本书有很多东西比<Windows程序设计第五版>中的更加详细. 1.Unicode:宽字节字符集 这是一个国际的字符标准,16位,最大可支持65 ...

  6. CSU-2049 象棋

    CSU-2049 象棋 Description 車是中国象棋中的一种棋子,它能攻击同一行或同一列中没有其他棋子阻隔的棋子.一天,小度在棋盘上摆起了许多車--他想知道,在一共N×M个点的矩形棋盘中摆最多 ...

  7. Leetcode 542.01矩阵

    01矩阵 给定一个由 0 和 1 组成的矩阵,找出每个元素到最近的 0 的距离. 两个相邻元素间的距离为 1 . 示例 1: 输入: 0 0 0 0 1 0 0 0 0 输出: 0 0 0 0 1 0 ...

  8. JavaScript内存分配

    1.栈内存和堆内存 栈内存为自动分配的内存空间,由系统自动释放堆内存是动态分配的内存,大小不固定,也不会自动释放 js的值类型直接分配在栈内存中,引用类型分配在堆内存中引用类型变量保存的是引用类型的指 ...

  9. 寻找web根目录

    有时候,在利用一些命令执行漏洞时,可以以较高权限执行一些操作,但在我们想要上传shell的时候,却找不到网站根目录,下面是一些日常使用的寻找网站根目录的方法,记录下: 1.jsp 查看当前路径,在这个 ...

  10. 2 - Django基础

    一.Django流程 Django是使用python编写的web框架,遵守MTV设计思想. 实现原理: 1,浏览器发起请求. 2,Django根据URL Conf指向view(Views) 3,vie ...