上一篇文章中列举了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  | 

