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