当前位置 : 主页 > 编程语言 > java >

说说异常(Exception)吧

来源:互联网 收集:自由互联 发布时间:2023-09-06
两万字详解Java异常,面试再也不怕被问到! (qq.com) Java的异常都是 Throwable 的子类,他包含两个子类 程序本身可以捕获并且可以处理的异常。Exception 这种异常又分为两类:运行时异常和

两万字详解Java异常,面试再也不怕被问到! (qq.com)

Java的异常都是Throwable的子类,他包含两个子类

  1. 程序本身可以捕获并且可以处理的异常。Exception 这种异常又分为两类:运行时异常和编译时异常。
    • RuntimeException 类及其子类,Java 编译器不会检查它。属于非受检异常。当程序中可能出现这类异常时,倘若既"没有通过throws声明抛出它",也"没有用try-catch语句捕获它",还是会编译通过。比如NullPointerException空指针异常、ArrayIndexOutBoundException数组下标越界异常、ClassCastException类型转换异常、ArithmeticExecption算术异常。
    • 编译时异常,Exception 中除 RuntimeException 及其子类之外的异常。属于受检查异常。要么通过throws进行声明抛出,要么通过try-catch进行捕获处理,否则不能通过编译。比如 IO相关的异常,ClassNotFoundException(没有找到指定的类异常),SQLException。
  2. Error类及其子类,代表程序中无法处理的错误,表示运行应用过程中出现了严重的错误。一般是JVM出现问题。这些错误是非受检异常,非代码性错误,此类错误发生时,应用程序不应该去处理。我们不应该实现任何新的Error子类。eg. Virtual MachineError(虚拟机运行错误)、NoClassDefFoundError(类定义错误)等。比如 OutOfMemoryError:内存不足错误;StackOverflowError:栈溢出错误。

追问:什么是受检查异常什么是非受检异常?

受检异常:编译器要求必须处理的异常。除 RuntimeException 及其子类外,其他的 Exception 异常都属于受检异常。

非受检异常:编译器不会进行检查并且不要求必须处理的异常,包括运行时异常(RuntimeException极其子类)和错误(Error)。

追问:throw和throws的区别是什么?

throw关键字用在方法内部,只能用于抛出一种异常。用来抛出方法或者代码块中的异常,受检查异常和非受检异常都可以被抛出。

throws关键字用在方法声明上,可以抛出多个异常,用来表示该方法可能抛出的异常列表。一个方法用 throws 标识了可能抛出的异常列表,调用该方法的方法中必须包含可处理异常的代码,否则也要在方法签名中用 throws 关键字声明相应的异常。

追问:JVM是如何处理异常的?

在一个方法中发生异常的话,这个方法会创建一个异常对象,并转交给JVM,该异常对象包含异常名称、异常描述以及异常发生时应用程序的状态。创建异常对象并转交给JVM的过程称为抛出异常。

可能有一系列的方法调用,最终才进入抛出异常的方法,这一系列方法调用的有序列表叫做调用栈。

JVM会顺着调用栈会查找是否有可以处理异常的代码,如果有,则调用异常处理代码。当JVM发现可以处理异常的代码时,会把发生的异常传递给他。如果JVM没有找到可以处理该异常的代码块,就会将该异常转交给默认的异常处理器,默认异常处理器打印异常信息并终止应用程序。

追问:try-catch-finally 如何使用?

  • try块:用于捕获异常。其后可接零个或多个 catch 块,如果没有 catch 块,则必须跟一个 finally 块。
  • catch块:用于处理 try 捕获到的异常。
  • finally 块:无论是否捕获或处理异常,finally 块里的语句都会被执行。当在 try 块或 catch 块中遇到 return 语句时,finally 语句块将在方法返回之前被执行。

追问:下面代码输出?

public static void main(String[] args) {
    System.out.println(f(2));
}

public static int f(int value) {
    try {
        return value * value;
    } finally {
        if (value == 2) {
            return 0;
        }
    }
}

输出0,

当 try 语句和 finally 语句中都有 return 语句时,try 语句块中的 return 语句会被忽略。这是因为 try 语句中的 return 返回值会先被暂存在一个本地变量中,当执行到 finally 语句中的 return 之后,这个本地变量的值就变为了 finally 语句中的 return 返回值。

追问:try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?

public static int getInt() {
    int a = 10;
    try {
        System.out.println(a / 0);
        a = 20;
    } catch (ArithmeticException e) {
        a = 30;
        return a;
        /*
         * return a 在程序执行到这一步的时候,这里不是return a 而是 return 30;这个返回路径就形成了
         * 但是呢,它发现后面还有finally,所以继续执行finally的内容,a=40
         * 再次回到以前的路径,继续走return 30,形成返回路径之后,这里的a就不是a变量了,而是常量30
         */
    } finally {
        a = 40;
    }
 return a;
}
// 执行结果:30
public static int getInt() {
    int a = 10;
    try {
        System.out.println(a / 0);
        a = 20;
    } catch (ArithmeticException e) {
        a = 30;
        return a;
    } finally {
        a = 40;
        //如果这样,就又重新形成了一条返回路径,由于只能通过1个return返回,所以这里直接返回40
        return a; 
    }
}
// 执行结果:40

会执行,在 return 前执行。

在 finally 中改变返回值的做法是不好的,因为如果存在 finally 代码块,try中的 return 语句不会立马返回调用者,而是记录下返回值待 finally 代码块执行完毕之后再向调用者返回其值,然后如果在 finally 中修改了返回值,就会返回修改后的值。

追问:finally 中的代码什么情况下不会执行?

try {
    System.out.println("Try to do something");
    throw new RuntimeException("RuntimeException");
} catch (Exception e) {
    System.out.println("Catch Exception -> " + e.getMessage());
    // 终止当前正在运行的Java虚拟机
    System.exit(1);
} finally {
    System.out.println("Finally");
}

输出:

Try to do something
Catch Exception -> RuntimeException

以下情况finally 中的代码不会执行。

  1. 虚拟机终止运行
  2. 程序所在的线程死亡。
  3. 关闭 CPU。

追问:知道try-with-resources

为了实现资源的自动释放,要求使用的类实现了AutoCloseable接口。

private  static void tryWithResourceTest(){
    try (Scanner scanner = new Scanner(new FileInputStream("c:/abc"),"UTF-8")){
        // code
    } catch (IOException e){
        // handle exception
    }
}

try 代码块退出时,会自动调用 scanner.close 方法,和把 scanner.close 方法放在 finally 代码块中不同的是,若 scanner.close 抛出异常,则会被抑制,抛出的仍然为原始异常。

上一篇:说说泛型吧
下一篇:没有了
网友评论