目录
| 总框架
Java异常体系
-
Error:严重问题。如内存溢出(程序的源代码有语法错误,无法处理。只能修改源码)
-
RuntimeException:运行时异常。如空指针异常、数组越界异常、除零异常(无需显示处理,也可以和编译时异常一样处理)
-
RuntimeException之外的异常:编译期异常。如日期格式化异常、IO异常(必须显示处理,否则无法编译)
-
总结:Error异常必须修改代码来解决。Exception包含运行期异常和编译期异常,它们都可以用 try...catch 和 throws 来处理异常。但是区别在于运行期异常可以选择性处理 或者不处理,而编译期异常必须被try...catch或throw处理
产生异常的几种方式
-
程序运行时,由于代码产生的异常(运行时异常)
-
程序有编译期异常(本质上是由于类的成员方法throw了异常)
-
人为使用 throw 关键字产生异常对象(即人为通过代码创造异常)
throw
-
必须写在方法内部!
-
只能new Exception的异常对象(运行时异常 或 编译期异常),其中若抛出的是编译期异常则必须trycatch或者throws,若是运行时异常则可以选择不管
-
throw产生的异常,必须被处理:要么throws,要么try...catch
-
异常对象 也是一个对象,需要在程序运行时才能使用
-
语法
throw new XXXException("异常描述");
下面的代码演示了抛出异常的使用
int[] array = null;
getElement(array , 1);
public static int getElement(int[] arr , int index) throws NullPointerException{
if(arr == null){
throw new NullPointerException("Array is null"); //抛出异常,控制台提示“Array is null”
}
}
| Exception 异常处理(throws)
概述
-
前面说过,Error错误只能修改源码,而运行时异常可以选择不显示处理或者选择和编译期异常一样显示处理
但是 Exception的编译期异常必须显示处理! 否则无法通过编译
-
通过 throws 或 try...catch 进行编译期异常的显示处理(异常的过程:创建异常对象 → 抛出异常对象 → 处理异常)
-
下面介绍一下编译期异常的两种处理方法:throws、try...catch
throws抛出异常
-
区别一下throws 和 throw 的区别
-
throw的作用:
-
把异常从当前方法层层抛给调用当前方法的上级方法
-
若每一层方法都没有 try...catch,都是throws,则一直向上抛
-
若抛到当前父方法是Main,且Main中没有try...catch...,则抛给JVM,当前线程中断。
-
-
throw语法
修饰符 返回类型 方法名(参数列表) throws XXXException{
throws new XXXException("描述信息"); //手动抛出异常
XXXX //或者代码运行的时候产生了运行时异常
XXXX //或者代码有编译期异常
}
-
注意事项-
必须在方法声明的地方使用
-
只能抛Exception的异常
-
方法内若抛出了多个异常对象 / 有多个编译期异常,那么throws后面也必须声明多个异常,或者声明其父类
-
重写覆盖的方法中,覆盖方法可抛出的 编译期异常 的种类个数 ≤ 原方法的异常种类个数;原方法中抛出异常的范围 ≥ 覆盖重写方法中异常的范围
运行期异常则没有上述限制,子父类的覆盖方法之间没有抛异常的种类、个数要求。
-
| Exception 异常处理(try...catch)
try...catch 捕捉异常 的 基本语法
-
try...catch 自己承担异常,而throws是把异常推卸给别人。建议在Main中使用try...catch,在其它方法或者需要处理编译期异常的类中使用throws
-
try...catch 的流程:执行业务代码 → 若发现异常则捕捉并在catch中处理 → 执行finally → 继续执行try...catch后面的其它代码
try{
//业务代码
}catch(Exception e){
//捕获异常并打印
e.printStackTrace();
}finally{
//收尾代码(常用于关闭流)
}
-
可以写多个catch。但是对于有父子类关系的异常类,从上到下必须遵循 子类 → 父类 的原则
-
JDK1.8之后可以使用 | 符号来把多个异常写在同一个catch括号里。同样的,对于有父子类关系的异常类需要遵循 从左到右 子类 → 父类的原则
try{
//业务代码
}catch(FileNotFoundException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}finally{
//收尾代码(常用于关闭流)
}
try{
//业务代码
}catch(FileNotFoundException | IOException e){
e.printStackTrace();
}finally{
//收尾代码(常用于关闭流)
}
异常对象的常用方法
String msg = e.getMessage(); //获取异常的简单描述信息(黑字)
e.printStackTrace(); //打印异常追踪的堆栈信息(红字)
-
注:打印异常堆栈信息的线程和main的线程是不同的线程。因此红色的堆栈信息的输出位置不一定总是固定的。
-
异常堆栈信息的作用:让程序猿判断异常来源 调试代码
-
getMessage 的作用是:配合 printStackTrace 来提示出了异常。一般可以不使用这个方法
finally 的深入介绍
-
在finally子句中的代码最后执行,并且是一定会执行的,即使try语句块的代码出现了异常
-
注意:try、catch、finally中的代码块都是各自独立的局部变量
-
常把“用于资源释放/关闭的语句”写在finally中
-
若try中有return代码,则 JVM会在将运行到return的时候跳转到finally中的语句体,执行完成后才会进入return(return语句执行,方法必然结束)
若try和finally中都有return,则JVM遇到try的return的时候跳转到finally中,发现finally也有return,直接返回finally中的return
总得来说,return一定是最后执行的,且return语句一旦执行,整个方法必须结束。
-
遇到System.exit( 0 ); 必定直接退出虚拟机!无视finally
| 自定义异常
创建自定义异常类
//直接继承异常父类即可。父类已经把异常信息的操作都完成了,所在子类只要在构造时,将异常信息传递给父类通过super 语句即可。
public class MyException extends Exception{
//无参构造
public MyException(){
super();
}
//有参构造:传递异常说明参数
public MyException(String s){
super(s);
}
//有参构造:传递异常说明参数、异常对象
public CustomException(String message, Throwable cause){
super(message,cause);
}
//有参构造:传递异常对象
public CustomException(Throwable cause) {
super(cause);
}
}
使用自定义异常类
Step1.可以先自己创建一个自定义异常的类
Step2.在方法中:将自定义异常类的对象抛出(使用自定义异常)。
Step3.对于继承Exception的编译期异常,我们还要进行throws或者try...catch操作
//以上面我们自己创建的 MyException 为例
public class MainClass{
public static void main(String[] args){
//调用方法,假设这个方法内有编译期异常被抛出
try{
int num = 0;
method();
}catch(MyException e){
//捕获自定义异常
e.printStackTrace();
}finally{
System.out.println("finally代码块");
}
}
//方法中throw异常,并throws抛出
public void method(int x) throws MyException{
if(x == 0){
throw new MyException("异常:num为0");
}
}
}
应用
-
自定义异常类常用于下列场景:Java现有的异常类无法满足需求或者功能不全,则需要自己手动编写一个异常类来使用。
-
自定义异常类的本质就是继承已有的Java异常类,在其功能上做扩展。(继承)