面试:Java 异常处理

1. finally块中的代码什么时候被执行

try-catch-finally

finally保证了无论出现什么情况,finally块中的内容一定会被执行。

任何语句只能在return之前执行(除非碰到 exit函数),因此 finally 块里的代码也是在真正 return 前执行的。此外,如果ty-finally或者 catch-finally中都有 return,那么 finally 块中的 return 语句将会覆盖别处的 return语句,最终返回到调用者那里的是 finallyreturn的值

上面返回结果是


execute finally

1

上面返回结果是


execute finally

3

由于一个方法内部定义的遍都存储在栈中,当函数结束后,对应的栈就被回收了,变量就不存在了,因此 return返回时是复制一份变量然后返回。对基本类型,在 finally 块中改变 return 的值对返回值没有影响,而对引用类型的数据会有影响:


public static int testFinally1(){

int result = 1;

try{

result = 2;

return result;

}

catch(Exception e){

return 0;

}

finally{

result = 3;

System.out.println("execuete finally1");

}

}

public static StringBuffer testFinally2(){

StringBuffer s = new StringBuffer("Hello");

try{

return s;

}

catch(Exception e){

return null;

}

finally{

s.append(" World");

System.out.println("execuete finally2");

}

}



public static void main(String[] args){

int resultVal = testFinally1();

System.out.println(resultVal);

StringBuffer resultRef = testFinally2();

System.out.println(resultRef);

}

运行结果为:


execute finally1

2

execute finally2

Hello World

程序执行到 return 的时会先将返回值存储到一个指定的位置,然后去执行 finally 块,最后再返回。finally 中对 result 的修改不会影响那个 return 的副本。所以最后 return 的还是 try 里面返回的 2。而如果 return 的是引用,那个位置的引用是会被修改的。

引申:出现在Java程序中的finally块是不是一定会被执行?

不一定:比如在进入 try 之前就抛异常了,或者在 try 里面强行退出 System.exit(0)

2. 异常处理的原理是什么

异常是指程序运行时(非编译时)所发生的错误。异常捕获处理的目的是为了提高程序的安全性和鲁棒性。

Java语言把异常当作对象来处理,并定义了一个基类(java.lang.Throwable)作为所有异常的父类。在 Java API中,已经定义了许多异常类,这些异常类分为Eror(错误)和 Exception(异常)两大类。

违反语义规则包括两种情况:

一种是Java类库内置的语义检查,例如当数组下标越界时,会引发 Index OutOfBounds Exception,当访间 null 的对象时,会引发 NullPointerExcepion

另一种情况是Java允许开发人员扩展这种语义检査,开发人员可以创建自己的异常类(所有异常都是 java.lang.Throwable 的子类),并自由选择在何时用 throw 关键字抛出

3. 运行时异常和普通异常的区别

Java 提供两种错误的异常类:ErrorException,它们共同的父类是 Throwable

Error 是指运行期间出现了非常严重的错误,是不可恢复的,由于这属于 JVM 层次的严重错误,会导致程序终止。而且编译器不会检查 Error 是否被处理,因此程序中不推荐捕获 Error ,主要是因为 Runtime Error 通过是由于逻辑错误导致的,属于应该解决的错误。也就是说一个正确的程序不应该存在 ErrorOutOfMemory,ThreadDeath等都属于 Error

Exception表示可恢复的异常,是编译器可以捕捉到的。它包含两种类型:检查异常(checked exception)和运行时异常( runtime exception) 和运行时异常。

image-20181115210540197

其他要注意的问题:

  1. Java 异常处理用到了多态的概念,只能先捕获子类再捕获基类,否则先捕获基类再捕获子类,捕获子类的代码将拥有不会执行。

  1. 要尽早抛出异常,同时对捕获的异常进行处理、或从错误中修复、或让程序终止执行。对捕获的异常不处理的习惯很不好,不利于调试。但也不是抛出异常越多越好,有些异常比如运行时异常,根本不用处理。

  2. 可以更具自己的需求定义异常类,只要继承自 Exception 类即可

  3. 异常能处理就处理,不能处理就抛出