Java异常的捕获及处理
一、Java异常简介
什么是异常?
程序在运行的时候,发生的不被期望的问题,它阻止了程序按照程序员的预期正常执行,这就是异常。在异常发生的时候,是任程序自生自灭,立刻退出终止。在Java中,即为Java在编译或运行或者运行过程中出现的错误。
Java提供了更为优秀的解决方法:异常处理机制。
异常处理机制能让程序在异常发生时,按照代码的预先设定的异常处理逻辑,针对性地处理异常,让程序尽最大可能恢复正常并继续执行,且保持代码的清晰。
Java中的异常不仅可以是函数中的语句执行时引发的,也可以是程序员通过throw 语句手动抛出的,只要在Java程序中产生了异常,就会用一个对应类型的异常对象来封装异常,JRE就会试图寻找异常处理程序来处理异常。
异常的好处:
例如, 我们用伪代码演示一下开始一局王者荣耀的过程。
LBYL 风格的代码(不使用异常)
boolean ret = false;
ret = 登陆游戏();
if (!ret) {
处理登陆游戏错误;
return;
}
ret = 开始匹配();
if (!ret) {
处理匹配错误;
return;
}
ret = 游戏确认();
if (!ret) {
处理游戏确认错误;
return;
}
EAFP 风格的代码(使用异常)
try {
登陆游戏();
开始匹配();
游戏确认();
选择英雄();
载入游戏画面();
...
} catch (登陆游戏异常) {
处理登陆游戏异常;
} catch (开始匹配异常) {
处理开始匹配异常;
} catch (游戏确认异常) {
处理游戏确认异常;
} catch (选择英雄异常) {
处理选择英雄异常;
} catch (载入游戏画面异常) {
处理载入游戏画面异常;
}
......
二、处理异常
Java异常机制用到的几个关键字:try、catch、finally、throw、throws。
捕获异常
基本语法:
try{
有可能出现异常的语句 ;
}[catch (异常类型 异常对象) {
} ... ]
[finally {
异常的出口
}]
- try 代码块中放的是可能出现异常的代码。
- catch 代码块中放的是出现异常后的处理行为。
- finally 代码块中的代码用于处理善后工作, 会在最后执行。其中 catch 和 finally 都可以根据情况选择加或者不加。
异常处理流程
- 程序先执行 try 中的代码
- 如果 try 中的代码出现异常, 就会结束 try 中的代码, 看和 catch 中的异常类型是否匹配.
- 如果找到匹配的异常类型, 就会执行 catch 中的代码
- 如果没有找到匹配的异常类型, 就会将异常向上传递到上层调用者.
- 无论是否找到匹配的异常类型, finally 中的代码都会被执行到(在该方法结束之前执行).
- 如果上层调用者也没有处理的了异常, 就继续向上传递,一直到 main 方法也没有合适的代码处理异常, 就会交给 JVM 来进行处理, 此时程序就会异常终止
注意事项:
- catch 不能独立于 try 存在。
- catch里面不能没有内容
- try, catch, finally 块之间不能添加任何代码。
- finally里面的代码最终一定会执行
- 如果程序可能存在多个异常,需要多个catch进行捕获。
抛出异常
除了 Java 内置的类会抛出一些异常之外, 程序猿也可以手动抛出某个异常. 使用 throw 关键字完成这个操作
public static void main(String[] args) {
System.out.println(divide(10, 0));
}
public static int divide(int x, int y) {
if (y == 0) {
throw new ArithmeticException("抛出除 0 异常");
}
return x / y;
}
// 执行结果
Exception in thread "main" java.lang.ArithmeticException: 抛出除 0 异常
三种类型的异常
-
检查性异常:最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。
-
运行时异常: 运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
-
错误: 错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译也检查不到的。
三、Java异常的分类
异常的根接口Throwable,其下有2个子接口,Error和Exception。
Error:指的是JVM错误,这时的程序并没有执行,无法处理;
Exception:指的是程序运行中产生的异常,用户可以使用处理格式处理。
顶层类 Throwable 派生出两个重要的子类, Error 和 Exception
其中 Error 指的是 Java 运行时内部错误和资源耗尽错误,应用程序不抛出此类异常。 这种内部错误一旦出现,除了告知用户并使程序终止之外, 再无能无力,这种情况很少出现。
Exception 是我们程序猿所使用的异常类的父类。其中 Exception 有一个子类称为 RuntimeException , 这里面又派生出很多我们常见的异常类NullPointerException , IndexOutOfBoundsException 等。
throw和throws都是在异常处理中常用的关键字,区别:
throw:指的是在方法中人为抛出一个异常对象(这个异常对象可能是自己实例化时已存在的);
throws:在方法的声明上使用,表示此方法在调用时必须处理异常。
四、自定义异常
在 Java 中可以自定义异常。如果要自定义异常类,则扩展Exception类即可
例如,实现一个用户登录功能
class UserError extends Exception {
public UserError(String message) {
super(message);
}
}
class PasswordError extends Exception {
public PasswordError(String message) {
super(message);
}
}
public static void main(String[] args) {
try {
login("admin", "666666");
} catch (UserError userError) {
userError.printStackTrace();
} catch (PasswordError passwordError) {
passwordError.printStackTrace();
}
}
public static void login(String userName, String password) throws UserError,
PasswordError {
if (!TestDemo.userName.equals(userName)) {
throw new UserError("用户名错误");
}
if (!TestDemo.password.equals(password)) {
throw new PasswordError("密码错误");
}
System.out.println("登陆成功!");
}
注意事项:
- 自定义异常通常会继承自 Exception 或者 RuntimeException
- 继承自 Exception 的异常默认是受查异常
- 继承自 RuntimeException 的异常默认是非受查异常