今天调试了一下Tomcat关闭代码,在执行关闭时会调用Catalina
对象的stopServer
方法,代码如下:
|
|
啥意思呢,就是在执行stopServer
方法时通过Socket
来发送一个SHUTDOWN
字符串到指定Tomcat的Server监听端口(默认为8005),来告诉Tomcat要执行关闭操作。
接收这个字符串的代码在StandardServer
的await
方法中,代码如下:
|
|
注意看下第41行的代码ch = stream.read()
,这个用来接收一个字符。再看下第46行,说明已经接收完毕,通常应该ch
应该是-1,然后break。
但在关闭时断点如果执行完stream.flush()
后,await
方法在接收最后一个字符的时候会一直等待,直到timeOut指定的时间,然后会报如下异常:
|
|
这说明这个socket并未关闭,回过头来看stopServer
中的代码,发现在try-catch
中并未显式关闭socket
和stream
,如果断点再走一步的话,在await
中的接收就变的正常了,说明在try
语句块中执行完毕后自动关闭了socket
和stream
。
这种写法还真是第一次见,孤陋寡闻了。。。
其实,这是Java7中提供的一个新的异常处理机制,它能够很容易地关闭在try-catch语句块中使用的资源。
还有一个名字,叫做try-with-resources
旧的代码风格
在Java7以前,代码中使用的资源需要被明确地关闭,这个在写的时候就会有些繁琐,例如下面的代码:
|
|
可以看到,上面代码在执行new FileInputStream("test.txt")
、in.read()
和in.close()
时都可能抛出异常。
那么到这里可以分析一下,如果在try
语句块中抛出了异常,finally
语句块仍然会执行,然而finally
语句块在执行in.close()
时也可能会抛出异常。
这时问题来了,如果try
语句块中抛出了异常,finally
语句块也抛出了异常,那么到底是哪个异常会在方法返回时向外传播?
其实在上面的代码中,如果都抛出异常,则在finally
语句块中抛出的异常会向外传播,try
语句块中的异常被抑制了。
try-with-resources代码风格
在Java7之后,上面的代码还可以这么写:
|
|
上面代码只是把InputStream放到了try
语句后面的小括号中来声明创建一个FileInputStream
对象,在try
语句块运行结束时会对FileInputStream
对象自动进行关闭。
为什么会这样?
因为FileInputStream
实现了java.lang.AutoCloseable
接口,可以看下对应的类结构:

所有实现了java.lang.AutoCloseable
接口的类都可以在try-with-resources
结构中使用。
那么再考虑一下之前提到过的问题,如果这时对FileInputStream
对象自动关闭(会调用close方法)时抛出了异常,并且in.read()
也抛出了异常,那么在方法执行完毕时,in.read()
抛出的异常会向外传播,FileInputStream
对象关闭时抛出的异常将被抑制。这与之前旧的代码风格的异常抛出方式正好相反。
try-with-resources使用多个资源
在try-with-resources
中可以使用多个资源,而且多个资源都能被自动关闭,代码如下:
|
|
这里创建了两个资源:FileInputStream
和BufferedInputStream
。当try
语句块运行结束时,这两个资源都会被自动关闭,而且关闭的顺序与创建的顺序相反(先关闭BufferedInputStream
,后关闭FileInputStream
),稍后会验证。
AutoCloseable接口
先来查看一下java.lang.AutoCloseable
接口的定义:
|
|
可以看到,只有一个close()
方法。
AutoCloseable接口的实现
下面自定义一个类,来实现这个接口:
|
|
该类实现了AutoCloseable
接口,下面来使用这个类:
|
|
运行查看结果:
|
|
验证多个资源的关闭
代码如下:
|
|
运行后的输出结果如下:
|
|
可以看到,两个资源都被自动关闭了,而且顺序与创建的顺序相反。
验证被抑制的异常
|
|
上面代码定义了两个异常,在main
方法中捕获并输出异常栈,结果如下:
|
|
可见,try-with-resources
中自动关闭时调用close()
方法抛出的异常被抑制了,捕获到的是say()
方法抛出的异常MyException1
。
验证自动关闭和finally的执行顺序
从上面代码可以看出,先输出了异常的信息,然后才输出I'm in finally
,可见,在finally
语句块执行之前自动关闭就已经被执行了。
总结
从以上的分析可以看出,try-with-resources
风格可以实现以下几种情况:
- 任何实现了AutoCloseable接口的类,在
try()
里声明该类实例的时候,在try
语句块结束时,都会调用该实例的close()
方法 - 调用
close
方法时抛出的异常会被try
语句块中抛出的异常抑制 - 在
finally
语句块执行前,try()
中声明实例的close()
方法总被调用 try()
中声明实例的close()
方法总会被调用,即使try
语句块中出现了异常try()
中声明实例的关闭顺序与创建顺序相反