AutoCloseable.java
通過測試案例分析AutoCloseable功能,測試代碼如下
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.Test;
import java.io.IOException;
public class AutoCloseableTest {
private static final Logger log = LogManager.getLogger(AutoCloseableTest.class);
/**
* try-with-resource 寫法
* 問題1:實現AutoCloseable接口
* 如果方法沒有處理異常而是將異常拋出,由調用者處理,那麼
* 調用者捕獲的是最先異常的地方(RuntimeException),同時也會指出抑制的異常信息(Suppressed: java.io.IOException)
* 異常信息:
* java.lang.RuntimeException: Resource 邏輯執行異常。。。
*
* at Resource.doSomething(AutoCloseableTest.java:123)
* ......
* Suppressed: java.io.IOException: Resource 關閉資源出現異常。。。
* at Resource.close(AutoCloseableTest.java:131)
* at AutoCloseableTest.newStyle(AutoCloseableTest.java:23)
* ... 22 more
*
* @throws Exception
*/
@Test
public void newStyle() throws Exception {
try (Resource resource = new Resource();
HttpConn httpConn = new HttpConn()) {
resource.doSomething();
httpConn.doSomething();
}
}
/**
* 總結:
* 1. try-with-resource 寫法與 AutoCloseable 接口的使用;
* 2. 資源關閉的順序與資源定義的順序相反,所以定義時需要考慮關閉的先後順序來決定定義的先後順序;
* 3. 無論是否拋出異常,(資源必須存在,如果不存在就沒有所謂關閉)系統都會自動關閉實現AutoCloseable接口的資源
* 4. 使用try-catch-resources結構,try-block塊拋出異常後先執行所有資源(try的()中聲明的)
* 的close方法然後在執行catch裏面的代碼然後纔是finally.
* @throws Exception
*/
@Test
public void newStyle1() {
try (Resource resource = new Resource();
HttpConn httpConn = new HttpConn()) {
resource.doSomething();
httpConn.doSomething();
} catch (IOException e) {
log.info("try-with-resource執行到IOException...");
e.printStackTrace();
} catch (Exception e) {
log.info("try-with-resource執行到Exception...");
e.printStackTrace();
} finally {
log.info("try-with-resource執行到finally塊...");
}
}
/**
* try-catch-finally 寫法
* 問題2:
* 如果方法沒有處理異常而是將異常拋出,由調用者處理,那麼
* 本來代碼應該在 resource.doSomething();的時候拋出 RuntimeException,
* 但是查看結果發現finally中拋出的異常IOException,這就導致根據異常信息定位不到準確的異常位置。
* 執行結果:
* [11:28:22:964] [INFO] - Resource1.init(AutoCloseableTest.java:200) - Resource1初始化。。。
* [11:28:22:968] [INFO] - HttpConn1.init(AutoCloseableTest.java:235) - HttpConn1初始化。。。
* [11:28:22:968] [INFO] - Resource1.doSomething(AutoCloseableTest.java:207) - Resource1邏輯執行。。。
* [11:28:22:969] [INFO] - HttpConn1.close(AutoCloseableTest.java:249) - HttpConn1 資源關閉中。。。
* [11:28:22:969] [INFO] - Resource1.close(AutoCloseableTest.java:214) - Resource1 資源關閉中。。。
*
* java.io.IOException: Resource1 關閉資源出現異常。。。
*
* at Resource1.close(AutoCloseableTest.java:216)
* at AutoCloseableTest.oldStyle(AutoCloseableTest.java:75)
* @throws Exception
*/
@Test
public void oldStyle() throws Exception {
Resource1 resource = null;
HttpConn1 httpConn = null;
try {
resource = new Resource1();
httpConn = new HttpConn1();
resource.doSomething();
httpConn.doSomething();
} finally {
if (httpConn != null) {
httpConn.close();
}
if (resource != null) {
resource.close();
}
}
}
/**
* try-catch-finally 寫法
* 弊端:可能忘了關閉系統資源,或者關閉的順序不對,代碼不夠優雅等等
*/
@Test
public void oldStyle1() {
Resource1 resource = null;
HttpConn1 httpConn = null;
try {
resource = new Resource1();
httpConn = new HttpConn1();
resource.doSomething();
httpConn.doSomething();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (httpConn != null) {
try {
httpConn.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (resource != null) {
try {
resource.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
class Resource implements AutoCloseable {
private static final Logger log = LogManager.getLogger(Resource.class);
private boolean flag1 = false;
private boolean flag2 = false;
private boolean flag3 = false;
public Resource() throws Exception {
init();
}
public void init() throws Exception {
log.info(this.getClass().getName() + "初始化。。。");
if (flag1) {
throw new Exception(this.getClass().getName() + " 初始化異常。。。");
}
}
public void doSomething() throws RuntimeException {
log.info(this.getClass().getName() + "邏輯執行。。。");
if (flag2) {
throw new RuntimeException(this.getClass().getName() + " 邏輯執行異常。。。");
}
}
@Override
public void close() throws IOException {
log.info(this.getClass().getName() + " 資源關閉中。。。");
if (flag3) {
throw new IOException(this.getClass().getName() + " 關閉資源出現異常。。。");
}
}
}
class HttpConn implements AutoCloseable {
private static final Logger log = LogManager.getLogger(HttpConn.class);
private boolean flag1 = false;
private boolean flag2 = false;
private boolean flag3 = false;
public HttpConn() throws Exception {
init();
}
public void init() throws Exception {
log.info(this.getClass().getName() + "初始化。。。");
if (flag1) {
throw new Exception(this.getClass().getName() + " 初始化異常。。。");
}
}
public void doSomething() throws RuntimeException {
log.info(this.getClass().getName() + "邏輯執行。。。");
if (flag2) {
throw new RuntimeException(this.getClass().getName() + " 邏輯執行異常。。。");
}
}
@Override
public void close() throws IOException {
log.info(this.getClass().getName() + " 資源關閉中。。。");
if (flag3) {
throw new IOException(this.getClass().getName() + " 關閉資源出現異常。。。");
}
}
}
class Resource1 {
private static final Logger log = LogManager.getLogger(Resource1.class);
private boolean flag1 = false;
private boolean flag2 = false;
private boolean flag3 = false;
public Resource1() throws Exception {
init();
}
public void init() throws Exception {
log.info(this.getClass().getName() + "初始化。。。");
if (flag1) {
throw new Exception(this.getClass().getName() + " 初始化異常。。。");
}
}
public void doSomething() throws RuntimeException {
log.info(this.getClass().getName() + "邏輯執行。。。");
if (flag2) {
throw new RuntimeException(this.getClass().getName() + " 邏輯執行異常。。。");
}
}
public void close() throws IOException {
log.info(this.getClass().getName() + " 資源關閉中。。。");
if (flag3) {
throw new IOException(this.getClass().getName() + " 關閉資源出現異常。。。");
}
}
}
class HttpConn1 {
private static final Logger log = LogManager.getLogger(HttpConn1.class);
private boolean flag1 = false;
private boolean flag2 = false;
private boolean flag3 = false;
public HttpConn1() throws Exception {
init();
}
public void init() throws Exception {
log.info(this.getClass().getName() + "初始化。。。");
if (flag1) {
throw new Exception(this.getClass().getName() + " 初始化異常。。。");
}
}
public void doSomething() throws RuntimeException {
log.info(this.getClass().getName() + "邏輯執行。。。");
if (flag2) {
throw new RuntimeException(this.getClass().getName() + " 邏輯執行異常。。。");
}
}
public void close() throws IOException {
log.info(this.getClass().getName() + " 資源關閉中。。。");
if (flag3) {
throw new IOException(this.getClass().getName() + " 關閉資源出現異常。。。");
}
}
}
說明:沒有提及的flag均爲false1. 驗證問題1
修改Resource的flag2和flag3爲true,執行newStyle()結果如下
[11:59:13:149] [INFO] - Resource.init(AutoCloseableTest.java:150) - Resource初始化。。。
[11:59:13:154] [INFO] - HttpConn.init(AutoCloseableTest.java:186) - HttpConn初始化。。。
[11:59:13:154] [INFO] - Resource.doSomething(AutoCloseableTest.java:157) - Resource邏輯執行。。。
[11:59:13:154] [INFO] - HttpConn.close(AutoCloseableTest.java:201) - HttpConn 資源關閉中。。。
[11:59:13:154] [INFO] - Resource.close(AutoCloseableTest.java:165) - Resource 資源關閉中。。。
java.lang.RuntimeException: Resource 邏輯執行異常。。。
at Resource.doSomething(AutoCloseableTest.java:159)
at AutoCloseableTest.newStyle(AutoCloseableTest.java:32)
Suppressed: java.io.IOException: HttpConn 關閉資源出現異常。。。
at HttpConn.close(AutoCloseableTest.java:203)
at AutoCloseableTest.newStyle(AutoCloseableTest.java:34)
... 22 more
Suppressed: java.io.IOException: Resource 關閉資源出現異常。。。
at Resource.close(AutoCloseableTest.java:167)
at AutoCloseableTest.newStyle(AutoCloseableTest.java:34)
... 22 more
2. 驗證:資源關閉的順序與資源定義的順序相反,所以定義時需要考慮關閉的先後順序來決定定義的先後順序;
修改Resource和HttpConf的flag均爲false,執行 newStyle1() 結果如下
[12:03:22:601] [INFO] - Resource.init(AutoCloseableTest.java:150) - Resource初始化。。。
[12:03:22:606] [INFO] - HttpConn.init(AutoCloseableTest.java:186) - HttpConn初始化。。。
[12:03:22:606] [INFO] - Resource.doSomething(AutoCloseableTest.java:157) - Resource邏輯執行。。。
[12:03:22:606] [INFO] - HttpConn.doSomething(AutoCloseableTest.java:193) - HttpConn邏輯執行。。。
[12:03:22:606] [INFO] - HttpConn.close(AutoCloseableTest.java:201) - HttpConn 資源關閉中。。。
[12:03:22:607] [INFO] - Resource.close(AutoCloseableTest.java:165) - Resource 資源關閉中。。。
[12:03:22:607] [INFO] - AutoCloseableTest.newStyle1(AutoCloseableTest.java:59) - try-with-resource執行到finally塊...
3. 驗證:無論是否拋出異常,(資源必須存在,如果不存在就沒有所謂關閉)系統都會自動關閉實現AutoCloseable接口的資源
1)修改Resource的flag1=true,執行newStyle1() 結果如下
注意沒有關閉資源的操作
java.lang.Exception: Resource 初始化異常。。。
at Resource.init(AutoCloseableTest.java:152)
at Resource.<init>(AutoCloseableTest.java:146)
at AutoCloseableTest.newStyle1(AutoCloseableTest.java:48)
[12:06:37:896] [INFO] - Resource.init(AutoCloseableTest.java:150) - Resource初始化。。。
[12:06:37:900] [INFO] - AutoCloseableTest.newStyle1(AutoCloseableTest.java:56) - try-with-resource執行到Exception...
[12:06:37:901] [INFO] - AutoCloseableTest.newStyle1(AutoCloseableTest.java:59) - try-with-resource執行到finally塊...
2)修改Resource的flag1=false,修改HttpConf的flag1=true,執行newStyle1() 結果如下
注意這裏只有關閉Resource的操作,並且可以驗證ry-block出現異常時的執行順序
[12:08:32:258] [INFO] - Resource.init(AutoCloseableTest.java:150) - Resource初始化。。。
[12:08:32:262] [INFO] - HttpConn.init(AutoCloseableTest.java:186) - HttpConn初始化。。。
[12:08:32:263] [INFO] - Resource.close(AutoCloseableTest.java:165) - Resource 資源關閉中。。。
[12:08:32:263] [INFO] - AutoCloseableTest.newStyle1(AutoCloseableTest.java:56) - try-with-resource執行到Exception...
java.lang.Exception: HttpConn 初始化異常。。。
at HttpConn.init(AutoCloseableTest.java:188)
at HttpConn.<init>(AutoCloseableTest.java:182)
at AutoCloseableTest.newStyle1(AutoCloseableTest.java:49)
[12:08:32:264] [INFO] - AutoCloseableTest.newStyle1(AutoCloseableTest.java:59) - try-with-resource執行到finally塊...
4. 驗證try-catch-finally的異常抑制問題
修改 Resource1的flag2和flag3爲true,執行oldStyle() 結果如下
本應該出現 RuntimeException,但是隻拋出了IOException,這就是異常抑制問題。
[14:29:57:603] [INFO] - Resource1.init(AutoCloseableTest.java:221) - Resource1初始化。。。
[14:29:57:607] [INFO] - HttpConn1.init(AutoCloseableTest.java:256) - HttpConn1初始化。。。
[14:29:57:608] [INFO] - Resource1.doSomething(AutoCloseableTest.java:228) - Resource1邏輯執行。。。
[14:29:57:608] [INFO] - HttpConn1.close(AutoCloseableTest.java:270) - HttpConn1 資源關閉中。。。
[14:29:57:608] [INFO] - Resource1.close(AutoCloseableTest.java:235) - Resource1 資源關閉中。。。
java.io.IOException: Resource1 關閉資源出現異常。。。
at Resource1.close(AutoCloseableTest.java:237)
at AutoCloseableTest.oldStyle(AutoCloseableTest.java:96)
如有問題,請留言指正!謝謝!