下面是一些java代码。
public class SomeClass {
private Connection connection;
public SomeClass(Connection c) {
connection = c;
}
public void someWork(){
Connection c;
try {
// do something
} catch (Exception e) {
// some exception code
} finally {
if (conn != null){
try {c.close();} catch (Exception e) {}
}
}
}
}
但是我不喜欢代码
if (conn != null){
try {c.close();} catch (Exception e) {}
}
所以我认为code
...catch (Exception e) {
// some exception code
} finally {
c = null;
}
但我看到'流对象未被垃圾收集'
我不会在finally块中使用try-catch语句。请给我另一条路。
创建一个静态实用程序方法:
private static void closeQuietly(final Connection conn)
{
if (conn == null)
return;
try {
conn.close();
} catch (WhateverException ignored) {
}
}
然后conn = whatever(); // create it there, not in the try block
try {
// work
} catch (WhateverException e) {
// whatever
} finally {
closeQuietly(conn);
}
但是最终,你还是会在finally代码块中使用try/catch。
注意捕获Exception
是一个坏主意。捕获由conn.close()
专门引发的异常。捕获Exception
意味着捕获所有RuntimeException
;这是一件坏事。
如果使用Guava,还可以使用Closeables.closeQuietly()
,它的功能与上面的代码基本相同。你也可以看看Closer
,这是非常有趣的。
最后(双关语):如果您使用Java 7,请使用try-with-resources语句来代替(参见AutoCloseable
):
try (
conn = whatever();
) {
// ...
}
另一种解决方法是将方法内部的catch全部删除:
public void someWork() throws SQLException{
Connection c;
try {
// do something
} finally {
if (conn != null){
c.close();
}
}
}
然后在调用someWork
方法的主方法之外的方法中进行错误检查或者将其包装在调用someWork()
的另一个方法中:
public void callSomeWork(){
try{
someWork();
}catch(SQLException ex){
//handle SQL error here
}
}
您可能不喜欢这些代码,但在这种情况下它是必要的。(尽管您可以将代码包装在一个方法中,例如@fge的closeQuietly
方法。)
但是还有一个更根本的问题。更改someWork
以关闭finally
块中的连接不足以防止泄漏…在这种情况下!!
为什么?
考虑一下:
SomeClass sc = new SomeClass(createConnection());
// do some stuff
sc.someWork();
Q1:如果在"做一些事情"代码中抛出异常会发生什么?
Q2:如果在创建/构造SomeClass
时抛出异常会发生什么?(注意:即使使用最简单的构造函数,这也是可能的。例如,可以通过new
操作抛出OOME…)
答案:Connection对象泄露…两个垒都有!
解决方法是在try/catch/finally语句内部或之前分配资源(例如Connection对象);例如
Connection c = createConnection();
try {
// do something
} catch (SomeException e) {
// some exception code
} finally {
if (c != null){
try {c.close();} catch (SomeException e) {}
}
}
在Java 7中你也可以这样写:
try (Connection c = createConnection()) {
// do something
} catch (SomeException e) {
// some exception code
}
条件是Connection
实现了AutoCloseable
接口。Java 7版本是一个更复杂的实现的"语法糖",它负责检查null
,调用close()
并智能地处理任何产生的异常。(JLS给出了"try with resource"实际做什么的细节。)
记录:
问题不是垃圾收集/收集。问题是
Connection
对象需要关闭。分配
null
不会导致对象被关闭。它甚至不会导致垃圾回收。充其量,它可以使对象符合进行垃圾收集…抓
Exception
真是个坏主意。阅读这篇文章:需要权威的来源来解释为什么你不应该抛出或捕获java.lang.Exception
如果未关闭并设置为NULL,则可能会离开僵尸连接。
从Java 7开始,它应该是这个
try (Connection conn = DriverManager.getConnection(props)) {
...
}
Connection conn = DriverManager.getConnection();
try {
...
} finally {
conn.close();
}
如果无效且未关闭,请确保所有方法总是获得新的打开连接
你唯一需要知道的是,任何时候你得到一个连接,你必须关闭它(因此最后块),否则你可能会让该连接在你的应用程序中打开,最终耗尽可用的连接。
让其他人做try-catch的东西,并使用Apache Commons IOUtils
其中一个closeQuietly()
-方法(例如这个)应该是你所需要的:
等价于InputStream.close(),除了任何异常将被忽略。这通常用于finally块。