近做一个很简单的函数,要两个返回值,我打算有一个类封装一下返回,后来领导要求使用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(); } } } }
因篇幅问题不能全部显示,请点此查看更多更全内容