【Java】异常

本文深入讲解Java异常处理,涵盖try-catch-finally结构,编译时与运行时异常的区别,以及自定义异常的创建与应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

定义

尽管人人希望自己身体健康,处理的事情都能顺利进行,但在实际生活中总会遇到各种状况,比如感冒发烧,工作时电脑蓝屏、死机等。同样,在程序运行的过程中,也会发生各种非正常状况,例如,程序运行时磁盘空间不足、网络连接中断、被装载的类不存在等。
针对这种情况, Java语言引入了异常,以异常类的形式对这些非正常情况进行封装,通过异常处理机制对程序运行时发生的各种问题进行处理。

异常发生后,程序会立即结束,无法继续向下执行

Java提供了大量的异常类,这些类都继承自java.lang.Throwable类。
在这里插入图片描述

分类

Throwable有两个直接子类Error和Exception,其中,Error代表程序中产生的错误,Exception代表程序中产生的异常。
● Error类称为错误类,它表示Java程序运行时产生的系统内部错误或资源耗尽的错误,这类错误比较严重,仅靠修改程序本身是不能恢复执行的。举一个生活中的例子,在盖楼的过程中因偷工减料,导致大楼坍塌,这就相当于一个Error。例如,使用java命令去运行一个不存在的类就会出现Error错误。
● Exception类称为异常类,它表示程序本身可以处理的错误,在Java程序中进行的异常处理,都是针对Exception类及其子类的。在Exception类的众多子类中有一个特殊的子类—RuntimeException类,RuntimeException类及其子类用于表示运行时异常。 Exception类的其他子类都用于表示编译时异常。

常用方法

Throwable类中的常用方法如下表。
在这里插入图片描述

try-catch-finally

出现异常后,程序会立即终止。为了解决异常,Java提供了对异常进行处理的方式一一异常捕获。异常捕获使用try…catch语句实现,try…catch具体语法格式如下:

try{
    //程序代码块
}catch(ExceptionType(Exception类及其子类) e){
    //对ExceptionType的处理
} 

上述语法格式中,在try代码块中编写可能发生异常的Java语句,catch代码块中编写针对异常进行处理的代码。
当try代码块中的程序发生了异常,系统会将异常的信息封装成一个异常对象,并将这个对象传递给catch代码块进行处理。
catch代码块需要一个参数指明它所能够接收的异常类型,这个参数的类型必须是Exception类或其子类。

示例:

1 public class Example25 {
2	public static void main(String[] args) {
3         //下面的代码定义了一个try…catch语句用于捕获异常
4		try {                                      
5			int result = divide(4, 0);    //调用divide()方法
6			System.out.println(result);   
7		} catch (Exception e) {            //对异常进行处理
8			System.out.println("捕获的异常信息为:" + e.getMessage());
9		}
10		System.out.println("程序继续向下执行...");
11	}
12 //下面的方法实现了两个整数相除
13	public static int divide(int x, int y) { 
14		int result = x / y;           //定义一个变量result记录两个数相除的结果
15		return result;                 //将结果返回
16	}
17 }

在这里插入图片描述
上述代码中,第4~9行代码是对可能发生异常的代码用try…catch语句进行了处理。在try代码块中发生除0异常时,程序会通过catch语句捕获异常,第8行代码在catch语句中通过调用Exception对象的getMessage()方法,返回异常信息“/ by zero”。catch代码块对异常处理完毕后,程序仍会向下执行,而不会终止程序。
需要注意的是,在try代码块中,发生异常语句后面的代码是不会被执行的,如上述代码中第6行代码的打印语句就没有执行。

在程序中,有时候会希望有些语句无论程序是否发生异常都要执行,这时就可以在try…catch语句后,加一个finally代码块。

1 public class Example26 {
2	public static void main(String[] args) {
3          //下面的代码定义了一个try…catch…finally语句用于捕获异常
4		try {
5			int result = divide(4, 0);       //调用divide()方法
6			System.out.println(result);
7		} catch (Exception e) {               //对捕获到的异常进行处理
8			System.out.println("捕获的异常信息为:" + e.getMessage());
9             			 return;                             //用于结束当前语句
10		} finally {                             
11			System.out.println("进入finally代码块");
12		}
13 System.out.println("程序继续向下执行…");
14	}
15     //下面的方法实现了两个整数相除
16	public static int divide(int x, int y) {
17		int result = x / y;           //定义一个变量result记录两个数相除的结果
18		return result;                 //将结果返回
19	}
20 }

在这里插入图片描述
上述代码中,第9行代码是在catch代码块中增加了一个return语句,用于结束当前方法,程序第13行代码就不会执行了,而finally代码块中的代码仍会执行,不受return语句影响。也就是说不论程序是发生异常还是使用return语句结束,finally中的语句都会执行。因此,在程序设计时,通常会使用finally代码块处理完成必须做的事情,如释放系统资源。

注意 finally中的代码块在一种情况下是不会执行的,那就是在try…catch中执行了System.exit(0)语句。System.exit(0)表示退出当前的Java虚拟机,Java虚拟机停止了,任何代码都不能再执行了。

throws关键字

在实际开发中,大部分情况下我们会调用别人编写方法,并不知道别人编写的方法是否会发生异常。针对这种情况,Java允许在方法的后面使用throws关键字对外声明该方法有可能发生的异常,这样调用者在调用方法时,就明确地知道该方法有异常,并且必须在程序中对异常进行处理,否则编译无法通过。

throws关键字声明抛出异常的语法格式如下:

修饰符 返回值类型 方法名(参数1,参数2.....)throws 异常类1, 异常类2.....{
        //方法体.....
}

从上述语法格式中可以看出,throws关键字需要写在方法声明的后面,throws后面需要声明方法中发生异常的类型。

示例:

1 public class Example27 {
2	public static void main(String[] args) {
3		int result = divide(4, 2);    //调用divide()方法
4		System.out.println(result);
5	}
6     //下面的方法实现了两个整数相除,并使用throws关键字声明抛出异常
7	public static int divide(int x, int y) throws Exception {
8		int result = x / y;           //定义一个变量result记录两个数相除的结果
9		return result;                 //将结果返回
10	}
11 }

上述代码报错:
在这里插入图片描述
在上述代码中,第3行代码调用divide()方法时传入的第二个参数为2,程序在运行时不会发生被0除的异常,但是由于定义divide()方法时声明了抛出异常,调用者在调用divide()方法时就必须进行处理,否则就会发生编译错误。

1 public class Example28 {
2	public static void main(String[] args) {
3         //下面的代码定义了一个try…catch语句用于捕获异常
4		try {
5			int result = divide(4, 2);   //调用divide()方法
7			System.out.println(result); 
8		} catch (Exception e) {           //对捕获到的异常进行处理
9			e.printStackTrace();          //打印捕获的异常信息
10		}
11	}
12 //下面的方法实现了两个整数相除,并使用throws关键字声明抛出异常
13	public static int divide(int x, int y) throws Exception {
14		int result = x / y;      //定义一个变量result记录两个数相除的结果
15		return result;            //将结果返回
16	}
17 }

在这里插入图片描述
上述代码由于使用了try…catch对divide()方法进行了异常处理,因此程序可以编译通过,运行后正确的打印出了运行结果2。程序运行结果如图。

由于使用了try…catch对divide()方法进行了异常处理,因此程序可以编译通过,运行后正确的打印出了运行结果2。下面修改上述程序,将divide()方法抛出的异常继续抛出。

1 public class Example29 {
2	public static void main(String[] args)throws Exception {
3		int result = divide(4, 0);   // 调用divide()方法
4		System.out.println(result);
5	}
6    // 下面的方法实现了两个整数相除,并使用throws关键字声明抛出异常
7	public static int divide(int x, int y) throws Exception {
8		int result = x / y;          // 定义一个变量result记录两个数相除的结果
9		return result;                // 将结果返回
10	}
11 }

在这里插入图片描述
上述代码,在main()方法继续使用throws关键字将Exception抛出,程序虽然可以通过编译,但从运行结果可以看出,在运行时期由于没有对“/by zero”的异常进行处理,最终导致程序终止运行。

编译时异常和运行时异常

在实际开发中,经常会在程序编译时产生一些异常,这些异常必须要进行处理,这种异常被称为编译时异常,也称为checked异常。另外还有一种异常是在程序运行时产生的,这种异常即使不编写异常处理代码,依然可以通过编译,因此被称为运行时异常,也称为unchecked异常。

编译时异常

在Exception类中,除了RuntimeException类及其子类,Exception的其他子类都是编译时异常。编译时异常的特点是Java编译器会对异常进行检查,如果出现异常就必须对异常进行处理,否则程序无法通过编译。
处理编译时期的异常有两种方式,具体如下:
(1)使用try…catch语句对异常进行捕获处理。
(2)使用throws关键字声明抛出异常,调用者对异常进行处理。

运行时异常

RuntimeException类及其子类都是运行时异常。运行时异常的特点是Java编译器不会对异常进行检查。也就是说,当程序中出现这类异常时,即使没有使用try…catch语句捕获或使用throws关键字声明抛出,程序也能编译通过。运行时异常一般是由程序中的逻辑错误引起的,在程序运行时无法恢复。

自定义异常

JDK中定义了大量的异常类,虽然这些异常类可以描述编程时出现的大部分异常情况,但是在程序开发中有时可能需要描述程序中特有的异常情况,例如,前面讲解的程序中的divide()方法,不允许被除数为负数。为了解决这个问题,Java允许用户自定义异常,但自定义的异常类必须继承自Exception或其子类。

// 下面的代码是自定义一个异常类继承自Exception
public class DivideByMinusException extends Exception{
	public DivideByMinusException (){
		super();          	// 调用Exception无参的构造方法
	}
	public DivideByMinusException (String message){
		super(message); 	// 调用Exception有参的构造方法
	}
}

在实际开发中,如果没有特殊的要求,自定义的异常类只需继承Exception类,在构造方法中使用super()语句调用Exception的构造方法即可。
自定义异常类中使用throw关键字在方法中声明异常的实例对象,格式如下:

throw Exception异常对象

示例:

1 public class Example30 {
2	public static void main(String[] args) {
3		int result = divide(4, -2);   			
4         System.out.println(result);
5	}
6	//下面的方法实现了两个整数相除
7	public static int divide(int x, int y) {
8		if(y<0){ 
9			throw new DivideByMinusException("除数是负数");		}
10		int result = x / y;   	// 定义一个变量result记录两个数相除的结果
11		return result;         	// 将结果返回
12	}
13 }

在这里插入图片描述
从运行结果可以看出,程序在编译时就发生了异常。因为在一个方法内使用throw关键字抛出异常对象时,需要使用try…catch语句对抛出的异常进行处理,或者在divide()方法上使用throws关键字声明抛出异常,由该方法的调用者负责处理。但是程序没有这样做。
为了解决上面的问题,对程序进行修改,在divide()方法上,使用throws关键字声明抛出DivideByMinusException异常,并在调用divide()方法时使用try…catch语句对异常进行处理。

1 public class Example31 {
2	public static void main(String[] args) {
3              // 下面的代码定义了一个try…catch语句用于捕获异常
4		try {
5			int result = divide(4, -2);  			
6              System.out.println(result);
7		} catch (DivideByMinusException e) {     // 对捕获到的异常进行处理
8			System.out.println(e.getMessage()); // 打印捕获的异常信息
9		}
10	}
11 // 下面的方法实现了两个整数相除,并使用throws关键字声明抛出自定义异常
12	public static int divide(int x, int y) throws DivideByMinusException {
13		if (y < 0) {
14			throw new DivideByMinusException("除数是负数");
15		}
16		int result = x / y;     // 定义一个变量result记录两个数相除的结果
17		return result;           // 将结果返回
18	}
19 }
   

在这里插入图片描述
上述代码中的main()方法中,第4~9行代码使用try…catch语句捕获处理divide()方法抛出的异常。在调用divide()方法时,如果传入的被除数为负数,程序会抛出一个自定义的DivideByMinusException异常,该异常最终被catch代码块捕获处理,并打印出异常信息。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值