上一篇文章中列举了JavaIO中FileDescriptor和File类提供的一些文件操作,这些操作还只是对文件系统中的文件进行创建或删除操作。鉴于大一玩过Window编程(对Linux API不是很熟悉),所以这篇文章会从Windows C API去分析一下Java提供给我们的几个文件读写类。
建议结合着源代码看这篇文章(这篇文章就是记录我看源代码的过程,这里的java版本是1.8.0_131)
RandomAccessFile
这个类就是完全模仿C语言的文件读写操作,允许随机读取,想读文件的哪个部分就可以把文件流指针指到哪儿。下面会列一张表将这个类中的常用方法和标准C语言API进行对比,然后再看一下Java在Native层是怎么实现这个类的:
Java | C |
---|---|
public int read(byte b[], int off, int len) public int read(byte b[]) public final void readFully(byte b[]) public final void readFully(byte b[], int off, int len) | size_t fread( void *buffer, size_t size, size_t count, FILE *stream ); |
public int read() | int fgetc( FILE *stream ); int getc( FILE *stream ); |
public void write(byte b[]) public void write(byte b[], int off, int len) | size_t fwrite( const void *buffer, size_t size, size_t count, FILE *stream ); |
public void write(int b) | int fputc( int ch, FILE *stream ); int putc( int ch, FILE *stream ); |
public void seek(long pos) public int skipBytes(int n) | int fseek( FILE *stream, long offset, int origin ); int fsetpos( FILE *stream, const fpos_t *pos ); void rewind(FILE *stream); |
public native long getFilePointer() | long ftell( FILE *stream ); int fgetpos( FILE *stream, fpos_t *pos ); |
RandomAccessFile还同时实现了DataOutput, DataInput
两个接口,所以同时拥有了DataInputStream
和DataOutputStream
两个类的基本方法。
open
1 | // 首先从构造方法开始看 |
下面看一下native层是怎么打开文件的:
1 | ///////////////////////////////////////////////////////////////////////// |
点击这里可以查看CreateFile
的API文档:
1 | // 返回文件句柄 |
read
1 | // 所有的read方法最终都会辗转调用这两个方法 |
Native层代码:
1 | ///////////////////////////////////////////////////////////////////// |
点击这里查看ReadFile的API文档
1 | BOOL WINAPI ReadFile( |
write
1 | // 所有的write方法最终都会调用这两个方法 |
Native层源码:
1 | ////////////////////////////////////////////////////////////////////////////// |
点击这里查看WriteFile的API文档
1 | BOOL WINAPI WriteFile( |
seek
1 | private native void seek0(long pos) throws IOException; |
Native层源码:
1 | ///////////////////////////////////////////////////////////////// |
点击这里查看SetFilePointEx的API文档
1 | BOOL WINAPI SetFilePointerEx( |
getFilePointer
1 | public native long getFilePointer() throws IOException; |
Native层源码:
1 | JNIEXPORT jlong JNICALL |
RandomAccessFile类最终调用的是Windows的四个API:OpenFile,ReadFile,WriteFile,GetFilePointer
FileInputStream和FileOutputStream
FileInputStream和FileOutputStream与C++的STL中的文件流API类似:面向对象,RandomAccessFile仅仅以面向对象方式封装了文件读写。Java的文件流功能上肯定不如C++,要知道C++的运算符重载,模板类等语言特性让C++的文件操作简单了很多(相反调试也变得更加困难)。
C++中std::basic_ifstream
代表了文件输入流,std::basic_ofstream
代表了文件输出流,又因为C++有多继承的特性所以还有一个std::basic_fstream
融合了前两者的功能(实际上并非继承自前两个类而是继承自basic_iostream类)。
不过C++虽好,但Java的native实现仍使用C语言(实际上Java API几乎都是C语言实现,而JVM的实现Hotspot才使用到了C++)。
事实上FileInputStream和FileOutputStream的实现和RandomAccessFile几近一致:
FileInputStream的native方法
1 | private native void open0(String name) throws FileNotFoundException; |
Native层源码:
1 | JNIEXPORT void JNICALL |
查看GetFileType和GetFileSizeEx的API文档
1 | // FILE_TYPE_CHAR 字符文件,典型的如:打印设备或控制台 |
FileOutputStream的native方法
FileOutputStream的native实现就更简单了
1 | private native void open0(String name, boolean append) throws FileNotFoundException; |
Native源码:
1 | JNIEXPORT void JNICALL |