一、在学习nio相关api的时候,通过FileChannel.map方法获取直接字节缓冲区的时候报了一个NonWritableChannelException的异常
测试代码如下:
public static void testMappedByteBuffer() throws IOException {
// 这里不能通过文件输出流去获取通道,因为获取到的通道是只读的
FileInputStream fileInputStream = new FileInputStream("D:\\text.txt");
FileChannel channel = fileInputStream.getChannel();
MappedByteBuffer map = channel.map(FileChannel.MapMode.READ_WRITE, 0, 5);
map.put((byte)'A');
fileInputStream.close();
channel.close();
}
异常信息如下
Exception in thread "main" java.nio.channels.NonWritableChannelException
at sun.nio.ch.FileChannelImpl.map(FileChannelImpl.java:874)
at com.joe.TestBuffer.testMappedByteBuffer(TestBuffer.java:138)
at com.joe.TestBuffer.main(TestBuffer.java:15)
报错位置是在FileChannelImpl.java:874,点进去看下
代码逻辑大概意思是,通过FileChannelImpl.map获取直接字节缓冲区的时候,会判断你传入的MapMode是不是非只读(READ_ONLY),如果是非只读,他会判断writable这个变量是否为true,而writable是FileChannelImpl实例化的时候传进来的
而我这里的管道获取是通过文件输入流(FileInputStream)的getChannel方法,所以去看下这个方法的代码,如下
通过这里可以看到,getChannel调用的是FileChannelImpl的open(),这个open其实也是会调用FileChannelImpl的构造去创建FileChannelImpl,这里传进去的第四个参数为false,所以通过FileInputStream.getChannel()获取的是一个只读的管道,所以刚才那个writable为false,所以会抛出NonWritableChannelException异常。
二、解决方法
①将MappedByteBuffer map = channel.map(FileChannel.MapMode.READ_WRITE, 0, 5);中的MapMode.READ_WRITE改为MapMode.READ_ONLY,但是这样就没有意义了,因为我是想通过直接字节缓冲区去修改文件,这样改为只读,那么并不能进行修改
②改用RandomAccessFile获取管道,因为RandomAccessFile支持读取和写入随机访问文件
他的getChannel方法创建的是即支持读又支持写的Channel
三、最终的代码
public static void testMappedByteBuffer() throws IOException {
// 这里不能通过文件输出流去获取通道,因为获取到的通道是只读的
/*FileInputStream fileInputStream = new FileInputStream("D:\\text.txt");
FileChannel channel = fileInputStream.getChannel();*/
// 这里的"rw"是指支持读和写
RandomAccessFile randomAccessFile = new RandomAccessFile("D:\\text.txt","rw");
FileChannel channel = randomAccessFile.getChannel();
MappedByteBuffer map = channel.map(FileChannel.MapMode.READ_WRITE, 0, 5);
map.put((byte)'A');
randomAccessFile.close();
channel.close();
}