一、引言:通道(Channel):由 java.nio.channels 包定义的。Channel 表示 IO 源与目标打开的连接。Channel 类似于传统的“流”,起到连接和运输的作用,这里说的运输并不是对数据的直接运输,而是运输缓冲区。看一下通道。
如果你还不知道什么是缓冲区:点击https://blog.csdn.net/IPI715718/article/details/89193050。
Java 为 Channel 接口提供的最主要实现类如下:
- FileChannel:用于读取、写入、映射和操作文件的通道。
- DatagramChannel:通过 UDP 读写网络中的数据通道。
- SocketChannel:通过 TCP 读写网络中的数据。
- ServerSocketChannel:可以监听新进来的 TCP 连接,对每一个新进来的连接都会创建一个 SocketChannel。
二、获取通道的几种方式:
1. 通过类的实例来获取channel, 同一方法是: 实例.getChannel()
- FileInputStream
- FileOutputStream
- RandomAccessFile
- DatagramSocket
- Socket
- ServerSocket
举例:
RandomAccessFile accessFile = new RandomAccessFile("d://a.jpg", "r");
FileChannel channel = accessFile.getChannel();
2. 通过通道的静态方法 open() 打开并返回指定通道
举例:获得FileChannel
FileChannel.open(Paths.get("D://a.jpg"), StandardOpenOption.READ);
看一下帮助文档
public static FileChannel open(Path path,
OpenOption... options)
throws IOException
打开或创建文件,返回文件通道以访问该文件。
参数:
path-要打开或创建的文件的路径
option-指定如何打开文件的选项
返回:新的文件通道
public static FileChannel open(Path path,
Set<? extends OpenOption> options,
FileAttribute<?>... attrs)
throws IOException
打开或创建文件,返回文件通道以访问该文件。
path-要打开或创建的文件的路径
options-指定如何打开文件的选项
attrs-创建文件时要自动设置的文件属性的可选列表
参数Option是个接口类型
public interface OpenOption {
}
它的实现类 是个枚举类型
StandardOpenOption.WRITE,StandardOpenOption CREATE
public enum StandardOpenOption implements OpenOption {
//打开进行读访问。
READ,
//打开进行写入访问。
WRITE,
//如果打开文件进行写入访问,则字节将写入文件的结尾而不是开头
APPEND,
//如果该文件已存在,并且已打开以进行写入访问,则其长度将被截断为0。
TRUNCATE_EXISTING,
//如果新文件不存在,则创建新文件
CREATE,
//创建新文件,如果该文件已存在则失败。
CREATE_NEW,
//关闭时删除
DELETE_ON_CLOSE,
//稀疏文件。
SPARSE,
SYNC,
//要求对文件内容的每个更新都同步写入基础存储设备。
DSYNC;
}
3. 获取通道的其他方式是使用 Files 类的静态方法 newByteChannel() 获取字节通道。
三、通道的数据传输
将 Buffer 中数据写入 Channel
例如:
//将buffer中的数据写入channel
channel.write(buffer);
从 Channel 读取数据到 Buffer
例如:
//将通道内的数据读入buffer中
channel.read(buffer);
分散(Scatter)和聚集(Gather)
分散读取(Scattering Reads)是指从 Channel 中读取的数据“分散”到多个 Buffer 中。
注意:按照缓冲区的顺序,从 Channel 中读取的数据依次将 Buffer 填满。
分散(Scatter)和聚集(Gather)
聚集写入(Gathering Writes)是指将多个 Buffer 中的数据“聚集”到 Channel。
注意:按照缓冲区的顺序,写入 position 和 limit 之间的数据到 Channel
举例:
public static void main(String[] args) {
FileChannel inchannel = null;
FileChannel outchannel = null;
try {
//分散读取
inchannel = FileChannel.open(Paths.get("d://a1.txt"), StandardOpenOption.READ);
ByteBuffer buffer1 = ByteBuffer.allocate(128);
ByteBuffer buffer2 = ByteBuffer.allocate(10);
//参数是个ByteBuffer数组
inchannel.read(new ByteBuffer[]{buffer1,buffer2});
//聚合写入
outchannel = FileChannel.open(Paths.get("d://a2.txt"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
outchannel.write(new ByteBuffer[]{buffer1,buffer2});
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
outchannel.close();
inchannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
通道之间的数据传输
利用channel.transferto( )和channel.chansferFrom( )
例子:
public static void main(String[] args) {
RandomAccessFile randomAccessFile1 = new RandomAccessFile("d://a1.txt", "r");
FileChannel inchannel = randomAccessFile1.getChannel();
RandomAccessFile randomAccessFile2 = new RandomAccessFile("d://a2.txt", "rw");
FileChannel outchannel = randomAccessFile2.getChannel();
inchannel.transferTo(0, inchannel.size(), outchannel);
outchannel.transferFrom(inchannel, 0, inchannel.size());
// public abstract long transferTo(long position,
// long count,
// WritableByteChannel target)
// throws IOException
// 将字节从此通道的文件传输到给定的可写入字节通道。
// 试图读取从此通道的文件中给定 position 处开始的 count 个字节,并将其写入目标通道。此方法的调用不一定传输所有请求的字节;是否传输取决于通道的性质和状态。如果此通道的文件从给定的 position 处开始所包含的字节数小于 count 个字节,或者如果目标通道是非阻塞的并且其输出缓冲区中的自由空间少于 count 个字节,则所传输的字节数要小于请求的字节数。
//
// 此方法不修改此通道的位置。如果给定的位置大于该文件的当前大小,则不传输任何字节。如果目标通道中有该位置,则从该位置开始写入各字节,然后将该位置增加写入的字节数。
//
// 与从此通道读取并将内容写入目标通道的简单循环语句相比,此方法可能高效得多。很多操作系统可将字节直接从文件系统缓存传输到目标通道,而无需实际复制各字节。
//
// 参数:
// position - 文件中的位置,从此位置开始传输;必须为非负数
// count - 要传输的最大字节数;必须为非负数
// target - 目标通道
// 返回:
// 实际已传输的字节数,可能为零
//
// public abstract long transferFrom(ReadableByteChannel src,
// long position,
// long count)
// throws IOException
// 将字节从给定的可读取字节通道传输到此通道的文件中。
// 试着从源通道中最多读取 count 个字节,并将其写入到此通道的文件中从给定 position 处开始的位置。此方法的调用不一定传输所有请求的字节;是否传输取决于通道的性质和状态。如果源通道的剩余空间小于 count 个字节,或者如果源通道是非阻塞的并且其输入缓冲区中直接可用的空间小于 count 个字节,则所传输的字节数要小于请求的字节数。
//
// 此方法不修改此通道的位置。如果给定的位置大于该文件的当前大小,则不传输任何字节。如果该位置在源通道中,则从该位置开始读取各字节,然后将该位置增加读取的字节数。
//
// 与从源通道读取并将内容写入此通道的简单循环语句相比,此方法可能高效得多。很多操作系统可将字节直接从源通道传输到文件系统缓存,而无需实际复制各字节。
//
// 参数:
// src - 源通道
// position - 文件中的位置,从此位置开始传输;必须为非负数
// count - 要传输的最大字节数;必须为非负数
// 返回:
// 实际已传输的字节数,可能为零
}
这里我输不明白为什么可以这样?不是说通道不能存储数据吗?不是说只有缓冲区可以存储数据?那为啥两个通道可以传输数据?点开这两个方法的源码我发现是个抽象方法没有方法体。是在想不通到底是咋实现的?
有知道的大神给我留言或者给个连接
谢谢!!!!!!!!!!!!!!!!!!!!!!!!!!!