首页 > 关于java中关闭流疑问

关于java中关闭流疑问

下面这段代码的作用是压缩,会用到ZipOutputStream

    ZipOutputStream zipOutputStream = null;
    try {
      zipOutputStream = new ZipOutputStream(new FileOutputStream(zipPath));
    } catch (FileNotFoundException e) {
        throw new IllegalArgumentException("zipPath error " + "", e);
    } finally {
        if (null != zipOutputStream) {
            try {
                zipOutputStream.close();
            } catch (IOException e) {
                logger.error("", e);
            }
        }
    }

错误放大版1:

    ZipOutputStream zipOutputStream = null;
    try {
        FileOutputStream fileOutputStream =   new FileOutputStream(zipPath);
        throw new RuntimeException();
        zipOutputStream = new ZipOutputStream(fileOutputStream);
    } catch (FileNotFoundException e) {
        throw new IllegalArgumentException("zipPath error " + "", e);
    } finally {
        if (null != zipOutputStream) {
            try {
                zipOutputStream.close();
            } catch (IOException e) {
                logger.error("", e);
            }
        }
    }

错误放大版2:

    ZipOutputStream zipOutputStream = null;
    try {
      zipOutputStream = new ZipOutputStream(new FileOutputStream(zipPath),null);
    } catch (FileNotFoundException e) {
        throw new IllegalArgumentException("zipPath error " + "", e);
    } finally {
        if (null != zipOutputStream) {
            try {
                zipOutputStream.close();
            } catch (IOException e) {
                logger.error("", e);
            }
        }
    }
    

这里我有一个疑问,在new FileOutputStream(zipPath)的过程中如果已经打开了这个文件,但是在new ZipOutputStream过程中出错了,这个时候zipOutputStream并没有创建成功,所以在finally中不会调用close方法,造成的结果就是这个文件不会被关闭。
所以这种关闭方式是不是会有漏洞,还是我的理由有问题?


这个例子有漏洞,不过漏洞不在这里,而是 FileOutputStream
如果 new ZipOutputStream()调用失败,这个构造方法会抛出运行时异常,而且不会关闭传递进来的 FileOutputStream 对象。
由于调用者也不持有该对象的引用,也无法释放资源,会造成打开的文件描述符没有关闭。
如果使用java8的话可以用 try with,或者在finally块里面释放FileOutputStream 对象持有的资源,这样是比较保险的做法。


解决方法很简单你直接close掉传递的FileOutputStream即可。
这个漏洞是存在的,可以从代码层面解决,这种装饰器模式虽然很好,但是对被装饰的对象管理不当就会出现这种情况。


在这个过程中只会发生FileNotFoundException异常,而此时ZipOutputStream对象还没有创建,没有任何流被打开,所以当然是不需要关闭的。所以这段代码没有问题



    /**
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        Test test = new Test();
        test.test1();
        System.out.println(123);
    }

    public void test1() {
      try {
          ArrayList<Double> al = new ArrayList<Double>();
          for (int i = 0; i < 1000000; i++) {
              for (int j = 0; j < 1000000; j++) {
                  al.add(Math.random());
              }
          }
      } catch (Throwable e) {
          System.out.println("catch");
      } finally {
          System.out.println("finally");
      }
  }

输出:

finally
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.lang.Double.valueOf(Unknown Source)
    at qmk.TestAnything.test1(TestAnything.java:50)
    at qmk.TestAnything.main(TestAnything.java:41)

结论:

发生OutOfMemeoryError时,finally可以执行,但是catchmain方法后面的打印语句都没有执行,虚拟机直接奔溃了。

我之前说发生内存溢出时finally不会执行是不对的,事实表明finally是会执行的,特此更正。

@brayden

【热门文章】
【热门文章】