Java中的RAII。。。资源处置总是那么丑陋吗?
我刚刚使用了Java文件系统API,并实现了以下用于复制二进制文件的函数。最初的源代码来自Web,但我添加了try/catch/finally子句,以确保在退出函数之前,如果发生错误,缓冲流将被关闭(从而释放操作系统资源) 我缩减了函数以显示模式:Java中的RAII。。。资源处置总是那么丑陋吗?,java,design-patterns,raii,resource-management,Java,Design Patterns,Raii,Resource Management,我刚刚使用了Java文件系统API,并实现了以下用于复制二进制文件的函数。最初的源代码来自Web,但我添加了try/catch/finally子句,以确保在退出函数之前,如果发生错误,缓冲流将被关闭(从而释放操作系统资源) 我缩减了函数以显示模式: public static void copyFile(FileOutputStream oDStream, FileInputStream oSStream) throw etc... { BufferedInputStream oSBuff
public static void copyFile(FileOutputStream oDStream, FileInputStream oSStream) throw etc...
{
BufferedInputStream oSBuffer = new BufferedInputStream(oSStream, 4096);
BufferedOutputStream oDBuffer = new BufferedOutputStream(oDStream, 4096);
try
{
try
{
int c;
while((c = oSBuffer.read()) != -1) // could throw a IOException
{
oDBuffer.write(c); // could throw a IOException
}
}
finally
{
oDBuffer.close(); // could throw a IOException
}
}
finally
{
oSBuffer.close(); // could throw a IOException
}
}
据我所知,我不能将两个close()
放在finally子句中,因为第一个close()
很可能抛出,然后第二个就不会执行
我知道C#具有Dispose模式,该模式可以使用关键字处理此问题
我甚至更清楚C++代码会是什么样子(使用java类API):
我遗漏了一些东西,或者我真的必须用Java生成丑陋和臃肿的代码,只是为了处理缓冲流的close()
方法中的异常
(请告诉我我哪里错了…)
编辑:是我,还是在更新此页面时,我看到问题和所有答案在几分钟内减少了一个百分点?有人在还押anonymous时玩得太开心了吗?
编辑2:McDowell提供了一个非常有趣的链接,我觉得我必须在这里提到:
编辑3:根据McDowell的链接,我无意中发现了一个Java7的方案,其模式类似于C#using模式:。我的问题被明确地描述了。显然,即使使用Java7do
,问题仍然存在。不幸的是,这种类型的代码在Java中往往有点臃肿
顺便说一下,如果对oSBuffer.read或oDBuffer.write的某个调用引发异常,那么您可能希望让该异常渗透到调用层次结构中
在finally子句中对close()进行无保护调用将导致原始异常被close()调用生成的异常替换。换句话说,Faily close()-方法可能会隐藏由read()或write()生成的原始异常。因此,我认为您希望忽略close()引发的异常,前提是其他方法没有引发异常
我通常通过在内部try中包含一个显式的close调用来解决这个问题:
try {
while (...) {
read...
write...
}
oSBuffer.close(); // exception NOT ignored here
oDBuffer.close(); // exception NOT ignored here
} finally {
silentClose(oSBuffer); // exception ignored here
silentClose(oDBuffer); // exception ignored here
}
试一试{
而(…){
阅读
写
}
oSBuffer.close();//此处不忽略异常
oDBuffer.close();//此处不忽略异常
}最后{
silentClose(oSBuffer);//此处忽略异常
silentClose(oDBuffer);//此处忽略异常
}
静态空隙silentClose(可关闭c){
试一试{
c、 close();
}捕获(IOIE){
//忽略;调用方必须有此意图
}
}
最后,为了提高性能,代码可能应该使用缓冲区(每次读/写多个字节)。不能通过数字来证明这一点,但是更少的调用应该比在上面添加缓冲流更有效。在大多数情况下,对于Java 6及更低版本,try/finally模式是处理流的正确方法
有些人主张默默地关闭溪流。出于以下原因,请小心操作:
Java 7引入了try with resources:
类型将自动关闭:
public class Foo {
public static void main(String[] args) {
class CloseTest implements AutoCloseable {
public void close() {
System.out.println("Close");
}
}
try (CloseTest closeable = new CloseTest()) {}
}
}
这是有问题的,但是你在网上发现的代码非常糟糕
关闭缓冲区流将关闭下面的流。你真的不想那样做。您所要做的就是刷新输出流。另外,为文件指定底层流也没有意义。性能很差,因为您一次只复制一个字节(实际上,如果您使用java.io,use可以使用transferTo/transferFrom,后者速度更快)。当我们讨论这个问题时,变量名很糟糕。因此:
public static void copy(
InputStream in, OutputStream out
) throw IOException {
byte[] buff = new byte[8192];
for (;;) {
int len = in.read(buff);
if (len == -1) {
break;
}
out.write(buff, 0, len);
}
}
如果您发现自己经常使用try finally,那么您可以使用“executearound”成语来解决这个问题
在我看来:Java应该有某种方式来关闭作用域末尾的资源。我建议添加private
作为一元后缀运算符,以在封闭块的末尾结束。是的,java就是这样工作的。还有控制反转-对象的用户必须知道如何清理对象,而不是在对象自身清理之后清理对象本身。不幸的是,这会导致大量清理代码分散在java代码中
C#具有“using”关键字,当对象超出范围时自动调用Dispose。Java没有这样的功能。对于复制文件等常见IO任务,上面所示的代码正在重新发明轮子。不幸的是,JDK没有提供任何更高级别的实用程序,但apache commons io提供
例如,包含用于处理文件和目录(包括复制)的各种实用程序方法。另一方面,如果您确实需要在JDK中使用IO支持,则包含一组closequity()方法,这些方法可以关闭读卡器、写入器、流等,而不会引发异常。如果您以这种方式静默关闭,那么如果close引发异常,您的代码不会进行错误处理。许多流(如BufferedOutputStream)在close上写入数据。McDowell:是的,如果close()抛出异常,它应该捕获异常。请注意,close()-调用首先在try块中进行!最后一个块是确保在任何方法引发异常时进行清理。对吗?(注意,我忘记了在第一个post版本中关闭一个缓冲区。)BufferedOutputStream有点问题。我赞成显式同花顺(在非例外情况下),但你必须记住它。IIRC,close在JavaSE1.6之前已经中断了异常处理。我知道close()中的异常非常重要。但是,如果close()-调用是在最后一次调用write()之后进行的,那么这不应该确保正确跟踪异常吗?麦克道尔,请确认这里是否有瑕疵;然后我会自己撤销代码,但我真的很想知道。:)有些加密/压缩有点令人讨厌。不仅
/** transcodes text file from one encoding to another */
public static void transcode(File source, Charset srcEncoding,
File target, Charset tgtEncoding)
throws IOException {
try (InputStream in = new FileInputStream(source);
Reader reader = new InputStreamReader(in, srcEncoding);
OutputStream out = new FileOutputStream(target);
Writer writer = new OutputStreamWriter(out, tgtEncoding)) {
char[] buffer = new char[1024];
int r;
while ((r = reader.read(buffer)) != -1) {
writer.write(buffer, 0, r);
}
}
}
public class Foo {
public static void main(String[] args) {
class CloseTest implements AutoCloseable {
public void close() {
System.out.println("Close");
}
}
try (CloseTest closeable = new CloseTest()) {}
}
}
public static void copy(
InputStream in, OutputStream out
) throw IOException {
byte[] buff = new byte[8192];
for (;;) {
int len = in.read(buff);
if (len == -1) {
break;
}
out.write(buff, 0, len);
}
}