FileOutputStream
用户打开文件并获取输出流。
public FileOutputStream(File file,boolean append) throws FileNotFoundException { String name = (file != null ? file.getPath() : null); SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkWrite(name); } if (name == null) { throw new NullPointerException(); } if (file.isInvalid()) { throw new FileNotFoundException("Invalid file path"); } this.fd = new FileDescriptor(); fd.attach(this); this.append = append; // 记录是否是append追加模式 this.path = name; open(name, append); } private void open(String name,boolean append) throws FileNotFoundException { open0(name, append); } private native void open0(String name,boolean append) throws FileNotFoundException;
// jdk/src/solaris/native/java/io/FileOutputStream_md.c JNIEXPORTvoid JNICALL Java_java_io_FileOutputStream_open(JNIEnv *env, jobjectthis, jstring path, jboolean append) { // 使用O_WRONLY,O_CREAT模式打开文件,如果文件不存在会新建文件 // 如果java中指定append参数为true,则使用O_APPEND追加模式 // 如果java中指定append参数为false,则使用O_TRUNC模式,如果文件存在内容,会清空掉 fileOpen(env, this, path, fos_fd, O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC)); }
fileOpen
之后的流程与 FileInputStream
的一致,可以参考 JDK源码阅读-FileInputStream
FileOutputStream
提供了三个write函数:
public void write(int b)throws IOException { write(b, append); } public void write(byte b[])throws IOException { writeBytes(b, 0, b.length, append); } public void write(byte b[], int off, int len)throws IOException { writeBytes(b, off, len, append); } private native void write(int b, boolean append)throws IOException; private native void writeBytes(byte b[], int off, int len, boolean append) throws IOException;
// jdk/src/solaris/native/java/io/FileOutputStream_md.c JNIEXPORTvoid JNICALL Java_java_io_FileOutputStream_write(JNIEnv *env, jobjectthis, jint byte, jboolean append) { writeSingle(env, this, byte, append, fos_fd); } JNIEXPORTvoid JNICALL Java_java_io_FileOutputStream_writeBytes(JNIEnv *env, jobject this, jbyteArray bytes, jint off, jint len, jboolean append) { writeBytes(env, this, bytes, off, len, append, fos_fd); }
// jdk/src/share/native/java/io/io_util.c void writeSingle(JNIEnv *env, jobjectthis, jint byte, jboolean append, jfieldID fid) { // Discard the 24 high-order bits of byte. See OutputStream#write(int) char c = (char) byte; jint n; // 获取记录在FileDescriptor中的文件描述符 FD fd = GET_FD(this, fid); if (fd == -1) { JNU_ThrowIOException(env, "Stream Closed"); return; } // 追加模式和普通模式使用不同的函数 if (append == JNI_TRUE) { n = IO_Append(fd, &c, 1); } else { n = IO_Write(fd, &c, 1); } if (n == -1) { JNU_ThrowIOExceptionWithLastError(env, "Write error"); } } void writeBytes(JNIEnv *env, jobjectthis, jbyteArray bytes, jint off, jint len, jboolean append, jfieldID fid) { jint n; char stackBuf[BUF_SIZE]; char *buf = NULL; FD fd; // 判断Java传入的byte数组是否是null if (IS_NULL(bytes)) { JNU_ThrowNullPointerException(env, NULL); return; } // 判断off和len参数是否数组越界 if (outOfBounds(env, off, len, bytes)) { JNU_ThrowByName(env, "java/lang/IndexOutOfBoundsException", NULL); return; } // 如果写入长度为0,直接返回0 if (len == 0) { return; } else if (len > BUF_SIZE) { // 如果写入长度大于BUF_SIZE(8192),无法使用栈空间buffer // 需要调用malloc在堆空间申请buffer buf = malloc(len); if (buf == NULL) { JNU_ThrowOutOfMemoryError(env, NULL); return; } } else { buf = stackBuf; } // 复制Java传入的byte数组数据到C空间的buffer中 (*env)->GetByteArrayRegion(env, bytes, off, len, (jbyte *)buf); if (!(*env)->ExceptionOccurred(env)) { off = 0; while (len > 0) { // 获取记录在FileDescriptor中的文件描述符 fd = GET_FD(this, fid); if (fd == -1) { JNU_ThrowIOException(env, "Stream Closed"); break; } // 追加模式和普通模式使用不同的函数 if (append == JNI_TRUE) { n = IO_Append(fd, buf+off, len); } else { n = IO_Write(fd, buf+off, len); } if (n == -1) { JNU_ThrowIOExceptionWithLastError(env, "Write error"); break; } off += n; len -= n; } } if (buf != stackBuf) { free(buf); } }
IO_Write
/ IO_Append
虽然看起来是两个不同的函数,其实是两个不同的宏定义,指向同一个函数 handleWrite
:
// jdk/src/solaris/native/java/io/io_util_md.h #defineIO_Write handleWrite #defineIO_Append handleWrite
handleWrite
中调用 write
系统调用写入数据:
// jdk/src/solaris/native/java/io/io_util_md.c ssize_t handleWrite(FD fd, const void *buf, jint len) { ssize_t result; RESTARTABLE(write(fd, buf, len), result); return result; }
FileOutputStream#write(byte[], int, int)
的主要流程:
0<len<=BUF_SIZE write
FileOutputStream#write(byte[], int, int)
public void close()throws IOException { synchronized (closeLock) { if (closed) { return; } closed = true; } if (channel != null) { channel.close(); } fd.closeAll(new Closeable() { public void close()throws IOException { close0(); } }); }
FileOutputStream
关闭文件的逻辑和 FileInputStream
关闭文件的逻辑是一样的,参考 JDK源码阅读-FileDescriptor