持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第29天,点击查看活动详情
引入
异常,简单来说就是程序出问题了。那么编译代码提示的错误信息,就是异常吗?
之前我们所看到的编译错误,大部分只是代码中存在语法错误的提示信息,而不是异常。
慢慢的随着学习的深入,我们后面会看到,程序中有些可能会出现的异常,在编译时会给出提示信息,让你提前做好异常处理。
异常的最高父类是 Throwable,在 java.lang 包下。
Throwable 类的构造方法主要有:
-
public Throwable()
构造一个对象,其错误信息串为 null。
-
public Throwable(String message)
构造一个对象,其错误信息串为 message。
Throwable 类的方法主要有:
| 方法 | 说明 |
|---|---|
| public String getMessage() | 返回对象的错误信息 |
| public void printStackTrace() | 输出对象的跟踪信息到标准错误输出流 |
| public void printStackTrace(PrintStream s) | 输出对象的跟踪信息到输出流 s |
| public String toString() | 返回对象的简短描述信息 |
Throwable 类的子类有 Error和 Exception。
Error 错误并不能通过程序代码来解决问题,而 Exception 违例基本是程序员在编写代码时所造成的问题,所以往往我们会将 Error 设为天灾,Exception 设为人祸。 😲
所以我们之后所书写的代码中,基本都是在处理 Exception 问题。Exception 类也有很多的子类,提供我们使用,如下图:
下面我们通过一个实例来理解异常
假设有m个苹果,我们把他分给h个小朋友。 一般会这样写
// 苹果数
int apple_Num = 0;
// 学生数
int student_Num = 0;
System.out.println("**_现在给孩子们分苹果_**");
Scanner input = new Scanner(System.in);
System.out.print("请输入桌子上有几个苹果:");
apple_Num = input.nextInt();
System.out.print("请输入班上有几个孩子:");
student_Num = input.nextInt();
System.out.println("班上每个孩子分得多少苹果:" + apple_Num / student_Num);
但是这样有一个问题,如果孩子个数为0,就会出现除数为0情况。
马上我们就想到了,可以采用 while 循环的方式进行判断,如果用户输入的孩子数为 0,则要求用户继续输入,通过这种方式,就解决了除数为 0 的异常产生的问题
while (student_Num == 0) {
System.out.print("请输入班上有几个孩子(孩子数不能为 0):");
student_Num = input.nextInt();
}
System.out.println("班上每个孩子分得多少苹果:" + apple_Num / student_Num);
采用判断语句的方式进行异常的处理,首先必须意识到哪些地方可能出现异常,在可能出现异常的地方加入判断语句和处理代码。这种处理方式对程序员的要求高,因为开发者很难列举出所有的异常发生情况,而且代码量大,程序结构相对混乱。对于此类情况,一种较好的解决方案,就是使用异常处理机制。
异常处理捕获方式
Java 的异常处理机制中仍体现了面向对象的思想。
- Java 程序的执行过程中如出现异常,会自动生成一个异常类对象,该异常对象将被提交给 Java 运行时系统,这个过程称为抛出异常。
- 当 Java 运行时系统接收到异常对象时,会寻找能处理这一异常的代码并把当前异常对象交给其处理,这一过程称为捕获异常。
- 如果 Java 运行时系统找不到可以捕获异常的方法,则运行时系统将终止,相应的 Java 程序也将退出。
程序员通常只能处理Exception ,而对Error无能为力。
我们来看一下捕获异常的基本结构:
try{
// 有异常出现的语句,无异常语句也可以放在这个语句块中
}catch(ExceptionClass e){
// 当产生 ExceptionClass 类型异常时的处置代码
}[catch(ExceptionClass2 e){
// 当产生 ExceptionClass2 类型异常时的处置代码
}catch(ExceptionClassN e){
// 当产生 ExceptionClassN 类型异常时的处置代码
}]
从结构中我们可以看到,try 和 catch 是关键字,用来处理异常的。该结构中,catch 语句块中一定不要空着,啥都不写,这是一种很危险的情况,如果空着,表示捕获异常,而不做任何处理。
try 语句块:
- 将可能出现异常的代码都放在 try 代码块中。
- try 语句块中发现异常,剩下的语句将不再执行。
catch 语句块:
- 在 catch 语句块中是对异常对象进行处理的代码。
- 每个 try 语句块可以伴随一个或多个 catch 语句,用于处理可能产生的不同类型的异常对象。
- 通过 getMessage( ) 获取异常信息或 printStackTrace( ) 跟踪异常信息。
那么上面的小朋友案例我们可以写成
try {
System.out.print("请输入桌子上有几个苹果:");
apple_Num = input.nextInt();
System.out.print("请输入班上有几个孩子:");
student_Num = input.nextInt();
System.out.println("班上每个孩子分得多少苹果:" + apple_Num / student_Num);
}catch (Exception e){
System.out.println("孩子不能为0");
return;
}
这是最简化的书写方式,都采用 Exception 异常类捕获。
⭐ 注意:当使用多个 catch 进行异常捕获时,需要考虑前后顺序,也就是说需要先捕获子类异常,再捕获父类异常,不相关的异常类没有关系。(如果有多个异常,最后一个catch才是捕获Exception)
catch(Exception e){
System.out.println(e.getMessage());
}catch(ArithmeticException e){
e.printStackTrace();
}catch(NumberFormatException e){
e.printStackTrace();
}
System.out.println(c);
那么 ArithmeticException 和 NumberFormatException 异常捕获的 catch 语句块将永远都不会被执行,当然程序编译时就会给出错误提示信息。
异常处理抛出方式
异常的另一种处理方式,便是抛出异常,需要使用关键字 throws 。
异常抛出处理,主要是在方法声明部分使用。
语法格式:
[public|protected|private] 返回类型 方法名(参数列表) throws 异常类[, 异常类2, ..., 异常类n]{
// 方法体
}
throws 抛出的异常可以是 0 或多个,也就是说声明方法时可以不抛出异常,也可以抛出 1 个,或多个。
在定义一个方法的时候,如果并不能确定如何处理其中可能出现的异常,可以不在方法中对异常进行处理,而是将可能发生的异常让这个方法的调用者来处理。
比如我们之前小朋友的案例
public void method() throws Exception{
。。。
System.out.print("请输入班上有几个孩子:");
student_Num = input.nextInt();
System.out.println("班上每个孩子分得多少苹果:" + apple_Num / student_Num);
}
System.out.println("孩子们非常开心!");
}
接着,我们需要来强调的是主动抛出异常,使用关键字 throw。
🤔 那么什么是主动抛出异常呢?
之前讲到异常处理机制,当系统发现有异常,会自动产生一个异常对象,然后交给运行时系统,这叫抛出异常;主动抛出异常,那就是不需要系统生成异常对象,而是我们在编写代码时,对于可能会出现异常的部分,自己创建异常对象将它抛出,这就是主动抛出异常。
来看看语法结构:
throw new 异常类(message);
⭐ 注意:这里是 throw 而不是 throws,有很多人会搞混 throw 和 throws,你只需要记住一点,throw 关键字后只能抛出一个确切的异常类对象,而 throws 后可以抛出多个异常类,而非 new 的对象。
那么异常方法的重写需要方法重新要求+ 抛出的异常要么和父类一样,或是父类抛出异常的子类,但是不能是抛出异常的父类。
父类 ClassA 中的 method() 方法:
public void method() throws ArithmeticException, NumberFormatException{
}
子类 ClassB 中重写父类 method() 方法:
可以只抛出父类中的一个异常类。
public void method() throws ArithmeticException{
}
或是
public void method() throws NumberFormatException{
}
或是
public void method() throws ArithmeticException,NumberFormatException{
}
不抛出异常类也是可以的。
public void method(){
}
但是抛出的异常不能大于父类抛出的异常,则是错误的。
public void method() throws Exception{
}