java 的异常机制主要依赖于 try、catch、finally、throw和throws这五个关键字
对于一个程序开发人员,需要尽可能地预知所有可能发生的情况,尽可能地保证程序在所有糟糕的情形下都可以运行
对于构造大型、健壮、可维护性的应用而言,错误处理是整个应用需要考虑的重要方面,程序员在开发程序的过程,是一个创造的过程,这个过程需要有全面的考虑,仅做“对”的事情是远远不够的
Java的异常处理机制可以让程序具有极好的容错性,让程序更加健壮,当程序出现意外情形时,系统会自动生成一个Exception对象来通知程序,从而实现将“业务功能实现代码”和“错误处理代码”分离,提供更好的可读性
1.使用try…catch捕获异常
try{
//业务实现代码
...
} catch(Exception e) {
}
如果执行try块里的业务逻辑时出现异常,系统自动生成一个异常对象,该异常对象被提交给java运行环境,这个过程被称为抛异常。
当java运行时环境收到异常消息时,会寻找能处理该异常对象的catch块,如果找到合适的catch块,则把该异常对象交给该catch块处理,这个过程被称为捕获异常,如果java运行时环境找不到捕获该异常的catch块,则运行时环境终止,程序就此退出。
2.异常类的继承体系
java把所有非正常的情况分为两种:异常(Exception)和错误(Error),它们都继承父类Throwable
Error错误一般指与虚拟机相关的问题,如系统崩溃、虚拟机错误等,这种错误无法恢复或不可捕获,将导致程序中断。通常应用程序无法处理这些错误,因此不应该试图使用catch块来捕捉Error对象。
进行异常捕获的时候不仅应该把Exception类对于的catch块放在最后,而且所有父类异常的catch块都应排在子类异常的catch块后面(简称:先处理小异常,再处理大异常),否则出现编译错误
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7mf3Ehlu-1625416839477)(7EAC4FF2A2844FD296A03E9B1D443082)]
java7提供多异常捕获,在java7之前,一个catch块只能捕获一种类型的异常,但从java7开始,一个catch块可以捕获多种类型的异常,使用时需要注意两点:
1.捕获多种异常类型时,多种异常类型之间用竖线(|)隔开
2.捕获多种异常类型时,异常变量有隐式的final修饰,因此程序不能对异常对象重新赋值
3.访问异常信息
当java运行时决定调用某个catch块来处理该异常对象时,会将异常对象赋给catch块后的异常参数,程序即可通过该参数来获得异常的相关信息
所有的异常信息都包含了如下几个常用方法:
- getMessage(): 返回该异常的详细描述字符串
- printStackTrace(): 将该异常的跟踪栈信息输出到标准错误输出
- printStackTrace(PrintStream s): 将该异常的跟踪栈信息输出到指定输出流
- getStackTrace():返回该异常的跟踪栈信息
4.Checked异常和Runtime异常体系
Java的异常被分为两大类:Checked异常和Runtime异常(运行时异常)。
所有的RuntimeException类及其子类的实例被称为Runtime异常;
不是RuntimeException类及其子类的异常实例被称为Checked异常
5.throws声明抛出异常
使用throws声明抛出异常的思路是:当前方法不知道如何处理该异常,该异常应由上一级调用者处理;如果调用方也不知道如何处理该异常,异常会向上一级抛出,该异常将交给JVM处理
JVM处理异常的方式是,打印异常的跟踪栈信息,并中止程序
throws声明抛出只能在方法定义中使用,throws可以声明抛出多个异常类,多个异常类之间以逗号隔开,throws声明抛出的语法格式如下:
throws ExceptionClass1, ExceptionClass2...
一旦使用了throws抛出该异常,程序就无需使用try…catch来捕获该异常了
6.throw抛出异常
当程序出现错误时,系统会自动抛出异常,除此之外,java也运行程序自行抛出异常,自行抛出异常使用throw语句来完成(注意此处的throw没有后面的s,注意区分throws)
如果throw抛出的异常是checked异常,则该throw语句要么处于try块里,显示捕获该异常,要么放在一个带throws声明抛出的方法中,即是将该异常交给调用者处理
如果throw语句抛出的异常是Runtime异常,则该语句无需放在try块里,也无需放在带throws声明的方法中
public class ExceptionDemo {
//setHeight方法声明抛出HeightException异常,是Checked异常,所以调用该方法的代码要么处于try...catch块
//要么处于另一个带throws声明抛出的方法中
public void setHeight(int height) throws HeightException{
if (height < 0) {
throw new HeightException("身高 不可小于0");
}
System.out.println("身高为" + height);
}
public static void main(String[] args) {
ExceptionDemo ex = new ExceptionDemo();
try {
ex.setHeight(-2);
} catch (HeightException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("身高设置完成");
}
//自定义异常类
class HeightException extends Exception {
public HeightException(String exc){
System.out.println(exc);
}
}
}
7.java的异常跟踪栈
异常对象的printStackTrace()方法用于打印异常的跟踪栈信息,根据printStackTrace()方法的输出结果,开发者可以找到异常的源头,并跟踪一路触发的过程
public class ExceptionDemo {
//setHeight方法声明抛出HeightException异常,所以调用该方法的代码要么处于try...catch块
//要么处于另一个带throws声明抛出的方法中
public void setHeight(int height) throws HeightException{
if (height < 0) {
throw new HeightException("身高 不可小于0");
}
System.out.println("身高为" + height);
}
public static void main(String[] args) throws HeightException {
ExceptionDemo ex = new ExceptionDemo();
ex.setHeight(-2);
System.out.println("身高设置完成");
}
class HeightException extends Exception {
public HeightException(String exc){
System.out.println(exc);
}
}
}
第一行的信息详细显示了异常的类型和异常的详细信息
接下来跟踪堆栈记录程序中所有发生的异常点,各行显示被调用方法中执行的停止位置,并标明类、类中的方法名、与故障点对应的文件的行。一行行地往下看,跟踪栈总是最内部的被调用方法逐渐上传,直至最外部业务操作的起点。
8.特别注意
主线程try...catch不能捕捉子线程的异常
public class ThreadException extends Thread{
public void run() {
for(int i = 0; i < 30; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
if (i == 10) {
System.out.println(1/0);
}
}
}
public static void main(String[] args) {
System.out.println("::main() " + Thread.currentThread().getName());
ThreadException ex = new ThreadException();
ex.start();
try {
ex.info();
} catch (Exception e) {
System.out.println(e.toString());
}
}
public void info() {
System.out.println("::info() " + Thread.currentThread().getName());
System.out.println(1/0);
}
}