通道
通道是一个对象,通过它可以读取和写入数据,当然了所有数据都通过Buffer对象来处理。我们永远不会将字节直接写入通道中,相反是将数据写入包含一个或者多个字节的缓冲区。同样不会直接从通道中读取字节,而是将数据从通道读入缓冲区,再从缓冲区获取这个字节。
在NIO中,提供了多种通道对象,而所有的通道对象都实现了Channel接口。它们之间的继承关系如下图所示:
Java 为 Channel 接口提供的最主要实现类如下:
•FileChannel:用于读取、写入、映射和操作文件的通道。
•DatagramChannel:通过 UDP 读写网络中的数据通道。
•SocketChannel:通过 TCP 读写网络中的数据。
•ServerSocketChannel:可以监听新进来的 TCP 连接,对每一个新进来的连接都会创建一个 SocketChannel。
获取通道的一种方式是对支持通道的对象调用
getChannel() 方法。支持通道的类如下:
FileInputStream
FileOutputStream
RandomAccessFile
DatagramSocket
Socket
ServerSocket
获取通道的其他方式是使用 Files 类的静态方法 newByteChannel() 获
取字节通道。或者通过通道的静态方法 open() 打开并返回指定通道。
FileChannel的常用方法
分散和聚集
分散读取(Scattering Reads)是指从Channel 中读取的数据“分散”到多个Buffer 中。
注意:按照缓冲区的顺序,从 Channel 中读取的数据依次将 Buffer 填满。
聚集写入(Gathering Writes)是指将多个Buffer 中的数据“聚集”到Channel。
注意:按照缓冲区的顺序,写入 position 和 limit 之间的数据到 Channel 。
下面为实例程序:
package com.test.nio;
import org.junit.Test;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Map;
import java.util.Set;
/**
* 一、通道(Channel):用于源节点与目标节点的连接。在 Java NIO中负责缓冲
* 区中数据的传输。Channel本身不存储数据,因此需要配合缓冲区进行传输
* 二、通道的主要实现类
* java.nio.channels.Channel 接口:
* |--FileChannel
* |--SocketChannel
* |--ServerSocketChannel
* |--DatagramChannel
* 三:获取通道
* 1.Java 针对支持通道的类提供了getChannel()方法
* 本地 IO:
* FileInputStream/FileOutputStream
* RandomAccessFile
*
* 网络 IO
* Socket
* ServerSocket
* DatagramSocket
* 2.在JDK 1.7 中的NIO.2针对各个通道提供了静态方法 open()
* 3.在JDK 1.7 中的NIO.2的Files工具类的newByteChannel()
* 四:通道之间的数据传输
* transferFrom()
* transferTo()
*
* 五:分散(Scattrer)与聚集(Gather)
* 分散读取(Scattering Reads):将通道中的数据分散到多个缓冲区中
* 聚集写入(Gathering Writers):将多个缓冲区中的数据聚集到同道中
*
* 六:字符集:Charset
* 编码(encode):字符串->字节数组
* 解码(decode):字节数组-字符串
*
*/
public class TestChannel {
@Test
public void test7()throws Exception{
Charset cs1=Charset.forName("GBK");
//获取编码器
CharsetEncoder ce=cs1.newEncoder();
//获取解码器
CharsetDecoder cd=cs1.newDecoder();
CharBuffer charBuffer=CharBuffer.allocate(1024);
charBuffer.put("今天天气不错");
charBuffer.flip();
//编码
ByteBuffer buffer1=ce.encode(charBuffer);
for(int i=0;i<buffer1.limit();i++){
System.out.println(buffer1.get());
}
//解码
buffer1.flip();
CharBuffer buffer2=cd.decode(buffer1);
System.out.println(buffer2.toString());
System.out.println("-------------------------");
buffer1.flip();
Charset cs2=Charset.forName("UTF-8");
CharBuffer buffer3=cs2.decode(buffer1);
System.out.println(buffer3.toString());
}
//查看Java支持的字符集
@Test
public void test6(){
Map<String,Charset> map=Charset.availableCharsets();
Set<Map.Entry<String,Charset>> set=map.entrySet();
for(Map.Entry<String,Charset> entry:set){
System.out.println(entry.getKey()+"="+entry.getValue());
}
}
//分散和聚集
@Test
public void test5()throws Exception{
RandomAccessFile raf1=new RandomAccessFile("e:\\122.txt","rw");
//1.获取通道
FileChannel channel1=raf1.getChannel();
//2.分配制定大小的缓冲区
ByteBuffer buffer1=ByteBuffer.allocate(100);
ByteBuffer buffer2=ByteBuffer.allocate(1024);
//3.分散读取
ByteBuffer[] buffers={buffer1,buffer2};
channel1.read(buffers);
for(ByteBuffer buffer:buffers){
buffer.flip();
}
System.out.println(new String(buffers[0].array(),0,buffers[0].limit()));
System.out.println("-------------------------");
System.out.println(new String(buffers[1].array(),0,buffers[1].limit()));
//4.聚集写入
RandomAccessFile raf2=new RandomAccessFile("e:\\123.txt","rw");
FileChannel channel2=raf2.getChannel();
channel2.write(buffers);
}
//通道直接的数据传输(直接缓冲区)
@Test
public void test4() throws Exception{
//只读模式
FileChannel inChannel=FileChannel.open(Paths.get("e:\\122.txt"), StandardOpenOption.READ);
//读写模式 StandardOpenOption.CREATE 当目标文件不存在时 创建一个新文件 CREATE_NEW则文件已存在时 会报异常
FileChannel outChannel=FileChannel.open(Paths.get("e:\\123.txt"),StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE);
inChannel.transferTo(0,inChannel.size(),outChannel);
//下面的方法与上面效果相同
//outChannel.transferFrom(inChannal,0,inChannal.size());
inChannel.close();
outChannel.close();
}
//使用直接缓冲区完成文件的复制(内存映射文件)
@Test
public void test3()throws Exception{
//只读模式
FileChannel inChannel=FileChannel.open(Paths.get("e:\\122.txt"), StandardOpenOption.READ);
//读写模式 StandardOpenOption.CREATE 当目标文件不存在时 创建一个新文件 CREATE_NEW则文件已存在时 会报异常
FileChannel outChannel=FileChannel.open(Paths.get("e:\\123.txt"),StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE);
//内存映射文件
MappedByteBuffer inMappedBuf=inChannel.map(FileChannel.MapMode.READ_ONLY,0,inChannel.size());
MappedByteBuffer outMappedBuf=outChannel.map(FileChannel.MapMode.READ_WRITE,0,inChannel.size());
byte[] dst=new byte[inMappedBuf.limit()];
//将缓冲区中是的数据读入到数组
inMappedBuf.get(dst);
//将数组中的数据放入缓冲区
outMappedBuf.put(dst);
inChannel.close();
outChannel.close();
}
//利用通道完成文件的复制(非直接缓冲区)
@Test
public void test2()throws Exception{
FileInputStream fin=new FileInputStream("e:\\122.txt");
FileOutputStream fos=new FileOutputStream("e:\\123.txt");
//1.获取通道
FileChannel inChannel=fin.getChannel();
FileChannel outChannel=fos.getChannel();
inChannel.transferTo(0,inChannel.size(),outChannel);
//2.分配制定大小的缓冲区
ByteBuffer buffer=ByteBuffer.allocate(1024);
//3.将通道中的数据存入缓冲区中
while (inChannel.read(buffer)!=-1){
//4.切换到读数据模式
buffer.flip();
//5.将缓冲区中的数据写入通道中
outChannel.write(buffer);
//6.清空缓冲区
buffer.clear();
}
outChannel.close();
inChannel.close();
fos.close();
fin.close();
}
//利用通道读取数据
@Test
public void test1()throws Exception{
FileInputStream fin=new FileInputStream(new File("e:\\122.txt"));
//获取通道
FileChannel fc=fin.getChannel();
//创建缓冲区
ByteBuffer buffer=ByteBuffer.allocate(1024);
//读取数据到缓冲区
fc.read(buffer);
buffer.flip();
while (buffer.remaining()>0){
byte b=buffer.get();
System.out.println((char)b);
}
//剩余可操作的数量
/* while (fc.read(buffer)!=-1){
byte b=buffer.get();
System.out.println((char)b);
}*/
fc.close();
fin.close();
}
}