在学习、工作与生活中,我们也经常会遇到一些非正常情况,例如电脑蓝屏、死机,感冒发烧,车票丢失等等。
在程序运行的过程中,也会发生这种非正常状况,例如程序运行时磁盘空间不足、网络连接中断、被加载的类不存在等。
在《Java讲课笔记08:数组》里,我们遇到过数组下标越界异常:
异常提示信息非常明确,指出程序中出现了数组下标越界异常。由于程序中第11行给不存在的数组元素scores[4]赋值,导致错误。这个异常发生后,程序会立即结束,无法继续向下执行,比如第12行的输出语句就没有被执行。
针对程序中的非正常情况,Java语言中引入了异常,以异常类
的形式对这些非正常情况进行封装,并通过异常处理机制
对程序运行时发生的各种问题进行处理。
Thorwable类(表示可抛出)是所有异常和错误的超类,两个直接子类为Error和Exception,分别表示错误和异常。
Exception称为异常类,它表示程序本身可以处理的错误。在Java程序开发中进行的异常处理,都是针对Exception类及其子类的。
Error称为错误,表示Java运行时产生的系统内部错误或资源耗尽的错误,是比较严重的,仅靠修改程序本身是不能恢复执行的,例如系统崩溃,虚拟机错误等。
方法声明 | 功能描述 |
---|---|
String getMessage() | 返回此throwable的详细消息字符串 |
void printStackTrace() | 将此throwable及其追踪输出至标准错误流 |
void printStackTrace(PrintStream s) | 将此throwable及其追踪输出到指定的输出流 |
在程序编译时期产生的异常,而这些异常必须要进行处理,也称为checked异常
。
在Exception的子类中,除了RuntimeException类及其子类外,其他子类都是编译时异常。比如IOException(EOFException、FileNotFoundException)、SQLException。
在程序编写过程中,Java编译器就会对编写的代码进行检查,如果出现比较明显的异常就必须对异常进行处理,否则程序无法通过编译。
这种异常即使不编写异常处理代码,依然可以通过编译,也称为unchecked异常
。
异常类名称 | 异常类说明 |
---|---|
ArithmeticException | 算术异常 |
IndexOutOfBoundsException | 下标越界异常 |
ClassCastException | 类型转换异常 |
NullPointerException | 空指针异常 |
NumberFormatException | 数字格式化异常 |
运行时异常是在程序运行时由Java虚拟机自动进行捕获处理的
,即使没有使用try…catch语句捕获或使用throws关键字声明抛出,程序也能编译通过
,只是在运行过程中可能报错。
运行时异常一般是由于程序中的逻辑错误引起的,在程序运行时无法恢复。
当程序发生异常时,会立即终止,无法继续向下执行。为了保证程序能够有效的执行,Java中提供了一种对异常进行处理的方式——异常捕获。
try {
// 可能发生异常的语句
} catch(Exception类或其子类 e){
// 对捕获的异常进行相应处理
}
package net.hw.lesson18;
import java.util.Scanner;
/**
* 功能:演示异常捕获
* 作者:华卫
* 日期:2020年05月16日
*/
public class Example1806 {
/**
* 整数除法
*
* @param m
* @param n
* @return q
*/
public static int divide(int m, int n) {
int q = m / n;
return q;
}
public static void main(String[] args) {
int m, n, q;
Scanner sc = new Scanner(System.in);
System.out.print("m = ");
m = sc.nextInt();
System.out.print("n = ");
n = sc.nextInt();
q = divide(m, n);
System.out.println(m + " ÷ " + n + " = " + q);
System.out.println("程序到此执行完毕!");
}
}
如果除数n不为0,程序运行正常,输出除法的结果:
如果除数n为0,程序抛出异常后终止,无法输出除法的结果:
public static int divide(int m, int n) {
try {
int q = m / n;
return q;
} catch (Exception e) { // 捕获异常进行处理
System.err.println("异常信息:" + e.getMessage());
}
return Integer.MIN_VALUE; // 程序异常时返回最小整数
}
public static void main(String[] args) {
int m, n, q;
Scanner sc = new Scanner(System.in);
System.out.print("m = ");
m = sc.nextInt();
System.out.print("n = ");
n = sc.nextInt();
q = divide(m, n);
if (q == Integer.MIN_VALUE) {
System.err.println("程序发生了异常!");
} else {
System.out.println(m + " ÷ " + n + " = " + q);
}
System.out.println("程序到此执行完毕!");
}
/ by zero
”。catch(){}代码块对异常处理完毕后,程序仍会向下执行,执行return Integer.MIN_VALUE;
,而不会因为异常而终止运行。int q = m / n;
”后面的返回语句“return q;
”就没有执行。try {
// 可能发生异常的语句
} catch(Exception类或其子类 e){
// 对捕获的异常进行相应处理
} finally {
// 无论是否发生异常,此代码块代码总会执行
}
System.exit(0)
语句,finally{}中的代码就不会被执行。因为System.exit(0)
语句表示退出当前的Java虚拟机,既然Java虚拟机都停止了,当然任何代码都不能再执行了。System.exit(0)
语句,于是finally{}代码块里的语句就没有被执行,输出信息里就没有“无论程序是否异常,都会执行~”了。如果有多个异常要处理,一般是先处理小异常,再依次处理较大的异常。
一般在程序开发中,开发者通常会意识到程序可能出现的问题,可以直接通过try…catch对异常进行捕获处理。但有些时候,方法中代码是否会出现异常,开发者并不明确或者并不急于处理,为此,Java允许将这种异常从当前方法中抛出,然后让后续的调用者在使用时再进行异常处理。
在Java中,将异常抛出需要使用throws关键字来实现,该关键字用在会抛出异常的方法名称后,同时支持一次性抛出多种类型的异常。
[修饰符] 返回值类型 方法名([参数类型 参数名1...]) throws 异常类1, 异常类2, ... {
// 方法体...
}
throws关键字需要写在方法声明的后面,并在后面需要声明方法中发生异常的类型。
package net.hw.lesson18;
import java.util.Scanner;
/**
* 功能:演示throws关键字
* 为方法声明抛出异常
* 作者:华卫
* 日期:2020年05月16日
*/
public class Example1807 {
public static int divide(int m, int n) throws Exception {
int q = m / n;
return q;
}
public static void main(String[] args) {
int m, n, q;
Scanner sc = new Scanner(System.in);
System.out.print("m = ");
m = sc.nextInt();
System.out.print("n = ");
n = sc.nextInt();
q = divide(m, n);
System.out.println(m + " ÷ " + n + " = " + q);
System.out.println("程序到此执行完毕!");
}
}
但是,程序编译报错,有未被处理的异常:
将鼠标移到红色波浪线上,按组合键
,系统给出两种快速解决方案:
package net.hw.lesson18;
import java.util.Scanner;
/**
* 功能:演示throws关键字
* 为方法声明抛出异常
* 作者:华卫
* 日期:2020年05月16日
*/
public class Example1807 {
public static int divide(int m, int n) throws Exception {
int q = m / n;
return q;
}
public static void main(String[] args) {
int m, n, q;
Scanner sc = new Scanner(System.in);
System.out.print("m = ");
m = sc.nextInt();
System.out.print("n = ");
n = sc.nextInt();
try {
q = divide(m, n);
System.out.println(m + " ÷ " + n + " = " + q);
} catch (Exception e) {
System.err.println("异常信息:" + e.getMessage());
}
System.out.println("程序到此执行完毕!");
}
}
当调用者在调用有抛出异常的方法时,除了可以在调用程序中直接进行try…catch异常处理外,也可以根据提示使用throws关键字继续将异常抛出,这样程序也能编译通过。但是,程序发生了异常,终究是需要进行处理的,如果没有被处理,程序就会非正常终止。
当传入sort()方法的数组参数arr不是null时,结果如下:
当传入sort()方法的数组参数arr是null时,结果如下: