Java中的异常
1.概念
- 异常指程序在执行过程中,出现的非正常情况,最终会导致JVM的非正常停止。
- 产生异常就是创建一个异常对象,并抛出这个对象。
- Java处理异常的方式是中断处理。
- 注意:异常不是语法错误,语法错了编译不通过,不会产生字节码文件,根本不能运行。
2.异常体系
- 异常的根类是Throwable,其下有两个子类——Error、Exception,均在java.lang包中,通常说的异常是Exception类。
- Exception类是编译期异常,在写代码过程中程序出现的异常,它有一个子类是RuntimeException,是运行期异常,程序运行过程中出现的问题。
- Error错误必须修改源代码,才能解决。
3.异常的产生过程
- JVM检测出程序发生异常,JVM根据异常产生的原因创建一个异常对象,异常对象包括异常产生的原因、内容、位置。
- 若语句所在的方法没有处理异常的逻辑,那么JVM就会把异常抛出给方法的调用者来处理这个异常。
- 若方法的调用者也没有处理这个异常的逻辑,继续向上抛出异常,直至调用关系顶层的JVM。
- JVM接受这个异常后,会把异常对象以红字打印在控制台,包括异常产生的原因、内容、位置,同时,终止正在执行的程序,即中断处理。
4.异常的处理
- 格式:throw new XXXException("异常产生的原因");
- 注意:(1)throw必须放在方法内部,(2)throw后边new的对象必须时Exception对象或者是Exception的子类对象,(3)若throw关键字后是RuntimeException或RuntimeException的子类对象,我们可以不用处理这个异常,默认交给JVM处理(打印异常对象,中断处理),例如NullPointerException、ArrayIndexOutOfBoundsException,(4)若throw关键字后是编译期异常,我们必须要处理这个异常,要么throws,要么try catch,如FileNotFoundException,(5)工作中我们首先必须对传递过来的参数进行合法性校验,如果参数不合法,我们必须给调用者抛出异常,说明传递的参数有问题,(6)判断Object对象是否为空,可以调用Object.requireNonNull(Object object)方法。
- 使用情况:如果方法内部throw抛出了编译期异常,而没有捕获处理try catch,则必须通过throws关键字进行声明,让调用者去处理。
- 格式:修饰符 返回值类型 方法名(参数列表) throws XXXException{ ...throw new XXXException("");...}
- 注意:(1)throws必须写在方法声明处,(2)方法内部如果抛出了多个异常对象,那么throws后边也声明这些对象,逗号间隔,(3)如果抛出的多个异常对象存在子父类关系,那么throws声明父类异常即可,(4)调用了一个声明抛出异常的方法,就必须处理声明这个异常,要么继续throws向上抛出异常,交给方法的调用者,最终交给JVM,要么try catch自己处理异常。
- 格式:try{可能产生异常的代码} catch(定义一个异常的变量,用来接收抛出的异常对象){产生异常后的处理内容,工作中一般会把异常信息记录到日志中}...catch(异常类名 变量名){}
- 注意:(1)try中可能会抛出多个异常对象,那么就可以使用多个catch来处理这些异常对象,(2)如果try中产生了异常,那么就会执行catch中的异常处理逻辑,执行完catch中的处理逻辑后,继续执行try catch后的代码,(3)如果try中没有产生异常,就不会执行catch中的异常处理逻辑,执行完try中的代码后,继续执行try catch后的代码。
5.Throwable类中三个打印异常的方法
- String getMessage():返回异常对象的简短描述
- String toString():返回异常对象的详细消息字符串
- void printStackTrace():JVM打印异常对象,默认此方法,打印的异常信息是最全面的
6.finally代码块
- 有些代码无论异常是否发生,都要执行,因为异常会导致程序跳转,异常后的语句不能执行,放在finally里可以保证这些代码能够被执行到。
- 格式:try{可能产生异常的代码} catch(定义一个异常的变量,用来接收抛出的异常对象){产生异常后的处理内容,工作中一般会把异常信息记录到日志中}...catch(异常类名 变量名){}finally{无论代码是否发生异常,都要被执行的语句}
- 注意:(1)finally不能单独使用,必须和try搭配使用,(2)finally一般用于资源释放、回收,无论程序是否发生异常,最后都要进行资源释放,(3)如果finally代码块中有return语句,返回的永远都是finally中的结果,应该避免出现这种情况。
7.多异常的捕获处理
- 多个异常分别处理:多个try catch,每个try catch处理一个异常
- 多个异常一次捕获,多次处理:一个try,多个catch,注意:多个catch里边定义的异常变量,如果有子父类关系,捕获子类异常变量的catch必须写在父类的前边,否则会报错,因为抛出的异常对象,会从上到下依次赋值给catch中定义的异常变量。
- 多个异常一次捕获,一次处理:catch里定义的变量是多个异常对象的父类,甚至可以写Exception,所有异常都能捕获到。
8.异常注意事项
- 如果父类方法抛出多个异常,子类重写父类方法,抛出异常的类型应该是和父类相同的异常、父类异常的子类异常或不抛出异常。
- 如果父类方法没有抛出异常,子类重写父类方法时也不能抛出异常,只能自己捕获处理异常。
- 总结一句话:父类异常什么样,子类异常就什么样。
9.自定义异常
- 格式:public Class XXXException extends Exception | RuntimeException(){空参构造方法 带异常信息的构造方法}
- 注意:(1)自定义异常类类名一般以Exception结尾,说明该类是一个异常类,(2)必须继承Exception或RuntimeException,(3)所有异常类的空参构造方法会调用父类空参构造方法,(4)所有异常类都会有一个带有异常信息的构造方法,带异常信息的构造方法会调用父类异常的带异常信息的构造方法,让父类处理这个异常信息,(4)try抛出了异常,不执行try catch后的语句的方法是在catch中加一个return语句。