目录
1、存入文件
下面给个例子,简单地把内容写入到指定文件中。
public class FileChannelTest {
public static void main(String[] args){
try {
// 先把字符串内容写入source.txt文件中
String message = "hello, how are you?";
// 创建一个bytebuffer内存块
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
// 将字符串内容存入bytebuffer
byteBuffer.put(message.getBytes());
// 创建文件输出流
FileOutputStream fileOutputStream = new FileOutputStream("./src/fileChannel/source.txt");
// 创建文件通道
FileChannel fileChannel0 = fileOutputStream.getChannel();
// 切换到读模式
byteBuffer.flip();
// 将bytebuffer中的内容写入到通道中
fileChannel0.write(byteBuffer);
// 关闭资源
fileChannel0.close();
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
运行结果如下,字符串已经成功写入到文件中。
2、读取文件
下面给了一个简单的例子,从文件中读取数据。
public class FileChannelTest1 {
public static void main(String[] args){
try {
// 创建文件输入流
FileInputStream fileInputStream = new FileInputStream("./src/fileChannel/source.txt");
// 创建一个bytebuffer内存块
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
// 创建文件通道
FileChannel fileChannel = fileInputStream.getChannel();
// 从文件通道中读取内容到bytebuffer中
fileChannel.read(byteBuffer);
// 输出读取到的内容
System.out.println(new String(byteBuffer.array()));
// 关闭资源
fileChannel.close();
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
运行结果如下。
3. 直接将数据从一个文件通道写入另一个文件通道
下面是一个简单的例子,关键在于两个方法,这两个方法是使用了零拷贝。
transferFrom(ReadableByteChannel src, long position, long count)
transferTo(ReadableByteChannel src, long position, long count)
其实这个两个方法效果一样的,只不过是源通道和目的通道的视角不同而已。
public class FileChannelTransferTest {
public static void main(String[] args){
try {
// 创建文件输入流
FileInputStream fileInputStream = new FileInputStream("./src/fileChannel/source.txt");
// 创建文件通道
FileChannel fileChannelSource = fileInputStream.getChannel();
// 创建文件输出流
FileOutputStream fileOutputStream = new FileOutputStream("./src/fileChannel/destination.txt");
// 创建文件通道
FileChannel fileChannelDestination = fileOutputStream.getChannel();
// 将文件数据从源通道传入目的通道,达到将文件数据复制到另一个文件中去
fileChannelSource.transferTo(0, fileInputStream.available(), fileChannelDestination);
// 关闭资源
fileChannelSource.close();
fileChannelDestination.close();
fileInputStream.close();
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
运行结果如下,destination.txt 文件中已经有了 source.txt 文件中的内容了。
4. 直接内存
我们之前用的ByteBuffer,其实底层是 HeapByteBuffer,这种内存块是属于应用层的JVM所管理的,是用户态的,于是就会出现数据在内核态和用户态之间的复制,但是可以省去这个复制工作,用的技术就是零拷贝技术,即直接内存。
MappedByteBuffer 就是直接内存,它将文件中的内容映射到直接内存中,我们读取文件也好,往文件中写入数据也好,就是直接针对于直接内存的,然后系统底层会自动为我们将数据的改变持久化到磁盘文件中的。
下面给一个简单的例子。
public class DirectByteBufferTest {
public static void main(String[] args) throws IOException {
RandomAccessFile randomAccessFile = new RandomAccessFile("./src/fileChannel/source.txt", "rw");
FileChannel fileChannel = randomAccessFile.getChannel();
MappedByteBuffer mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, randomAccessFile.length());
mappedByteBuffer.put("I am fine, thanks".getBytes());
fileChannel.close();
randomAccessFile.close();
}
}
运行结果如下。
出现这种结果的原因是,我们将 source.txt 文件的全部内容都映射到了直接内存,也写入了“I am fine, thanks”,它是从文件开始出以覆盖的方式写入,因此覆盖了之前的内容,只剩下了“u?”。
再看看 MappedByteBuffer 的全部读写操作,其实是继承自 ByteBuffer 的,使得控制起来更加灵活。
ByteBuffer put(byte b); // 以覆盖的方式写入文件一个字节。
ByteBuffer put(byte[] array); //以覆盖的方式写入一个字节数组。
ByteBuffer put(ByteBuffer buffer); // 以覆盖的方式写入一个 ByteBuffer 的内容。
ByteBuffer put(int index, byte b); // 以覆盖的方式在位置 index 处写入一个字节。
ByteBuffer put(byte[] array, int offset, int length); // 以覆盖的方式写入一个字节数组的 offset 到 offset + length - 1 范围的字节。
ByteBuffer putChar(char value); //以覆盖的方式写入一个字符。
ByteBuffer putChar(char value, int index); // 以覆盖的方式在index处写入一个字符。
当然类似的还有:putInt(), putFloat(), putDouble(),putLong(),putShort()。
byte get(); // 获取文件的一个字节
byte get(int index); //获取文件index处的一个字节
Bytebuffer get(byte[] dst); // 获取文件的内容,尽量填满 dst 数组。
ByteBuffer get(byte[] dst, int offset, int length); // 获取文件的内容,尽量填满 dst 数组的 offset 到 offset + length - 1 范围。
char getChar(); // 获取文件一个字符
char getChar(int index); // 获取文件的 index 位置处的一个字符
类似的还有:getInt(), getLong(), getShort(), getFloat(), getDouble()。