Java中的异常处理,面试看这一篇就够了

欢迎跳转到本文原文链接 ,给出您的建议Backend_Notes


程序运行过程中总会因为各种原因而抛出异常,此时我们需要的是当用户期望出现错误时,程序能够采用一些理智的行为:

  • 返回到一种安全状态,并能够让用户执行一些其他的命令,或者
  • 运行用户保存所有操作结果,并以妥善的方法终止程序

如果某个方法不能够采用正常的途径完成它的任务,就可以通过另一个路径退出方法,在这种情况下,方法会抛出一个封装了错误信息的对象。需要注意的是:这个方法会立刻退出,并不会返回任何值。此外,调用这个方法的代码也无法继续执行,取而代之的是,异常处理机制开始搜索能够处理这种异常情况的异常处理器(exception handler)。

异常的分类

在 Java 程序中,异常对象都是派生于 Throwable 类的实例

Exception

The Throwable class is the superclass of all errors and exceptions in the Java language. Only objects that are instances of this class (or one of its subclasses) are thrown by the Java Virtual Machine or can be thrown by the Java throw statement. Similarly, only this class or one of its subclasses can be the argument type in a catch clause. For the purposes of compile-time checking of exceptions, Throwable and any subclass of Throwable that is not also a subclass of either RuntimeException or Error are regarded as checked exceptions.[2]

翻译:

只有 Throwable 对象或其子类对象可以被 JVM 抛出,或被 throw 语句抛出,并且只有这里对象可以放在 catch 语句块中。

为了在编译期进行检查异常,除了 RuntimeExceptionError,其他 Throwable 对象 及其子类都是 checked exceptions

Error

An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch. Most such errors are abnormal conditions. The ThreadDeath error, though a “normal” condition, is also a subclass of Error because most applications should not try to catch it.

A method is not required to declare in its throws clause any subclasses of Error that might be thrown during the execution of the method but not caught, since these errors are abnormal conditions that should never occur. That is, Error and its subclasses are regarded as unchecked exceptions for the purposes of compile-time checking of exceptions.[2]

翻译:

Error 所表示的一下严重错误,在程序中一般不应该 catch (可以catch但是没必要),大部分的 Error 都是异常情况。

方法签名中也不需要将 Error 通过 throws 进行声明,因为这些 Error 都是异常情况下才有,一般不会出现,因此为了在编译期检查异常, Error 及其子类都作为 unchecked exceptions

Error 类层次结构描述了 Java 运行时系统的内部错误和资源耗尽错误应用程序不应该抛出这种类型的对象。如果出现了这样的错误,出了通知给用户,并尽力使程序安全地终止外,再无能为力了,这种情况很少。

Exception

Exception 层次中有两个分支:

  • RuntimeException:由程序错误导致的异常,如:
    • ClassCastException
    • IndexOutOfBoundsExceptions
    • NullPointerException
  • 其他异常:程序本身没问题,但由于像 I/O 错误引起的异常:
    • FileNotFoundException
    • FileSystemException

Java 语言中异常 也可以根据编译器检查是否给异常提供了异常处理器(exception handler)进行分类。

  • checked Exception:除了受查异常的其他异常
  • unchecked Exception: Error类及其子类对象以及 RuntimeException 类及其子类对象

声明受查异常

一个 方法不仅需要告诉编译期将要返回什么值,还要告诉编译期有可能发送什么错误,因此方法应该首部用 throws 声明所有可能抛出的异常,多个异常可以用逗号隔开,如:

public FileInputStream(String name) throws FileNotFoundException {}
public loadImage(String name) throws FileNotFoundException, EOFException {}

当调用上述构造器方法时如果发送了异常,此时不会初始化一个新的 FileInputStream 对象,而是会抛出一个 FileNotFoundException异常对象,运行时系统就会开始搜索异常处理器,以便知道如何处理异常对象。

需要在方法上声明异常的情况:

  • 调用了一个抛出受查异常的方法
  • 程序运行中发现错误,并且利用 throw 抛出

注意

  • 我们不能声明 Error 以及 RuntimeException,即非受查异常。
  • 如果子类覆盖了超类的方法,子类方法中声明的受查异常(checked exception)不能比超类方法中声明的更通用(即子类方法中可以抛出更特定的异常,或者不抛出异常)
  • 如果一个类中声明抛出某特定异常类,那么抛出的异常会是这个类及其子类的对象

**一旦方法抛出了异常,这个方法就不可能返回到调用者。**也就是说,不必为返回的默认值或错误代码担忧。

创建异常类

自己在开发中创建异常类可以继承自 Exception 或其子类。

class MyException extends Exception {
    public MyException() {
    }

    public MyException(String message) {
        super(message);
    }
}

捕获异常

程序中出现的异常可以使用 try catch 语句进行捕获,通常应该捕获那些知道如何处理的异常,而将不知道如何处理的异常继续进行传递。

再次抛出异常与异常链

我们在 catch 字句中可以抛出一个异常,这样做的目的是改变异常的类型

try {
	// access the database
} catch (SQLException e) {
	Throwable se = new ServletException("database error");
    // 设置原始异常
	se.initCause(e);
	throw se;
}

// 获取原始异常
Throwable e = se.getCause();

finally 子句

当代码抛出一个异常时,就会终止方法中剩余代码的处理,并退出这个方法的执行。如果方法获得了一些本地的资源,并且只有这个方法自己知道,有如果这些资源在退出方法之前必须被回收,那么就会产生资源回收问题。一种解决方案是捕获并重新抛出所有异常。但是这种解决方案比较乏味,这是因为需要在两个地方清除所分配的资源,一个在正常的代码中,另一个在异常代码中。

Java 使用 finally 字句可以解决上述回收资源的问题。不管异常是否被捕获,finally 字句的代码都会被执行。如果用 Java 编写数据库程序,就需要使用同样的技术关闭与数据库的连接,

InputStream in = new FileInputStream(..);

try {
    // 1
    // code that might throw exceptions
    // 2
} catch (IOException e) {
    // 3
    // show error message
    // 4
} finally {
    // 5
    // in.close();
}
// 6

上述代码中,三种情况会执行 finally 子句:

  • 没有抛出异常,执行代码:1, 2, 5, 6
  • 程序在 try 语句块中发生异常:
    • 如果 catch 中没有抛出异常:执行代码: 1,3,4,5,6
    • 如果 catch 中抛出异常,异常将抛回这个方法的调用者:执行代码: 1,3,5
  • 程序抛出异常,但这个异常不被 catch 所捕获,执行代码: 1, 5
finally 对返回结果的影响

若 finally 中带有 return语句,则最终返回的结果是 finally 中返回的值:

public class ExceptionTest {
    public static void main(String[] args) {
        System.out.println(test());
    }
    
    private static int test() {
        int i = 1;
        try {
            throw new Exception();
        } catch (Exception e) {
            return i;
        } finally {
            i++;
            return  233;
        }
    }
}

// output
// 233

若 finally 中对返回的值进行操作,当返回的值为基本数据类型时不会有影响,但是返回的值为引用类时会有影响:查看反编译文件可知,返回的值是对 try catch 语句块中值的拷贝,所有对基本类型不会有影响,但引用类型会有影响。

public class ErrorTest {
    public static void main(String[] args) {
        System.out.println(test());
        System.out.println(test2()[0]);
        System.out.println(test3());
    }

    private static Integer  test() {
        Integer i = 5;
        try {
            throw new Exception("jhh");
        } catch (Exception e) {
            return i;
        } finally {
            i++;
        }
    }

    private static int[] test2() {
        int[] nums = {1};
        try {
            throw new Exception();
        } catch (Exception e) {
            return nums;
        } finally {
            nums[0] = 2222;
        }
    }

    private static Person test3() {
        Person p = new Person("Alice");
        try {
            throw new Exception();
        } catch (Exception e) {
            return p;
        } finally {
            p.setName("Bob");
        }
    }
}

// output
// 5
// 2222
// Person{name='Bob'}

带资源的 try 语句

一般对涉及到资源操作的代码如下

open a resource
try {
    work with the resource
} finally {
    clouse the resource
}

如果资源属于实现了 AutoCloseable 或 Closeable 接口的类, JDK7 中提供带资源的 try 语句:

try (Resource res = ..) {
    work with res
}

当 try 语句退出时, 会自动调用 res.close()

处理异常时可以考虑早抛出,晚捕获的原则。

Reference

  1. Java核心技术卷一
  2. Class Throwable
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值