Java异常和错误

Java异常和错误

java.lang.Throwable类是java中所有异常(Exception)和错误(Error)的超类

异常和错误的区别在于:异常能够被程序本身捕获并处理,错误是程序无法处理的

异常

Exception异常分为两大类

  1. 运行时异常:都是RuntimeException类及其子类异常,如空指针异常,数组越界异常。这些异常是不受检异常,程序中可以选择捕获处理,也可以不处理。这种异常一般是由程序逻辑错误出错引起的,程序应从逻辑角度避免此类异常的产生。该类异常的特点是java编译器不会去检查他,也就是当程序中可能出现此异常,即使没有try-catch捕获,也没有throw抛出异常,也能够编译通过。
  2. 非运行时异常(编译异常):即RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序角度来说是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException,SQLException,等和用户自定义的Exception异常,一般情况下不自定义检查异常。

JVM检测到异常时会做的两件事情:

  1. JVM会根据产生异常的原因创建一个异常对象,异常对象内包含了异常产生的(内容,原因,位置)
  2. 在产生异常的语句块中如果没有异常处理逻辑,JVM将会把异常对象逐级抛给方法的调用者,直到找到异常处理逻辑,当抛到main方法时,就会交给JVM自己处理异常。
  3. JVM自己处理异常时,会将异常对象的内容以红色字体打印在控制台,并中断当前执行的程序

throw 关键字

作用:可以在指定的方法中抛出指定的异常

使用格式:throw new xxxException(“产生异常的原因”);

注意:

  1. throw 关键字必须写在方法内部
  2. throw 关键字后边new 的对象只能是Exception或者Exception的子类对象
  3. throw 关键字抛出的指定异常对象,我们就必须处理这个异常对象
    • 如果抛出的是运行时异常,或者运行时异常的子类对象,我们可以不处理,默认交给JVM虚拟机处理(打印异常对象,中断程序)
    • 如果抛出的是编译异常,就必须处理这个异常,要么throws,要么try catch捕获

工作中注意:

  1. 必须首先对方法传递的参数进行合法性校验
  2. 如果参数不合法,那么我们就必须使用抛出异常的方式,告知方法的调用者,传递的参数有问题

实例:

package com.seele.exception;

//实际项目中,不要捕获 Java 类库中定义的继承自 RuntimeException 的运行时异常类,如:IndexOutOfBoundsException
//或NullPointerException,这类异常由程序员预检查
public class ThrowTest {
    public static void main(String[] args) {
        int[] arr = {0,1,2};
        int el = getElement(arr,3);
    }

    public static int getElement(int[] arr,int index){
        if (arr == null) {
            throw new NullPointerException("数组为空数组");
        }
        if ( index < 0 || index > arr.length - 1) {
            throw new ArrayIndexOutOfBoundsException("数组越界");
        }
        int el = arr[index];
        return el;
    }
}

控制台输出:

Exception in thread “main” java.lang.ArrayIndexOutOfBoundsException: 数组越界
at com.seele.exception.ThrowTest.getElement(ThrowTest.java:16)
at com.seele.exception.ThrowTest.main(ThrowTest.java:6)

简化判断对象是否为null

if (arr == null) {
	throw new NullPointerException("数组为空数组");
}
//简化写法
Objects.requireNonNull(arr,"数组为空数组");

throws 关键字

throws关键字:异常处理的第一种方式,交给调用者去处理

作用:

  • 当方法内部抛出异常的时候,我们就必须处理这个异常对象
  • 可以使用throws关键字处理对象,会把异常对象声明逐级抛出给方法的调用者处理(自己不处理,交给别人处理),如果逐级抛出没有调用者处理,那么最终将交由JVM处理,中断程序

使用格式:在方法声明时使用

访问修饰符 返回值类型 方法名(参数列表) throws AAAException,BBBException{
	throw new AAAException("产生原因");
	throw new BBBException("产生原因");
}

注意:

  1. throws 关键字必须写在方法声明处
  2. throws 关键字后边声明的异常必须是Exception或者是Exception的子类
  3. 方法内部如果抛出了多个异常对象,那么throws 后面就必须也声明多个异常
    • 如果抛出的多个对象存在继承关系,那么直接声明父类异常即可
  4. 调用了一个声明抛出异常的方法,我们就必须处理声明的异常
    • 要么继续使用throws声明抛出,交给方法的调用者处理,或继续抛出最终交给JVM处理
    • 要么try-catch自己处理异常

try-catch-finally 关键字

try-catch-finally :异常处理的第二种方式,自己处理异常

格式:

try {
	可能产生异常的代码
} catch (定义异常变量,用来接收try中抛出的异常对象){
	异常的处理逻辑,捕获异常对象后,怎么处理对象
	一般在工作中,会把异常的信息记录到一个日志中
} finally {
	无论是否成功都将执行的代码块,多用于资源释放,如IO流的关闭
     可以根据情况决定是否添加此代码块
}

注意:

  1. try 中可能抛出多个异常对象,那么就可以用多个catch语句来分别处理这些异常对象
  2. 如果try 中出现了异常,那么就将执行catch中的异常处理逻辑,执行完毕catch中的处理逻辑,继续执行try-catch之后的代码;如果try中没有出现异常,那么就不会执行catch中的异常处理逻辑,继续执行try 语句块中的代码
  3. finally中无论是否成功都将执行,多用于资源释放,如IO流的关闭

自定义异常

java提供的异常类不够我们使用,需要自己自定义一些异常类

格式:

public class xxxException extends Exception | RuntimeException {
	添加一个空参构造
	添加一个带异常信息的构造方法
}
package com.seele.exception;

//自定义异常类
public class RegisterException extends Exception {

    //添加一个空参构造
    public RegisterException() {

    }
    //添加一个带异常的有参构造
    //查看源码发现,所有的异常类都有一个带异常信息的构造方法,方法内部会调用父类带异常信息的构造方法,让父类来处理这个异常信息
    public RegisterException(String message) {
        super(message);
    }
}

注意:

  1. 自定义异常类一般都是以Exception做结尾,说明该类是一个异常类
  2. 自定义异常类必须继承Exception或者RuntimeException
    • 继承Exception:自定义异常就是一个编译器异常,如果方法内部抛出了该异常,那么就必须处理这个异常,要么throws,要么try-catch
    • 继承RuntimeException:自定义异常就是一个运行期异常,无需处理,交给虚拟机处理(中断处理)

项目中对异常处理的相关约定和准则

1、不要捕获 Java 类库中定义的继承自 RuntimeException 的运行时异常类,如:IndexOutOfBoundsException / NullPointerException,这类异常由程序员预检查来规避,保证程序健壮性。

正例: if(obj != null) {…}
反例: try { obj.method() } catch(NullPointerException e){…}

2、异常不要用来做流程控制,条件控制,因为异常的处理效率比条件分支低。这个坑大家要注意了。

3、对大段代码进行 try-catch,这是不负责任的表现。 catch 时请分清稳定代码和非稳定代码,稳定代码指的是无论如何不会出错的代码。对于非稳定代码的 catch 尽可能进行区分异常类型,再做对应的异常处理。

4、捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之,如果不想处理它,请将该异常抛给它的调用者。最外层的业务使用者,必须处理异常,将其转化为用户可以理解的内容。

5、有 try 块放到了事务代码中, catch 异常后,如果需要回滚事务,一定要注意手动回滚事务。

6、finally 块必须对资源对象、流对象进行关闭,有异常也要做 try-catch。说明: 如果 JDK7,可以使用 try-with-resources 方式。

7、不能在 finally 块中使用 return, finally 块中的 return 返回后方法结束执行,不会再执行 try 块中的 return 语句。

8、捕获异常与抛异常,必须是完全匹配,或者捕获异常是抛异常的父类。说明: 如果预期对方抛的是绣球,实际接到的是铅球,就会产生意外情况。

9、方法的返回值可以为 null,不强制返回空集合,或者空对象等,必须添加注释充分说明什么情况下会返回 null 值。调用方需要进行 null 判断防止 NPE 问题。
说明: 本规约明确防止 NPE 是调用者的责任。即使被调用方法返回空集合或者空对象,对调用者来说,也并非高枕无忧,必须考虑到远程调用失败,运行时异常等场景返回 null 的情况。

10、在代码中使用“抛异常”还是“返回错误码”,对于公司外的 http/api 开放接口必须使用“错误码”; 而应用内部推荐异常抛出; 跨应用间 RPC 调用优先考虑使用 Result 方式,封装 isSuccess、 “错误码”、 “错误简短信息”。

11、定义时区分 unchecked / checked 异常,避免直接使用 RuntimeException 抛出,更不允许抛出 Exception 或者 Throwable,应使用有业务含义的自定义异常。推荐业界已定义过的自定义异常,如: DAOException / ServiceException 等。

12、对于运行时异常,我们不要用try…catch来捕获处理,而是在程序开发调试阶段,尽量去避免这种异常,一旦发现该异常,正确的做法就会改进程序设计的代码和实现方式,修改程序中的错误,从而避免这种异常。捕获并处理运行时异常是好的解决办法,因为可以通过改进代码实现来避免该种异常的发生。对于受检查异常,没说的,老老实实去按照异常处理的方法去处理,要么用try…catch捕获并解决,要么用throws抛出!对于Error(运行时错误),不需要在程序中做任何处理,出现问题后,应该在程序在外的地方找问题,然后解决。

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 游动-白 设计师:白松林 返回首页