FileChannel继承了ScatteringByteChannel,ByteChannel和GatheringByteChannel。在类中还引入了文件锁。下面是部分的API
public class FileLock implements AutoClosable{
public FileChannel channel()
public long position()
public long size()
public boolean isShared()
public boolean overlaps(long position, long size)
public boolean isValid()
public void release()
public String toString()
}
文件通道总是运行在阻塞模式而且不能在非阻塞模式下工作。面向流的非阻塞模式对文件操作来说意义不大。对于文件来说,真正需要的是异步IO,从操作系统发出一条或多条IO请求而不需要等待它们完成,而是在完成之后收到通知。
FileChannel对象可以通过调用RandomAccessFile,FileInputStream,FileOutputStream的getChannel方法得到,调用这个方法得到一个FileChannel对象,拥有和File对象一样的访问权限。
FileChannel是一个抽象类,你通过getChannel方法得到的是一个具体实现,往往实现中有着大量native代码。FileChannel是线程安全的。多线程可以同时调用他们的方法而不会引起任何问题,但不是所有操作都是多线程的,影响到通道的位置和文件大小是单线程的。如果有线程试图做这样的操作,那么它必须等待另一个线程执行完毕。
FileChannel保证同一个JVM中的所有实例看到的文件视图都是一致的。但这个视图和非JVM线程看到的可能相同也可能不同,这高度取决于底层的操作系统和文件系统。一般情况下来说,都是相同的。
FileChannel中的position,代表着数据下一个写入或者读取的地方,无参数的position方法,返回一个long类型的当前位置。第二个需要一个long类型的参数,如果你把参数设置为负,会抛出IllegalArgumentException,但是如果设置的超出了文件的大小不会抛出异常,但不会更改文件的大小。如果读操作超出了文件的大小,会返回EOF,如果是写操作,就会更改文件的大小。每个FileChannel对象都和一个File descriptor有着一对一的联系。而File descriptor是channel创建的时候被共享的。也就是说他们可以看到彼此对position的更改,如下例所示:
RandomAccessFile randomAccessFile = new RandomAccessFile("filename","r");
//设置position
randomAccessFile.seek(1000);
//创建一个channel
FielChannel fileChannel = randomAccessFile.getChannel();
System.out.println(fileChannel.position());//打印1000
randomAccessFile.seek(500);
System.out.println(fileChannel.position());//500
fileChannel.position(200);
System.out.println(randomAccessFile.getFilePointer());//200
和buffer相似,FileChannel的read方法和write方法都可以携带一个position参数,这样子比起不带参数的read,write方法更为高效,因为不需要更新channel的状态,可以直接通过native code实现。最好的是,多线程可以同时访问一个文件而不需要相互打扰。
如果视图读取文件的结尾,会收到EOF,如果在这时,往超出文件大小的地方写入,文件就会自动增长,但在这EOF和此时写入的位置之间无数据的地方就要看具体的操作系统和文件系统了,可能会导致一个file hole。当你觉得有必要调整文件的大小时,调用truncate方法,携带一个long类型作为参数,如果大于你此时文件的大小,无事发生过。但是如果小于,大于这个数值的数据会被丢弃。
最后force方法,要求强制把现在已经做了的更改应用到磁盘文件上,参数表明是否更新元数据(如文件所有者,访问权限,最后一次修改时间等)