异常

概述

异常是指程序在执行过程中出现的非正常情况,如果不处理最终会导致JVM的非正常停止

异常的抛出机制:Java把不同异常用不同的类表示,一旦发生某种异常,就创建该异常类型的对象,并且抛出(throw)。然后程序员可以捕获(catch)到这个异常现象,并处理;如果没有捕获这个异常对象,那么这个异常对象会导致程序的终止。

常见异常

Error:StackOverflowError、OOM

Exception:

编译时异常(受检异常)在执行javac.exe时出现的异常:

  • ClassNotFoundException
  • FileNotFoundException
  • IOException

运行时异常(非受检异常)在执行java.exe时出现的异常:

  • ArrayIndexOutOfBoundsException
  • NullPointerException
  • ClassCastException
  • NumberFormatException
  • InputMismatchException
  • ArithmeticException

异常处理

方式一:try-catch-finally

基本结构:

1
2
3
4
5
6
7
8
9
10
try{
......//可能产生异常的代码
}catch(异常类型1 e){
......//当产生异常类型1时的处置措施
}catch(异常类型1 e){
......//当产生异常类型2时的处置措施
}
finally{
......//无论是否异常都会执行
}

使用细节:

  • 如果声明多个异常类型且存在子父类关系,则子类异常要写在上面。否则无关上下。
  • catch中异常处理的方式:
    • 自己编写输出的语句
    • printStackTrace():打印异常的详细信息
    • getMessage():获取发生异常的原因
  • try中声明的结构,除了try结构后就不可以再调用了
  • 一定要被执行的代码写在finally
    • 无论try中或catch中是否存在仍未被处理的异常,无论try中或catch中是否存在return,都会执行。唯一的例外:System.exit(0)会强行终止当前运行的VM
    • 在开发中,有一些资源(输入流、输出流、数据库连接、Sokect),在使用完后,必须显式地进行关闭,否则会导致内存泄漏。

方式二:throws+异常类型

格式:public void test() throws 异常类型1,异常类型2,.....{//可能存在编译时异常的代码}

throws处理异常的方式,仅是将可能出现的异常抛给了此方法的调用者。调用者仍要考虑如何处理异常。

方法重写的要求:(针对编译时异常)

**(重要)**子类抛出的异常类型要么和父类异常类型一样,要么是父类异常类型的子类。

throws和try-catch-finally的选择:

  • 如果程序代码中,涉及到资源的调用(流、数据库连接、网络连接等),则必须考虑使用try-catch-finally来处理,保证不出现内存泄漏。
  • 如果父类被重写的方法没有throws异常类型,则子类重写的方法中如果出现异常,只能考虑使用try-catch-finally进行处理,不能throws。
  • 开发中,方法a中依次调用了方法b,c,d等方法,方法b,c,d之间是递进关系。此时,如果方法b,c,d中有异常,我们通常选择使用throws,而方法a中通常选择使用try-catch-finally。

抛出异常对象

异常的处理过程中有两个过程,一个是“抛”,一个是“抓“
“抛”的过程在程序上就是产生异常对象的过程,具体又可以分为自动抛和手动抛,这里的throw就是手动抛的过程
“抓”的过程实际上就是处理异常的过程,上述的throws和try-catch-finally就是处理异常的过程。

throw的使用:写在方法里,格式是throw 异常对象,这样就可以产生一个异常对象。

例子:

1
2
3
4
5
6
try{
throw new Exception("手动抛出异常");//创建一个名为手动抛出异常的异常对象
}catch (Exception e) {
System.out.println("捕获异常");
System.out.println(e.getMessage());
}

这个例子是想说明,自动抛和手动抛没有本质上的区别,都能被try-catch捕获。

自定义异常

如何自定义异常?

  • 继承于现有的异常体系,通常继承于RuntimeException/Exception
  • 通常提供几个重载的构造器
  • 提供一个全局常量,声明为:static final long serialVersionUID

使用:通过throw 自定义异常类的对象,将异常对象抛出

如果自定义异常类是非运行时异常,则必须考虑如何处理此异常类的对象

为什么要自定义异常类呢?

我们其实更关心的是,通过异常的名称就能直接判断此异常出现的原因。既然如此,我们就有必要在实际开发场景中,不满足我们指定的条件时,指明我们自己特有的异常类。通过此异常类的名称就能判断出具体出现的原因。