通道(Channel
)是数据源和Java程序之间的开放连接,用于执行I/O
操作。Channel
接口在java.nio.channels
包中。通道(Channel
)接口只声明了两个方法:close()
和isOpen()
。
各种渠道
ReadableByteChannel
用于使用read()
方法将数据从数据源读取到字节缓冲区中。WritableByteChannel
用于使用write()
方法将数据从字节缓冲区写入数据宿。
ByteChannel
能够分别使用read()
和write()
方法读取和写入字节数据。ScatteringByteChannel
将数据从数据源读取到多个字节缓冲区中。 从已知的文件格式或类似的数据源读取数据是有用的,其中在一些固定长度的报头中提供数据,随后是可变长度的主体。
GatheringByteChannel
从多个字节缓冲区中写出数据。
创建通道
要获得一个通道,旧的方式使用java.io
包中的类I/O
来创建InputStream
和OutputStream
的对象。java.nio.channels
包中的Channels
类是一个实用程序类,它有许多静态方法将流转换为通道,反之亦然。
Channels
类还提供了将读写器转换为通道的方法,反之亦然。例如,如果有一个名为myInputStream
的输入流对象,获得一个ReadableByteChannel
如下:
ReadableByteChannel rbc = Channels.newChannel(myInputStream)
如果有一个名为rbc
的ReadableByteChannel
,可以获得如下的基本InputStream
对象:
InputStream myInputStream = Channels.newInputStream(rbc)
FileInputStream
和FileOutputStream
类有一个称为getChannel()
的新方法来返回一个FileChannel
对象。FileChannel
用于读取和写入数据到文件。从FileInputStream
获取的FileChannel
对象以只读模式打开。
FileInputStream fis = new FileInputStream("test1.txt")
FileChannel fcReadOnly = fis.getChannel() // A read-only channel
从FileOutputStream
对象获取的FileChannel
对象以只写模式打开。
FileOutputStream fos = new FileOutputStream("test1.txt")
FileChannel fcWriteOnly = fos.getChannel() // A write-only channel
如果从RandomAccessFile
获取一个FileChannel
,它将以只读,只写或读写模式打开,这取决于创建RandomAccessFile
对象的方式。
以下代码为不同种类的文件流获取FileChannel
对象:
// read-only mode
RandomAccessFile raf1 = new RandomAccessFile("test1.txt", "r")
FileChannel rafReadOnly = raf1.getChannel() // A read-only channel
// read-write mode
RandomAccessFile raf2 = new RandomAccessFile("test1.txt", "rw")
FileChannel rafReadWrite = raf2.getChannel() // A read-write channel
读/写文件
FileChannel
对象维护位置变量作为缓冲区。FileChannel
的read()
和write()
方法有两种类型:相对位置读/写和绝对位置读/写。
当打开一个FileChannel
时,它的位置设置为0
,这是文件的开始。当使用相对read()
方法从FileChannel
读取时,它的位置增加读取的字节数。
从FileChannel
读取的绝对位置不会影响其位置。可以使用position()
方法获取FileChannel
对象的当前位置值。使用position(int newPosition)
方法将其位置设置为新位置。
通道也是可自动关闭的。如果使用try-with-resources
语句来获得一个通道,通道将被自动关闭,这样就不用显示地调用通道的close()
方法。
以下代码从名为test1.txt
的文件中读取文本。
import java.io.File
import java.io.FileInputStream
import java.io.IOException
import java.nio.ByteBuffer
import java.nio.channels.FileChannel
public class Main {
public static void main(String[] args) {
File inputFile = new File("test1.txt")
if (!inputFile.exists()) {
System.out.println("The input file " + inputFile.getAbsolutePath()
+ " does not exist.")
System.out.println("Aborted the file reading process.")
return
}
try (FileChannel fileChannel = new FileInputStream(inputFile).getChannel()) {
ByteBuffer buffer = ByteBuffer.allocate(1024)
while (fileChannel.read(buffer) > 0) {
buffer.flip()
while (buffer.hasRemaining()) {
byte b = buffer.get()
System.out.print((char) b)
}
buffer.clear()
}
} catch (IOException e) {
e.printStackTrace()
}
}
}
上面的代码生成以下结果。
The input file F:\website\yiibai\worksp\test1.txt does not exist.
Aborted the file reading process.
示例
以下代码显示如何使用缓冲区和通道写入文件。
import java.io.File
import java.nio.channels.FileChannel
import java.io.IOException
import java.nio.ByteBuffer
import java.io.FileOutputStream
public class Main {
public static void main(String[] args) {
File outputFile = new File("test.txt")
try (FileChannel fileChannel = new FileOutputStream(outputFile)
.getChannel()) {
String text = getText()
byte[] byteData = text.toString().getBytes("UTF-8")
ByteBuffer buffer = ByteBuffer.wrap(byteData)
fileChannel.write(buffer)
} catch (IOException e1) {
e1.printStackTrace()
}
}
public static String getText() {
String lineSeparator = System.getProperty("line.separator")
StringBuilder sb = new StringBuilder()
sb.append("test")
sb.append(lineSeparator)
sb.append("test")
sb.append(lineSeparator)
sb.append("test")
sb.append(lineSeparator)
sb.append("test")
return sb.toString()
}
}
复制文件的内容
可以使用缓冲区和通道来复制文件。获取源文件和目标文件的FileChannel
对象,并对源FileChannel
对象调用transferTo()
方法或调用目标FileChannel
对象上的transferFrom()
方法。
以下代码显示如何复制文件。
import java.io.FileInputStream
import java.io.FileOutputStream
import java.nio.channels.FileChannel
public class Main {
public static void main(String[] args) throws Exception {
FileChannel sourceChannel = new FileInputStream("sourceFile").getChannel()
FileChannel sinkChannel = new FileOutputStream("newFile").getChannel()
// Copy source file contents to the sink file
sourceChannel.transferTo(0, sourceChannel.size(), sinkChannel)
}
}