Java中的异常类都继承自Throwable
类,Throwable
有两个子类:Exception(异常)
和Error(错误)
, 二者都各自包含大量子类。
Java的异常可分为:错误、不受检查异常、受检查异常。
Error(错误)
: 是程序无法处理的错误,一旦出现会导致严重后果,一般会停止运行。Exception(异常)
: 是程序本身可以处理的异常,又分为不受检查异常和受检查异常。
RuntimeException
及其子类都是不检查异常,该类异常可以捕捉处理,也可以不捕捉处理。Error
和RuntimeException
之外的所有异常都是受检查的,要么使用try...catch
捕捉,要么在方法上声明throws
。一般情况下finally
总会被执行,即使前面执行到了return
语句,在执行完之前也会执行finally
,在finally
中使用return
语句是非常不好的习惯。
finally
的return
会覆盖其他return
的值,如下标记B就吞掉了标记A的返回值;finally
的return
会吞掉catch
块中抛出的异常,如下标记D就吞掉了标记C抛出的异常。public class TestException {
boolean testEx() {
boolean ret = true;
try {
testEx1(); /* 将不会抛出异常,因为testEx1中catch块抛出的异常被finally里的return吞掉了;
若testEx1中的finally里无return,则会抛出异常
*/
return ret; /* 该语句正常执行,该语句标记为A */
} catch (Exception e) { /* 捕获不到异常 */
System.out.println("testEx, catch exception"); //不打印
} finally {
System.out.println("testEx, finally"); //打印
ret = false;
return ret; /* 该语句会吞掉之前标记A的return值,该语句标记为B */
}
}
boolean testEx1() throws Exception {
try {
int a = 9 / 0; /* 将抛出 ArithmeticException */
return true; /* 执行不到 */
} catch (Exception e) { /* 捕获异常 */
System.out.println("testEx1, catch exception"); // 打印
throw e; /* 抛出异常 该语句标记为C */
} finally {
System.out.println("testEx1, finally"); // 打印
return false; /* 返回,并且catch中跑出的异常被忽略了 该语句标记为D */
}
}
public static void main(String[] args) {
TestException testException = new TestException();
System.out.println(testException.testEx()); // 将输出标记B的返回值: false
}
}
另外,由于finally
块一定会执行,所以可以在函数返回结果之前修改返回值,但一定要小心,有些情况下能够修改返回值,有些情况下是无法修改的。
// TestFinally.java
public class TestFinally {
Man test1() {
Man man1 = new Man();
Man man2 = new Man();
try {
return man1; // 返回man1
} finally {
man2.setName("world");
man1 = man2; // 修改man1
}
}
Man test2(){
Man man1 = new Man();
Man man2 = new Man();
try {
return man1; // 返回man1
} finally {
man1.setName("world"); // 修改man1
}
}
public static void main(String[] args) {
TestFinally testFinally = new TestFinally();
System.out.println(testFinally.test1()); // 输出 name:hello
System.out.println(testFinally.test2()); // 输出 name:world
}
}
// Man.java
public class Man{
private String name = "hello";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "name:" + name;
}
}
test1
中man1 = man2;
确实执行了,但是在执行finally
块之前,return
的值就确定了,所以返回的man1
仍是原来的;
test2
中man1.setName("world");
也执行了,并且在此之前,return
的值也确定了,但这句并未改变man1
的值(引用地址),而是改变了name
属性的值。