在java7之前我们打开/关闭JDBC连接,打开/关闭文件输入输出流时都会采用一套固定的套路代码来打开资源 使用资源 关闭资源。类似于下面的这段:
InputStream inputStream = null;
OutputStream outputStream = null;
try {
//打开输入输出流
inputStream = new FileInputStream("");
outputStream = new FileOutputStream("");
byte[] temp = new byte[1024];
int len = -1;
while ((len = inputStream.read(temp)) != -1) {
outputStream.write(temp);
}
outputStream.flush();
} catch (Exception e) {
//包装异常或者抛出或者打印输出
} finally {
//关闭输入输出流
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException ioE) {
//关闭资源时发生异常
ioE.printStackTrace();
}
}
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException ioE) {
//关闭资源时发生异常
ioE.printStackTrace();
}
}
}
上面这段代码中我最不喜欢写的就是关闭资源的写法显得特别的臃肿和多余。但是在JAVA7 中有一个新的 try-with-resource 的语法糖,该语法糖就简化了上面这种开启关闭资源的写法,我们不用自己写代码来关闭这些资源了,语法糖能够帮助我们自定关闭。该语法糖适用于实现了java.lang.AutoCloseable 或者 java.io.Closeable的资源类,这俩接口都需要实现一个close方法。
自定义资源类:
public class MyResouce implements AutoCloseable {
private String name;
public MyResouce(String name){
this.name=name;
}
@Override
public void close() throws Exception {
System.out.println(name+"资源被关闭了");
}
public void openResouse(){
System.out.println(name+"资源被打开了");
}
public class TryWithResourceTest {
@Test
public void test() {
try (MyResouce myResouce = new MyResouce("资源1");) {
myResouce.openResouse();
System.out.println("=======处理了一大堆事情=======");
TimeUnit.SECONDS.sleep(2);
System.out.println("=======事情处理完了=======");
} catch (Exception e) {
e.printStackTrace();
}
}
}
结果:
资源1资源被打开了
=======处理了一大堆事情=======
=======事情处理完了=======
资源1资源被关闭了
看到了吧,close方法是被自动调用的,不需要我们自己写代码调用。也可以使用多个资源最后关闭的时候是按照构建FILO的顺序进行资源关闭的。也就是在try()括号内先实例化的后关闭,后实例化的先关闭。
public class TryWithResourceTest {
@Test
public void test() {
try (MyResouce myResouce = new MyResouce("资源1");MyResouce myResouce2 = new MyResouce("资源2");) {
myResouce.openResouse();
myResouce2.openResouse();
System.out.println("=======处理了一大堆事情=======");
TimeUnit.SECONDS.sleep(2);
System.out.println("=======事情处理完了=======");
} catch (Exception e) {
e.printStackTrace();
}
}
}
结果:
资源1资源被打开了
资源2资源被打开了
=======处理了一大堆事情=======
=======事情处理完了=======
资源2资源被关闭了
资源1资源被关闭了
在旧try finally 写法中的资源关闭时如果try块的处理逻辑和finally块中关闭资源代码同时发生异常,则向外抛出的的异常只有finally块的异常,try 块的异常会被屏蔽掉。这可不是我们所想要的。用了try-with-resource 之后就可以将所有的异常都抛出。
public class MyResouce implements AutoCloseable {
private String name;
public MyResouce(String name){
this.name=name;
}
@Override
public void close() throws Exception {
System.out.println(name+"正在关闭资源...");
throw new IOException(name+"资源在关闭时发生异常");
}
public void openResouse() throws Exception {
System.out.println(name+"资源被打开了");
}
}
public class TryWithResourceTest {
@Test
public void test() {
try{
testExceptionThrow();
}catch (Exception ex){
ex.printStackTrace();
}
}
private void testExceptionThrow() throws Exception {
MyResouce myResouce1=new MyResouce("资源1");
try{
myResouce1.openResouse();
throw new Exception("业务处理异常了");
}finally {
if(myResouce1!=null){
myResouce1.close();
}
}
}
}
结果:
java.io.IOException: 资源1资源在关闭时发生异常
at com.sane.mu.MyResouce.close(MyResouce.java:15)
at com.sane.mu.TryWithResourceTest.testExceptionThrow(TryWithResourceTest.java:42)
at com.sane.mu.TryWithResourceTest.test(TryWithResourceTest.java:27)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
资源1资源被打开了
资源1正在关闭资源...
下面是使用try-with-resource 语法糖的写法,此处同样会抛出两个异常,而我们这次可以看到所有的异常信息。
public class TryWithResourceTest {
@Test
public void test() {
try{
testExceptionThrow();
}catch (Exception ex){
ex.printStackTrace();
}
}
private void testExceptionThrow() throws Exception {
try(MyResouce myResouce1=new MyResouce("资源1");){
myResouce1.openResouse();
throw new Exception("业务处理异常了");
}
}
}
资源1资源被打开了
资源1正在关闭资源...
java.lang.Exception: 业务处理异常了
at com.sane.mu.TryWithResourceTest.testExceptionThrow(TryWithResourceTest.java:38)
at com.sane.mu.TryWithResourceTest.test(TryWithResourceTest.java:27)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Suppressed: java.io.IOException: 资源1资源在关闭时发生异常
at com.sane.mu.MyResouce.close(MyResouce.java:15)
at com.sane.mu.TryWithResourceTest.testExceptionThrow(TryWithResourceTest.java:39)
... 23 more
完