Java基础—异常总结

前言

        在日常的开发以及平时的学习练习中,异常相信对于大家来讲并不陌生,但是对于异常的具体使用、底层实现以及分类等等可能并不是很了解。今天我就抽出了一点时间系统的整理了异常的各个知识点,希望能够帮助到大家对于Java 异常的理解与学习。

正文

一、异常的定义

异常是一个在程序执行期间发生的事件,它中断正在执行程序的正常指令流。我自己的的理解是异常通常是一种人为规定的不正常的现象,通常是写的程序产生了一些不符合规范的事情,而且异常一旦产品,程序直接挂掉。

二、异常的分类

通过我自己整理的这一张继承关系结构图,我们可以很清晰的看到异常这个庞大体系的继承关系。

其中我们可以看到 Exception类 下面又细分了两个分支(子类),分别是运行时异常(也称非检查异常[ Unchecked Exception ])和非运行时异常(也称检查异常[ Checked Exception ]),需要注意的是 Error 也是属于非检查异常。

1、运行时异常

运行时异常也称非检查异常,指的是 Error 和 RuntimeException 这两种。既然是叫运行时异常,那肯定是在程序运行期间才可能被检查出的异常,那自然编译器在编译的时候是不会检查此类异常的,并且不要求处理该类异常,所以此类异常也叫非检查异常(或者不检查异常),这一类异常一般是由程序逻辑错误引起的,在程序中可以选择捕获处理,也可以不处理。

下图我整理了一些常见的运行时异常,如下表格所示:

异常 描述 父类
ArithmeticException 算术运算异常 RuntimeException
IllegalArgumentException 不合法参数异常 RuntimeException
IndexOutOfBoundsException 索引越界异常。下面由两个子类异常 RuntimeException
NullPointerException 空指针异常 RuntimeException
NumberFormatException 数字格式化异常 IllegalArgumentException
ArrayIndexOutOfBoundsException 数组下标越界异常。 IndexOutOfBoundsException
ClassCastException 类型转换异常 RuntimeException

2、非运行时异常

非运行时异常指的是除开 Error 和 RuntimeException 极其子类之外的异常。既然叫做非运行时异常,顾名思义,就是不是在程序运行期间检查的异常,既然不是在程序运行期间检查,那就是编译器在编译期间就会检查此类异常,所以此类异常也叫检查异常(或者叫编译时异常)。此类异常在程序运行过程中极有可能产生问题,如果程序中出现此类异常(比如说 IOException ),必须对该异常进行处理,要么使用 try-catch 捕获,要么使用 throws语句抛出,否则编译不通过。

下图我整理了一些常见的非运行时异常,如下表格所示:

异常 描述 父类
InterruptedException 被终止异常 Exception
ClassNotFoundException 无法找到指定的类异常 ReflectiveOperationException
IOException 流异常 Exception
FileNotFoundException 文件未找到异常 IOException
SQLException 操作数据库异常 Exception
CloneNotSupportedException 不支持克隆异常 Exception
NoSuchMethodException 方法未找到异常 ReflectiveOperationException

三、异常的处理

既然异常产生了,就需要我们去处理。处理异常不是让异常消失了,处理异常指的是处理掉异常之后,后续的代码不会因为此异常而终止执行,代码中的异常处理其实是对非检查异常也就是运行时异常的处理。

异常的处理有两种方式捕获异常或者抛出异常这两种方式。

1、捕获异常,通过try…catch…finally(其中finally可有可无)语句块来处理

其中 try/catch 代码块放在异常可能发生的地方,把可能会发生异常的代码直接放到 try 块里面,如果在 try 块里面有异常发生,try 块代码里面后面的代码就不执行了,直接匹配 catch 里面的异常,如果没有异常发生, 就直接执行完try里面的代码,catch 里面的代码不执行。

try…catch 语法结构如下:

try{
    //程序代码
}catch (Exception e){

}

其中 try 不能单独出现,后面必须添加 catch 或者 finally ,意思就是要么 try…catch,要么 try…finally,如下:

//写法1
try{
    //程序代码
}catch (Exception e){

}
//写法2
try{
  //程序代码
}finally {

}

//写法3
try{
  //程序代码
}catch (Exception e){
  
} finally {

}

其中,catch 可以有多个存在

try{
  //程序代码
}catch (ArrayIndexOutOfBoundsException e1){
  //程序代码
}catch (IndexOutOfBoundsException e2){
  //程序代码
}catch (RuntimeException e3){
  //程序代码
}

其中 catch 语句包含要捕获异常类型的声明。当保护代码块中发生一个异常时,try 后面的 catch 块就会被检查。如果发生的异常包含在 catch 块中,异常会被传递到该 catch 块,这和传递一个参数到方法是一样。如果catch 有多个,捕获的异常需要从小到大捕获。

关于try 、catch 、finally 关键字的执行顺序

我们先来看看下面代码的执行:

public static void main(String[] args) {
  System.out.println("try开始");
  String str = null;
  int length = str.length();
  System.out.println("try完毕");
}

上面代码的执行结果是:

try开始
Exception in thread “main” java.lang.NullPointerException
at com.example.javabasic.javabasic.ExceptionAndError.ExceptionTest.main(ExceptionTest.java:12)

再来看看下面代码的执行:

public static void main(String[] args) {
  System.out.println("程序最早开始执行");
  try{
    System.out.println("try开始");
    String str = null;
    int length = str.length();
    System.out.println("try完毕");
  } catch (ArrayIndexOutOfBoundsException e1){
    System.out.println("数组下标越界异常");
  }
  System.out.println("产生异常之后的所有程序");
}

上面代码的执行结果是:

程序最早开始执行
Exception in thread “main” java.lang.NullPointerException
try开始
at com.example.javabasic.javabasic.ExceptionAndError.ExceptionTest.main(ExceptionTest.java:14)

再来看看下面的代码的执行:

public static void main(String[] args) {
  System.out.println("程序最早开始执行");
  try{
    System.out.println("try开始");
    String str = null;
    int length = str.length();
    System.out.println("try完毕");
  } catch (NullPointerException e1){
    System.out.println("空指针异常");
  }
  System.out.println("产生异常之后的所有程序");
}

上面代码的执行结果是:

程序最早开始执行
try开始
空指针异常
产生异常之后的所有程序

        通过上面三个例子,其实我们可以发现如果程序中如果有代码会出现异常,当你没有手动进行捕获的时候,当异常发生,代码直接中断,后面的代码也不会执行,当然这是显而易见的;如果你捕获了与之匹配的异常,那么在try块里面发生异常的后面的代码不会执行,位于try块之后的代码都会正常执行;如果你捕获了异常,但是发生的异常与你捕获的异常不匹配,程序也会立刻终止,后面的代码都不会执行。

        try、catch、finally关键字的执行顺序的优先级是,try块中的代码会被执行,如果出现异常则执行最匹配的catch块。但是无论是否有匹配的catch块,只要有相关的finally块,那么finally块都会被执行,虽然finally块的优先级最低。

2、抛出异常,也就是在具体位置不处理,直接抛出,通过throws/throw到上层再进行处理。

        当我们遇到可能发生的异常的时候,如果我们觉得自己手动处理太麻烦,自己不想处理怎么办呢?方法总比困难多,我们可以将异常抛出去,换而言之,就是丢给别人去处理,其中抛出异常有两种方式throws和throw。

第一种方式:throws声明异常

运用于方法声明之上,用于表示当前方法不处理异常,而是提醒该方法的调用者来处理异常,比如下面两个代码块

public static void main(String[] args) {
  try {
    testException();
  } catch (InterruptedException e) {
    e.printStackTrace();
  }
}

private static void testException() throws InterruptedException {
    Thread.sleep(100);
}

或者:

public static void main(String[] args) throws InterruptedException {
  testException();
}
private static void testException() throws InterruptedException {
    Thread.sleep(100);
}

需要注意的是如果是主函数main方法调用了会抛出异常的方法,那么一般都是不建议主方法再抛出去的(如果主方法抛出去,就相当于抛给虚拟机处理了,然而虚拟机是不会处理的。),而是在主方法里进行捕获异常,再进行处理。

其中,throws抛出异常的时候可以抛出多个异常,通过逗号隔开,例如下方代码:

private static void testException() throws InterruptedException, ArrayIndexOutOfBoundsException, ArithmeticException{
    Thread.sleep(100);
}
第二种方式:throw抛出异常

        用在方法内,用来抛出一个异常对象,将这个异常对象传递到调用者处,并结 束当前方法的执行。

四、自定义异常

        Jdk 提供了很多异常,其实基本都够我们平时的日常使用了,但是,当 Jdk 提供好的这些所有异常都不能描述或者满足我的问题的时候,我们其实可以自己定义一个异常来进行描述自己问题的异常,也就是自定义异常。自定义异常都必须是 Throwable 的子类,也就是:

  • 继承 Throwable

  • 继承 Exception

  • 继承 RuntimeException

那么到底继承哪一个呢?看我们的需要,

如果希望写一个检查异常类,则需要继承 Exception 类或者 Throwable 类,一般继承 Exception 类。

如果希望写一个运行时异常类(也就是非检查异常),则需要继承 RuntimeException 类。

在平时开发中以及在项目上,一般都是选择继承 RuntimeException 类的,例如下面我定义一个自定义运行时异常:

/**
 * 自定义运行时异常
 * @Date: 2020/4/21 9:54
 */
public class MyRuntimeException extends RuntimeException{
  public MyRuntimeException(){
  }

  //如果想对自定义异常进行描述
  public MyRuntimeException(String message){
      super(message);
  }
}

测试一下自定义异常:

public static void main(String[] args){
  testMyRuntimeException();
}
/**
 * 自定义异常
 */
private static void testMyRuntimeException() {
  throw new  MyRuntimeException("我的自定义异常");
}

执行结果为:
在这里插入图片描述

五、错误

        Error 意思就是错误,Error 表示系统致命的错误,Error 是程序中无法处理的错误,表示运行应用程序中出现了严重的错误,通常是一些物理性的错误,这些错误是不可查的,因为它们在应用程序的控制和处理能力之外,而且绝大多数是程序运行时不允许出现的状况。Error 类对象由 Java 虚拟机生成并抛出,大多数错误与代码编写者所执行的操作无关。错误比如 JVM 虚拟机本身出现了问题、比如 StackOverFlowError 栈内存溢出、OutOfMemoryError 堆内存溢出等。在 Java 中,错误通过 Error 的子类描述。

总结

以上就是我对Java异常的总结,如果在阅读的时候遇到什么问题欢迎评论留言哦~~

哈哈,附上今晚买的新鲜的西瓜哈哈哈,是不是看起来很好吃T_T

文章不定期更新中,未完待续哦~~~

猜你喜欢

转载自blog.csdn.net/weixin_42135693/article/details/105671522