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均为false
1. 验证问题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)
如有问题,请留言指正!谢谢!