java程序员面试笔试宝典4.6异常处理

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

    问题:try{}里面有return语句,那么紧跟在try后面的finally{}中的代码是否会被执行?如果会的话什么时候被执行,在return之前还是之后?
    任何语句要执行,都必须在return之前,除非碰到exit函数
    如果try-finally或者catch-finally中都有return语句,那么finally块中的return语句将会覆盖别处的return语句,最终返回调用者的是finally中的return.

1public class Test{

    public static int testFinally(){
        try {
            return 1;
        } catch (Exception e) {
            return 0;
        } finally{
            System.out.println("execute finally");
        }
    }


    public static void main(String[] args) {
        int result = testFinally();
        System.out.println(result);
    }
}
结果:
execute finally
12public class Test{

    public static int testFinally(){
        try {
            return 1;
        } catch (Exception e) {
            return 0;
        } finally{
            System.out.println("execute finally");
            return 3;
        }
    }


    public static void main(String[] args) {
        int result = testFinally();
        System.out.println(result);
    }
}

结果:
execute finally
33:
此外:由于在一个方法内部定义的变量存储在栈中,当这个函数结束后,其对应的栈就被回收,方法体中的变量不存在了。
因此return在返回时不是直接返回变量的值,而是复制一个然后返回。
对于基本数据类型的变量没有影响,引用类型的变量有影响。
public class Test{

    public static int testFinally1(){
        int result = 1;
        try {
            result = 2;
            return result;
        } catch (Exception e) {
            return 0;
        } finally{
            result = 3;
            System.out.println("execute 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("execute finally2");
        }
    }

    public static void main(String[] args) {
        int resultVal = testFinally1();
        System.out.println(resultVal);
        StringBuffer resultRef = testFinally2();
        System.out.println(resultRef);
    }
}

说明:
程序在执行到return时,会首先将返回值存储在一个指定的位置,其次区执行finally块,最后再返回。
testFinally1调用return前,先把result=2存储在一个指定的位置,然后再执行finally中的代码,此时修改程序的值不会影响到程序的返回结果。
testFinally2调用return前,首先把s存储到一个指定的位置,由于s为引用类型,因此在finally中修改s将会修改返回的结果。

引申:出现在Java程序中的finally块是不是一定会被执行?
不一定
1)当程序在进入try语句块之前就出现异常,会直接结束,不会执行finally块中的代码
例4public class Test{

    public static void testFinally(){
        int i = 5/0;
        try {
            System.out.println("try block");
        } catch (Exception e) {
            System.out.println("catch block");
        } finally{
            System.out.println("finally block");
        }
    }

    public static void main(String[] args) {
        testFinally();
    }
}

结果:
Exception in thread "main" java.lang.ArithmeticException: / by zero
    at com.zcl.test.Test.testFinally(Test.java:5)
    at com.zcl.test.Test.main(Test.java:16)


2)当程序在try块中强制退出时也不会执行finally块中的代码
例5public class Test{

    public static void testFinally(){
        try {
            System.out.println("try block");
            System.exit(0);
        } catch (Exception e) {
            System.out.println("catch block");
        } finally{
            System.out.println("finally block");
        }
    }

    public static void main(String[] args) {
        testFinally();
    }
}

结果:
try block
上例在try块中通过调用System.exit(0);强制退出了程序,导致finally块中的代码没有被执行。

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

异常是指程序运行时(非编译时)所发生的非正常情况或错误,当程序违反了语义规则时,JVM就会将出现的错误表示为一个异常并抛出。
这个异常可以在catch块中进行捕获,然后进行处理。
而异常处理的目的是为了提高程序的安全性和鲁棒性。
Java语言把异常当作对象,并定义了一个基类(java.lang.Throwable)作为所有异常的父类。
在Java API中,已经定义了许多异常类,这些异常类分为Error(错误)和Exception(异常)两大类。
违反语义规则包括两种情况:
1)java类库内置的语义检查,例如数组下标越界:IndexOutOfBoundsException,当访问Null时,NullPointException;
2)Java允许开发人员扩展这种语义检查,创建自己的异常类,并选择在何时用throw关键字抛出异常。

3.运行时异常和普通异常有什么区别?

Error表示程序在运行期间发生了非常严重的错误,这种错误不可恢复,属于JVM层次错误,导致程序终止执行。
此外编译器不会检查Error是否被处理,不推荐区捕获Error类型异常
因为这种异常多源于逻辑错误,是可以解决的,即一个正确的程序中不应该存在Error的。
例如:OutOfMemoryError,ThreadDeath

Exception表示可恢复的异常,是编译器可以捕捉的。分为检查异常和运行时异常。
1)检查异常:所有继承自Exception并且不是运行时异常的异常都是检查异常。
如IO异常和SQL异常
这种异常都发生在编译阶段,java编译器强制程序区捕获此类的异常,即把可能会出现这些异常的代码放在try块中,把对异常的处理放在catch块中。
这种异常一般在如下情况下使用:
a.异常的发生并不会导致程序的出错,进行处理后可以继续执行后续的操作。例如:当连接数据库失败后,可以重新连接后进行后续操作。
b.程序依赖于不可靠的外部条件,例如系统IO.

2)运行时异常:编译器没有强制对其进行捕获并处理。如果不进行处理,当出现此异常时,会有JVM来处理。
例如:NullPointException(空指针异常)
ClassCastException(类型转换异常)
ArrayIndexOutOfBoundsException(数组越界异常)
ArrayStoreException(数组存储异常)
BufferOverflowException(缓冲区 溢出异常)
ArithmeticException(算术异常)等。

出现运行时异常后,系统会把异常一直往上层抛,直到遇到处理代码为止。若没有处理代码则抛到最上层。
多线程就用Thread.run()方法抛出,单线程就用main()方法抛出
抛出之后,如果是线程,那么 这个线程也就退出了,如果是主程序抛出的异常,那么整个程序也就退出了。
所以,如果不对运行时的异常进行处理,后果非常严重,一旦发生,要么线程中止,要么主程序中止。


注意:
1)Java异常处理用到了多态的概念,如果在异常 处理过程中,先捕获了基类,然后再捕获子类,那么捕获的子类代码块将永远不会被执行。
因此,再进行异常捕获时,先捕获子类,再不会基类的异常信息。
正确的写法:
        try {
            //
        } catch (SQLException e) {
            //
        } catch (Exception e2) {
            //
        }

错误的写法:
        try {
            //
        } catch (Exception e) {
            //
        } catch (SQLException e2) {
            //
        }
2)尽早抛出异常,并对捕获的异常进行处理,对于运行时异常,实际上根本不需要进行处理。
3)可以根据实际的需求自定义异常类,这些自定义的异常类只要继承自Exception类即可。
4)异常能处理就处理,不能处理就抛出。
1:下面程序能否编译通过,如果把ArithmeticException换成IOException呢?
public class ExceptionTypeTest {

    public void dosomething() throws ArithmeticException{
        System.out.println("123");
    }

    public static void main(String[] args) {
        ExceptionTypeTest et = new ExceptionTypeTest();
        et.dosomething();
    }
}

能,ArithmeticException属于运行时异常,编译器没有强制必须捕获并处理
换为IOException不行,因为为检查异常,编译器强制捕获此类异常,否则会报错。

笔1:异常包含下列哪些内容?C
A:程序中的语法错误
B:程序中的编译错误
C:程序执行过程中遇到的事先没有预料到的情况
D:程序事先定义好的可能出现的意外情况。

笔2:下面关于异常的说法正确的是?D
A:一旦出现异常,程序运行就中止了
B:如果一个方法声明将抛出某个异常,它就必须真的抛出那个异常。
C:再Catch子句中匹配异常是一种精确匹配。
D:可能抛出系统异常的方法是不需要声明异常的。


猜你喜欢

转载自blog.csdn.net/m0_37301141/article/details/80425245