异常机制是指当程序出现错误后,程序如何处理。当出现错误后,程序执行的流程发生改变,程序的控制权转移到异常处理器。
Throwable 类是 Java 语言中所有错误或异常的超类。上图是网上比较常见的一种图,给出了我们常见的几个异常;
Error表示程序在运行期间出现了十分严重、不可恢复的错误,在这种情况下应用程序只能中止运行,例如JAVA 虚拟机出现错误。Error是一种unchecked Exception,编译器不会检查Error是否被处理,在程序中不用捕获Error类型的异常。一般情况下,在程序中也不应该抛出Error类型的异常。
Checked Exception异常(上图中的粉色异常),所有继承自Exception 并且不是RuntimeException 的异常都是checked Exception,上图中的 IOException 和 ClassNotFoundException。JAVA 语言规定必须对checked Exception作处理,编译器会对此作检查,要么在方法体中声明抛出(throws)checked Exception,要么使用catch语句捕获checked Exception进行处理,不然不能通过编译。
RuntimeException 是一种Unchecked Exception,即表示编译器不会检查程序是否对RuntimeException作了处理,在程序中不必捕获RuntimException类型的异常,也不必在方法体声明抛出RuntimeException类。RuntimeException发生的时候,表示程序中出现了编程错误,所以应该找出错误修改程序,而不是去捕获RuntimeException。
异常处理的流程
几点说明:
运行时异常表示无法让程序恢复运行的异常,导致这种异常的原因通常是由于执行了错误的操作。一旦出现错误,建议让程序终止。
受检查异常表示程序可以处理的异常。要么 try 要么 throws,否则调用会出错,连编译也无法通过。当然,这两种异常都是可以通过程序来捕获并处理的,例如:
public static int testNoFinally1(){
int b = 20;
int c=0;
try {
c = b/0;//这里抛出了运行时异常
System.out.println("try block");
return b += 80;
}
catch (Exception e) {
System.out.println("catch block");
}
finally {
System.out.println("finally block");
}
System.out.println("after catch /0");
return c;
}
输出结果:
catch block
finally block
after catch /0
0
catch,finally
组成 try...catch...finally
、try...catch
、try...finally
三种结构,catch语句可以有一个或多个,finally语句最多一个,try、catch、finally这三个关键字均不能单独使用。throw用来抛出一个异常,在方法体内。语法格式为:throw 异常对象。
throws用来声明方法可能会抛出什么异常,在方法名后,语法格式为:throws 异常类型1,异常类型2…异常类型n。
当我们子类要重写父类中的方法,如果父类的方法有异常声明,那么子类重写这个方法时候,所要声明的异常不应该比父类的大。只能是小等,或者可以没有。
Return 与 Finally
以下两种情况 finally 语句是不会被执行的:
(1)try语句没有被执行到,如在try语句之前就返回了比如在try之前抛出了异常,这样finally语句就不会执行,这也说明了finally语句被执行的必要而非充分条件是:相应的try语句一定被执行到。
(2)在try块中有System.exit(0);这样的语句,System.exit(0);是终止Java虚拟机JVM的,连JVM都停止了,所有都结束了,当然finally语句也不会被执行到。
Finally语句的执行与return的关系:不知道finally语句是在try的return之前执行还是之后执行?
答: finally 语句是在 try 的 return 语句执行之后 (return expression 中 expression表达式执行之后),return返回之前 (return返回expression结果之前)执行。
public static int testFinalReturn1() {
int b = 20;
try {
System.out.println("try block");
//这个return 可以分两步 第一计算 b+=80; 第二步:返回 结果100;
//在执行完第一步时,回去执行finally里面的东西,然后再执行第二步;
//如果是return 100这样的表达式,可以把第一步看中100=100也会在执行完finally的东西执行返回;
return b += 80;
} catch (Exception e) {
System.out.println("catch block");
} finally {
System.out.println("finally block");
//由于第一步b+=80使得b=100所以这个判断肯定会执行
if (b > 25) {
System.out.println("b>25, b = " + b);
//b=b-30; //不建议代码,我们在finally主要是做释放资源和善后的结果;
//如果我们在这里修改了b的值 不会影响到return的b=100的结果,
}
}
return b;
}
输出结果:
System.out.println(testFinalReturn1());
try block
return statement
finally block
after return
public static int testFinalReturn2() {
int b = 20;
try {
System.out.println("try block");
return b += 80;
} catch (Exception e) {
System.out.println("catch block");
} finally {
System.out.println("finally block");
if (b > 25) {
System.out.println("b>25, b = " + b);
b=b-30; //不建议代码,我们在finally主要是做释放资源和善后的结果;
}
return b;//这里的return语句会覆盖上面return语句的第二步所以b=100-30,
}
//return b;
}
输出结果:
System.out.println(testFinalReturn2());
try block
finally block
b>25, b = 100
70
注意: 这说明finally里的return直接返回了,就不管try中是否还有返回语句,finally里加上return过后,finally外面的return b就变成不可到达语句了,也就是永远不能被执行到,所以需要注释掉否则编译器报错;
public static int testFinalReturnModify1() {
int b = 20;
try {
System.out.println("try block");
//在此Step1 b=100, Step2 return 100;
return b += 80;
} catch (Exception e) {
System.out.println("catch block");
} finally {
System.out.println("finally block");
if (b > 25) {
System.out.println("b>25, b = " + b);
}
//对b的值进行了修改;不会影响到原来值,
b = 150;
}
//由于上面已在Step2中返回来了这里就不会再执行;
return 2000;
}
输出结果:
System.out.println(testFinalReturnModify1());
try block
finally block
b>25, b = 100
100
public static Integer testFinalReturnModify2() {
Integer b = new Integer(20);
Integer c = new Integer(80);
try {
System.out.println("try block");
//在此自动拆箱Step1 b=100, Step2 return 100;
return b += c;
} catch (Exception e) {
System.out.println("catch block");
} finally {
System.out.println("finally block");
if (b > 25) {
System.out.println("b>25, b = " + b);
}
//对b的值进行了修改;不会影响到原来值,
b = 150;
}
//由于上面已在Step2中返回来了这里就不会再执行;
return 2000;
}
输出结果
System.out.println(testFinalReturnModify2());
try block
finally block
b>25, b = 100
100
public static String testFinalReturnModify4() {
String str = "hello";
try {
System.out.println("try block");
//这个比较特殊,限制性finally 在进行拼接返回;
return str+" world";
} catch (Exception e) {
System.out.println("catch block");
} finally {
System.out.println("finally block");
System.out.println("str = " + str);//hello
if ("hello world".equals(str)) {//false
System.out.println("str = " + str);
}
//对 str 的值进行了修改;不会影响到原来值,
str = "abc";
}
//由于上面已在Step2中返回来了这里就不会再执行;
return "def";
}
输出结果:
System.out.println(testFinalReturnModify4());
try block
finally block
str = hello
hello world
public static Map testFinalReturnModifyRef(){
Map<String, String> map = new HashMap<String, String>();
//在此对KEY赋予初始值 INIT;
map.put("KEY", "INIT");
try {
map.put("KEY", "TRY");
return map;
}
catch (Exception e) {
map.put("KEY", "CATCH");
}
finally {
map.put("KEY", "FINALLY");
//将这个引用赋为 null;
map = null;
}
return map;
}
输出结果:
System.out.println(testFinalReturnModifyRef());
{KEY=FINALLY}
static class Student{
private String name;
private int age;
public Student(){}
public Student(String name,int age){
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
}
public static Student testFinalReturnModifyRef2(){
Student stu = new Student("zhangsan",30);
//在此对KEY赋予初始值 INIT;
try {
return stu;
}
catch (Exception e) {
System.out.println("catch..");
stu.setName("catch");
}
finally {
System.out.println("finally -->"+stu );
System.out.println("finally..");
//编译不通过 意思是说这个stu不可以访问;
stu.setName("finally");
}
return stu;
}
输出结果:
finally -->Student [name=zhangsan, age=30]
finally..
Student [name=finally, age=30]
public static int test4() {
int b = 20;
try {
System.out.println("try block");
b = b / 0;//抛出运行时异常
//下面这个语句不会再执行,而是直接执行处理完异常以后的语句;
return b += 80;
} catch (Exception e) {
b += 15;
System.out.println("catch block: b+=15 ->" + b);
} finally {
System.out.println("finally block");
if (b > 25) {
System.out.println("b>25, b = " + b);
}
b += 50;
}
//处理完异常以后,从异常处理完结束的地方继续往下执行:
return 204;
}
输出结果:
System.out.println(test4());
try block
catch block: b+=1535
finally block
b>25, b = 35
204
public static int test5() {
int b = 20;
try {
System.out.println("try block");
//抛出异常
b = b /0;
//下面这个语句用于执行不了了;
return b += 80;
} catch (Exception e) {
System.out.println("catch block");
//step 1 b=20+15 step2 return 35;
return b += 15;
} finally {
System.out.println("finally block");
if (b > 25) {
System.out.println("b>25, b = " + b);
}
//不会影响到上面的35;
b += 50;
}
//这里执行不到,不需要写 编译不通过;
//return b;
}
输出结果:
System.out.println(test5());
try block
catch block
finally block
b>25, b = 35
35
在此特别鸣谢以下前辈给出的经验和知识的分享;
Java常见的异常小结:
异常的深入研究与分析:
Java异常体系详解:
Java finally语句到底是在return之前还是之后执行 重点感谢