您的当前位置:首页正文

FileInputStream 源码分析

2024-03-17 来源:榕意旅游网


近做一个很简单的函数,要两个返回值,我打算有一个类封装一下返回,后来领导要求使用StringBuilder返回,不用StringBuffer,因为局部变量StringBuffer最后还是要编译为StringBuilder,因为StringBuilder没有任何同步。

问了一下大家的看法,风南说InputStream 的int read(byte[] b) 从输入流中读取一定数量的字节并将其存储在缓冲区数组 b 中,也是类似的,看来有必要研究一下IO了。 InputStream类中重要的函数,只有一个抽象的有用, public abstract int read() throws IOException; 其他两个read的意义不大。

public int read(byte b[], int off, int len) throws IOException 和 public int read(byte b[]) throws IOException { return read(b, 0, b.length); }

下面看它的子类FileInputStream的实现, /* File Descriptor - handle to the open file */ private FileDescriptor fd;

这个很重要,这是文件句柄,C语言使用fopen函数的都知道。 /**

* Opens the specified file for reading. * @param name the name of the file */

private native void open(String name) throws FileNotFoundException;

/**

* Reads a byte of data from this input stream. This method blocks * if no input is yet available. *

* @return the next byte of data, or -1 if the end of the * file is reached.

* @exception IOException if an I/O error occurs. */

public native int read() throws IOException; /**

* Reads a subarray as a sequence of bytes. * @param b the data to be written * @param off the start offset in the data

* @param len the number of bytes that are written * @exception IOException If an I/O error has occurred. */

private native int readBytes(byte b[], int off, int len) throws IOException; 子类修改了父的实现 /**

* Reads up to b.length bytes of data from this input

* stream into an array of bytes. This method blocks until some input * is available.

*

* @param b the buffer into which the data is read.

* @return the total number of bytes read into the buffer, or * -1 if there is no more data because the end of * the file has been reached.

* @exception IOException if an I/O error occurs. */

public int read(byte b[]) throws IOException { return readBytes(b, 0, b.length); } /**

* Reads up to len bytes of data from this input stream * into an array of bytes. If len is not zero, the method * blocks until some input is available; otherwise, no * bytes are read and 0 is returned. *

* @param b the buffer into which the data is read. * @param off the start offset in the destination array b * @param len the maximum number of bytes read.

* @return the total number of bytes read into the buffer, or * -1 if there is no more data because the end of * the file has been reached.

* @exception NullPointerException If b is null.

* @exception IndexOutOfBoundsException If off is negative, * len is negative, or len is greater than * b.length - off

* @exception IOException if an I/O error occurs. */

public int read(byte b[], int off, int len) throws IOException { return readBytes(b, off, len); }

看出readBytes是关键的所在,windows的native code中只有 JNIEXPORT void JNICALL

Java_java_io_FileInputStream_close0(JNIEnv *env, jobject this) { handleClose(env, this, fis_fd); }

是平台相关的。 其他的在share中 FileInputStream.c代码

jfieldID fis_fd; /* id for jobject 'fd' in java.io.FileInputStream */ /************************************************************** * static methods to store field ID's in initializers */

得到fd的值,这个很重要

JNIEXPORT void JNICALL

Java_java_io_FileInputStream_initIDs(JNIEnv *env, jclass fdClass) { fis_fd = (*env)->GetFieldID(env, fdClass, \"fd\}

/************************************************************** * Input stream */

JNIEXPORT void JNICALL

Java_java_io_FileInputStream_open(JNIEnv *env, jobject this, jstring path) { fileOpen(env, this, path, fis_fd, O_RDONLY); }

JNIEXPORT jint JNICALL

Java_java_io_FileInputStream_read(JNIEnv *env, jobject this) { return readSingle(env, this, fis_fd); }

JNIEXPORT jint JNICALL

Java_java_io_FileInputStream_readBytes(JNIEnv *env, jobject this, jbyteArray bytes, jint off, jint len) {

return readBytes(env, this, bytes, off, len, fis_fd); }

JNIEXPORT jlong JNICALL

Java_java_io_FileInputStream_skip(JNIEnv *env, jobject this, jlong toSkip) {

jlong cur = jlong_zero; jlong end = jlong_zero; FD fd = GET_FD(this, fis_fd);

if ((cur = IO_Lseek(fd, (jlong)0, (jint)SEEK_CUR)) == -1) { JNU_ThrowIOExceptionWithLastError(env, \"Seek error\"); } else if ((end = IO_Lseek(fd, toSkip, (jint)SEEK_CUR)) == -1) { JNU_ThrowIOExceptionWithLastError(env, \"Seek error\"); }

return (end - cur); }

JNIEXPORT jint JNICALL

Java_java_io_FileInputStream_available(JNIEnv *env, jobject this) { jlong ret;

FD fd = GET_FD(this, fis_fd); if (IO_Available(fd, &ret)) { if (ret > INT_MAX) { ret = (jlong) INT_MAX; }

return jlong_to_jint(ret); }

JNU_ThrowIOExceptionWithLastError(env, NULL); return 0;

}

主要是readBytes的实现,打开io_util.c文件 /* IO helper functions */ int

readSingle(JNIEnv *env, jobject this, jfieldID fid) { int nread; char ret;

FD fd = GET_FD(this, fid); nread = IO_Read(fd, &ret, 1); if (nread == 0) { /* EOF */ return -1;

} else if (nread == JVM_IO_ERR) { /* error */

JNU_ThrowIOExceptionWithLastError(env, \"Read error\"); } else if (nread == JVM_IO_INTR) {

JNU_ThrowByName(env, \"java/io/InterruptedIOException\}

return ret & 0xFF; }

/* The maximum size of a stack-allocated buffer. */

#define BUF_SIZE 8192 int

readBytes(JNIEnv *env, jobject this, jbyteArray bytes, jint off, jint len, jfieldID fid) {

int nread, datalen; char stackBuf[BUF_SIZE]; char *buf = 0; FD fd;

if (IS_NULL(bytes)) {

JNU_ThrowNullPointerException(env, 0); return -1; }

datalen = (*env)->GetArrayLength(env, bytes); if ((off < 0) || (off > datalen) ||

(len < 0) || ((off + len) > datalen) || ((off + len) < 0)) {

JNU_ThrowByName(env, \"java/lang/IndexOutOfBoundsException\return -1; }

if (len == 0) { return 0;

} else if (len > BUF_SIZE) { buf = malloc(len); if (buf == 0) {

JNU_ThrowOutOfMemoryError(env, 0); return 0; } } else {

buf = stackBuf; }

fd = GET_FD(this, fid); nread = IO_Read(fd, buf, len); if (nread > 0) {

(*env)->SetByteArrayRegion(env, bytes, off, nread, (jbyte *)buf); } else if (nread == JVM_IO_ERR) {

JNU_ThrowIOExceptionWithLastError(env, \"Read error\"); } else if (nread == JVM_IO_INTR) { /* EOF */

JNU_ThrowByName(env, \"java/io/InterruptedIOException\} else { /* EOF */ nread = -1; }

if (buf != stackBuf) { free(buf); }

return nread; }

注意IO_Read在io_util_md.h做了手脚的, #define IO_Read JVM_Read

其实就是JVM_Read,JVM_Read在hotspot源码中定义的 //%note jvm_r6

return (jint)hpi::read(fd, buf, nbytes); hpi定义了大量的函数 // HPI_FileInterface

static inline char* native_path(char *path); static inline int file_type(const char *path);

static inline int open(const char *name, int mode, int perm); static inline int close(int fd);

static inline jlong lseek(int fd, jlong off, int whence); static inline int ftruncate(int fd, jlong length); static inline int fsync(int fd);

static inline int available(int fd, jlong *bytes);

static inline size_t read(int fd, void *buf, unsigned int nBytes); static inline size_t write(int fd, const void *buf, unsigned int nBytes); static inline int fsize(int fd, jlong *size);

这就是和平台相关的代码了。window下通过HPIDECL宏转换的

java.io.FileInputStream是从文件系统中获取一个文件的原始字节。如果要读取字符流,可以用java.io.FileReader。

Java代码

 public class FileInputStream extends InputStream { 

 private FileDescriptor fd; 

 private FileChannel channel = null; 

 // 调用参数为File的构造函数

 public FileInputStream(String name) throws FileNotFoundException {  this(name != null ? new File(name) : null);  } 

 // 获取文件的路径并检查权限  // 实例化FileDescriptor并赋给fd  // 打开文件(native方法)

 public FileInputStream(File file) throws FileNotFoundException {  String name = (file != null ? file.getPath() : null);

 SecurityManager security = System.getSecurityManager();  if (security != null) {

 security.checkRead(name);  }

 if (name == null) {

 throw new NullPointerException();  }

 fd = new FileDescriptor();  open(name);  } 

 // 检查权限和输入是否为空  // 把参数对象赋给fd

 public FileInputStream(FileDescriptor fdObj) {

 SecurityManager security = System.getSecurityManager();  if (fdObj == null) {

 throw new NullPointerException();  }

 if (security != null) {

 security.checkRead(fdObj);  }

 fd = fdObj;  } 

 // 根据文件名打开指定的文件

 private native void open(String name) throws FileNotFoundException;



 // 读取1个字节的数据,如果没有输入,则该方法阻断  public native int read() throws IOException; 

 // 从off位置开始,读取len长度字节的数据到字节数组b中

 private native int readBytes(byte b[], int off, int len) throws IOException; 

 // 读取所有的字节到字节数组b中,如果输入不可用,则该方法阻断  public int read(byte b[]) throws IOException {  return readBytes(b, 0, b.length);  } 

 // 读取len长度的字节到字节数组b中,如果len不为0并且输入不可用,则该方法阻断,如果为0则返回0

 public int read(byte b[], int off, int len) throws IOException {  return readBytes(b, off, len);  } 

 // 从输入流中跳过并丢弃 n 个字节的数据。

 //出于各种原因,skip 方法最终跳过的字节数可能更少一些,甚至可能为 0。如果 n 为负,则抛出 IOException,即使 InputStream 超类的 skip 方法在这种情况下没有执行任何操作。返回实际跳过的字节数。

 //此方法跳过的字节可能多于底层文件中剩余的字节。这不会产生异常,并且跳

过的字节数可能包括底层文件的 EOF(文件结束符)之后的一些字节数。如果试图在跳过末尾之后读取流,那么会返回指示文件末尾的 -1。  public native long skip(long n) throws IOException; 

 // 返回下一次对此输入流调用的方法可以不受阻塞地从此输入流读取(或跳过)的估计剩余字节数。

 // 在某些情况下,非阻塞的读取(或跳过)操作在执行很慢时看起来受阻塞,例如,在网速缓慢的网络上读取大文件时

 public native int available() throws IOException; 

 // 关闭流并且释放所有和这个流关联的系统资源  public void close() throws IOException {  if (channel != null)  channel.close();  close0();  } 

 // 返回表示到文件系统中实际文件的连接的FileDescriptor对象,该文件正被当前的FileInputStream实例所使用

 public final FileDescriptor getFD() throws IOException {  if (fd != null) return fd;  throw new IOException();  }



 // 返回和这个流关联的唯一FileChannel对象

 // 返回的文件通道的初始位置和从文件中当前读取的字节数相等。从这个流中读取字节会让通道的位置增加。改变通道的位置,通过显式的的或者读取,会改变流的文件位置。

 public FileChannel getChannel() {  synchronized (this) {  if (channel == null)

 channel = FileChannelImpl.open(fd, true, false, this);  return channel;  }  } 

 private static native void initIDs(); 

 private native void close0() throws IOException; 

 static {  initIDs();  } 

 // 确保没有对象和当前流关联时,调用close()方法来释放资源  protected void finalize() throws IOException {

 if (fd != null) {  if (fd != fd.in) {  close();  }  }  }  }

因篇幅问题不能全部显示,请点此查看更多更全内容