一、异常的概念和作用
概念:
以下程序执行过程中发生了不正常的情况,这种不正常的情况叫做异常。
public class ExceptionTest01 {
public static void main(String[] args) {
int a = 10;
int b = 0;
int c = a / b;
System.out.println("a / b == " + c);
}
}
以上程序会在控制台输出下述错误信息,这个信息就是异常信息,是 JVM 打印的。
作用:
Java 是很完善的语言,提供了异常的处理方式。Java 会把异常信息打印输出到控制台,供程序员参考。程序员看到异常信息后,可以对程序进行修改,是程序更加健壮。
存在形式:
异常在 Java 中以类的形式存在,每一个异常类都可以创建异常对象。
二、异常的分类和继承结构
异常主要分为:错误、编译时异常(受控异常/受检异常)、运行时异常(非受控异常/未受检异常)。
- 错误:如果应用程序出现了 Error,那么将无法恢复,只能重新启动应用程序,最典型的 Error 的异常是 StackOverflowError。
- 编译时异常:出现了这种异常必须预先进行处理,不处理 Java 程序将无法编译通过。
- 运行时异常:此种异常可以不用显示的处理,例如被 0 除异常,Java 没有要求我们一定要处理。
编译时异常和运行时异常,都是发生在运行阶段。
三、异常的两种处理方式
第一种方式:
在方法声明的位置上,使用 throws 关键字,抛给上一级。谁调用我,我就抛给谁。抛给上一级。
第二种方式:
使用 try…catch 语句进行异常的捕捉。这件事发生了,谁也不知道,因为我给抓住了。
例:我是某集团的一个销售员,因为我的失误,导致公司损失了1000元,“损失1000元”这可以看做是一个异常发生了。我有两种处理方式:
- 第一种方式:我把这件事告诉我的领导。【异常上抛】
- 第二种方式:我自己掏腰包把这个钱补上。【异常的捕捉】
注意:Java 中异常发生之后如果一直上抛,最终抛给了 main 方法,main 方法继续向上抛,抛给了调用者 JVM,JVM 知道这个异常发生,只有一个结果:终止java程序的执行。
1、在方法定义处采用 throws 声明异常,如果声明的异常为编译时异常,那么调用方法必须处理此异常,否则编译器报错。
public class ExceptionTest02 {
public static void main(String[] args) {
doSome(); // 此处编译器会报错,因为没有处理编译时异常
}
// ClassNotFoundException 直接父类是 Exception,属于编译时异常
public static void doSome() throws ClassNotFoundException{
System.out.println("doSOme 执行");
}
}
上述程序在 doSome() 处会报错,因为没有处理编译时异常。此时有两种处理方式:
①在 main 方法声明的位置上使用throws,继续上抛异常,抛给调用者:
public static void main(String[] args) throws ClassNotFoundException {
doSome();
}
②try…catch 进行捕捉:
public static void main(String[] args){
try {
doSome();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
2、只要异常没有被捕捉,采取上抛的话,此代码的后续代码不会执行。try 语句块中某一行代码出现异常,该行代码后面的代码不会执行,会跳到 catch 语句块中执行。try…catch结束后,后续的代码也可以执行。
public class ExceptionTest03 {
public static void main(String[] args) {
try {
m1(); // 这条语句出现异常,捕捉后直接跳到 catch 中执行
System.out.println("hello"); //永远不会执行
}catch(Exception e) {
System.out.println("文件路径有问题!");
}
System.out.println("结束了"); // 可以执行
}
public static void m1() throws FileNotFoundException{
m2();
System.out.println("m1执行"); // 永远不会执行
}
public static void m2() throws FileNotFoundException{
new FileInputStream("......"); // 源码中抛出了 FileNotFoundException 异常
System.out.println("m2执行"); // 永远不会执行
}
}
3、深入try…catch
-
catch 后面的小括号,可以写具体的异常类型,也可以写该异常类型的子类型。
-
catch 可以写多个,建议一个一个地处理,精确处理,这样有利于程序的调试。
-
catch 写多个的时候,从上到下的类型必须遵守从小到大。
-
JDK 8 中 catch 后面一个括号中可以写多个异常类型,用“ | ”符号分隔。
4、异常对象的两个方法
-
取得异常描述信息:getMessage();
-
取得异常的堆栈信息(比较适合于程序调试阶段):printStackTrace();
public class ExceptionTest04{
public static void main(String[] args) {
NullPointerException np = new NullPointerException("空指针异常");
System.out.println(np.getMessage());
System.out.println("--------");
np.printStackTrace(); // 异步方式打印的
}
}
5、try…catch…finally…
异常的捕获和处理需要采用 try 和 catch 来处理,具体格式如下:
try {
}catch(OneException e) {
}catch(TwoException e) {
}finally {
}
-
try 中包含了可能产生异常的代码。
-
try 后面是 catch,catch 可以有一个或多个,catch 中是需要捕获的异常。
-
当 try 中的代码出现异常时,出现异常下面的代码不会执行,马上会跳转到相应的 catch 语句块中,如果没有异常不会跳转到 catch 中。
-
finally 表示,不管是否出现异常,finally 里的代码都执行,finally 和 catch可以分开使用,但 finally 必须和 try 一块使用,如 try { }finally { } 也是正确的。
finally 在任何情况下都会执行,通常在 finally 里关闭资源。
深入 finally:
public class ExceptionTest05 {
public static void main(String[] args) {
int a = 100;
int b= 10;
try {
int c = a / b;
System.out.println(c);
// 以下两种情况取一种测试
// return; // 会执行 finally,顺序是先try,再执行finally,最后执行return
System.exit(-1); // 不会执行 finally,退出 Java 虚拟机了
}catch(ArithmeticException ae) {
ae.printStackTrace();
}finally {
//只有 java 虚拟机退出不会执行 finally,其他任何情况下都会执行 finally
System.out.println("----------finally---------");
}
}
6、final、finally、finalize区别
-
final:是关键字,表示最终的、不可变的,被 final 修饰的类无法被继承,修饰的方法无法覆盖,修饰的变量不能重新赋值。
-
finally:是关键字,和 try 联合使用在异常处理机制中,finally 语句块中的语句一定会执行.
-
finalize:是标识符,是 Object 类中的一个方法名,这个方法是由垃圾回收器负责调用的。
四、自定义异常
Java 中自定义异常通常分两步:
-
写一个类继承 Exception 或 RuntimeException。
-
提供两个构造方法,一个无参的,一个有参带 String 参数的。
自定义异常通常继承于 Exception 还是 RuntimeException,应该看具体情况来定。
public class ExceptionTest06 {
public static void main(String[] args) {
try {
method1(10, 0);
}catch(MyException e) {
System.out.println(e.getMessage());
}
}
private static void method1(int value1, int value2) throws MyException {
if (value2 == 0) {
// 创建异常对象并抛出
throw new MyException("除数为 0");
}
int value3 = value1 / value2;
System.out.println(value3);
}
}
// 自定义异常类
class MyException extends Exception {
public MyException() {
//调用父类的默认构造函数
super();
}
public MyException(String s) {
//手动调用父类的构造方法
super(s);
}
}