当前位置 : 主页 > 编程语言 > java >

Java--异常详解

来源:互联网 收集:自由互联 发布时间:2022-07-22
Java--异常详解 ​​异常分类​​ ​​Throwable​​ ​​Error​​ ​​Exception​​ ​​RuntimeExceptionn​​ ​​CheckedException​​ ​​异常处理方式​​ ​​throw​​ ​​throws​​ ​​j


Java--异常详解

  • ​​异常分类​​
  • ​​Throwable​​
  • ​​Error​​
  • ​​Exception​​
  • ​​RuntimeExceptionn​​
  • ​​CheckedException​​
  • ​​异常处理方式​​
  • ​​throw​​
  • ​​throws​​
  • ​​jvm自动抛出​​
  • ​​throw和throws的异同​​
  • ​​异常的捕获​​
  • ​​异常调用链​​

异常分类

如果某个方法不能按照正常的途径完成任务,就可以通过另一种路径退出方法。在这种情况下会抛出一个封装了错误信息的对象。此时,这个方法会立刻退出同时不返回任何值。另外,调用这个方法的其他代码也无法继续执行,异常处理机制会将代码执行交给异常处理器。

Java--异常详解_Java异常

Throwable

Throwable 是 Java 语言中所有错误或异常的超类。

也就是说,如果你要抛出或者声明一个异常,那么,这个异常必须继承Throwable.

public class Main {

public static void main(String[] args) {

}


public void testMyError() throws MyError{
throw new MyError();
}

public void testMyErrorNon() throws MyErrorNon {
throw new MyErrorNon();
}

public void testMyException() throws MyException {
throw new MyException();
}

public void testMyExceptionNon() throws MyExceptionNon{
throw new MyExceptionNon();
}
}

Java--异常详解_Java异常处理方式_02

Error

Error 类是指 java 运行时系统的内部错误和资源耗尽错误。应用程序不会抛出该类对象。如果出现了这样的错误,除了告知用户,剩下的就是尽力使程序安全的终止.

Java--异常详解_Java异常捕获原则_03

Exception

Java--异常详解_Java异常调用链_04

Exception 又 有 两 个 分 支 , 一 个 是 运 行 时 异 常 RuntimeException , 一个是CheckedException.

RuntimeExceptionn

运行时异常

如 : NullPointerException 、 ClassCastException ; 一 个 是 检 查 异 常CheckedException,如 I/O 错误导致的 IOException、SQLException。 RuntimeException 是那些可能在 Java 虚拟机正常运行期间抛出的异常的超类。 如果出现 RuntimeException,那么一定是程序员的错误.

CheckedException

检查异常

一般是外部错误,这种异常都发生在编译阶段,Java 编译器会强制程序去捕获此类异常,即会出现要求你把这段可能出现异常的程序进行 try catch,该类异常一
般包括几个方面:

  • 试图在文件尾部读取数据
  • 试图打开一个错误格式的 URL
  • 试图根据给定的字符串查找 class 对象,而这个字符串表示的类并不存在
  • 异常处理方式

    throw

    Java--异常详解_Java异常调用链_05

    throws

    Java--异常详解_Java异常分类_06

    jvm自动抛出

    Java--异常详解_Java异常调用链_07

    Java--异常详解_Java异常调用链_08

    throw和throws的异同

    相同点:

  • 都继承Throwable
  • 程序的执行顺序收到影响
  • 不一定每次都出现,概率不确定
  • 消极处理异常的方式,只是抛出或者可能抛出异常,但是不会由方法去处理异常,真正的处理异常常有方法的调用者处理。
  • 不同点:

    • 位置不同:
  • throws用在方法上,后面跟的是异常类,可以跟多个;而throw用在方法内,后面跟的是异常对象。
    • 功能不同:
      2. throws用来声明异常,让调用者知道该方法可能出现的问题,可以给出预先的处理方式;throw抛出具体的异常问题对象,执行到throw,就无法在继续按正常的顺序继续执行了。跳转到调用者,并将具体的问题对象抛给调用者。
      3. throws表示出现异常的一种可能性,并不一定会发生这些异常;throw则是抛出了异常,执行throw则一定抛出了某种异常。

    异常的捕获

    Java--异常详解_Java异常处理方式_09

    public class Main {

    public static void main(String[] args) {
    try {
    testcatch();
    } catch (MyException4 myException4) {
    myException4.printStackTrace();
    } catch (MyException3 myException3) {
    myException3.printStackTrace();
    } catch (MyException2 myException2) {
    myException2.printStackTrace();
    } catch (MyException1 myException1) {
    myException1.printStackTrace();
    } catch (Exception e) {
    e.printStackTrace();
    } catch (Throwable throwable) {
    throwable.printStackTrace();
    }
    }

    public static void testcatch() throws Throwable, MyException4, MyException3, MyException2, MyException1, Exception, Error {
    }

    }

    异常的捕获,最靠近try的,异常范围是最小的。异常匹配时,从上到下进行匹配,如果将范围大的异常捕获放在前面,那么后面比这个异常范围小的异常,永远得不到调用。

    异常调用链

    每个方法内部出现异常后,要么方法内部使用try捕获异常,方法内解决这个异常。使程序继续运行下去。

    要么将异常抛出,由方法的调用者处理这个异常。

    public class OMain {

    public static void main(String[] args) {
    try {
    new OMain().test4();
    } catch (MyException4 myException4) {
    myException4.printStackTrace();
    }
    }

    public void test4() throws MyException4 {
    try {
    test3();
    } catch (MyException3 myException3) {
    throw new MyException4();
    }
    }

    public void test3() throws MyException3 {
    try {
    test2();
    } catch (MyException2 myException2) {
    throw new MyException3();
    }
    }

    public void test2() throws MyException2 {
    try {
    test1();
    } catch (MyException1 myException1) {
    throw new MyException2();
    }
    }

    public void test1() throws MyException1 {
    try {
    test();
    } catch (Exception e) {
    throw new MyException1();
    }
    }

    public void test() throws Exception {
    throw new Exception();
    }

    }

    在这个例子中,真正的异常是在test方法中出现的,但是因为每一个方法都在自己的方法内部捕获了下层异常,然后抛出自己的异常。

    此时整个异常已经失真了,从异常堆栈根本无法知道问题出在哪里:

    Java--异常详解_Java异常处理方式_10

    异常调用链的完整,可以帮助我们更快的定位问题。

    public class OMain {

    public static void main(String[] args) {
    try {
    new OMain().test4();
    } catch (MyException4 myException4) {
    myException4.printStackTrace();
    }
    }

    public void test4() throws MyException4 {
    try {
    test3();
    } catch (MyException3 myException3) {
    MyException4 myException4 = new MyException4();
    myException4.initCause(myException3);
    throw myException4;
    }
    }

    public void test3() throws MyException3 {
    try {
    test2();
    } catch (MyException2 myException2) {
    MyException3 myException3 = new MyException3();
    myException3.initCause(myException2);
    throw myException3;
    }
    }

    public void test2() throws MyException2 {
    try {
    test1();
    } catch (MyException1 myException1) {
    MyException2 myException2 = new MyException2();
    myException2.initCause(myException1);
    throw myException2;
    }
    }

    public void test1() throws MyException1 {
    try {
    test();
    } catch (Exception e) {
    MyException1 myException1 = new MyException1();
    myException1.initCause(e);
    throw myException1;
    }
    }

    public void test() throws Exception {
    throw new Exception();
    }

    }

    在Throwable中,有一个属性cause,默认是自己。这个属性就是记录当前异常的上一个异常是谁。如果cause就是自己,那么,说明这个异常就是最先抛出的异常。

    Java--异常详解_Java异常处理方式_11

    这样整个异常调用链就完整了:

    Java--异常详解_Java异常处理方式_12

    其调用关系,可以完整的从堆栈中看出。

    这是最常见的场景。

    我们假设这个调用是上下级的调用:

    test4->test3->test2->test1->test

    其中越是下级,做的工作越简单,也越是底层方法的调用。在底层方法得到调用中,就会出现各种各样的异常(什么异常都有可能)

    在例子中出现的是Exception。

    在test方法中,可能完全不明白为什么出现异常,或者说,出现异常的原因是什么,如何解决?这些问题在test方法中都无法解决的。

    于是test方法将自己无法解决的异常抛出。

    在test1方法中,捕获到了异常,可能test1交给test是读取一个文件,结果出现了文件没找到的异常。那么,在test1方法中就知道了,是文件没找到,但是,对于没找到的文件,该如何处理呢?test1就不知道了,于是,继续向上抛出异常,此时异常被进行细化,由文件没找到的异常转换为文件读取失败。

    在test2方法中,捕获到了异常,可能test2交给test1是读取一个文件夹的文件,结果出现了部分文件未找到。那么在test2方法中,我们知道了,是部分文件没找到,而不是文件夹内全部的文件没找到。在test2方法中,可以确定,是某一个文件没有找到,那么对于这个文件,我们是要忽略,还是重试呢?test2也不知道,此时只能转换异常,然后继续抛出。转换的异常更加细化,部分文件未找到。

    。。。。

    以此类推,最终异常被交给了主线程,当主线程也无法确认后,将问题抛给用户,由用户决定。用户决定如何处理。

    这个过程可以类比我们使用操作系统复制整个文件夹的过程。

    里面是一层层的划分,当出现无法抉择的异常,最终抛给用户决定。

    遇到可以抉择的异常,直接处理。(一个文件失败自动重试3次。。)

    将异常交由调用者处理,是非常正确的处理方式。\

    这种方式是金字塔结构,越是底层的方法,异常范围越大。

    ==还有一种是倒金字塔结构,越是底层的方法,异常越小。==对于这种结构,无需做任何捕获,直接抛出即可。

    public class OMain {

    public static void main(String[] args) {
    try {
    new OMain().test();
    } catch (Throwable e) {
    e.printStackTrace();
    }
    }

    public void test4() throws MyException4 {
    throw new MyException4();
    }

    public void test3() throws MyException3 {
    test4();
    }

    public void test2() throws MyException2 {
    test3();
    }

    public void test1() throws MyException1 {
    test2();
    }

    public void test() throws Throwable {
    test1();
    }

    }

    Java--异常详解_Java异常_13


    上一篇:java处理扩大与缩小图片
    下一篇:没有了
    网友评论