Java中的FileInputStream是否需要close问题

目录
  • FileInputStream 类简介
    • FileInputStream 的 finalize() 方法
  • 实际测试
    • 结论
  • 会有其他问题吗
  • 主动 close 的方式
  • 总结

FileInputStream 类简介

FileInputStream 类在 Java 中非常常用,用来读取文件流的。而这种读取方式往往会涉及到流的关闭 close。

如果不关闭 FileInputStream 流会有问题吗?会导致内存泄漏吗?

FileInputStream 的 finalize() 方法

Java 中每个 Object 都有 finalize() 方法,用来在 gc 的时候调用。

而 FileInputStream 类中的 finalize() 方法中有一点特别的,它重写了这个方法,并且在里面进行了 close()。

如下代码:

    /**
     * Ensures that the <code>close</code> method of this file input stream is
     * called when there are no more references to it.
     *
     * @exception  IOException  if an I/O error occurs.
     * @see        java.io.FileInputStream#close()
     */
    protected void finalize() throws IOException {
        if ((fd != null) &&  (fd != FileDescriptor.in)) {
            /* 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();
        }
    }

可以看到,只要触发了 gc,那么就会调用 finalize() 方法,那么就会自动 close 流。当被 close 之后,也就不会发生内存泄漏了。

那么,不主动关闭,并且不主动触发 System.gc() 的话,它会被 JVM 回收吗?

实际测试

为了更加直观地看到是否调用了 finalize() 方法,这里新建一个 MyFileInputStream 类 extends FileInputStream,为的是重写 FileInputStream 的 finalize() 方法,给里面加入一行打印输出。

代码如下:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

/**
 * a class extends FileInputStream to test method finalize()
 *
 * @author sleepybear - https://blog.csdn.net/qq_36670734
 * @date 2022/1/9 21:02
 */
public class MyFileInputStream extends FileInputStream {
    public MyFileInputStream(File file) throws FileNotFoundException {
        super(file);
    }

    /**
     * 重写了 finalize 方法,把父类的 finalize 中的方法复制到此处,并且在其中添加打印信息
     *
     * @throws IOException 异常
     */
    @Override
    protected void finalize() throws IOException {
        if ((this.getFD() != null) && (this.getFD() != FileDescriptor.in)) {
            /* 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();
            System.out.println("finalize and close");
        } else {
            System.out.println("only call finalize");
        }
    }
}

可以看到,只要执行了 finalize() 方法,那么就会打印一行 “finalize”。

然后新建测试类,如下代码:

import java.io.File;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

/**
 * 测试 MyFileInputStream 的 Finalize
 *
 * @author sleepybear - https://blog.csdn.net/qq_36670734
 * @date 2022/1/9 21:10
 */
public class TestFinalize {
    /**
     * 计数
     */
    public static int count = 0;

    public static void main(String[] args) throws InterruptedException {
        listFiles("D://Work//");
        System.out.println("遍历结束,等待 2 秒");
        TimeUnit.MILLISECONDS.sleep(1000 * 2L);
        System.out.println("显式调用 gc");
        System.gc();
        TimeUnit.MILLISECONDS.sleep(1000 * 2L);
        System.out.println("结束");
    }

    /**
     * 递归遍历所有文件,若遍历到 2000 的整数倍,那么输出这个文件的 md5
     *
     * @param path 路径
     */
    public static void listFiles(String path) {
        if (path == null || path.length() == 0) {
            return;
        }

        File file = new File(path);
        if (!file.exists()) {
            return;
        }

        if (file.isDirectory()) {
            // 遇到文件夹,往里面继续递归
            File[] files = file.listFiles();
            if (files != null && files.length > 0) {
                for (File dir : files) {
                    listFiles(dir.getAbsolutePath());
                }
            }
        }

        if (file.isFile()) {
            // 遇到是文件,那么计数 +1
            count++;
            if (count % 2000 == 0) {
                // 如果是 2000 的整数倍,那么打印文件的 md5,md5 打印需要用到 commons-codec-1.15.jar
                try {
                    // 这里直接 new MyFileInputStream 并没有显式 close,同时工具方法里面也没有调用 close()
                    String md5 = org.apache.commons.codec.digest.DigestUtils.md5Hex(new MyFileInputStream(file));
                    System.out.println("count = " + count + ", md5 = " + md5);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

运行代码,得到如下的结果:

count = 2000, md5 = da6a56cda0772e7b321621472f1ca9ce
count = 4000, md5 = 8fc19cf5f7675179ed14abce784e29da
count = 6000, md5 = f93186d553b72e168f84ef9870285a17
count = 8000, md5 = 6af44868883a83b7ae4a0c3529ebc6ef
count = 10000, md5 = f570fdda290a62db840538c460e46510
count = 12000, md5 = 26ae171433b7c355223fa43f9d605110
count = 14000, md5 = c5f924cee8c51c19825af3713044b67a
count = 16000, md5 = deda72e7ef14674a49295f460301b4cf
count = 18000, md5 = 08753370d8c5bbda239e4d349892730c
count = 20000, md5 = df1213e1584803bf0a549c5a1c14f111
count = 22000, md5 = 9751d0dbc67c75cb446bdaf2d2434f66
count = 24000, md5 = 962cf50af21894734a78493e2f4df31b
count = 26000, md5 = f9556c74d758838e9c2202921de50056
count = 28000, md5 = 2a2699c13ff46dd34305bc5c7b780b52
count = 30000, md5 = 71af55db4adf6c7b2e667f031a809e91
count = 32000, md5 = bdef65ff9a12c5808b491f4b0be362d1
count = 34000, md5 = 9f1da8e150bfe5077c7ab82b3727aba0
count = 36000, md5 = 648422e1e6b89a1e331e407e6c7fc652
count = 38000, md5 = d1d9e7a656db7d0a4e79110fdb3f3462
count = 40000, md5 = 50b6c156bf30447d4f0b891677b75617
count = 42000, md5 = 1be6de12ec79e310675f1b1e5f1e891c
count = 44000, md5 = 027ca2c40a3d9b2d8f7818673cb6329c
count = 46000, md5 = 07e1a13fc5e3e5fdd3cacf998e57eaa8
count = 48000, md5 = c3bf74579b053ccdd5bb6bed7c8c5ab1
count = 50000, md5 = 78a2a70250a4df4f21523e0b94d6bca4
count = 52000, md5 = 769f5ea0d0a2c2b89d82a2bf2dbdbedd
count = 54000, md5 = c092d2f664c726da95f058018134bdfb
count = 56000, md5 = dc4d6f6ac6212f91d0aba93d692564c4
count = 58000, md5 = 217926c75b000f1dea20d319e9aeebbf
count = 60000, md5 = b437b7e80f6c52a42c3e4fe74be49c48
count = 62000, md5 = 9a92a6cf85e5513550ab801e12867dc9
count = 64000, md5 = b92bc3f149a121039aa8fe6a05f47b35
count = 66000, md5 = 064fd7ca2040cb21615e205b1f294e19
count = 68000, md5 = b4d20b20526769ef73702108c519cf25
count = 70000, md5 = 2436edd2550c69c1c2a519edfee2b853
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
count = 72000, md5 = 6513a7abfe9f456814216f4040d82f2e
count = 74000, md5 = 7b1a34f38c218fa59b775543e1d2784f
count = 76000, md5 = 5e02f191cec09d5a938233dd43bec96d
count = 78000, md5 = 8e80d1b1e0c42af8937fc570e94145d4
count = 80000, md5 = 42c2b3d42c7966e47a1de23e3c89eca6
count = 82000, md5 = ce45fc5afea20d7e5a4fc79ddc3b04cb
count = 84000, md5 = 51bd1280db5b549968b855f7d25f7075
count = 86000, md5 = c90629033ec6b8fbfe5f17f63a44cfa3
count = 88000, md5 = 71b5e1bdc7f76444fb0fe88bc0e3a669
count = 90000, md5 = a930f1957f273fd5cb0d7fc984096ce4
count = 92000, md5 = badfe45e2c5917dcec2d0be1a03583de
count = 94000, md5 = 9e74b89c1f8e8ecfb00b84001b45ddd7
count = 96000, md5 = 7b4464b79280f9ac23e4b241508aa2d1
count = 98000, md5 = d79fabe6804056b0a6572e95f7c970c0
count = 100000, md5 = bcb8f86c91f8e106bdce40584ddba54b
count = 102000, md5 = 8f578f7a6dbcbde77d375a2e7c98ceee
count = 104000, md5 = aa39503815c583a38b409c92e04e5d14
count = 106000, md5 = 34ec7897529f1b00a78b13db223f907b
count = 108000, md5 = ea83e11ece4e7fdcc23bd9214f486ae3
count = 110000, md5 = 05d69a87ebf4133795aad1aae4a99ebb
count = 112000, md5 = bcc781b71ff6b10a57923af9fcc85b38
count = 114000, md5 = 5e468c6233db3f6a4311ebafa6e35eb6
count = 116000, md5 = 365a5b3af1dd522ed7c9a8b106f90224
count = 118000, md5 = 9117c65ed9ff083256a86af10ab88d65
count = 120000, md5 = 97f24779279cfe2fa1d573ef83c21009
count = 122000, md5 = ba6b12f0b76b192d9dd74965919bad95
count = 124000, md5 = b54c8105da76b3f13dcf78495bc2ff52
count = 126000, md5 = 648422e1e6b89a1e331e407e6c7fc652
count = 128000, md5 = eb115942b0abf38b881353debe4bdb92
count = 130000, md5 = e469a50bf7cfd9f1fb95f7d5badc1641
count = 132000, md5 = 2be1bd409def8cfc4f32a49af4bf5e9f
count = 134000, md5 = e3a20ac5df9d81a2ce301886fdfc0f07
count = 136000, md5 = 77269649674cc0fdf4b379a523a3a829
count = 138000, md5 = aead3fc8f945488ab71cf64271f46c54
count = 140000, md5 = e7098eafd97649e4a77edea89d0d22ab
count = 142000, md5 = 28d392b26bbd86fb3312960b82572060
count = 144000, md5 = f37ea0c388bec909255c602adc0cdfe4
count = 146000, md5 = d91659c455a4f66f9a16c8f287ce9fc9
count = 148000, md5 = 338364a0d43b018333d3143a466e1cf2
遍历结束,等待 2 秒
显式调用 gc
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close
finalize and close

结论

上述测试可以看到,即使没有主动 close 流,MyFileInputStream 类(或者是 FileInputStream 类)在结束之后,也会自动被回收。在回收的时候调用 finalize() 方法自行 close 流。

所以在使用 FileInputStream 类,不显式调用 close 方法的话,一般不会造成内存泄漏。

会有其他问题吗

不主动 close 流的话,文件句柄还占用着,如果 JVM 没有及时清理这些流的话,那么可能导致资源泄露问题,可能会因为过多打开文件导致 CPU 和 RAM 占用居高,甚至无法再打开新的文件进行读写。

主动 close 的方式

    FileInputStream fileInputStream = null;
    try {
        fileInputStream = new FileInputStream(file);
        fileInputStream.read();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (fileInputStream != null) {
            fileInputStream.close();
        }
    }

当 Java 1.7 之后,可以使用 try-with-resources 方式自动 close 流

    try (FileInputStream fileInputStream = new FileInputStream(file)) {
        fileInputStream.read();
    } catch (IOException e) {
        e.printStackTrace();
    }

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • vue中el-input绑定键盘按键(按键修饰符)

    vue怎么写键盘事件 vue允许将按键值作为修饰符来使用,如监听回车事件,有两种写法,如下代码: <input type="text" @keyup.13="console.log($event)"></input> <input type="text" @keyup.enter="console.log($event)"></input> 但要注意的是:当我们在在el-inpu

  • Input系统之InputReader处理按键事件详解

    目录 前言 认识按键事件 处理按键事件 扫描码映射按键码 结束 前言 前面几篇文章已经为 Input 系统的分析打好了基础,现在是时候进行更深入的分析了. 通常,手机是不带键盘的,但是手机上仍然有按键,就是我们经常使用的电源键以及音量键.因此还是有必要分析按键事件的处理流程. 那么,掌握按键事件的处理流程,对我们有什么用处呢?例如,手机上添加了一个功能按键,你知道如何把这个物理按键映射到上层,然后处理这个按键吗?又例如,如果设备是不需要电源键,但是系统默认把某一个按键映射为电源键,那么我们如何使

  • vue实现一个获取按键展示快捷键效果的Input组件

    遇到一个需求,页面内要自定义快捷键,这就需要可以有地方设置和展示快捷键,找了一圈Element UI发现没有能稍微改改就能用的组件,所以自己动手写了一个. 这个只有快捷键展示功能,快捷键实际绑定生效的话是依赖传回的快捷键数据,由另外的组件处理的.目前只测试了Chrome的环境. 效果如下: 关键点 虽然看起来像是一个Input但在组件内实际上是展示一个标签效果,还需要有删除按钮.这就得在输入框内放下html代码,浏览器的Input组件显然不适合,这就只能自己仿一个类Input组件效果了. foc

  • adb shell input keyevent 控制按键输入的数值(收藏版)

    adb shell的功能很强大,可以使用很多功能,今天我们说下通过控制按键输入:adb shell input keyevent xx ,具体数值xx如下 KEYCODE_CALL 进入拨号盘 5 KEYCODE_ENDCALL 挂机键 6 KEYCODE_HOME 按键Home 3 KEYCODE_MENU 菜单键 82 KEYCODE_BACK 返回键 4 KEYCODE_SEARCH 搜索键 84 KEYCODE_CAMERA 拍照键 27 KEYCODE_FOCUS 拍照对焦键 80 K

  • java中的FileInputStream三种read()函数用法

    目录 FileInputStream流的三种read()函数对比 1. int read( )函数 2. read( byte [ ] ) 3. read(byte[ ] ,int off ,int len) FileInputStream中的read()方法 总结 FileInputStream流的三种read()函数对比 首先我们先创建FileIputStream流对文件进行读取 public static void main(String[] args) throws IOExceptio

  • uniapp中input聚焦禁止软键盘弹出方法

    需求:在进入页面时需要input聚焦,但是不弹出软键盘 方法一:使用uniapp自带方法uni.hideKeyboard()来隐藏软键盘 方法二:使用readonly属性规定输入字段为只读 方法三:使用document.activeElement.blur()移开焦点 方法四:通过div模拟input来实现 以上方法网上都有,但是都没有解决我的需求,下面是我使用的方法 方法五:通过定时器来实现隐藏键盘 一般项目通常使用方法一就可以解决,或者日期类型也可以通过以上方法来实现. 我们项目有物理按键,

  • Java中的FileInputStream 和 FileOutputStream 介绍_动力节点Java学院整理

    FileInputStream 和 FileOutputStream 介绍 FileInputStream 是文件输入流,它继承于InputStream. 通常,我们使用FileInputStream从某个文件中获得输入字节. FileOutputStream 是文件输出流,它继承于OutputStream. 通常,我们使用FileOutputStream 将数据写入 File 或 FileDescriptor 的输出流. FileInputStream 函数接口 FileInputStream

  • Java中的FileInputStream是否需要close问题

    目录 FileInputStream 类简介 FileInputStream 的 finalize() 方法 实际测试 结论 会有其他问题吗 主动 close 的方式 总结 FileInputStream 类简介 FileInputStream 类在 Java 中非常常用,用来读取文件流的.而这种读取方式往往会涉及到流的关闭 close. 如果不关闭 FileInputStream 流会有问题吗?会导致内存泄漏吗? FileInputStream 的 finalize() 方法 Java 中每个

  • java中struts2实现文件上传下载功能实例解析

    本文实例讲述了java中struts2实现文件上传下载功能实现方法.分享给大家供大家参考.具体分析如下: 1.文件上传 首先是jsp页面的代码 在jsp页面中定义一个上传标签 复制代码 代码如下: <tr>      <td align="right" bgcolor="#F5F8F9"><b>附件:</b></td>      <td bgcolor="#FFFFFF">

  • java 中Spark中将对象序列化存储到hdfs

    java 中Spark中将对象序列化存储到hdfs 摘要: Spark应用中经常会遇到这样一个需求: 需要将JAVA对象序列化并存储到HDFS, 尤其是利用MLlib计算出来的一些模型, 存储到hdfs以便模型可以反复利用. 下面的例子演示了Spark环境下从Hbase读取数据, 生成一个word2vec模型, 存储到hdfs. 废话不多说, 直接贴代码了. spark1.4 + hbase0.98 import org.apache.spark.storage.StorageLevel imp

  • Java中IO流简介_动力节点Java学院整理

    Java io简介 Java io系统的设计初衷,就是为了实现"文件.控制台.网络设备"这些io设置的通信.例如,对于一个文件,我们可以打开文件,然后进行读取和写入.在java 1.0中,java提供的类都是以字节(byte)为单位,例如,FileInputStream和FileOutputStream.而到了java 1.1,为了与国际化进行接轨,在java io中添加了许多以字符(Unicode)为单位进行操作的类. 在java io的称呼中,我们经常会提到"输入流&qu

  • java中常用工具类之字符串操作类和MD5加密解密类

    java中常用的工具类之String和MD5加密解密类 我们java程序员在开发项目的是常常会用到一些工具类.今天我分享一下我的两个工具类,大家可以在项目中使用. 一.String工具类 package com.itjh.javaUtil; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import

  • java中读写Properties属性文件公用方法详解

    前言 大家都知道Java中有个比较重要的类Properties(Java.util.Properties),主要用于读取Java的配置文件,各种语言都有自己所支持的配置文件,配置文件中很多变量是经常改变的,这样做也是为了方便用户,让用户能够脱离程序本身去修改相关的变量设置.像Python支持的配置文件是.ini文件,同样,它也有自己读取配置文件的类ConfigParse,方便程序员或用户通过该类的方法来修改.ini配置文件.在Java中,其配置文件常为.properties文件,格式为文本文件,

  • Java 中的 DataInputStream 介绍_动力节点Java学院整理

    DataInputStream 介绍 DataInputStream 是数据输入流.它继承于FilterInputStream. DataInputStream 是用来装饰其它输入流,它"允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型".应用程序可以使用DataOutputStream(数据输出流)写入由DataInputStream(数据输入流)读取的数据. DataInputStream 函数列表 DataInputStream(InputStream in

  • 深入探讨JAVA中的异常与错误处理

    异常与错误: 异常: 在Java中程序的错误主要是语法错误和语义错误,一个程序在编译和运行时出现的错误我们统一称之为异常,它是VM(虚拟机)通知你的一种方式,通过这种方式,VM让你知道,你(开发人员)已经犯了个错误,现在有一个机会来修改它.Java中使用异常类来表示异常,不同的异常类代表了不同的异常.但是在Java中所有的异常都有一个基类,叫做Exception. 错误: 它指的是一个合理的应用程序不能截获的严重的问题.大多数都是反常的情况.错误是VM的一个故障(虽然它可以是任何系统级的服务).

随机推荐