更新时间:2022-12-27 12:09:13 来源:极悦 浏览1019次
不同于编译出错,这里的异常指的是运行时异常,指的是程序已经编译通过得到 class 文件了, 再由 JVM 执行过程中出现的错误,如我们之前常见的空指针异常、除0异常、数组越界异常等
如: 空指针异常
public static void main(String[] args) {
String str = null;
System.out.println(str.equals("1"));
}
执行结果:
NullPointerException 即为空指针异常
JDK中的内置异常类均在java.lang包下
java.lang.Throwable类是Java中所有异常或者错误的超类
LBYL:
Look Before You Leap. 在操作之前就做充分的检查.
EAFP:
It's Easier to Ask Forgiveness than Permission. "事后获取原谅比事前获取许可更容易". 也就是先操作, 遇到问题再处理.
在java中主要是用EAFP方法,先做,如果有异常,我们再对异常处理
通常是用try - catch - finally进行捕获异常
如果某方法可能存在异常,但不想在这里处理,我们就可以使用throws抛出异常给调用它的上一级,上一级在调用该方法时使用try - catch - finally 捕获异常
try - catch -finally捕获异常
try{
// 有可能出现异常的代码块
}catch ((异常类 异常对象)Exception e){
// 捕获到该异常后执行的代码
}finally{
// 无论是否有异常发生都会执行的代码,善后处理
}
}
注意:
(1)catch语句可有0 - n个,即可以不写catch语句,也可以写多个,如果可能抛出的异常不止一个,就可以写多个catch语句
(2)finally语句可有0或1个,无论是否捕获到异常,均会执行finally语句,finally语句通常用于资源的关闭释放等操作
(3)catch语句中,catch的小括号里即是写捕获到的异常类的异常对象,如上述代码中的 Exception e,就是Exception 异常类的对象e
(4)这里要说明,Exception类是所有异常类的共同父类,所以当可能有多个异常或者具体是说明异常自己也不清楚的时候,可以用Exception类代替,但并不推荐这种写法,因为这样写不便于排查异常原因,所以通常还是选择用多个catch语句捕获异常
异常处理流程:
程序先执行 try 中的代码
如果 try 中的代码出现异常, 就会结束 try 中的代码, 看和 catch 中的异常类型是否匹配.
如果找到匹配的异常类型, 就会执行 catch 中的代码如果没有找到匹配的异常类型, 就会将异常向上传递到上层调用者.
无论是否找到匹配的异常类型, finally 中的代码都会被执行到(在该方法结束之前执行).
如果上层调用者也没有处理的了异常, 就继续向上传递.一直到 main 方法也没有合适的代码处理异常, 就会交给 JVM 来进行处理, 此时程序就会异常终止.
(1)Error类是程序内部错误,程序员无法捕获处理,所以 我们主要关注Exception类及其子类
(2)异常按照是否必须显式处理又分为非受查异常和受查异常
非受查异常:
不强制要求处理,只有Error类和RuntimeException类及其子类是非受查异常,其他都是受查异常
受查异常:
必须使用try -catch捕获异常或者throws抛出异常,否则会编译报错!!!
如果一个方法产生了异常,但并没有对该异常进行处理,则会将异常抛出给其方法调用者,也就是说,只要异常没有遇到处理方法,就会一直抛给调用它的上一级,直至被处理或者最后扔给JVM
在 JVM 中有一块内存空间称为 "虚拟机栈" 专门存储方法之间的调用关系. 当代码中出现异常的时候, 我们就可以使用 e.printStackTrace(); 的方式查看出现异常代码的调用栈.
throws:用在方法声明上,明确表示该方法会产生该异常,但方法本身并没有对异常进行处理,所以该方法的异常会抛给调用它的上一级,所以 throws 关键字, 把可能抛出的异常显式的标注在方法定义的位置. 从而提醒调用者要注意捕获这些异常.
throw:用在方法内部,表示人为抛出异常,通常和自定义异常结合使用,在抛出异常后,该方法就会结束
如下面这段代码块,就是throw 和 throws 的用法
// throws用于方法声明,提醒调用者注意对这一异常捕获处理
public static int divide(int x, int y) throws ArithmeticException {
if (y == 0) {
// throw用于方法内部,人为抛出该异常
throw new ArithmeticException("抛出除 0 异常");
}
return x / y;
}
Java 中虽然已经内置了丰富的异常类, 但是我们实际场景中可能还有一些情况需要我们对异常类进行扩展, 创建符合我们实际情况的异常.
例如可能我们会用到具体的登录异常,密码错误异常等,这些都需要我们自己去定义
自定义异常通常会继承自 Exception 或者 RuntimeException
继承自 Exception 的异常默认是受查异常
继承自 RuntimeException 的异常默认是非受查异常
下面以登录异常为例,我们自己创建两个异常类
import java.util.Scanner;
//自定义异常类
//以用户登录为例
public class Login {
private static String name = "阿衡";
private static String password = "123456";
public static void main(String[] args) {
try {
login();
System.out.println("登录成功!");
} catch (nameException e) {
System.out.println("用户名错误!");
// 打印错误堆栈信息
e.printStackTrace();
}
}
// 因为nameException继承自受查异常,所以这里在抛出该异常时,方法声明必须throws该异常交至上一级try-catch,否则会编译报错
public static void login() throws nameException {
System.out.println("请输入您的用户名:");
Scanner scanner = new Scanner(System.in);
String name1 = scanner.next();
System.out.println("请输入用户密码:");
String password1 = scanner.next();
// 如果输入的用户名和预定的不一致,则抛出用户名错误异常
if (!name1.equals(name)) {
throw new nameException("用户名错误!");
}
// 如果密码不一致,则抛出密码错误异常
if (!password1.equals(password)) {
throw new passwordException("密码错误!");
}
}
}
//用户名错误异常
//受查异常,必须显示使用try-catch或throws抛出
class nameException extends Exception {
public nameException(String msg) {
super(msg);
}
}
//密码错误异常
//非受查异常,可以只用于抛出而不使用try-catch或throws处理
class passwordException extends RuntimeException {
public passwordException(String msg) {
super(msg);
}
}
值得注意的是,密码错误异常继承自非受查异常,所以我们可以不在login方法中用throws显式抛给上一级,在main中也没有显式的用catch语句捕获处理
但是,对于受查异常,我们看到,如果不处理会编译报错
最后,综上,我们来个重点小总结:
(1)java用try-catch-finally捕获异常进行处理
(2)throw关键字用于抛出异常
(3)throws关键字则是声明有该异常且我未处理
(4)对于本方法的异常没有处理的或者直接使用throw的我们都会抛给上一级让上一级捕获处理异常
(5)同时,我们可以自定义异常类
(6)最后,就是注意区分非受查异常(Runtimexception及其子类)和受查异常
0基础 0学费 15天面授
Java就业班有基础 直达就业
业余时间 高薪转行
Java在职加薪班工作1~3年,加薪神器
工作3~5年,晋升架构
提交申请后,顾问老师会电话与您沟通安排学习