JKD源码分析之AutoCloseable

版权声明:此文章为作者原创,转载需要经过作者同意! https://blog.csdn.net/JavaDad/article/details/80985731
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)
如有问题,请留言指正!谢谢!

猜你喜欢

转载自blog.csdn.net/JavaDad/article/details/80985731
今日推荐