目录
- 异常概述
- 异常分类
- 声明及抛出
- 捕获异常
- 自定义异常
- 异常堆栈
异常概述
异常指不期而至的各种状况,它在程序运行的过程中发生。作为开发者,我们都希望自己写的代码永远都不会出现 bug,然而现实告诉我们并没有这样的情景。如果用户在程序的使用过程中因为一些原因造成他的数据丢失,这个用户就可能不会再使用该程序了。所以,对于程序的错误以及外部环境能够对用户造成的影响,我们应当及时报告并且以适当的方式来处理这个错误。
之所以要处理异常,也是为了增强程序的鲁棒性。
异常都是从 Throwable 类派生出来的,而 Throwable 类是直接从 Object 类继承而来。你可以在 Java SE 官方 API 文档中获取更多关于它们的知识。
异常分类
异常通常有四类:
- Error:系统内部错误,这类错误由系统进行处理,程序本身无需捕获处理。
- Exception:可以处理的异常。
- RuntimeException:可以捕获,也可以不捕获的异常。
- 继承 Exception 的其他类:必须捕获,通常在 API 文档中会说明这些方法抛出哪些异常。
平时主要关注的异常是 Exception 下的异常,而 Exception 异常下又主要分为两大类异常,一个是派生于 RuntimeExcption 的异常,一个是除了 RuntimeExcption 体系之外的其他异常。
RuntimeExcption 异常(运行时异常)通常有以下几种:
- 错误的类型转换
- 数组访问越界
- 访问 null 指针
- 算术异常
一般来说,RuntimeException 都是代码逻辑出现问题。
非 RuntimeException(受检异常,Checked Exception)一般有:
打开一个不存在的文件
没有找到具有指定名称的类
操作文件异常
受检异常是编译器要求必须处理的异常,必须使用 try catch 处理,或者使用 throw 抛出,交给上层调用者处理。
声明及抛出
throw 抛出异常
当程序运行时数据出现错误或者我们不希望发生的情况出现的话,可以通过抛出异常来处理。
异常抛出语法:
throw new 异常类();ThrowTest.java
public class ThrowTest {public static void main(String[] args) {
Integer a = 1;
Integer b = null;
//当a或者b为null时,抛出异常
if (a == null || b == null) {
throw new NullPointerException();
} else {
System.out.println(a + b);
}
}
}Exception in thread "main" java.lang.NullPointerException
at ThrowTest.main(ThrowTest.java:8)
throws 声明异常
throws 用于声明异常,表示该方法可能会抛出的异常。如果声明的异常中包括 checked 异常(受检异常),那么调用者必须捕获处理该异常或者使用 throws 继续向上抛出。throws 位于方法体前,多个异常之间使用 , 分割。
ThrowsTest.java:
import java.io.FileInputStream;import java.io.FileNotFoundException;
public class ThrowsTest {
public static void main(String[] args) throws FileNotFoundException {
//由方法的调用者捕获异常或者继续向上抛出
throwsTest();
}
public static void throwsTest() throws FileNotFoundException {
new FileInputStream("/home/project/d.file");
}
}
编译运行:
Exception in thread "main" java.io.FileNotFoundException: /home/project/d.file (系统找不到指定的路径。)at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:195)
at java.io.FileInputStream.<init>(FileInputStream.java:138)
at java.io.FileInputStream.<init>(FileInputStream.java:93)
at ThrowsTest.throwsTest(ThrowsTest.java:13)
at ThrowsTest.main(ThrowsTest.java:8)
捕获异常
通常抛出异常后,还需要将异常捕获。使用 try 和 catch 语句块来捕获异常,有时候还会用到 finally。
对于上述三个关键词所构成的语句块,try 语句块是必不可少的,catch 和 finally 语句块可以根据情况选择其一或者全选。你可以把可能发生错误或出现问题的语句放到 try 语句块中,将异常发生后要执行的语句放到 catch 语句块中,而 finally 语句块里面放置的语句,不管异常是否发生,它们都会被执行。
你可能想说,那我把所有有关的代码都放到 try 语句块中不就妥当了吗?可是你需要知道,捕获异常对于系统而言,其开销非常大,所以应尽量减少该语句块中放置的语句。
自定义异常
尽管 Java SE 的 API 已经为我们提供了数十种异常类,然而在实际的开发过程中,你仍然可能遇到未知的异常情况。此时,你就需要对异常类进行自定义。
自定义一个异常类非常简单,只需要让它继承 Exception 或其子类就行。在自定义异常类的时候,建议同时提供无参构造方法和带字符串参数的构造方法,后者可以为你在调试时提供更加详细的信息。
// MyAriException.javapublic class MyAriException extends ArithmeticException {
//自定义异常类,该类继承自ArithmeticException
public MyAriException() {
}
//实现默认的无参构造方法
public MyAriException(String msg) {
super(msg);
}
//实现可以自定义输出信息的构造方法,将待输出信息作为参数传入即可
}
Test
// ExceptionTest.javaimport java.util.Arrays;
public class ExceptionTest {
public static void main(String[] args) {
int[] array = new int[5];
//声明一个长度为5的数组
Arrays.fill(array, 5);
//将数组中的所有元素赋值为5
for (int i = 4; i > -1; i--) {
//使用for循环逆序遍历整个数组,i每次递减
if (i == 0) {
// 如果i除以了0,就使用带异常信息的构造方法抛出异常
throw new MyAriException("There is an exception occured.");
}
System.out.println("array[" + i + "] / " + i + " = " + array[i] / i);
// 如果i没有除以0,就输出此结果
}
}
}
异常堆栈
当异常抛出后,我们可以通过异常堆栈追踪程序的运行轨迹,以便我们更好的 DEBUG。