6.1 finally块中的代码什么时候被执行 ?
try{}里有一个return语句,那么finally{}中的代码是否会被执行?什么时候被执行?
在Java语言的异常处理中,finally块的作用就是为了保证无论出现什么情况,finally块的代码一定会被执行。程序return意味着结束对当前函数的调用并跳出这个函数,因此任何语句只能在return之前(exit函数除外),因此finally块里的代码也是在return前执行的。
package org.base;
public class Test {
public static int testFinally(){
try {
return 1;
} catch (Exception e) {
// TODO: handle exception
return 0;
} finally {
System.out.println("execute Finally!");
}
}
public static void main(String[] args) {
int i = testFinally();
System.out.println(i);
}
}
运行结果:execute Finally!
1
package org.base;
public class Test {
public static int testFinally(){
try {
return 1;
} catch (Exception e) {
// TODO: handle exception
return 0;
} finally {
System.out.println("execute Finally!");
return 3;
}
}
public static void main(String[] args) {
int i = testFinally();
System.out.println(i);
}
}
运行结果:execute Finally!
3
结果可以看出finally块中的return语句覆盖了其他函数的return语句。
package org.base;
public class Test {
public static int testFinally1(){
int result = 1;
try {
result = 2;
return result;
} catch (Exception e) {
// TODO: handle exception
return 0;
} finally {
result = 3;
System.out.println("execute Finally1!");
}
}
public static StringBuffer testFinally2(){
StringBuffer sb = new StringBuffer("Hello");
try {
return sb;
} catch (Exception e) {
// TODO: handle exception
return null;
} finally {
sb.append(" World!");
System.out.println("execute Finally2!");
}
}
public static void main(String[] args) {
int result = testFinally1();
System.out.println(result);
StringBuffer s = testFinally2();
System.out.println(s);
}
}
运行结果:execute Finally1!
2
execute Finally2!
Hello World!
程序在执行到return时会首先将返回值存储在一个指定的位置,其次去执行finally块,最后再返回。 testFinally1中调用return前,先把result的值1存储在一个指定的位置,然后在执行finally块中的代码,此时修改result的值不会影响返回结果。testFinally2中,调用return前首先把s存储到一个指定位置,由于s为引用类型,因此在finally块中修改s将会修改程序的返回结果。
出现在Java程序中的finally块是不是一定会被执行?
不一定会被执行。
1)当程序在进入try语句块之前就出现异常时,会直接结束。
package org.base;
public class Test {
public static void testFinally(){
int i = 5 / 0;
try {
System.out.println("try block");
} catch (Exception e) {
// TODO: handle exception
System.out.println("catch block");
} finally {
System.out.println("finally block");
}
}
public static void main(String[] args) {
testFinally();
}
}
2)程序在try块中强制退出不会执行finally块中的代码
package org.base;
public class Test {
public static void testFinally(){
try {
System.out.println("try block");
System.exit(0);
} catch (Exception e) {
// TODO: handle exception
System.out.println("catch block");
} finally {
System.out.println("finally block");
}
}
public static void main(String[] args) {
testFinally();
}
}
运行结果:try block
6.2 异常处理的原理是什么?
异常是指程序运行时所发生的非正常情况或错误,JVM就会将出现的错误表示一个异常并抛出。这个异常可以在catch程序块进行捕获,然后进行处理。异常处理的目的则是为了提高程序的安全性和鲁棒性。
Java语言把异常当作对象来处理,并定义了一个基类(java.lang.Throwable)作为所有异常的父类。Java API中,将异常类分为Error(错误)和Exception(异常)两大类。
能使用throw抛出的异常:Error、Throwable、Exception、RuntimeException等。
6.3 运行时异常和普通异常有什么区别?
Error和Exception拥有共同的父类Throwable。
Error表示程序在运行期间出现了非常严重的错误,并且该错误不可恢复,导致程序终止执行。运行时异常多是由逻辑错误导致的,属于应该解决的错误。OutOfMemoryError、ThreadDeath等都属于错误。
Exception属于可恢复的异常,是编译器可以捕捉到的。包含:检查异常(Checked Exception)和运行时异常(Runtime Exception)。
(1)检查异常
1)异常的发生不会导致程序出错,进行处理后可以继续执行后续的操作。例如:当连接数据库失败后,可以重新连接后进行后续操作。
2)程序依赖于不可靠的外部条件,例如系统IO。
(2)运行时异常
不同于检查异常,编译器没有强制进行捕获并处理。如果不对这种异常进行处理,当出现这种情况时,会由JVM来处理。Java中常见的异常:NullPointerException(空指针异常)、ClassCastException(类型转换异常)、ArrayIndexOutOfBoundException(数组越界异常)、ArrayStoreException(数组存储异常)、BufferOverflowException(缓冲区溢出异常)、ArithmeticException(算术异常)等。
出现运行时异常后,系统会把异常一直往上层抛出,直到遇到处理代码为止。 若没有处理块,则抛到最上层;如果是多线程就用Thread.run()方法抛出异常,单线程用main()方法抛出异常。抛出之后,如果是线程,那么这个线程也就退出了。如果是主程序抛出的异常,那么整个程序也就退出了。要么是线程终止,要么是主程序终止。
使用异常处理注意的几个问题:
- Java异常处理用到了多态的概念,进行异常捕获时:先捕获子类,再捕获基类的异常信息。反之,子类的异常信息永远不会被执行。
try {
// access db code
} catch (ClassCastException e1) {
//deal with this exception
} catch (Exception e2) {
}
- 尽早抛出异常;
- 可以根据实际的需求自定义异常类,自定义异常类继承Exception即可;
- 异常不能处理就抛出。
问以下编译能否通过?
package org.base;
public class Test {
public static void f() throws ArithmeticException{
System.out.println();
}
public static void main(String[] args) {
f();
}
}
能。ArithmeticException属于运行时异常,编译器没有强制对其进行捕获并处理。但如果换成IOException后,由于IOException属于检查异常,编译器强制去捕获此类型的异常,不对异常处理会有编译错误。
package org.base;
import java.io.IOException;
public class Test {
public static void f() throws IOException{
System.out.println();
}
public static void main(String[] args) throws IOException {
f();// 不抛出异常,编译错误
}
}