【JavaSE】异常

异常概述

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

在Java中,使用不同的类来表示不同的异常(正所谓万物皆对象,因此异常也使用类来表示)。一旦程序出现某种异常就创建该异常类型的对象,并且抛出。然后,程序如果捕捉到这个异常对象,那么就会进行处理;如果没有捕捉到这个异常对象,那么就会导致程序非正常停止。

因此,程序员在编写代码的时候,就要充分考虑到各种可能发生的异常和错误,极力进行避免和预防。对于实在无法避免的,要编写相应的代码进行异常的检测、异常的处理,从而增强代码的健壮性。

异常的体系结构

Throwable

java.lang.Throwable 类是Java程序执行过程中发生的异常事件对应类的根父类。

Throwable 类存在两个子类,分别是:Error、Exception。

Error

Error 类表示的异常是:Java虚拟机无法解决的严重问题。如,JVM系统内部错误、资源耗尽等情况。一般是不会编写针对性的代码进行处理,毕竟编写代码也不好处理。

StatckOverflowError(栈内存溢出)和OutOfMemoryError(堆内存溢出,简称OOM)是Error较为常见的场景,一般中高级程序猿才能处理这种问题。

Exception

Exception 类表示的异常是:其他因编程错误或偶然的外在因素导致的一般性问题,需要编写针对性的代码进行处理,使程序继续运行。否则一旦发生异常,程序就会挂掉。例如:

  • 空指针异常
  • 试图读取不存在的文件
  • 网络连接中断
  • 数组角标越界

Exception 异常又分为编译时异常(在执行javac命令时出现的异常,因此也被称为受检异常)和运行时异常(在执行java命令时出现的异常,因此也被称为非受检异常)。

 异常的处理方式

try-catch-finally

抛抓模型

对于抛抓模型来说,分为两个过程:一抛一抓。

对于抛过程来说,在程序执行的过程中,一旦出现异常,那就会在产生异常的代码处生成对应异常类的对象,并将此对象抛出。一旦将对象抛出,那么就不会再继续执行代码,等待抓过程的实现。

对于抓过程来说,当抛出异常时,抓过程就开始了。程序针对抛过程产生的异常对象进行捕获处理。一旦将异常处理,那么程序就会继续执行,但是如果并没有处理掉,那么程序就会终止。

基本结构

try {

        // 可能产异常的代码

}

catch (异常类型1) {

        // 当产生异常类型1时的处置措施

}

catch (异常类型2) {

        // 当产生异常类型2时的处置措施

}

finally {

        // 无论是否发生异常,都无条件执行的代码

}

 对于try结构来说,其中存放的就是可能产生异常的代码。当异常出现时,就会生成对应异常类的对象并抛出,并且无论是否处理了异常,try结构中产生异常代码之后的代码都不会继续执行。值得注意的是,try结构中的变量,出了try结构就不能使用。

对于catch结构来说,其中存放的是处理某种异常类的代码。一个try结构可以对应无数个catch结构,毕竟代码可能产生的异常不止一种类型。对于try抛出的异常对象来说,catch结构就会进行匹配,一旦匹配上,就会执行catch结构中的代码,执行完毕之后,继续向下执行。值得注意的是,如果声明了多个catch结构,不同的异常类型不存在子父类的情况下,谁声明在上,谁声明在下都可以;如果多个类型满足子父类的情况,则子类必须声明在上面,父类必须声明在下边。

对于finally结构来说,其中存放的就是一定要执行的代码,也就是说,无论能否处理了异常,finally结构中的代码一定会执行(唯一例外的就是如果使用System.exit(0)来结束当前正在运行的Java虚拟机,那么就不会有任何的代码进行执行)。所以,当有一定要执行的代码时,就要把他放到finally结构中(例如在开发中,有一些资源,如输出流、输入流、数据库连接以及Socket连接等,在使用完成之后,必须有显式的关闭。否则,GC会认为这些代码还要使用,就不会进行回收利用,进而导致内存泄漏。像这样的就可以把关闭操作放在finallyt中)。

注意,try是必须存在的,catch和finally两者可以同时存在,也可以存在一个,但是不能都不存在。

catch中处理异常的方式

1. 自己编写输出语句,表示异常的出现。

2. printStackTrace(),打印异常的详细信息。(推荐)

3. getMessage(),获取发生异常的原因

开发心得

1. 对于运行时异常,通常不进行显式的处理。一旦在程序运行过程中,出现了运行时异常,那么就根据异常的提示信息修改代码即可。

2. 对于编译时异常,一定要进行处理,否则编译不通过。

throws

基本结构

public void test() throws 异常类型1, 异常类型2 ... {

        // 存在编译时异常

}

 对于throws这种处理异常的方式来说,其实就是在方法声明的地方抛出了异常,当A方法调用此方法时,A方法继续抛出异常,B方法调用A方法时,继续抛出异常。如果异常真正发生了,那么程序照样会终止。因此这种方式存在的一个问题就是:是否真正处理了异常?

是否真正处理了异常?

1. 从编译是否能通过的角度看,也看成是给出了异常万一出现的解决方法。而所谓的解决方案就是继续向上抛出。

2. 如果说,throws仅是将可能出现的异常抛给了此方法的调用者。调用者仍然需要考虑如何解决处理相关异常。那么从这个角度来说,就不算解决了异常。

开发中两种方式的选择

1. 资源一定要被执行:try-catch-finally。

2. 如果父类被重写的方法没有 throws 异常类型,则子类重写的方法中如果出现异常,只能考虑使用 try-catch-finally 进行处理,不能 throws。

3. 开发中,方法a中依此调用了b、c、d等方法,方法b、c、d之间是递进关系。此时,如果方法b、c、d中有异常,我们通常使用throws,而方法a中通常选择使用try-catch。

手动抛出异常

在实际开发中,如果出现不满足具体场景的代码问题,我们就有必要手动抛出一个指定类型的对象。通俗的说,就是在实际问题的解决中,我们不想出现某些数据,那此时就可以手动抛出。

结构:throw + 异常类的对象。

所谓的手动抛出,就是抛抓模型中的抛过程。只不过手动抛出,而非程序自动抛出。

自定义异常

如何自定义异常类?

1. 继承于现有的异常体系。

2. 通常提供几个重载的构造器。

3. 提供一个全局变量,声明为:static final long serialVersionUID

如何使用自定义异常类?

1. 在具体的代码中,满足指定条件的情况下,需要手动的使用throw + 自定义异常类对象的方式,将异常类对象抛出。

2. 如果自定义异常类是非运行时异常,则必须考虑如何处理此异常类的对象(正是异常处理的两种方式)。

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

更希望通过异常类的名称来判断具体出现的问题。

举例

/**
 * 自定义异常
 */

public class CustomException extends Exception{

    public CustomException() {
        super();
    }

    public CustomException(String message) {
        super(message);
    }

    public CustomException(Throwable cause) {
        super(cause);
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

王彬泽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值