【Java部分源码分析之io篇】4.FileOutputStream
2017-07-27 00:00
344 查看
FileOutputStream是OutputStream的子类,同样也是因为它比较常见,用得比较多,所以拿它来分析一下。它的结构与FileInputStream很相似,想看FileInputStream的源码分析可以去看我的另外一篇博文《【Java部分源码分析之io篇】2.FileInputStream》。
先来看看FileOutputStream的成员变量。
fd是文件描述符,它唯一的对应着一个文件,每打开一个文件都会有一个唯一的文件描述符,即使打开的是同一个文件。
append如果这个文件的打开模式是追加模式的话,则它为true。
channel是内置的文件管道,在NIO编程中会用到这个。
path是文件的路径。
closeLock和closed都是运用在文件输出流关闭的时候,两者都是判断条件。如果closeLock被其他对象锁住了,则不能关闭输出流;如果closed为false,也不能关闭输出流。
FileOutputStream有五个重载的构造方法。
可以看到前面三个构造方法都是调用的第四个构造方法。
第三个构造方法是利用一个file对象来进行构造输出流,先检查一下文件的写权限,路径是否为空,文件是否存在。然后生成一个文件描述符绑定这个输出流,fd.attach(this)的作用就是为了GC的时候跟踪所引用的对象,防止误GC。然后调用本地方法打开文件。open调用的是本地方法,这是在JVM层面上实现的方法。可以这么说,基本上所有与操作系统底层打交道的函数都是直接或间接调用JVM层面实现的函数。
第四个构造方法是利用一个文件描述符进行构造输出流,因为一个文件描述符唯一对应着一个文件,所以可以用来构造文件输出流。它的步骤与上面第三个方法是一致的。
接下来看看它的write方法。
可以看到关键的write和writeBytes方法都是调用本地方法实现的,其他的重载函数都是调用它们得来的。
来看FileInputStream的close方法。这个方法有个同步块,所以在多线程环境下的close操作是安全的。最后的方法体中的close0是一个本地方法,具体实现在JVM层面上,应该就是释放系统资源之类的。close0的声明如下:
我们再来看看finalize方法。
一般来说,类方法是很少有这个finalize方法的,这个方法主要在Object方法中实现,这里重写这个方法主要是为了防止GC的时候还有资源没有释放干净,在GC之前再次把引用的资源释放干净。
先来看看FileOutputStream的成员变量。
/** * The system dependent file descriptor. */ private final FileDescriptor fd; /** * True if the file is opened for append. */ private final boolean append; /** * The associated channel, initialized lazily. */ private FileChannel channel; /** * The path of the referenced file * (null if the stream is created with a file descriptor) */ private final String path; private final Object closeLock = new Object(); private volatile boolean closed = false;
fd是文件描述符,它唯一的对应着一个文件,每打开一个文件都会有一个唯一的文件描述符,即使打开的是同一个文件。
append如果这个文件的打开模式是追加模式的话,则它为true。
channel是内置的文件管道,在NIO编程中会用到这个。
path是文件的路径。
closeLock和closed都是运用在文件输出流关闭的时候,两者都是判断条件。如果closeLock被其他对象锁住了,则不能关闭输出流;如果closed为false,也不能关闭输出流。
FileOutputStream有五个重载的构造方法。
public FileOutputStream(String name) throws FileNotFoundException { this(name != null ? new File(name) : null, false); } public FileOutputStream(String name, boolean append) throws FileNotFoundException { this(name != null ? new File(name) : null, append); } public FileOutputStream(File file) throws FileNotFoundException { this(file, false); } 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; this.path = name; open(name, append); } public FileOutputStream(FileDescriptor fdObj) { SecurityManager security = System.getSecurityManager(); if (fdObj == null) { throw new NullPointerException(); } if (security != null) { security.checkWrite(fdObj); } this.fd = fdObj; this.append = false; this.path = null; fd.attach(this); }
可以看到前面三个构造方法都是调用的第四个构造方法。
第三个构造方法是利用一个file对象来进行构造输出流,先检查一下文件的写权限,路径是否为空,文件是否存在。然后生成一个文件描述符绑定这个输出流,fd.attach(this)的作用就是为了GC的时候跟踪所引用的对象,防止误GC。然后调用本地方法打开文件。open调用的是本地方法,这是在JVM层面上实现的方法。可以这么说,基本上所有与操作系统底层打交道的函数都是直接或间接调用JVM层面实现的函数。
第四个构造方法是利用一个文件描述符进行构造输出流,因为一个文件描述符唯一对应着一个文件,所以可以用来构造文件输出流。它的步骤与上面第三个方法是一致的。
接下来看看它的write方法。
private native void write(int b, boolean append) throws IOException; public void write(int b) throws IOException { write(b, append); } private native void writeBytes(byte b[], int off, int len, boolean append) throws IOException; 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); }
可以看到关键的write和writeBytes方法都是调用本地方法实现的,其他的重载函数都是调用它们得来的。
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(); } }); }
来看FileInputStream的close方法。这个方法有个同步块,所以在多线程环境下的close操作是安全的。最后的方法体中的close0是一个本地方法,具体实现在JVM层面上,应该就是释放系统资源之类的。close0的声明如下:
private native void close0() throws IOException;
我们再来看看finalize方法。
protected void finalize() throws IOException { if (fd != null) { if (fd == FileDescriptor.out || fd == FileDescriptor.err) { flush(); } else { /* if fd is shared, the references in FileDescriptor * will ensure that finalizer is only called when * safe to do so. All references using the fd have * become unreachable. We can call close() */ close(); } } }
一般来说,类方法是很少有这个finalize方法的,这个方法主要在Object方法中实现,这里重写这个方法主要是为了防止GC的时候还有资源没有释放干净,在GC之前再次把引用的资源释放干净。
相关文章推荐
- 【Java部分源码分析之io篇】5.Reader
- 【Java部分源码分析之io篇】0.传统IO输入输出流总览
- 【Java部分源码分析之io篇】3.OutputStream
- 【Java部分源码分析之io篇】2.FileInputStream
- 【Java部分源码分析之io篇】1.InputStream
- java部分源码分析——LinkedList
- java的HashTable的部分源码分析
- 【Java部分源码分析之lang篇】1.Object
- 【Java部分源码分析之lang篇】5.System
- Java Collections Framework之Arrays(method:sort(),binarySearch(),copyOf())部分源码分析(基于JDK1.6)
- 【Java部分源码分析之lang篇】4.StringBuilder与StringBuffer(二)
- 【Java部分源码分析之lang篇】3.StringBuilder与StringBuffer(一)
- 【Java部分源码分析之lang篇】2.String
- 《Java 源码分析》:Java NIO 之 Selector(第二部分selector.select())
- Java 自动装箱、拆箱机制及部分源码分析
- 【Java部分源码分析之io篇】7.FileReader
- 【Java部分源码分析之io篇】6.InputStreamReader
- owncloud源码分析1--部分修改Demo
- java tcp底层源码分析
- Java多线程-五中线程池分析以及AnsyncTask源码分析