java异常

理解 Throwable、Exception、Error 的分类

image

请对比Exception和 Error,运行时异常与一般异常有什么区别?

Exception 和 Error不同之处

  • Exception
    • 1.可以是可被控制(checked) 或不可控制的(unchecked)。
    • 2.表示一个由程序员导致的错误。
    • 3.应该在应用程序级被处理。
  • Error
    • 1.总是不可控制的(unchecked)。
    • 2.经常用来用于表示系统错误或低层资源的错误。
    • 3.如何可能的话,应该在系统级被捕捉。
  • 概括
    • 异常和错误的区别:异常能被程序本身可以处理,错误是无法处理。

程序出现异常,但是没有捕获到信息

程序明明出现了异常,也catch(Exception e)了,却没有捕获到任何信息。

  • 原因无非有两个:
    • 1.异常所在的线程跟你捕获的线程不是同一个线程;
    • 2.程序抛出的不是Exception而是Error。Error跟Exception一样都继承自Throwable,是指不应该被捕获的严重错误。
  • 为什么不该捕获Error呢?
    • 因为出现Error的情况会造成程序直接无法运行,所以捕获了也没有任何意义

一旦某个catch捕获到匹配的异常类,其它的catch还会执行吗?

  • 一旦某个catch捕获到匹配的异常类型,将进入异常处理代码。一经处理结束,就意味着整个try-catch语句结束。其他的catch子句不再有匹配和捕获异常类型的机会。
  • 对于有多个catch子句的异常程序而言,应该尽量将捕获底层异常类的catch子 句放在前面,同时尽量将捕获相对高层的异常类的catch子句放在后面。否则,捕获底层异常类的catch子句将可能会被屏蔽。
  • 举个例子:RuntimeException异常类包括运行时各种常见的异常,ArithmeticException类和ArrayIndexOutOfBoundsException类都是它的子类。因此,RuntimeException异常类的catch子句应该放在 最后面,否则可能会屏蔽其后的特定异常处理或引起编译错误。

异常思维导图

image

什么是异常

  • 什么是异常?
    • 异常是正常程序流程所不能处理或者没有处理的异常情况或异常事件,比如算术运算被0除,数组下标越界等。
    • Java采用try-catch-finally语句捕获并处理异常并且处理异常。
  • finally一定会执行吗
    • finally是异常处理的统一出口,常用来实现资源释放,比如关闭文件,关于数据库连接等。除非遇到System.exit()强制退出程序外,finally语句块无论是否发生异常都要执行。

2.异常

  1. 异常的概述和分类
    • A:异常的概述: 异常就是Java程序在运行过程中出现的错误。
    • B:异常的分类:
    • C:异常的继承体系
      • 异常的基类: Throwable
      • 严重问题: Error 不予处理,因为这种问题一般是很严重的问题,比如: 内存溢出
      • 非严重问题: Exception
        • 编译时异常: 非RuntimeException
        • 运行时异常: RuntimeException
  1. JVM默认是如何处理异常的
    • JVM默认是如何处理异常的
      1. main函数收到这个问题时,有两种处理方式:
        • a:自己将该问题处理,然后继续运行
        • b:自己没有针对的处理方式,只有交给调用main的jvm来处理
      2. jvm有一个默认的异常处理机制,就将该异常进行处理.
      3. 并将该异常的名称,异常的信息.异常出现的位置打印在了控制台上,同时将程序停止运行
  1. 异常处理的两种方式
    • try…catch…finally
    • throws
  1. try…catch的方式处理异常【掌握】

    • try…catch处理异常的基本格式
      1
      2
      3
      4
      5
      6
      7
      try    {
      可能出现问题的代码 ;
      }catch(异常名 变量名){
      针对问题的处理 ;
      }finally{
      释放资源;
      }
  2. 注意事项:

    • a:try中的代码越少越好
    • b:catch中要做处理,哪怕是一条输出语句也可以.(不能将异常信息隐藏)
    • c:处理多个异常
      • 1:能明确的尽量明确,不要用大的来处理。
      • 2:平级关系的异常谁前谁后无所谓,如果出现了子父关系,父必须在后面。
  1. 编译期异常和运行期异常的区别【理解】
    • 编译期异常和运行期异常的区别
    • Java中的异常被分为两大类:编译时异常和运行时异常。
      • 所有的RuntimeException类及其子类的实例被称为运行时异常,其他的异常就是编译时异常
      • 编译时异常: Java程序必须显示处理,否则程序就会发生错误,无法通过编译
      • 运行时异常: 无需显示处理,也可以和编译时异常一样处理
  1. throw的概述以及和throws的区别【掌握】

    • throws的方式处理异常:
      定义功能方法时,需要把出现的问题暴露出来让调用者去处理。那么就通过throws在方法上标识。
    • throw的概述:
      在功能方法内部出现某种情况,程序不能继续运行,需要进行跳转时,就用throw把异常对象抛出。
    • a:throws
    1. 用在方法声明后面,跟的是异常类名
    2. 可以跟多个异常类名,用逗号隔开
    3. 表示抛出异常,由该方法的调用者来处理
    4. throws表示出现异常的一种可能性,并不一定会发生这些异常

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      static void pop() throws NegativeArraySizeException {
      // 定义方法并抛出NegativeArraySizeException异常
      int[] arr = new int[-3]; // 创建数组
      }

      public static void main(String[] args) { // 主方法
      try { // try语句处理异常信息
      pop(); // 调用pop()方法
      } catch (NegativeArraySizeException e) {
      System.out.println("pop()方法抛出的异常");// 输出异常信息
      }
      }

      使用throws关键字将异常抛给调用者后,如果调用者不想处理该异常,可以继续向上抛出,但最终要有能够处理该异常的调用者。pop方法没有处理异常NegativeArraySizeException,而是由main函数来处理。

    • b:throw
    • 用在方法体内,跟的是异常对象名
    • 只能抛出一个异常对象名
    • 表示抛出异常,由方法体内的语句处理
      1
      2
      3
      4
      5
      6
      7
      8
      public PlayService getPlayService() {
      PlayService playService = BaseAppHelper.get().getPlayService();
      if (playService == null) {
      //待解决:当长期处于后台,如何保活?避免service被杀死……
      throw new NullPointerException("play service is null");
      }
      return playService;
      }
  • 异常的注意事项及如何使用异常处理【了解】
    • A:异常注意事项(针对编译期异常)
      • a:子类重写父类方法时,子类的方法必须抛出相同的异常或父类异常的子类。(父亲坏了,儿子不能比父亲更坏)
      • b:如果父类抛出了多个异常,子类重写父类时,只能抛出相同的异常或者是他的子集,子类不能抛出父类没有的异常
      • c:如果被重写的方法没有异常抛出,那么子类的方法绝对不可以抛出异常,如果子类方法内有异常发生,那么子类只能try,不能throws
    • B:如何使用异常处理
      • 原则:如果该功能内部可以将问题处理,用try,如果处理不了,交由调用者处理,这是用throws
      • 区别:
        • 后续程序需要继续运行就try
        • 后续程序不需要继续运行就throws
        • 如果JDK没有提供对应的异常,需要自定义异常。

Throwable类中的常用方法

  • 注意:catch关键字后面括号中的Exception类型的参数e。Exception就是try代码块传递给catch代码块的变量类型,e就是变量名。catch代码块中语句”e.getMessage();”用于输出错误性质。通常异常处理常用3个函数来获取异常的有关信息:
    • getCause():返回抛出异常的原因。如果 cause 不存在或未知,则返回 null。
    • getMeage():返回异常的消息信息。
    • printStackTrace():对象的堆栈跟踪输出至错误输出流,作为字段 System.err 的值。
  • 有时为了简单会忽略掉catch语句后的代码,这样try-catch语句就成了一种摆设,一旦程序在运行过程中出现了异常,就会忽略处理异常,而错误发生的原因很难查找。

Error(错误)

  • Error(错误):是程序无法处理的错误,表示运行应用程序中较严重问题。大多数错误与代码编写者执行的操作无关,而表示代码运行时 JVM(Java 虚拟机)出现的问题。例如,Java虚拟机运行错误(Virtual MachineError),当 JVM 不再有继续执行操作所需的内存资源时,将出现 OutOfMemoryError。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。

Exception(异常)

  1. Exception(异常):是程序本身可以处理的异常。
  2. Exception 这种异常分两大类运行时异常和非运行时异常(编译异常)。程序中应当尽可能去处理这些异常。
    1. 运行时异常:都是RuntimeException类及其子类异常,如NullPointerException(空指针异常)、IndexOutOfBoundsException(下标越界异常)等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。运行时异常的特点是Java编译器不会检查它,也就是说,当程序中可能出现这类异常,即使没有用try-catch语句捕获它,也没有用throws子句声明抛出它,也会编译通过。
    2. 非运行时异常 (编译异常):是RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常。

处理异常机制深入理解

抛出异常

  • 当一个方法出现错误引发异常时,方法创建异常对象并交付运行时系统,异常对象中包含了异常类型和异常出现时的程序状态等异常信息。运行时系统负责寻找处置异常的代码并执行。

捕获异常

  • 在方法抛出异常之后,运行时系统将转为寻找合适的异常处理器(exception handler)。潜在的异常处理器是异常发生时依次存留在调用栈中的方法的集合。当异常处理器所能处理的异常类型与方法抛出的异常类型相符时,即为合适 的异常处理器。运行时系统从发生异常的方法开始,依次回查调用栈中的方法,直至找到含有合适异常处理器的方法并执行。当运行时系统遍历调用栈而未找到合适 的异常处理器,则运行时系统终止。同时,意味着Java程序的终止。

异常处理方式不同

  • Java的异常(包括Exception和Error)分为可查的异常(checked exceptions)和不可查的异常(unchecked exceptions)。
    • 可查异常(编译器要求必须处置的异常):正确的程序在运行中,很容易出现的、情理可容的异常状况。可查异常虽然是异常状况,但在一定程度上它的发生是可以预计的,而且一旦发生这种异常状况,就必须采取某种方式进行处理。除了RuntimeException及其子类以外,其他的Exception类及其子类都属于可查异常。这种异常的特点是Java编译器会检查它,也就是说,当程序中可能出现这类异常,要么用try-catch语句捕获它,要么用throws子句声明抛出它,否则编译不会通过。
    • 不可查异常(编译器不要求强制处置的异常):包括运行时异常(RuntimeException与其子类)和错误(Error)。
    • 总结:一个方法所能捕捉的异常,一定是Java代码在某处所抛出的异常。简单地说,异常总是先被抛出,后被捕捉的。
  • 处理运行时异常
    • 由于运行时异常的不可查性,为了更合理、更容易地实现应用程序,Java规定,运行时异常将由Java运行时系统自动抛出,允许应用程序忽略运行时异常。
  • 处理Error
    • 对于方法运行中可能出现的Error,当运行方法不欲捕捉时,Java允许该方法不做任何抛出声明。因为,大多数Error异常属于永远不能被允许发生的状况,也属于合理的应用程序不该捕捉的异常。
  • 处理可查异常
    • 对于所有的可查异常,Java规定:一个方法必须捕捉,或者声明抛出方法之外。也就是说,当一个方法选择不捕捉可查异常时,它必须声明将抛出异常。

异常总结

异常总结

  1. try 块:用于捕获异常。其后可接零个或多个catch块,如果没有catch块,则必须跟一个finally块。
  2. catch 块:用于处理try捕获到的异常。
  3. finally 块:无论是否捕获或处理异常,finally块里的语句都会被执行。当在try块或catch块中遇到return语句时,finally语句块将在方法返回之前被执行。在以下4种特殊情况下,finally块不会被执行:
    1)在finally语句块中发生了异常。
    2)在前面的代码中用了System.exit()退出程序。
    3)程序所在的线程死亡。
    4)关闭CPU。

try-catch-finally规则

  1. 必须在 try 之后添加 catch 或 finally 块。try 块后可同时接 catch 和 finally 块,但至少有一个块。
  2. 必须遵循块顺序:若代码同时使用 catch 和 finally 块,则必须将 catch 块放在 try 块之后。
  3. catch 块与相应的异常类的类型相关。
  4. 一个 try 块可能有多个 catch 块。若如此,则执行第一个匹配块。即Java虚拟机会把实际抛出的异常对象依次和各个catch代码块声明的异常类型匹配,如果异常对象为某个异常类型或其子类的实例,就执行这个catch代码块,不会再执行其他的 catch代码块
  5. 可嵌套 try-catch-finally 结构。
  6. 在 try-catch-finally 结构中,可重新抛出异常。
  7. 除了下列情况,总将执行 finally 做为结束:JVM 过早终止(调用 System.exit(int));在 finally 块中抛出一个未处理的异常;计算机断电、失火、或遭遇病毒攻击。

try、catch、finally语句块的执行顺序

  1. 当try没有捕获到异常时:try语句块中的语句逐一被执行,程序将跳过catch语句块,执行finally语句块和其后的语句;
  2. 当try捕获到异常,catch语句块里没有处理此异常的情况:当try语句块里的某条语句出现异常时,而没有处理此异常的catch语句块时,此异常将会抛给JVM处理,finally语句块里的语句还是会被执行,但finally语句块后的语句不会被执行;
  3. 当try捕获到异常,catch语句块里有处理此异常的情况:在try语句块中是按照顺序来执行的,当执行到某一条语句出现异常时,程序将跳到catch语句块,并与catch语句块逐一匹配,找到与之对应的处理程序,其他的catch语句块将不会被执行,而try语句块中,出现异常之后的语句也不会被执行,catch语句块执行完后,执行finally语句块里的语句,最后执行finally语句块后的语句;

Throws抛出异常的规则

  1. 如果是不可查异常(unchecked exception),即Error、RuntimeException或它们的子类,那么可以不使用throws关键字来声明要抛出的异常,编译仍能顺利通过,但在运行时会被系统抛出。
  2. 必须声明方法可抛出的任何可查异常(checked exception)。即如果一个方法可能出现受可查异常,要么用try-catch语句捕获,要么用throws子句声明将它抛出,否则会导致编译错误
  3. 仅当抛出了异常,该方法的调用者才必须处理或者重新抛出该异常。当方法的调用者无力处理该异常的时候,应该继续抛出,而不是囫囵吞枣。
  4. 调用方法必须遵循任何可查异常的处理和声明规则。若覆盖一个方法,则不能声明与覆盖方法不同的异常。声明的任何异常必须是被覆盖方法所声明异常的同类或子类。

自定义异常

  1. 使用Java内置的异常类可以描述在编程时出现的大部分异常情况。除此之外,用户还可以自定义异常。用户自定义异常类,只需继承Exception类即可。
  2. 在程序中使用自定义异常类,大体可分为以下几个步骤。
    1. 创建自定义异常类。
    2. 在方法中通过throw关键字抛出异常对象。
    3. 如果在当前抛出异常的方法中处理异常,可以使用try-catch语句捕获并处理;否则在方法的声明处通过throws关键字指明要抛出给方法调用者的异常,继续进行下一步操作。
    4. 在出现异常方法的调用者中捕获并处理异常。
-------------本文结束,感谢您的阅读-------------
您的支持将鼓励我继续创作!!