Java中的异常处理

异常总是先抛出,后捕获的。下面我们将围绕着 5 个关键字来详细讲解如何抛出异常以及如何捕获异常

4. 抛出异常


4.1 实例

我们先来看一个除零异常的实例代码:

package com.caq.exception;

public class Demo01 {

//打印a/b的结果

public static void test(int a,int b){

System.out.println(a/b);

}

public static void main(String[] args) {

//调用test方法

test(5,0);

}

}

运行结果:

Exception in thread “main” java.lang.ArithmeticException: / by zero

at com.caq.exception.Demo01.test(Demo01.java:6)

at com.caq.exception.Demo01.main(Demo01.java:11)

Process finished with exit code 1

我们知道 0 是不能用作除数的,由于 test() 方法中除数 b0,所以代码将停止执行并显示了相关的异常信息,此信息为堆栈跟踪

上面的运行结果告诉我们:main 线程发生了类型为 ArithmeticException 的异常,显示消息为 by zero,并且提示了可能发生异常的方法和行号。

4.2 throw

上面的实例中,程序在运行时引发了错误,那么如何来显示抛出(创建)异常呢?

我们可以使用 throw 关键字来抛出异常,throw 关键字后面跟异常对象,改写上面的实例代码:

package com.caq.exception;

public class Demo01 {

//打印a/b的结果

public static void test(int a,int b){

if (b==0){

//抛出异常

throw new ArithmeticException(“被除数不能为零”);

}

System.out.println(a/b);

}

public static void main(String[] args) {

//调用test方法

test(5,0);

}

}

运行结果:

Exception in thread “main” java.lang.ArithmeticException: 被除数不能为零

at com.caq.exception.Demo01.test(Demo01.java:8)

at com.caq.exception.Demo01.main(Demo01.java:15)

代码在运行时同样引发了错误,但显示消息为 “除数不能为零”。我们看到 test() 方法中加入了条件判断,如果调用者将参数 b 设置为 0 时,会使用 throw 关键字来抛出异常,throw 后面跟了一个使用 new 关键字实例化的算数异常对象,并且将消息字符串作为参数传递给了算数异常的构造函数。

我们可以使用 throw 关键字抛出任何类型的 Throwable 对象,它会中断方法,throw 语句之后的所有内容都不会执行。除非已经处理抛出的异常。异常对象不是从方法中返回的,而是从方法中抛出的。

4.3 throws

可以通过 throws 关键字声明方法要抛出何种类型的异常。如果一个方法可能会出现异常,但是没有能力处理这种异常,可以在方法声明处使用 throws 关键字来声明要抛出的异常。例如,汽车在运行时可能会出现故障,汽车本身没办法处理这个故障,那就让开车的人来处理。

throws 用在方法定义时声明该方法要抛出的异常类型,如下是伪代码:

public void demoMethod() throws Exception1, Exception2, … ExceptionN {

// 可能产生异常的代码

}

throws 后面跟的异常类型列表可以有一个也可以有多个,多个则以 , 分割。当方法产生异常列表中的异常时,将把异常抛向方法的调用方,由调用方处理。

throws 有如下使用规则:

  1. 如果方法中全部是非检查异常(即 ErrorRuntimeException 以及的子类),那么可以不使用 throws 关键字来声明要抛出的异常,编译器能够通过编译,但在运行时会被系统抛出;

  2. 如果方法中可能出现检查异常,就必须使用 throws 声明将其抛出或使用 try catch 捕获异常,否则将导致编译错误;

  3. 当一个方法抛出了异常,那么该方法的调用者必须处理或者重新抛出该异常;

  4. 当子类重写父类抛出异常的方法时,声明的异常必须是父类所声明异常的同类或子类。

5. 捕获异常


使用 try 和 catch 关键字可以捕获异常。try catch 代码块放在异常可能发生的地方。它的语法如下:

Tips:

再IDEA里可以通过//ctrl+alt+t选择代码自动包裹

try {

// 可能会发生异常的代码块

} catch (Exception e1) {

// 捕获并处理try抛出的异常类型Exception

} catch (Exception2 e2) {

// 捕获并处理try抛出的异常类型Exception2

} finally {

// 无论是否发生异常,都将执行的代码块

}

我们来看一下上面语法中的 3 种语句块:

  1. try 语句块:用于监听异常,当发生异常时,异常就会被抛出;

  2. catch 语句块catch 语句包含要捕获的异常类型的声明,当 try 语句块发生异常时,catch 语句块就会被检查。当 catch 块尝试捕获异常时,是按照 catch 块的声明顺序从上往下寻找的,一旦匹配,就不会再向下执行。因此,如果同一个 try 块下的多个 catch 异常类型有父子关系,应该将子类异常放在前面,父类异常放在后面;

  3. finally 语句块:无论是否发生异常,都会执行 finally 语句块。finally 常用于这样的场景:由于 finally 语句块总是会被执行,所以那些在 try 代码块中打开的,并且必须回收的物理资源(如数据库连接、网络连接和文件),一般会放在 finally 语句块中释放资源。

try 语句块后可以接零个或多个 catch 语句块,如果没有 catch 块,则必须跟一个 finally 语句块。简单来说,try 不允许单独使用,必须和 catchfinally 组合使用,catchfinally 也不能单独使用。

实例如下:

package com.caq.exception;

public class Demo01 {

//打印a/b的结果

public static void test(int a,int b){

System.out.println(a/b);

}

public static void main(String[] args) {

//调用test方法

try {

test(5,0);

}catch (ArithmeticException e1){

System.out.println(“捕获的异常为:”+e1);

}finally {

System.out.println(“无论是否发送异常,我都会执行哦~”);

}

}

}

运行结果:

捕获的异常为:java.lang.ArithmeticException: / by zero

无论是否发送异常,我都会执行哦~

test() 方法中除数 b0,会发生除零异常,我们在方法调用处使用了 try 语句块对异常进行捕获;如果捕获到了异常, catch 语句块会对 ArithmeticException 类型的异常进行处理,此处打印了一行自定义的提示语句;

最后的 finally 语句块,无论发生异常与否,总会执行。

Java 7 以后,catch 多种异常时,也可以像下面这样简化代码:

try {

// 可能会发生异常的代码块

} catch (Exception | Exception2 e) {

// 捕获并处理try抛出的异常类型

} finally {

// 无论是否发生异常,都将执行的代码块

}

6. 自定义异常


自定义异常,就是定义一个类,去继承 Throwable 类或者它的子类。

Java 内置了丰富的异常类,通常使用这些内置异常类,就可以描述我们在编码时出现的大部分异常情况。一旦内置异常无法满足我们的业务要求,就可以通过自定义异常描述特定业务产生的异常类型。

实例:

public class ExceptionDemo4 {

static class MyCustomException extends RuntimeException {

/**

  • 无参构造方法

*/

public MyCustomException() {

super(“我的自定义异常”);

}

}

public static void main(String[] args) {

// 直接抛出异常

throw new MyCustomException();

}

}

运行结果:

Exception in thread “main” ExceptionDemo4$MyCustomException: 我的自定义异常

at ExceptionDemo4.main(ExceptionDemo4.java:13)

在代码中写了一个自定义异常 MyCustomException,继承自 RuntimeException,它是一个静态内部类,这样在主方法中就可以直接抛出这个异常类了。当然,也可以使用 catch 来捕获此类型异常。

7. 异常链


异常链是以一个异常对象为参数构造新的异常对象新的异常对象将包含先前异常的信息。简单来说,就是将异常信息从底层传递给上层,逐层抛出,我们来看一个实例:

public class ExceptionDemo5 {

/**

  • 第一个自定义的静态内部异常类

*/

static class FirstCustomException extends Exception {

// 无参构造方法

public FirstCustomException() {

super(“第一个异常”);

}

}

/**

  • 第二个自定义的静态内部异常类

*/

static class SecondCustomException extends Exception {

public SecondCustomException() {

super(“第二个异常”);

}

}

/**

  • 第三个自定义的静态内部异常类

*/

static class ThirdCustomException extends Exception {

public ThirdCustomException() {

super(“第三个异常”);

}

}

/**

  • 测试异常链静态方法1,直接抛出第一个自定义的静态内部异常类

  • @throws FirstCustomException

*/

public static void f1() throws FirstCustomException {

throw new FirstCustomException();

}

/**

  • 测试异常链静态方法2,调用f1()方法,并抛出第二个自定义的静态内部异常类

  • @throws SecondCustomException

*/

public static void f2() throws SecondCustomException {

try {

f1();

} catch (FirstCustomException e) {

throw new SecondCustomException();

}

}

/**

  • 测试异常链静态方法3,调用f2()方法, 并抛出第三个自定义的静态内部异常类

  • @throws ThirdCustomException

*/

public static void f3() throws ThirdCustomException {

try {

f2();

} catch (SecondCustomException e) {

throw new ThirdCustomException();

}

}

public static void main(String[] args) throws ThirdCustomException {

// 调用静态方法f3()

f3();

}

}

运行结果:

Exception in thread “main” ExceptionDemo5$ThirdCustomException: 第三个异常

at ExceptionDemo5.f3(ExceptionDemo5.java:46)

at ExceptionDemo5.main(ExceptionDemo5.java:51)

通过运行结果,我们只获取到了静态方法 f3() 所抛出的异常堆栈信息,前面代码所抛出的异常并没有被显示。

我们改写上面的代码,让异常信息以链条的方式 “连接” 起来。可以通过改写自定义异常的构造方法,来获取到之前异常的信息。实例如下:

public class ExceptionDemo6 {

/**

  • 第一个自定义的静态内部异常类

*/

static class FirstException extends Exception {

// 无参构造方法

public FirstException() {

super(“第一个异常”);

}

}

/**

  • 第二个自定义的静态内部异常类

*/

static class SecondCustomException extends Exception {

/**

  • 通过构造方法获取之前异常的信息

  • @param cause 捕获到的异常对象

*/

public SecondCustomException(Throwable caus

【一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义】

浏览器打开:qq.cn.hn/FTf 免费领取

e) {

super(“第二个异常”, cause);

}

}

/**

  • 第三个自定义的静态内部异常类

*/

static class ThirdCustomException extends Exception {

/**

  • 通过构造方法获取之前异常的信息

  • @param cause 捕获到的异常对象

*/

public ThirdCustomException(Throwable cause) {

super(“第三个异常”, cause);

}

}

/**

  • 测试异常链静态方法1,直接抛出第一个自定义的静态内部异常类

  • @throws FirstException

*/

public static void f1() throws FirstException {

throw new FirstException();

}

/**

  • 测试异常链静态方法2,调用f1()方法,并抛出第二个自定义的静态内部异常类

  • @throws SecondCustomException

*/

public static void f2() throws SecondCustomException {

try {

f1();

} catch (FirstException e) {

throw new SecondCustomException(e);

}

}

/**

  • 测试异常链静态方法3,调用f2()方法, 并抛出第三个自定义的静态内部异常类

  • @throws ThirdCustomException

*/

public static void f3() throws ThirdCustomException {

try {

f2();

} catch (SecondCustomException e) {

throw new ThirdCustomException(e);

}

}

public static void main(String[] args) throws ThirdCustomException {

// 调用静态方法f3()

f3();

}

}

运行结果:

Exception in thread “main” ExceptionDemo6$ThirdCustomException: 第三个异常

at ExceptionDemo6.f3(ExceptionDemo6.java:74)

at ExceptionDemo6.main(ExceptionDemo6.java:80)

Caused by: ExceptionDemo6$SecondCustomException: 第二个异常

at ExceptionDemo6.f2(ExceptionDemo6.java:62)

at ExceptionDemo6.f3(ExceptionDemo6.java:72)

… 1 more

Caused by: ExceptionDemo6$FirstException: 第一个异常

at ExceptionDemo6.f1(ExceptionDemo6.java:51)

at ExceptionDemo6.f2(ExceptionDemo6.java:60)

… 2 more

通过运行结果,我们看到,异常发生的整个过程都打印到了屏幕上,这就是一个异常链。

8. 小结


上一篇:springboot集成支付宝沙箱支付


下一篇:10.Java异常机制