Java 异常实战

前言:说到异常体系,可能对于一些初入职场的老铁会很头痛,不可以很清晰的描述异常是个什么状况。那么本文将经过打流水仗的方式给你们介绍一下工做中涉及的异常知识。首先能看到本文,说明也对异常是有了解的,因此文章开头就经过一些概念和小例子快速熟悉一下异常,紧接着介绍异常体系,对于理解整个异常体系架构很是有帮助,最后介绍了很是很是重要的自定义异常,学会了这些,在工做中遇到的常见异常问题,处理起来必定都会游刃有余啦。

异常概述

异常

异常就是在程序的运行过程当中所发生的不正常的事件,它会中断正在运行的程序java

异常处理

Java 编程语言使用异常处理机制为程序提供了错误处理的能力,好处就是:若是代码中存在了异常,可是进行了捕获处理,那么程序就会继续运行下去,不会由于一个异常致使程序中断运行。数据库

clipboard.png

例子1:若是程序可能存在异常可是没有作异常处理,那么将会致使程序不能正常的运行下去:编程

public class ExceptionTest {
    public static void main(String[] args) {
        int a = 10;
        int b = 0;
        int c = a / b;
        System.out.println(c);
        System.out.println("输出了本句话吗");
    }
}

输出结果是:
Exception in thread "main" java.lang.ArithmeticException: / by zero
at cn.caijiajia.cn.caijiajia.exception.ExceptionTest.main(ExceptionTest.java:12)

例子2:若是程序可能存在异常并作了异常处理,那么程序就会正常的运行下去:架构

public class ExceptionTest {
    public static void main(String[] args) {
        try {
            int a = 10;
            int b = 0;
            int c = a / b;
            System.out.println(c);
            System.out.println("异常代码下面的代码将都不会执行");
        } catch (ArithmeticException e) {
            System.out.println("处理异常的代码");
        }
        System.out.println("异常处理代码下面的程序将会继续执行而不会程序中断");
    }
}

输出结果是:
Exception in thread "main" java.lang.ArithmeticException: / by zero
at cn.caijiajia.cn.caijiajia.exception.ExceptionTest.main(ExceptionTest.java:12)
处理异常的代码
异常处理代码下面的程序将会继续执行而不会程序中断

Java 中如何处理异常

异常处理的 5 个关键字

try:执行可能产生异常的代码
catch:捕获异常并对异常状况作相应处理
finally:不管是否发生异常,代码总能执行。(释放资源,关闭数据库链接)
throws:声明可能抛出的各类异常(受检异常较多)
throw:手动的抛出异常(手动抛出咱们自定义的异常较多)编程语言

异常处理后程序运行状况

状况一:没产生异常学习

public void method() {
    try {
        // 代码段① [正常业务逻辑代码,此处不会产生异常]
    } catch (Exception e) {
        // 代码段② [对异常处理的代码段]
    }
    // 代码段③ [正常业务逻辑代码]
}

运行结果:
    代码段①
    代码段③

状况二:产生异常并捕获异常spa

public void method() {
    try {
        // 异常代码段① [正常业务逻辑代码,此处会产生异常]
        // 代码段② [正常业务逻辑代码]
    } catch (Exception e) {
        // 代码段③ [对异常处理的代码段]
    }
    // 代码段④ [正常业务逻辑代码]
}

运行结果:
    代码段①
    代码段③   
    代码段④

注意:若是想要正常的 代码段② 执行,那么能够把代码段从 trycatch 里面提出来,和 代码段④ 放在一块儿,当异常处理完以后,就能够同时去执行 代码段② 和 代码段④ 了。

状况三:产生异常并捕获异常,可是捕获异常类型不匹配产生异常类型指针

public void method() {
    try {
        // 异常代码段① [正常业务逻辑代码,此处会产生角标越界异常]
        // 代码段② [正常业务逻辑代码]
    } catch (IOException e) {
        // 代码段③ [对异常处理的代码段]
    }
    // 代码段④ [正常业务逻辑代码]
}

运行结果:
    代码段①
    
注意:若是捕获异常类型和产生的异常类型不匹配,那么就和没有处理异常状况同样了,trycatch 后面的代码段将都不会执行,发生异常就会致使程序中断运行

异常体系

异常类层次图

clipboard.png

Error 和 Exception

Error 类和 Exception 类的父类都是 Throwable 类code

Error 类通常是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢等。对于这类错误的致使的应用程序中断,仅靠程序自己没法恢复和和预防,遇到这样的错误,建议让程序终止。所以咱们在学习的时候主要是学会 Exception。对象

Exception 类表示程序能够处理的异常,能够捕获且可能恢复。遇到这类异常,应该尽量处理异常,使程序恢复运行,而不该该随意终止异常。

Exception 异常分类及处理

Exception 异常主要分为两大类:Checked Exception,Unchecked Exception;即受检异常和非受检异常(运行时异常)


Checked Exception:受检异常,即 Java 程序必须显式处理的异常,若是程序没有处理 Checked 异常,该程序在编译时就会发生错误没法编译。例如图中所示的 IOException

处理受检异常经过有以下两种方式:
方式一:经过 trycatch 显式处理异常,不然编译不经过

public class ExceptionTest {
    public static void main(String[] args) {
        try {
            // 经过 trycatch 显示处理异常
            FileInputStream fis = new FileInputStream(new File(""));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

方式二:经过 throws 抛出异常,让上层来处理异常,不然编译不经过

public class ExceptionTest {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream(new File(""));
    }
}

Unchecked Exception:非受检异常,即 RuntimeException 运行时异常。这些异常程序能够选择捕获处理,也能够不处理。这些异常通常是由程序逻辑错误引发的,程序应该从逻辑角度尽量避免这类异常的发生。好比常见的 NullPointerException、IndexOutOfBoundsException 就是运行时异常。不过按照经验来讲,这类异常要么尽可能避免,要么出现了就要作异常处理,从而保证程序的健壮性。

处理运行时异常经过有以下两种方式:
方式一:常见的空指针处理

// 经过写伪代码来演示其处理流程
if (对象 == null) {
    // 处理对象为 null 的逻辑
} else {
    // 处理对象不为 null 的逻辑
}

方式二:跟业务相关异常,抛出自定义异常

// 经过手机号注册业务逻辑
User user = dao.getByPhone(phone);
if (user != null) {
    // 说明此手机号码已经被注册,那么就抛出业务异常(即自定义异常)
    throw new MyRuntimeException("改用户已注册");
}
// 若是没有注册,继续走注册流程代码

总结:经过本小节异常分类及处理能够发现,受检异常处理起来很简单,由于程序若是不作显式处理,那么就会编译不经过,强制要求处理;而运行时异常则是看心情处理的,可是若是想要公司代码更加健壮,更少的出现问题,最好要作一下异常处理;可是若是作这个处理呢?对于这种运行时异常,大部分都是和业务相关的,好比手机号注册例子;这种状况下在 Java 的异常体系中并无相关异常类作处理,由于 Java 无论在智能,也不可能知道咱们的业务状况,固然就不会针对业务提供一些异常类供咱们使用,所以为了解决这个问题,自定义异常就出现了,它对于咱们处理业务中产生的运行时异常很是很是重要,接下来就来学习自定义异常。

自定义异常

产生缘由

Java 现有的异常类不能知足更多的业务异常处理,所以咱们要自定义合适的异常类来处理业务异常。

如何自定义异常类

第一步:声明一个类继承 Exception 或其子类

那么咱们声明的这个类到底继承谁呢?Exception?RuntimeException?
答案是:RuntimeException
缘由是:看了上面的异常类层次图,应该也能发现,Exception 下面有两大类子类,受检异常和运行时异常,若是咱们自定义的类继承了 Exception,则就会由于受检异常的存在而变成了受检异常类,这个时候咱们自定义的异常类,若是在程序中使用,那么就必须显式处理异常,要么 trycatch,要么抛出给上层;这样一来,使得咱们的程序很混乱,并且并无达到咱们预期的结果。而后当咱们自定义的类继承了 RuntimeException 以后,当咱们程序中想要使用的时候,直接 new 一个便可,而再也不须要显式去再多作处理了。

第二步:自定义异常类应至少包含四个构造方法

public class MyException extends RuntimeException {
    public MyException() {}

    public MyException(String msg) {
        super(msg);
    }

    public MyException(Throwable throwable) {
        super(throwable);
    }

    public MyException(String msg, Throwable throwable) {
        super(msg, throwable);
    }
}

第三步:在程序中使用咱们的自定义异常

public void testException() {
    User user = dao.getByPhone(phone);
    if (user != null) {
        // 由于 MyException 是继承 RuntimeException,因此这里直接抛出异常而不用作其它处理
        throw new MyException("该手机号已被注册");
    }
    // ...
} 

注:没错,这里又使用了这个手机号注册的例子,由于这个就是实实在在的在业务中的异常处理。业务是变幻无穷,可是它们可能产生的异常处理方式是不会变化的,按照这个思路去作异常处理便可。

tips:如上自定义的异常类中的构造方法是最基本的几个。每家公司的自定义异常可能都会有区别,好比定义一个 SuperException 类实现了 RuntimeException,而后在自定义 ClientException,ServerException 再去继承 SuperException;那么这就是一套自定义体系了,分为客户端异常和服务端异常,在须要作异常处理的地方使用对应的异常类并抛出异常错误信息便可了。再好比可能有的公司会在自定义异常类中在定义一些字段,code,msg 等,来表明某些业务码和对应的错误信息等。无论怎么样,咱们只要了解自定义异常的原理后,面对哪一个公司的自定义异常体系咱们都可以轻松应对。

异常总结

经过本文的学习,必定要掌握两个知识,一个是异常体系,要分清受检异常和运行时异常;另外一个就是自定义异常,知道如何自定义异常和如何使用自定义异常。

相关文章
相关标签/搜索