在Java中,程序出现的某些不正常行为,很难通过修改代码来解决的一些问题就是异常

注意:异常名实际上是类!!!

一、异常的体系结构

体系结构如下图:
在这里插入图片描述
箭头指向为被继承的类,与前面学习的类与类之间的继承相同,接下来对上图的一些困惑进行解释:

  • Throwable:是异常体系的顶层类,派生出两个重要的子类,Error和Exception
  • Error:指的是Java虚拟机无法解决的严重问题
  • Exception:异常后程序员可以通过代码处理,使得程序继续执行

二、异常的分类

1.编译时异常

在程序编译期间发生的异常,称为编译时异常,也称为受检查异常(Checked Exception)

2.运行时异常

在程序执行期间发生的异常,称为运行时异常,也称为非受检查异常(Unchecked Exception)

RunTimeException以及其子类对应的异常,都称为运行时异常。比如:NullPointerException、ArrayIndexOutOfBoundsException、ArithmeticException。
注意:如果说编写代码时的语法错误,并不能称之为异常!

三、异常的处理

1.在有异常时通知到位(防御式编程)

在这种状况下,有两种处理方式:

  • 事前防御性:先预计可能出现的问题,如果真的出现了预料中的问题再进行处理
  • 事后认错型:等到出现问题了再进行处理

这就是处理异常的两个不同的思路,在Java中,异常处理主要的5个关键字:throw、try、catch、final、throws。
接下来正式介绍异常应该如何处理

2.异常的抛出

抛出的意思就是如果出现了问题,你就要把这个问题告知给程序员,让其找到并修改.

具体语法如下:

throw new XXXException("异常产生的原因");

使用方法为:

public static int getElement(int[] array, int index){
if(null == array){
throw new NullPointerException("传递的数组为null");
}
if(index <
0 || index >
= array.length){
throw new ArrayIndexOutOfBoundsException("传递的数组下标越界");
}
return array[index];
}
public static void main(String[] args) {
int[] array = {
1,2,3
};
getElement(array, 3);
}

注意事项为:

  • throw必须写在方法体内部
  • 抛出的对象必须是ExceptionException的子类对象
  • 如果抛出的是RunTimeException或者RunTimeException的子类对象,则可以不用处理,交给JVM来处理
  • 如果抛出的是编译时异常,用户必须处理,否则程序无法通过编译
  • 当异常抛出后,其后的代码便不会再执行

3.异常的捕获

A.异常声明throws

(如果用户不想处理该异常,提醒方法的调用者处理异常)
语法格式:

修饰符 返回值类型 方法名(参数列表) throws 异常类型1,异常类型2...{
}
  • throws必须跟在方法的参数列表之后
  • 声明的异常必须是Exception或者其子类
  • 方法内部如果抛出了多个异常,throws之后必须跟上多个异常类型,用逗号隔开,如果他们之间有父子关系,仅仅声明父类即可

B.核心——try-catch捕获并处理

语法格式:

try{
//将可能要出现异常的代码放在这里
}catch(要捕获的异常类型 e){
//如果try中的代码抛出异常了,此处catch捕获时异常类型与try抛出的异常类型一致时,或者是try中抛出的异常基类时,就会被捕捉到
//处理完异常后,跳出try-catch,继续执行
}fianlly{
//此处代码一定执行
}

那如果是抛出多个异常对象,还是这样吗?
当然,仍然是,只不过是多种异常,多次捕获,catch可以写成这样:

catch (ArrayIndexOutOfBoundsException | NullPointerException e) {
//...
}

如图,可以用|来连接
那如果异常之间有父子关系的话,顺序有什么要求呢?
一定是子类在前catch,父类在后,否则会发生语法错误

4.异常的处理流程

方法之间是存在相互调用关系的, 这种调用关系我们可以用 “调用栈” 来描述. 在 JVM 中有一块内存空间称为"虚拟机栈" 专门存储方法之间的调用关系. 当代码中出现异常的时候, 我们就可以使用 e.printStackTrace(); 的方式查看出现异常代码的调用栈.

如果本方法中没有合适的处理异常的方式, 就会沿着调用栈向上传递

如果向上一直传递都没有合适的方法处理异常, 最终就会交给 JVM 处理, 程序就会异常终止(和我们最开始未使用 try-catch 时是一样的)

异常处理流程总结:

  • 程序先执行 try 中的代码
  • 如果 try 中的代码出现异常, 就会结束 try 中的代码, 看和 catch 中的异常类型是否匹配.
  • 如果找到匹配的异常类型, 就会执行 catch 中的代码
  • 如果没有找到匹配的异常类型, 就会将异常向上传递到上层调用者.
  • 无论是否找到匹配的异常类型, finally 中的代码都会被执行到(在该方法结束之前执行).
  • 如果上层调用者也没有处理的了异常, 就继续向上传递.
  • 一直到 main 方法也没有合适的代码处理异常, 就会交给 JVM 来进行处理, 此时程序就会异常终止.

四、自定义异常类

具体方法:

  1. 自定义异常类,然后继承自Exception 或者 RunTimeException
  2. 实现一个带有String类型参数的构造方法,参数含义:出现异常的原因

示例代码:

class UserNameException
extends Exception {
public UserNameException(String message) {
super(message);
}
}
class PasswordException
extends Exception {
public PasswordException(String message) {
super(message);
}
}

注意事项:

  • 自定义异常通常会继承自Exception或是RunTimeException
  • 继承自Exception的异常通常是受查异常(必须被处理的异常)
  • 继承自RunTimeException的异常通常是非受查异常

文章如有明显错误欢迎大家指正.

posted on 2025-09-22 09:28  lxjshuju  阅读(6)  评论(0)    收藏  举报