IO 包装流关闭

本文详细解析了Java中IO流的关闭机制,包括装饰模式下流的关闭逻辑及顺序,并通过示例代码验证了不同关闭顺序的效果。
援引了一位网友的解释, 自己稍加改动, 保存留用 :)

(1)JAVA的IO流使用了装饰模式,关闭最外面的流的时候会自动调用被包装的流的close()方吗?

(2)如果按顺序关闭流,是从内层流到外层流关闭还是从外层到内存关闭?



问题(1)解释:

如下例子代码:

FileInputStream is = new FileInputStream(".");
BufferedInputStream bis = new BufferedInputStream(is);
bis.close();


从设计模式上看:
java.io.BufferedInputStream是java.io.InputStream的装饰类。
BufferedInputStream装饰一个 InputStream 使之具有缓冲功能,is要关闭只需要调用最终被装饰出的对象的 close()方法即可,因为它最终会调用真正数据源对象的 close()方法。

BufferedInputStream的close方法中对InputStream进行了关闭,下面是jdk中附带的源代码:
java.io.BufferedInputStream的api:
close
public void close()throws IOException 关闭此输入流并释放与该流关联的所有系统资源。
复制代码
因此,可以只调用外层流的close方法关闭其装饰的内层流,验证例子:


public static void main(String[] args) throws Exception {
FileOutputStream fos = new FileOutputStream("d:\\a.txt");
OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8");
BufferedWriter bw = new BufferedWriter(osw);
bw.write("java IO close test");

bw.close();

}



验证ok


问题(2)解释:如果不想使用(1)方式关闭流,可以逐个关闭流(可能大家比较习惯吧)

如下例子:

public static void main(String[] args) throws Exception {
FileOutputStream fos = new FileOutputStream("d:\\a.txt");
OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8");
BufferedWriter bw = new BufferedWriter(osw);
bw.write("java IO close test");

//从内带外顺序顺序会报异常
fos.close();
osw.close();
bw.close();

}


报出异常:
Exception in thread "main" java.io.IOException: Stream closed
at sun.nio.cs.StreamEncoder.ensureOpen(StreamEncoder.java:26)
at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:99)
at java.io.OutputStreamWriter.write(OutputStreamWriter.java:190)
at java.io.BufferedWriter.flushBuffer(BufferedWriter.java:111)
at java.io.BufferedWriter.close(BufferedWriter.java:246)
at com.my.test.QQ.main(QQ.java:22)


如下例子:

public static void main(String[] args) throws Exception {
FileOutputStream fos = new FileOutputStream("d:\\a.txt");
OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8");
BufferedWriter bw = new BufferedWriter(osw);
bw.write("java IO close test");

// 从外到内顺序关闭ok
bw.close();
osw.close();
fos.close();
}



验证ok
一般情况下是:先打开的后关闭,后打开的先关闭

另一种情况:看依赖关系,如果流a依赖流b,应该先关闭流a,再关闭流b

例如处理流a依赖节点流b,应该先关闭处理流a,再关闭节点流b

当然完全可以只关闭处理流,不用关闭节点流。处理流关闭的时候,会调用其处理的节点流的关闭方法

如果将节点流关闭以后再关闭处理流,会抛出IO异常;
### Java IO关闭顺序与最佳实践 在Java IO操作中,关闭顺序和最佳实践是确保资源正确释放、避免资源泄漏的重要环节。IO通常涉及系统资源(如文件描述符、网络连接等),因此必须显式关闭。 #### 1. 关闭顺序 当使用多个进行嵌套包装时,如使用 `BufferedOutputStream` 包装 `FileOutputStream`,只需关闭最外层的即可。装饰器模式的设计使得关闭最外层时会自动关闭其内部包装[^3]。例如: ```java try (OutputStream out = new BufferedOutputStream(new FileOutputStream("file.txt"))) { out.write("data".getBytes()); } catch (IOException e) { e.printStackTrace(); } ``` 在此例中,`BufferedOutputStream` 被关闭时,其内部的 `FileOutputStream` 也会被自动关闭,无需额外操作。 #### 2. 最佳实践 - **使用 try-with-resources 语句**:Java 7 引入了 try-with-resources 语句,用于自动管理实现了 `AutoCloseable` 接口的资源。此方法确保在 try 块结束后资源被自动关闭,避免资源泄漏[^3]。示例如下: ```java try (FileInputStream fis = new FileInputStream("input.txt"); FileOutputStream fos = new FileOutputStream("output.txt")) { byte[] buffer = new byte[1024]; int length; while ((length = fis.read(buffer)) > 0) { fos.write(buffer, 0, length); } } catch (IOException e) { e.printStackTrace(); } ``` - **避免手动关闭带来的风险**:传统方式通过 `finally` 块手动关闭虽然可行,但容易出错,尤其是在多个同时存在的情况下。手动关闭需要嵌套的异常处理,代码冗长且易漏掉关闭操作[^1]。 - **合理使用装饰器模式**:Java IO 通过装饰器模式提供高度灵活性。在使用包装(如 `BufferedInputStream`、`DataOutputStream`)时,只需关闭最外层即可,系统会自动处理内部关闭[^3]。 - **注意异常处理**:在关闭时可能会抛出 `IOException`,应确保异常被正确捕获并处理,避免程序因异常而中断。使用 try-with-resources 可以简化异常处理逻辑。 - **避免重复关闭**:重复关闭同一个可能导致 `IOException`,因此应确保每个关闭一次。使用 try-with-resources 可以有效避免此类问题。 #### 3. 常见问题与建议 - **关闭顺序错误**:如果先关闭内部,再关闭外部,会导致外部关闭时尝试操作已关闭的内部,从而抛出异常。应始终关闭最外层[^3]。 - **资源泄漏风险**:未正确关闭可能导致文件锁未释放、内存泄漏等问题。使用 try-with-resources 是最安全的方式[^1]。 - **性能优化**:在处理大量数据或高并发场景时,合理使用缓冲(如 `BufferedInputStream`)可以提高性能,同时确保资源及时释放[^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值