在 Java 中对资源使用 try 是不是安全 - 它是不是检查可关闭对象是不是不为空,是不是在尝试关闭它时捕获异常
Posted
技术标签:
【中文标题】在 Java 中对资源使用 try 是不是安全 - 它是不是检查可关闭对象是不是不为空,是不是在尝试关闭它时捕获异常【英文标题】:Is it safe to use try with resources in Java - does it check if the closeable is not null and does it catch exceptions while trying to close it在 Java 中对资源使用 try 是否安全 - 它是否检查可关闭对象是否不为空,是否在尝试关闭它时捕获异常 【发布时间】:2017-10-30 16:02:10 【问题描述】:在 android 中使用 Java 的 try with resources
是否安全 - 它是否检查可关闭对象是否不为空,是否在尝试关闭它时捕获 close
抛出的异常?
如果我转换这个:
try
inChannel.transferTo(0, inChannel.size(), outChannel);
finally
if (inChannel != null)
inChannel.close();
if (outChannel != null)
outChannel.close();
到
try (FileChannel inChannel = new FileInputStream(src).getChannel();
FileChannel outChannel = new FileOutputStream(dst).getChannel())
inChannel.transferTo(0, inChannel.size(), outChannel);
它会在尝试调用close
之前检查inChannel
和outChannel
是否不为空吗?
另外,在这里使用 try 与资源是否安全:
try
cursor = context.getContentResolver().query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
new String[]MediaStore.Images.Media.DATA,
MediaStore.Images.Media._ID + " =? ",
new String[]"" + imageIdInMediaStore,
null);
if (cursor != null && cursor.getCount() > 0)
cursor.moveToFirst();
return cursor.getString(0);
else
return "";
catch (Exception e)
return "";
finally
if (cursor != null && !cursor.isClosed())
cursor.close();
cursor = null;
finally
块对 !cursor.isClosed()
进行了重要检查 - try with resources
会弄清楚如何做到这一点,还是我应该保持不变?
【问题讨论】:
***.com/questions/23611940/… 为什么说cursor.isClosed()
是“重要的检查”?只需致电cursor.close()
。 close()
方法要求是幂等的,所以 isClosed()
调用是多余的。在已经关闭的 cursor
上调用 close()
没有任何作用。正如javadoc 所说:如果流已关闭,则调用此方法无效。
为什么它们会为空?他们怎么可能是空的?
【参考方案1】:
找出自己最简单的方法是编写一个测试类,简单地告诉你它:
import junit.framework.TestCase;
public class __Test_AutoClosable extends TestCase
public void testWithNull() throws Exception
try (TestClosable tc = getNull())
assertNull("check existance of closable", tc);
public void testNonNullWithoutException() throws Exception
try (TestClosable tc = getNotNull(false))
assertNotNull("check existance of closable", tc);
public void testNonNullWithException() throws Exception
try (TestClosable tc = getNotNull(true))
assertNotNull("check existance of closable", tc);
catch(Exception e)
assertEquals("check message", "Dummy Exception", e.getMessage());
TestClosable getNull()
return null;
TestClosable getNotNull(boolean throwException)
return new TestClosable(throwException);
static class TestClosable implements AutoCloseable
private boolean throwException;
TestClosable(boolean throwException)
this.throwException = throwException;
@Override
public void close() throws Exception
if (throwException)
throw new Exception("Dummy Exception");
这个类运行没有错误,所以你的问题的答案是:
是的,null
作为响应是可能的,它在关闭阶段检查
close
上抛出的异常不会被捕获,但必须自己捕获和处理
如果您稍微考虑一下,那是完全有道理的。不支持 null 会使整个构造变得毫无用处,简单地忽略关闭,即使它们是隐式完成的,也是一件坏事。
编辑:上面的测试用例是“真实”测试的结果,例如被添加到您的源代码库中,您实际上应该检查是否已调用 autoclosable 中的关闭方法。
关于您的第二个问题:如果在某些情况下您不希望关闭发生,则 1:1 更改为 try-with-resources 不起作用,但您可以执行以下操作:
public class CursorClosable implements AutoClosable
private Cursor cursor;
public CursorClosable(Cursor cursor)
this.cursor = cursor;
public Cursor getCursor()
return cursor;
@Override
public void close() throws Exception
if (cursor != null && !cursor.isClosed())
cursor.close();
您的新代码将如下所示:
try (Cursor cursorac = new CursorClosable(context.getContentResolver().query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
new String[]MediaStore.Images.Media.DATA,
MediaStore.Images.Media._ID + " =? ",
new String[]"" + imageIdInMediaStore,
null))
Cursor cursor = cursorac.getCursor();
if (cursor != null && cursor.getCount() > 0)
cursor.moveToFirst();
return cursor.getString(0);
else
return "";
catch (Exception e)
return "";
我不确定检查isClosed
是否真的有必要,所以对于这个特定示例,您可能不需要这种“hack”,但对于您不想关闭的其他示例,它仍然有效资源。
【讨论】:
但是,如果try
块抛出异常,然后 close
调用也抛出异常,则 close
异常不会覆盖/替换第一个异常,而是附加将第一个异常作为 suppressed 异常。见The Java™ Tutorials - The try-with-resources Statement - Suppressed Exceptions。
谢谢。因此,如果try with resources
如果close
方法可能引发异常,或者如果有try with resources
不知道的其他检查(例如if !cursor.isClosed()
?
@K.R.只要资源对象实现Closeable
或AutoCloseable
,您当然可以受益。像isClosed()
这样的调用是不必要的,因为try-with-resources 块应该是唯一一个关闭对象的方法。此外,close
方法应该是幂等的,这意味着应该忽略重复调用。
@Lothar 在更新的答案中,CursorClosable
,cursor
变量不能为空。块内的所有代码都必须将cursor
更改为cursor.getCursor()
。不需要添加CursorClosable
,因为isClosed()
调用是多余的,因为close()
必须是idempotent。
@idempotent 我将 try-with-resource-block 中的结果名称更改为其他名称,并在实际块中调用 getCursor
来解决一个问题。另一点是有效的,但我尽量保持原始问题的代码不变,以便能够看到哪些变化和哪些保持不变(但后来提到实际示例不是一个好例子)。跨度>
以上是关于在 Java 中对资源使用 try 是不是安全 - 它是不是检查可关闭对象是不是不为空,是不是在尝试关闭它时捕获异常的主要内容,如果未能解决你的问题,请参考以下文章
在java中对每个方法使用一个大的try-catch是一种已知的好习惯吗? [关闭]
为要由 java 的 try with resources 语句管理的资源声明自变量
Java 代码字节:足智多谋的 Try-With-Resources