Java NIO中的Channel接口

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

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

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

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

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

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

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

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对象。

示例:读文件,一次读完

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

示例:读文件,循环读取

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

示例:写文件

 1         //创建Channel
 2         FileOutputStream out=new FileOutputStream("./2.txt");
 3         FileChannel channel=out.getChannel();
 4
 5         //创建Buffer
 6         ByteBuffer buffer=ByteBuffer.allocate(1024);
 7
 8         //将要写的数据放入Buffer中
 9         buffer.put("hello world!".getBytes());
10
11         //要调整好指针,标明可用数据。否则Buffer中的可用数据为空
12         buffer.flip();
13
14         //将Buffer中的数据写入Channel。会同步写入到文件中。
15         channel.write(buffer);
16
17         buffer.clear();
18         channel.close();
19         out.close();

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

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

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

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

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

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

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

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

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

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

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

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

注意:

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

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

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

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

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

原文地址:https://www.cnblogs.com/chy18883701161/p/10928979.html

时间: 2024-08-28 08:40:48

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

java NIO中的buffer和channel

缓冲区(Buffer):一,在 Java NIO 中负责数据的存取.缓冲区就是数组.用于存储不同数据类型的数据 根据数据类型不同(boolean 除外),提供了相应类型的缓冲区:ByteBufferCharBufferShortBufferIntBufferLongBufferFloatBufferDoubleBuffer 上述缓冲区的管理方式几乎一致,通过 allocate() 获取缓冲区 二.缓冲区存取数据的两个核心方法:put() : 存入数据到缓冲区中get() : 获取缓冲区中的数据

Java NIO系列(三) - Channel

前言 上文讲到Java NIO一些基本概念.在标准的IO中,都是基于字节流/字符流进行数据操作的,而在NIO中则是是基于Channel和Buffer进行操作,其中的Channel的虽然模拟了流的概念,实则大不相同. 本文将详细阐述NIO中的通道Channel的概念和具体的用法. Channel和Stream的区别 区别 Stream Channel 是否支持异步 不支持 支持 是否支持双向数据传输 不支持,只能单向 支持,既可以从通道读取数据,也可以向通道写入数据 是否结合Buffer使用 不

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

什么是缓冲区(Buffer) 定义 简单地说就是一块存储区域,哈哈哈,可能太简单了,或者可以换种说法,从代码的角度来讲(可以查看JDK中Buffer.ByteBuffer.DoubleBuffer等的源码),Buffer类内部其实就是一个基本数据类型的数组,以及对这个缓冲数组的各种操作: 常见的缓冲区如ByteBuffer.IntBuffer.DoubleBuffer...内部对应的数组依次是byte.int.double... 与通道的关系 在Java NIO中,缓冲区主要是跟通道(Chann

Java NIO中的Buffer 详解

Java NIO中的Buffer用于和NIO通道进行交互.如你所知,数据是从通道读入缓冲区,从缓冲区写入到通道中的.缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存.这块内存被包装成NIO Buffer对象,并提供了一组方法,用来方便的访问该块内存. Buffer的基本用法使用Buffer读写数据一般遵循以下四个步骤: 写入数据到Buffer    调用flip()方法    从Buffer中读取数据    调用clear()方法或者compact()方法 当向buffer写入数据时,

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

你好,我是彤哥,本篇是netty系列的第五篇. 简介 上一章我们一起学习了如何使用Java原生NIO实现群聊系统,这章我们一起来看看Java NIO的核心组件之一--Channel. 思维转变 首先,我想说的最重要的一个点是,学习NIO思维一定要从BIO那种一个连接一个线程的模式转变成多个连接(Channel)共用一个线程来处理的这种思维. 1个Connection = 1个Socket = 1个Channel,这几个概念可以看作是等价的,都表示一个连接,只不过是用在不同的场景中. 如果单从阻塞

Java NIO中的Glob模式详解

Java NIO中的Glob模式详解 作者:chszs,转载需注明.博客主页:http://blog.csdn.net/chszs 一.什么是Glob? 在编程设计中,Glob是一种模式,它使用通配符来指定文件名.例如:.java就是一个简单的Glob,它指定了所有扩展名为"java"的文件.Glob模式中广泛使用了两个通配符""和"?".其中星号表示"任意的字符或字符组成字符串",而问号则表示"任意单个字符&quo

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

什么是Scatter/Gather scatter/gather指的在多个缓冲区上实现一个简单的I/O操作,比如从通道中读取数据到多个缓冲区,或从多个缓冲区中写入数据到通道: scatter(分散):指的是从通道中读取数据分散到多个缓冲区Buffer的过程,该过程会将每个缓存区填满,直至通道中无数据或缓冲区没有空间: gather(聚集):指的是将多个缓冲区Buffer聚集起来写入到通道的过程,该过程类似于将多个缓冲区的内容连接起来写入通道: scatter/gather接口 如下是Scatte

Java NIO 之 Socket Channel

在Java NIO中用Channel来对程序与进行I/O操作主体的连接关系进行抽象,这些IO主体包括如文件.Socket或其他设备.简而言之,指代了一种与IO操作对象间的连接关系. 按照Channel接口的定义,Channel只有open和closed两种状态,只有在channel处于open状态下对其操作时才有效,而对closed的channel进行操作会导致抛出异常.相应的Channel接口也仅有isOpen()和close()两种方法. 在Socket编程中,常用的Channel类是Ser

java NIO中的Reactor相关知识汇总 (转)

一.引子 nio是java的IO框架里边十分重要的一部分内容,其最核心的就是提供了非阻塞IO的处理方式,最典型的应用场景就是处理网络连接.很多同学提起nio都能说起一二,但是细究其背后的原理.思想往往就开始背书,说来说去都是那么几句,其中不少人并不见的真的很理解.本人之前就属于此类,看了很多书和博客,但是大多数都只是讲了三件套和怎么使用,很少会很细致的讲背后的思想,那本次我们就来扒一扒吧.     很多博客描述nio都是这么说的:基于Reactor模式实现的多路非阻塞高性能的网络IO.那么我们就