2019独角兽企业重金招聘Python工程师标准>>>
Channel类似于传统的流对象,但与传统的流不同的是,Channel有两个主要的区别
(1)Channel可以直接将指定的文件的部分或全部直接映射成Buffer
(2)程序不能直接访问Channel中的数据,包括读、写都不行,Channel只能与Buffer进行交互。也就是说,如果要从Channel中取得数据,必须先用Buffer从Channel中取出一些数据,然后让程序从Buffer中取出这些数据。如果要将程序中的数据写入Channel,一样先让程序将数据放入Buffer中,程序再将Buffer里的输入写入Channel中.
Channel是一个接口,位于java.nio.channels下,系统为该接口提供了DatagramChannel,FileChannel,Pipe.SinkChannel,Pipe.SourceChannel,SelectableChannel,ServerSocketChannel,SocketChannel等实现类.。例如.Pipe.SinkChannel,Pipe.SourceChanel用于支持线程之间通信的管道Channel,而ServerSocketChannel,SocketChannel则是用于支持TCP网络通信的Channel
所有的Channel都不应该通过构造器来直接创建,而是通过传统的节点InputStream,OutputStream的getChannel方法来返回对应的Channel,不同的节点流获得的Channel不一样,例如FileInputStream,FileOutputStream的getChannel返回的是FileChannel,而PipedInputStream和PipedOutputStream的getChannel返回的是Pipe.SinkChannel,Pipe.SourceChannel
Channel中最常用的三类方法是map,read,和 write,其中map方法用于将Channel对应的部分或全部数据映射成ByteBuffer,而read或write方法都有一系列重载形式,这些方法用于从Buffer中读取数据或向Buffer里写入数据.
其中map方法的方法签名为 MappedByteBuffer map(FileChannel.MapMode mode,long position,long size):第一个参数执行映射时的模式,分别有只读,读写模式,而第二个,第三个参数用于控制将Channel的哪些数据映射成ByteBuffer
示例一
直接将FileChannel的全部数据映射成ByteBuffer的效果
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;
public class FileChannelTest
{
public static void main(String[] args)
{
FileChannel inChannel = null;
FileChannel outChannel = null;
try
{
File f = new File("FileChannelTest.java");
//创建FileInputStream,以该文件输入流创建FileChannel
inChannel = new FileInputStream(f).getChannel();
//将FileChannel里的全部数据映射成ByteBuffer
MappedByteBuffer buffer = inChannel.map(FileChannel.MapMode.READ_ONLY,
0 , f.length());
//使用GBK的字符集来创建需要编码器
Charset charset = Charset.forName("GBK");
//以文件输出流创建FileBuffer,用以控制输出
outChannel = new FileOutputStream("a.txt").getChannel();
//直接将buffer里的数据全部输出
outChannel.write(buffer);
//再次调用buffer的clear()方法,复原limit、position的位置
buffer.clear();
//创建解码器(CharsetDecoder)对象
CharsetDecoder decoder = charset.newDecoder();
//使用解码器将ByteBuffer转换成CharBuffer
CharBuffer charBuffer = decoder.decode(buffer);
//CharBuffer的toString方法可以获取对应的字符串
System.out.println(charBuffer);
}
catch (IOException ex)
{
ex.printStackTrace();
}
finally
{
try
{
if (inChannel != null)
inChannel.close();
if (outChannel != null)
outChannel.close();
}
catch (IOException ex)
{
ex.printStackTrace();
}
}
}
}
示例二
不仅InputStream,OutputStream包含了getChannel方法,在RandomAccessFile中也包含了一个getChannel方法,由RandomAccessFile返回的FileChannel是只读的,还是读写的Channel则取决于RandomAccessFile打开文件的模式。
如下例子,会把a.txt文件复制出来,追加到该文件后面
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;
public class RandomFileChannelTest
{
public static void main(String[] args)
{
FileChannel randomChannel = null;
try
{
File f = new File("a.txt");
//创建一个RandomAccessFile对象
RandomAccessFile raf = new RandomAccessFile(f, "rw");
//获取RandomAccessFile对应的Channel
randomChannel = raf.getChannel();
//将Channel中所有数据映射成ByteBuffer
ByteBuffer buffer = randomChannel.map(FileChannel.MapMode.READ_ONLY,
0 , f.length());
//把Channel的记录指针移动到最后
randomChannel.position(f.length());
//将buffer中所有数据输出
randomChannel.write(buffer);
}
catch (IOException ex)
{
ex.printStackTrace();
}
finally
{
try
{
if (randomChannel != null)
randomChannel.close();
}
catch (IOException ex)
{
ex.printStackTrace();
}
}
}
}
示例三
如果我们习惯了传统IO的“用Buffer来多次重次取数据”的过程,或者担心Channel对应的文件过大,使用Map方法一次将所有的文件映射到内存中引起性能下降,也可以使用Channel和Buffer传统用“用Buffer来多次重次取数据”的方式.
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
public class ReadFile
{
public static void main(String[] args)
{
FileChannel fcin = null;
try
{
//创建文件输入流
FileInputStream fis = new FileInputStream("ReadFile.java");
//创建一个FileChannel
fcin = fis.getChannel();
//定义一个ByteBuffer对象,用于重复取水
ByteBuffer bbuff = ByteBuffer.allocate(1024);
//将FileChannel中数据放入ByteBuffer中
while( fcin.read(bbuff) != -1 )
{
//锁定Buffer的空白区
bbuff.flip();
//创建Charset对象
Charset charset = Charset.forName("gb2312");
//创建解码器(CharsetDecoder)对象
CharsetDecoder decoder = charset.newDecoder();
//将ByteBuffer的内容转码
CharBuffer cbuff = decoder.decode(bbuff);
System.out.println(cbuff);
//将Buffer初始化,为下一次取数据做准备
bbuff.clear();
}
}
catch (IOException ex)
{
ex.printStackTrace();
}
finally
{
try
{
if (fcin != null)
fcin.close();
}
catch (IOException ex)
{
ex.printStackTrace();
}
}
}
}