5 Java异常处理机制
5.1 概念
异常:Java程序运行期出现的错误
异常机制提供了程序退出的安全通道,当出现错误后,程序执行的流程发生改变,程序的控制权转移到异常处理器
5.2 分类
按运行时分类:
运行时异常(
RuntimeException
)- 都是
RuntimeException
类及其子类异常,如NullPointerException
(空指针异常)等 - 这些异常是不可查异常,可以处理(抛出或捕获)也可以不处理
- 通常是由于编程人员逻辑错误等原因引起,编码过程中程序员应尽量避免
- 运行时异常将由Java运行时系统自动抛出,允许应用程序忽略运行时异常
- 都是
非运行时异常(编译异常)
- 是
RuntimeException
以外的异常,类型上属于Exception
类及其子类,如IOException
、SQLException
等以及用户自定义的Exception
异常 - 必须进行处理,如果不处理,程序就不能编译通过
- 是
按是否可查分类:
可查异常(
checked exceptions
)- 编译器要求必须处理(抛出或捕获)的异常
- 编译不通过时如果不处理会报错
不可查异常(
unchecked exceptions
)- 编译器不要求强制处理的异常,出现错误需要修改相应程序,不需要抛出或捕获
- 编译时不会报错
- 包括运行时异常(
RuntimeException
与其子类)和错误(Error
)
错误(
Error
):- 是程序无法处理的错误,表示运行应用程序中较严重问题,大多数错误与代码编写者执行的操作无关
- 例如:Java虚拟机运行错误(
Virtual MachineError
)或当 JVM 内存资源使用完时会现OutOfMemoryError
等 - 错误是不可查的,一般它们在应用程序的控制和处理能力之外
异常常用方法
e.printStackTrace()
:打印异常堆栈信息e.getMessage()
:获取异常信息,如果没有为null
e.getLocalizedMessage()
:一般由子类重写加入特定语言环境信息,默认与getMessage()
相同
异常与重写
- 重写的方法必须和被重写的方法抛出的异常相同,或者不抛出,不能抛出被重写方法异常的父类或子类
5.3 异常的捕获和处理
5.3.1 抛出异常
- 使用
throw 异常类()
抛出异常 - 使用
throws
在方法上声明抛出的异常,可以是多个 - 异常抛出后并没有真正处理异常,需要调用方进行处理,如果调用方不能处理,则继续向上抛出
5.3.2 捕获异常
- 使用
try
包裹可能出现异常的代码 - 使用
catch
捕获对应的异常对象,可以有多个 finally
中的代码不管有没有异常,最终都会被执行,finally
可以省略catch
到异常后,一定要做出处理,养成良好编程习惯,不要把异常吞掉catch
多个异常时,注意顺序,应该先catch
小类型,再父类型,如果是同级别的异常类,则不用注意顺序
5.3.3 代码示例
/**
* 异常体系
*/
public class ExceptionSys {
public static void main(String[] args) {
// 常见异常
commonExceptions();
// 当调用异常的方法时,需要进行捕获或者继续抛出
try {
testThrow(1, 0);
} catch (Exception e) {
// e.printStackTrace();
System.out.println(e.getMessage());
System.out.println(e.getLocalizedMessage());
}
testTryCatch();
}
// 常见异常
public static void commonExceptions() {
// NullPointerException : 空指针异常
byte[] bt = null;
System.out.println(bt[1]);
// ArrayIndexOutOfBoundsException : 数组下表越界异常
byte[] btt = new byte[2];
System.out.println(btt[3]);
// ArithmeticException : 算数异常
int a = 10, b = 0;
int c = a/b;
}
// 抛出异常
public static void testThrow(int a, int b) throws Exception {
if (b == 0)
throw new Exception(); // 也可以抛出确定的异常ArithmeticException或该异常的父类,如 throw new Exception();
int c = a/b;
System.out.println(c);
}
/*
* 注意抛出异常的顺序,父类放在最后,如果父类放在上面,会先进入父类,达不到想要的效果
* 也可以不catch子类,直接catch父类异常
*/
public static void testTryCatch() {
try {
byte[] bt = null;
System.out.println(bt[1]);
byte[] btt = new byte[2];
System.out.println(btt[3]);
} catch (NullPointerException e){
System.out.println("空指针异常");
} catch (ArrayIndexOutOfBoundsException e){
System.out.println("数组下标越界异常");
} catch (Exception e){
System.out.println("异常...");
} finally {
System.out.println("执行了finally");
}
}
}
5.4 自定义异常
5.4.1 方法
继承一个异常父类
- 如果说自定义异常是编译期异常那么继承
Exception
- 如果说自定义异常是运行期异常那么继承
RuntimeException
- 或者根据情况继承其他
Exctption
- 如果说自定义异常是编译期异常那么继承
添加构造方法
- 可以传入异常参数信息,也可以不写参数
5.4.2 代码示例
/**
* 自定义异常
*/
public class CustomException {
public static void main(String[] args) {
Bank bank = new Bank();
try {
bank.getMoney(100, 1000);
} catch (NoMoneyException e) {
e.printStackTrace(); // 打印异常堆栈信息
System.out.println("异常信息:" + e.getMessage()); // 获取异常信息
System.out.println(e.getLocalizedMessage()); // 一般由子类重写加入特定语言环境信息,默认与getMessage()相同
// 其他相关处理...
}
}
}
class Bank {
public void getMoney(double totalMoney, double getMoney) throws NoMoneyException {
if (totalMoney < getMoney) {
throw new NoMoneyException("余额不足...");
} else {
System.out.println("取款成功...");
}
}
}
// 自定义异常类
class NoMoneyException extends Exception {
// 自定义异常类中还可以进行其他操作,如传入一些相关参数,定义其他处理方法等
public NoMoneyException(String msg) {
super(msg); // 调用父类带参构造方法可以传入异常信息
}
}
class Xbank extends Bank {
/*
* 重写的方法必须和被重写的方法抛出的异常相同,或者不抛出
* 不能抛出被重写方法异常的父类或子类
*/
@Override
public void getMoney(double totalMoney, double getMoney) throws NoMoneyException {
// super.getMoney(totalMoney, getMoney);
}
}