文档章节

【Java部分源码分析之io篇】4.FileOutputStream

gongwilliam
 gongwilliam
发布于 2017/07/27 14:08
字数 1042
阅读 20
收藏 0

3 月,跳不动了?>>>

FileOutputStream是OutputStream的子类,同样也是因为它比较常见,用得比较多,所以拿它来分析一下。它的结构与FileInputStream很相似,想看FileInputStream的源码分析可以去看我的另外一篇博文《【Java部分源码分析之io篇】2.FileInputStream》。

先来看看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是文件的路径。

closeLockclosed都是运用在文件输出流关闭的时候,两者都是判断条件。如果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之前再次把引用的资源释放干净。

© 著作权归作者所有

gongwilliam
粉丝 2
博文 14
码字总数 12127
作品 0
深圳
私信 提问
加载中

评论(0)

泥沙砖瓦浆木匠/java-core-learning-example

感谢赞助的ta们 Java 核心系列教程,关于Java核心技术学习积累的例子,是初学者及核心技术巩固的最佳实践。 包括基础语法,OOP,字符串,集合,IO,反射,线程,网络等。 未完成模块:阿里J...

泥沙砖瓦浆木匠
2019/04/02
0
0
从1+1=2来理解Java字节码

背景 前不久《深入理解Java虚拟机》第三版发布了,赶紧买来看了看新版的内容,这本书更新了很多新版本虚拟机的内容,还对以前的部分内容进行了重构,还是值得去看的。本着复习和巩固的态度,...

木木匠
2019/12/30
6K
11
【目录导航】JAVA零基础进阶之路

【JAVA零基础入门系列】(已完结)导航目录 Day1 开发环境搭建 Day2 Java集成开发环境IDEA Day3 Java基本数据类型 Day4 变量与常量 Day5 Java中的运算符 Day6 Java字符串 Day7 Java输入与输出...

MFrank
2018/06/21
0
0
Java 208 道面试题:Java 基础模块答案

目前市面上的面试题存在两大问题:第一,题目太旧好久没有更新了,还都停留在 2010 年之前的状态;第二,近几年 JDK 更新和发布都很快,Java 的用法也变了不少,加上 Java 技术栈也加入了很多...

王磊的博客
2019/03/05
921
0
Java NIO原理 图文分析及代码实现

Java NIO原理图文分析及代码实现 前言: 最近在分析hadoop的RPC(Remote Procedure Call Protocol ,远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术...

囚兔
2015/04/29
349
1

没有更多内容

加载失败,请刷新页面

加载更多

开源:从“复兴”走向“商业化”

在美国版“知乎”Quora上搜“Open Source(开源)”,出来第一条问题是: “Linux的失败真的是因为开源吗?”。其中一个回答给我很多启发: “有些人把安卓和Chrome OS的成功归结于Linux开源...

编辑部的故事
22分钟前
15
0
JavaScript等同于printf / String.Format - JavaScript equivalent to printf/String.Format

问题: I'm looking for a good JavaScript equivalent of the C/PHP printf() or for C#/Java programmers, String.Format() ( IFormatProvider for .NET). 我正在寻找一个等效于C / PHP p......

javail
今天
27
0
什么是Android上的“上下文”? - What is 'Context' on Android?

问题: In Android programming, what exactly is a Context class and what is it used for? 在Android编程中, Context类到底是什么?它的用途是什么? I read about it on the developer......

技术盛宴
今天
26
0
OkHttp配置HTTPS访问+服务器部署

1 概述 OkHttp配置HTTPS访问,核心为以下三个部分: sslSocketFactory() HostnameVerifier X509TrustManager 第一个是ssl套接字工厂,第二个用来验证主机名,第三个是证书信任器管理类.通过OkHtt...

氷泠
今天
20
0
华为P40发布:搭载HMS硬刚谷歌,未涨价抢全球高端机市场

  文连线 Insight,作者向阳,编辑水笙   3 月 26 日晚,华为消费者业务 CEO 余承东登上台,以熟悉的英文口音开启了华为发布会,他说,“这就是我们的 P40 系列。”   以往华为P系列通...

水果黄瓜
今天
30
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部