最近看博客又看到了Java NIO相关的博客,其中有讲解NIO和传统IO关于文件复制的文章,看到了如下的代码:
/***
* channel用例
* 基于channel的文件复制
*/
@Test
public void fileCopyByChannel(){
try {
FileInputStream fileInputStream = new FileInputStream(fielPathIN);
FileOutputStream fileOutputStream = new FileOutputStream(fielPathOUT);
FileChannel inputStreamChannel = fileInputStream.getChannel();
FileChannel outputStreamChannel = fileOutputStream.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int i = 1;
while ((i = inputStreamChannel.read(buffer) )!= -1) {
buffer.flip();
Charset charset = StandardCharsets.UTF_8;
outputStreamChannel.write(buffer);
buffer.clear();
}
fileInputStream.close();
fileOutputStream.close();
inputStreamChannel.close();
outputStreamChannel.close();
} catch (Exception e) {
e.printStackTrace();
}
}
逻辑也比较简单,只是将一个文件中的文本内复制到另外一个文件中。
关键知识点
- FileInputStream 、FileOutputStream 通过.getChannel()获取FileChannel
- ByteBuffer.allocate(1024)分配堆内存,还有另外一种分配直接内存的方式allocateDirect。两者分配的内存前面的是归JVM管理的,儿后者不归JVM管理
- buffer.flip()切换为读模式
- buffer.flip()清空buffer。注意:并不是清空数据,而是重置状态
开始搞活
刚刚看到这段代码,我迫不及待去试了下,很nice!然后,脑子里就出了一个很奇怪的想法:如果在while循环里面没有buffer.flip()会怎样。
while中如果没有buffer.flip()
说搞就搞,注解注释掉,截图为证
运行一下代码看看效果,结果真的运行完成了,出乎意料了,难道flip可有可无了?然后就去磁盘里面看了代码运行完成的文件
看到这里,果然有问题,那就是flip方法是必须要有的。那么,为什么生成的代码只有源文件的一部分呢?是哪一部分呢?
flip方法详细解释
flip()方法将缓冲区的limit设置为当前的position,同时将position设置为0,以准备读取缓冲区中的数据。
clear方法详细解释
- 将position重置为0,以便下一次写入新的数据。
- 将缓冲区的limit设置为缓冲区的容量,表示缓冲区的可写入或可读取的上限。
显然,每次循环的最后都调用了clear方法,都会将buffer重置,所以每次都能从channel中读取到数据;因为没有调用flip方法,所以除了最后一次循环,之前循环读取完数据之后,postion、limit和capcity都在buffer的最后了,而读取的时候读的内容是从postion开始,一直到limit结束。
因为你最后一次读取如果不是1024的整数倍,假如说是100,那这一次的循环buffer的position是101,limit是1024,capcity是1024,那么将读取上一次从101-1024之间的内容。而除了最后一次循环的之前所有循环都读取不到数据。
说着可能太抽象了,还是画个图说明一下
所以说,最后一次读取到的是上一次没有覆盖完的内容。
那如果循环里面没有clear方法会怎么样呢?
while中如果没有buffer.clear()
详细看看以上关于clear方法的介绍,应该能知道会出现什么结果。因为clear会重置buffer的状态,如果不重置,那么从channel读取数据写入到buffer中时,只有第一次会是成功写入的,第一次之后position、limit和capcity都将指向最后,而写入时,要从position开始写一直到capcity结束;第一次写入完成之后将一直读取不了channel中的数据,因此也将会程序也将死循环。
总结
- buffer如果是写入之后要读取,那么读取之前一定要先flip
- buffer如果是读取之后要写入,那么写入之前一定要先clear
以上就是本篇文章所有内容,如果有写的不对的地方,欢迎大家指正,谢谢阅读!