try-catch-finally语句与return联合使用、try-catch对程序性能的影响。我做了九个对比小实验进行研究,得到以下结论:
**1、finally语句在return语句执行之后return返回之前执行的。
2、finally块中的return语句会覆盖try块中的return返回。
3、如果finally语句中没有return语句覆盖返回值,那么原来的返回值可能因为finally里的修改而改变也可能不变。如果返回基本数据类型不会受finally中的修改影响,如果是返回引用类型会受finally中的修改影响。
4、try块里的return语句在异常的情况下不会被执行,这样具体返回哪个看情况。
5、当发生异常后,catch中的return执行情况与未发生异常时try中return的执行情况完全一样。
6、无异常时候,try-catch几乎不影响程序运行时间。
7、有异常的时候,异常类型匹配越精确越少时间开销。
8、不单纯的用Exception过滤异常、尽量少地在try里面添加代码、尽量不在循环里使用try-catch**
实验只是对try-catch-finally的初步认识,如果深入原理研究还需要研究程序字节码。
第一个问题:finally语句是否一定会执行?
答案:不一定
有两种情况:(1)try之前程序就返回了 (2)try之前程序出错,导致无法向下执行。
代码清单一:
package trycatchfinallyDEMO;
public class FinallyTest {
static int test() {
int i = 1;
if (1 == i) {
return 0; //first case
}
System.out.println("previous statement");
// i /= 0; //second case
try {
System.out.println("try");
return i;
} finally {
System.out.println("finally");
}
}
public static void main(String[] args) {
System.out.println("return value is:" + test());
}
}
Output:
first case:return value is:0
second case:报错
以上两种情况,finally语句都没有得到执行。只有与finally相对应的try语句得到执行的时候finally才会执行。
第二个问题:try语句块得到执行的情况下,finally语句一定会得到执行?
答案:否定
代码清单二:
package trycatchfinallyDEMO;
public class FinallyTest {
static int test() {
int i = 1;
System.out.println("previous statement");
// i /= 0; //second case
try {
System.out.println("try");
System.exit(0);
return i;
} finally {
System.out.println("finally");
}
}
public static void main(String[] args) {
System.out.println("return value is:" + test());
}
}
Output:
previous statement
try
当一个线程在执行 try 语句块或者 catch 语句块时被打断(interrupted)或者被终止(killed),与其相对应的 finally 语句块可能不会执行。还有更极端的情况,就是在线程运行 try 语句块或者 catch 语句块时,突然死机或者断电,finally 语句块肯定不会执行了。
第三个问题:多catch如何匹配捕获
答案:按代码书写顺序寻找最接近的异常进行匹配,找到之后不再进行查找。
代码清单三:
package trycatchfinallyDEMO;
public class myException extends Exception {
public String toString(){
return "This is myException!";
}
}
package trycatchfinallyDEMO;
public class mulcatch {
static void f() throws myException {
System.out.println("throw a myException");
throw new myException();
}
static void g() throws Exception {
System.out.println("throw a Exception");
throw new Exception();
}
public static void main(String[] args) {
try {
f();// f()抛出异常得到处理后,下面的输出和g()不会得到执行。
System.out.println("**********************");
g();
} catch (myException e) {
System.out.println("first catch get it");
} catch (Exception e) {// Exception是基类,应该放在最后捕获,放在最一开始会出错。
System.out.println("second catch get it");
} finally {
System.out.println("program is end");
}
System.out.println("--------------------------------");
try {
g(); // 会被最接近的异常类型捕获
System.out.println("**********************");
f();
} catch (myException e) {
System.out.println("first catch get it");
} catch (Exception e) {//哪个catch捕获了异常就会由哪个catch处理,其他catch不执行
System.out.println("second catch get it");
} finally {
System.out.println("program is end");
}
}
}
Output:
throw a myException
first catch get it
program is end
throw a Exception
second catch get it
program is end
注意:
1、try块中,抛出异常的语句之后的语句不会得到执行。
2、Catch顺序应该是由派生到基类,由小到大捕获,Exception应该最后捕获。
3、会被最接近的异常类型捕获,一旦异常被捕获,其他catch就不会被执行。
第四个问题:finally语句和return语句执行顺序
答案:finally语句在return语句执行之后返回之前执行
在这里系统的讨论下return在try-catch-finally各语句块中的情况。
情况1: try-catch 中的return
代码清单四:
package trycatchfinallyDEMO;
public class tryandcatchwithreturn {
static int g(int i) {
try {
//i /= 0;//此处触发异常之后,就直接执行catch并返回,try里面剩下的语句不再执行。
System.out.println("g()");
return i;
} catch (Exception e) {
i += 3;
return i;
}
}
public static void main(String[] args) {
System.out.println(g(1));
}
}
Output:
g()
1
如果去掉注释输出就是4,如果注释catch当中的return就会报错,因为编译器认为try里面的return之前可能有异常产生,导致return得不到执行,所以要在函数finally或者函数尾部添加return,或者try-catch里同时添加return
情况2:try-finally中的return
代码清单五:
package trycatchfinallyDEMO;
public class tryandfinallywithreturn {
static int g(int i) {
try {
i += 2;
System.out.println("g()");
return i;
} catch (Exception e) {
} finally {
i += 5;
System.out.println(i);
//return i;
// finally里也有return,先執行try裏面的return并緩存,然后由于finally里面也有return,所以程序不再返回try进行return
// 而是直接在finally进行return
}
}
public static void main(String[] agrs) {
System.out.println(g(1));
}
}
Output:
g()
8
3
如果在finally中也添加return,那么输出为
g()
8
8
finally中无return时,程序会执行了try块中的return之后,缓存结果,然后执行finally块,最后返回缓存的结果,即使finally中对i进行了修改也没有不会反应到返回值中。如果finally里也有return的话,那么直接在finally里返回值,不在返回缓存结果。
情况3:try-catch-finally中都有return
代码清单六:
package trycatchfinallyDEMO;
public class trycatchfinallywithreturn {
static int g(int i) {
try {
i /= 0;
System.out.println("g()");
return i;
} catch (Exception e) {
i += 3;
System.out.println("catch:" + i);
return i;
} finally {
i += 5;
System.out.println("finally:" + i);
//return i;//和try-catch中return的情况一样,会缓存catch当中的return,如果finally没有return则返回缓存中的i
//如果finally中有return,则直接返回。
}
}
public static void main(String[] args) {
System.out.println(g(1));
}
}
Output:
catch:4
finally:9
4
如果在finally中添加return,那么输出为
catch:4
finally:9
9
类似于try-catch中return的情况,会缓存catch当中的return,如果finally没有return则返回缓存中的i。如果finally中有return,则直接返回。
第五个问题:返回引用的情况下呢?
代码清单七:
package trycatchfinallyDEMO;
public class recommenddemo {
static myClass myclass = new myClass();
static myClass g() {
try {
myclass.setTip("world");
return myclass;
} catch (Exception e) {
myclass.setTip("r u ok?");
return myclass;
} finally {
myclass.setTip("tencent");
//return myclass;
}
}
public static void main(String[] args) {
System.out.println(g());
}
}
Output:
hellotencent
可以看到即使finally中不进行return覆盖也会让finally块中操作反应到myclass
第六个问题:try-catch是否影响性能
情况1:不产生异常的时候
代码清单八:
package trycatchfinallyDEMO;
public class trycatchPerformance {
public static void main(String[] args) {
int result = 1;
long starttime1 = System.nanoTime();
for (int i = 0; i < 20; i++) {
result *= i;
}
long endtime1 = System.nanoTime();
System.out.println("不使用try-catch所用时间:" + (endtime1 - starttime1));
result = 1;
long starttime2 = System.nanoTime();
try {
for (int i = 0; i < 20; i++) {
result *= i;
}
} catch (Exception e) {
// TODO: handle exception
}
long endtime2 = System.nanoTime();
System.out.println("使用try-catch所用時間:" + (endtime2 - starttime2));
}
}
Output:
不使用try-catch所用时间:1555
使用try-catch所用時間:1556
实验表明在不产生异常的情况下,try-catch不会明显影响程序运行时间。
情况2:产生异常的时候
代码清单九:
package trycatchfinallyDEMO;
public class PerformanceWithEception {
public static void main(String[] args) {
int result = 1;
long starttime1 = System.nanoTime();
for (int i = 0; i < 20; i++) {
result *= i;
}
long endtime1 = System.nanoTime();
System.out.println("不使用try-catch所用时间:" + (endtime1 - starttime1));
result = 1;
long starttime2 = System.nanoTime();
try {
for (int i = 0; i < 20; i++) {
result *= i;
}
throw new mysecondException();
} catch (Exception e) {
// TODO: handle exception
}
long endtime2 = System.nanoTime();
System.out.println("使用try-catch Exception所用時間:" + (endtime2 - starttime2));
long starttime3 = System.nanoTime();
try {
for (int i = 0; i < 20; i++) {
result *= i;
}
throw new mysecondException();
} catch (myException e) {
// TODO: handle exception
}catch (Exception e) {
// TODO: handle exception
}
long endtime3 = System.nanoTime();
System.out.println("使用try-catch myException所用時間:" + (endtime3 - starttime3));
long starttime4 = System.nanoTime();
try {
for (int i = 0; i < 20; i++) {
result *= i;
}
throw new mysecondException();
} catch (mysecondException e) {
// TODO: handle exception
}catch (myException e) {
// TODO: handle exception
}
catch (Exception e) {
// TODO: handle exception
}
long endtime4 = System.nanoTime();
System.out.println("使用try-catch mysecondException所用時間:" + (endtime4 - starttime4));
}
}
Output:
不使用try-catch所用时间:622
使用try-catch Exception所用時間:22394
使用try-catch myException所用時間:2488
使用try-catch mysecondException所用時間:2177
由于多出对异常进行类型匹配以及相关处理操作,try-catch在有异常处理时,的确会消耗很多的运行时间。但是与不使用try-catch在时间上不具备可比性,因为它们是功能上的对比。
通过试验可知,catch到的异常类型越接近发生的异常越节省时间,如果单纯的用Exception去过滤会造成很大的时间开销。同时可以想到,尽量少地在try里面添加代码、尽量不在循环里使用try-catch、尽量精确地匹配异常等优化措施,从而提高性能。