对IO流关闭的思考

流必须要关闭的原因

java相对C,C++来说不需要手动释放内存,在对象引用被消除之后,正常情况下内存资源是会被垃圾回收,那么在使用完IO流之后为什么需要手动关闭.
这是为了回收系统资源,比如释放占用的端口,文件句柄,网络操作数据库应用等.对Unix系统来说,所有的资源都可以抽象成文件,所以可以通过lsof来观察。

看下面这个例子,我们创建许多的IO流但是不关闭

public class Hello {
    public static void main(String[] args) throws Exception {
        for (int i = 0; i < 100; i++) {
            FileInputStream is = new FileInputStream("/tmp/data/data"+i);
            byte[] bs = new byte[512];
            int len;
            while ((len = is.read(bs)) != -1) ;
            Thread.sleep(1000L);
        }
        Thread.sleep(10000L);
    }
}

运行这个程序之后,使用losf命令查看相关的文件句柄,会发现文件句柄始终在增长,当积累到一定时间之后会出现too many open files错误

如何关闭流

在java7以前流的关闭比较繁琐,我们使用try-finally块进行关闭操作,同时还要考虑流关闭过程中可能的异常

InputStream is = null;
OutputStream os = null;
try{
   // ...
}
finally{
  if(is != null)
     try{
       is.close();
     }
     catch(IOException e){}

  if(os != null)
     try{
       os.close()
     }
     catch(IOException e){}
}

在java7以后,我们可以使用try-with-resources,将需要关闭的流对象放在try的()中创建,需要注意的是只有实现Closeable接口的对象才可以放在这里创建

try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));) {
    String s;
    while ((s = reader.readLine()) != null) {
        if (s.equalsIgnoreCase("quit")){
            break;
        }
        System.out.println(s.toUpperCase());
    }
} catch (Exception e) {
    e.printStackTrace();
}

包装流的关闭

包装流关闭的时候会自动调用被包装的流的关闭方法

看下面这个例子
打开的文件句柄必须要关闭,但是这个例子是例外,从控制台中读取字符串并打印

try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));) {
    String s;
    while ((s = reader.readLine()) != null) {
        if (s.equalsIgnoreCase("quit")){
            break;
        }
        System.out.println(s.toUpperCase());
    }
} catch (Exception e) {
    e.printStackTrace();
}

这里使用了try-with-resources的写法(后面会具体的讲到)自动关闭reader对象,但是这里需要注意,BufferedReader包装的是System.in流,这个流是java的标准流用于接收键盘数据,如果这里讲System.in流关闭,那么其它的程序可能会出错,那么我们究竟需要关闭哪些流呢?统一的原则是 谁创建谁销毁,如果我们必须要在关闭不是自己创建的流对象,一定要通过文档告知接口的调用方

关闭包装流与被包装流的时候有没有顺序视情况而定

我们在使用缓存输出流的情况下,必须要优先关闭缓存输出流否则抛出异常.原因很简单,看下面这个例子,这里为了方便没有规范的关闭流

 FileOutputStream fos = new FileOutputStream(filepath);
 OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8");
 BufferedWriter bw = new BufferedWriter(osw);
 bw.write("java IO close test");
 // 从内带外顺序顺序会报异常
 fos.close();
 osw.close();
 bw.close();

缓存输出流的作用是将输出数据先缓存起来,等待缓存区满了之后一次性输出来提高通道利用率,在调用缓存输出流的close方法的时候,会调用被装饰对象的write方法,如果被装饰对象被提前关闭了,那么自然就抛出异常

所以上面的例子修改为

bw.close();
fos.close();
osw.close();

如果没有涉及到缓存输出流那么就无所谓关闭顺序了

FileOutputStream fileOutputStream = new FileOutputStream("c:\\a.txt");
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);

bufferedOutputStream.write("test write something".getBytes());
bufferedOutputStream.flush();

fileOutputStream.close();//先关闭被包装流
bufferedOutputStream.close();
  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值