Java NIO中的Channel接口
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接口的更多相关文章
- java NIO中的buffer和channel
缓冲区(Buffer):一,在 Java NIO 中负责数据的存取.缓冲区就是数组.用于存储不同数据类型的数据 根据数据类型不同(boolean 除外),提供了相应类型的缓冲区:ByteBufferC ...
- Java NIO学习笔记---Channel
Java NIO 的核心组成部分: 1.Channels 2.Buffers 3.Selectors 我们首先来学习Channels(java.nio.channels): 通道 1)通道基础 通道( ...
- Java NIO中核心组成和IO区别
1.Java NIO核心组件 Java NIO中有很多类和组件,包括Channel,Buffer 和 Selector 构成了核心的API.其它组件如Pipe和FileLock是与三个核心组件共同使用 ...
- Java NIO中的Buffer 详解
Java NIO中的Buffer用于和NIO通道进行交互.如你所知,数据是从通道读入缓冲区,从缓冲区写入到通道中的.缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存.这块内存被包装成NIO ...
- Java NIO中的缓冲区Buffer(一)缓冲区基础
什么是缓冲区(Buffer) 定义 简单地说就是一块存储区域,哈哈哈,可能太简单了,或者可以换种说法,从代码的角度来讲(可以查看JDK中Buffer.ByteBuffer.DoubleBuffer等的 ...
- 转载Java NIO中的Files类的使用
Java NIO中的Files类(java.nio.file.Files)提供了多种操作文件系统中文件的方法. Files.exists() Files.exits()方法用来检查给定的Path在文件 ...
- 5. 彤哥说netty系列之Java NIO核心组件之Channel
你好,我是彤哥,本篇是netty系列的第五篇. 简介 上一章我们一起学习了如何使用Java原生NIO实现群聊系统,这章我们一起来看看Java NIO的核心组件之一--Channel. 思维转变 首先, ...
- java 8中抽象类与接口的异同
1.java 8中抽象类与接口的异同 相同点: 1)都是抽象类型: 2)都可以有实现方法(以前接口不行): 3)都可以不需要实现类或者继承者去实现所有方法,(以前不行,现在接口中默认方法不需要实现者实 ...
- Java NIO中的通道Channel(二)分散/聚集 Scatter/Gather
什么是Scatter/Gather scatter/gather指的在多个缓冲区上实现一个简单的I/O操作,比如从通道中读取数据到多个缓冲区,或从多个缓冲区中写入数据到通道: scatter(分散): ...
随机推荐
- 01-Flutter移动电商实战-项目学习记录
一直想系统性的学习一下 Flutter,正好看到该课程<Flutter移动电商实战>的百度云资源,共 69 课时,由于怕自己坚持不下去(经常学着学着就不学了),故采用博客监督以记之. 1. ...
- 1 - smart(Maven:Package,Install,&,Log4j2)
mvn package 时,增加如下命令-Dmaven.test.skip=true 则表示package打包时,不执行也不编译测试用例,mvn package -Dmaven.test.skip=t ...
- ios开发第一步--虚拟机安装MAC OS X
暂时还没买Macbook,先用虚拟机练练手. 先说说准备工作,我是在win8下安装的,这个不是关键的,只要Vmware版本和MAC OS X版本确定就行了,win7下同样可以. 1.虚拟机Vmware ...
- 动态规划--找零钱 coin change
来自http://www.geeksforgeeks.org/dynamic-programming-set-7-coin-change/ 对于整数N,找出N的所有零钱的表示.零钱可以用S={s1,s ...
- 频繁模式挖掘中Apriori、FP-Growth和Eclat算法的实现和对比(Python实现)
最近上数据挖掘的课程,其中学习到了频繁模式挖掘这一章,这章介绍了三种算法,Apriori.FP-Growth和Eclat算法:由于对于不同的数据来说,这三种算法的表现不同,所以我们本次就对这三种算法在 ...
- java BigDecimal工具类
package com.core.calculate; import java.math.BigDecimal; import java.text.DecimalFormat; /** * Creat ...
- 编译php 转
LAOGAO added 20140907: configure: error: mcrypt.h not found. Please reinstall libmcrypt.复制代码 代码如下:wg ...
- 【距离GDOI:141天】 滚入数位DP的坑
作为博客园的第一篇...我都不知道要写什么了 ... 其实今天很没状态,就当吐槽吧... 嗯,被黄神带去写treap+可持久化线段树,然后在可持久化的删除上面跪了两天,真的是一跪不起.我已经连续多久没 ...
- javascript作用域链理解
执行上下文(Execution context,简称EC) 概念 每当控制器到达ECMAScript可执行代码的时候,就进入了一个执行上下文. javascript中,EC分为三种: ...
- aplusb 数论
题目描述 SillyHook 要给小朋友出题了,他想,对于初学者,第一题肯定是a+b啊, 但当他出完数据后神奇地发现.in 不见了,只留下了一些.out,他想还原.in,但 情况实在太多了,于是他想要 ...