前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java异常实现 及 原理

Java异常实现 及 原理

作者头像
用户7886150
修改2020-12-15 10:38:10
5340
修改2020-12-15 10:38:10
举报
文章被收录于专栏:bit哲学院

参考链接: 用Java打印异常消息的3种不同方式

Java的基本理念是:结构不佳的代码 不能运行

发现代码错误的时机:

    1、程序编译期间,由编译器对代码进行编译,遇到错误会给出提示

    2、程序运行期间,运行时出现了不可预料的错误,会抛出异常

异常的意义:不允许程序沿着其正常的路径继续走下去,并告诉我们程序发生了什么问题。

一个异常的执行顺序

1、new一个异常对象2、终止当前的执行程序。3、弹出异常对象的引用。4、异常处理机制接管被终止的执行程序。5、寻找一个恰当的地点(异常处理程序)继续执行程序。

一个异常的诞生

1、捕获异常

引入一个概念:监控区域 -- 一段可能产生异常的代码并且后面跟着处理异常的代码

(1)try块

一个方法内部出现问题,或者一个方法内部调用其他方法的时候出现问题,当前程序会立刻中断。如果你不希望程序中断,可以在这个块儿中增加一个“尝试”各种可能产生各种异常的方法调用,他就是try块儿

try{

    String ljh = null ;

    ljh.equals("qwe");

}

(2)catch异常处理程序

必须紧跟在try后面,当你希望对异常做出处理,异常处理程序就是你最好的选择,在这里你可以针对每种想要捕获的异常,准备相应的应对措施,这就是catch

try{

    String ljh = null ;

    ljh.equals("qwe");

}catch(Type1 e1){

    e1.printStackTrace();

}catch(Type2 e2){

    e2.printStackTrace();

}catch(Type3 e3){

    e3.printStackTrace();

} // etc...

当异常抛出后,异常处理机制会寻找与异常类型匹配的catch块儿,例如type2类型的异常就会执行e2.printStackTrace();

注意一点:当异常处理机制匹配到了一个异常处理程序时,会在当前异常程序处理完毕后结束整个异常处理环节,其他的异常处理程序不会再执行。

例如:Exception类型的异常涵盖了RuntimeException类型异常,但是并不会执行Exception异常处理程序

try{

    String ljh = null ;

    ljh.equals("qwe");

}catch(RuntimeException e){

    System.out.println("RuntimeException");

    e.printStackTrace();

}catch(Exception e){

    System.out.println("Exception");

    e.printStackTrace();

}

RuntimeException

java.lang.NullPointerException

    at com.umbrellacore.privilege.controller.aaa.main(aaa.java:7)

Process finished with exit code 0

异常处理理论上有两种基本模型:终止与恢复

终止模型:这种模型将假设错误非常关键,以至于程序无法返回到异常发生的地方继续执行,一旦异常抛出错误就意味着世界末日,意味着死亡,意味着GG

恢复模型:异常处理程序发现了错误,并且修复了错误然后重新调用出问题的方法,并且认为第二次调用该方法会成功。通常可以将try块放入while循环中,不断执行方法,直到得到满意的结果。

日复一日,年复一年,伟大的程序员们认为恢复模型会带来很多非通用性的代码,增加了维护难度,因为你需要穷举出各种可能的问题和异常的解决办法,并且一旦异常始终无法正常解决,就会陷入无限的循环中。说白了就是管的越细越会给自己造成不必要的麻烦。我们为什么不管一个大面儿,兵来将挡水来土掩,直接抛出来更容易维护。

2、创建异常对象

2.1 自定义异常

所谓的自定义异常就是java提供的异常体系无法满足你的需求,说白了就是有些异常系统无法预见,需要人为干预。所以才有自定义异常的方式,要自己定义一个异常必须从已有的异常类进行继承。

如何自定义异常类:

public class MyException extends Exception {

    public MyException(){}

    public MyException(String exceptionMassage){

        super(exceptionMassage); //明确调用基类的构造器,接收一个字符串为参数

    }

}

public static void main(String[] args) {

    try{

        String ljh = null ;

        try{

            ljh.equals("qwe");

        }catch(Exception e){

            throw new MyException("ljh = null");

        }

    }catch(MyException e){

        System.out.println("RuntimeException");

        e.printStackTrace();

    }

}

com.umbrellacore.privilege.controller.MyException: ljh = null

    at com.umbrellacore.privilege.controller.aaa.main(aaa.java:10)

RuntimeException

Process finished with exit code 0

所有的标准异常类都有两个构造器,一个是默认构造器,一个是接受字符串作为参数,以便把相关参数放入异常对象的构造器。

2.2 throw一个异常

2.1中出现了throw这个关键字,throw这个动作类似return,但是他们之间是不同的。可以简单的把异常处理看成一种不同的返回机制,此外还可以抛出任意类型的Throwable对象,他是异常类型的根类。

对于不同的错误信息,会抛出不同的异常类型,错误信息可以保存在异常对象的内部,或者用异常类的名字来暗示,通常异常对象中仅有的信息就是异常类型。

异常原理

上面说了那么多,对异常大概有了一个初步的了解,起码做到了会用,但是背后的原理究竟是什么,下面来完整剖析一下异常的基本原理。

1、继承关系

2、源码分析

Throwable : Throwable类是整个Java异常体系的超

 ======顶层Throwable构造器======

public Throwable() {

    fillInStackTrace(); //在Throwable对象中填充执行的堆栈信息。此方法在Throwable对象中记录当前线程的栈帧的状态信息

}

public Throwable(String message) {

    fillInStackTrace();

    detailMessage = message;

}

public Throwable(String message, Throwable cause) {

    fillInStackTrace();

    detailMessage = message;

    this.cause = cause;

}

public Throwable(Throwable cause) {

    fillInStackTrace();

    detailMessage = (cause==null ? null : cause.toString());

    this.cause = cause;

}

可以看到这里出镜最多的是fillInStackTrace这个方法,其他的都是detailMassage,也就是异常的语句。所以我们把关注点聚焦在fillInStackTrace这个方法上!

private static final StackTraceElement[] UNASSIGNED_STACK = new StackTraceElement[0];

private StackTraceElement[] stackTrace = UNASSIGNED_STACK; //stackTrace 指向 UNASSIGNED_STACK 

public synchronized Throwable fillInStackTrace() {

   if (stackTrace != null ||

            backtrace != null /* Out of protocol state */ ) {

            fillInStackTrace(0);

            stackTrace = UNASSIGNED_STACK;

        }

        return this;

    }

private native Throwable fillInStackTrace(int dummy);

从上述代码中来进行分析

首先定义了一个数组,数组的类型是StackTraceElement。而且采用了final类型的数组,证明其不可在引用其他类型

来看看这个Element都含哪些东西:

public final class StackTraceElement implements java.io.Serializable {

//可以看出这是一个final class,说明他是一个基础类不许被继承。

    private String declaringClass;   // 方法的类名

    private String methodName;        //方法名

    private String fileName;          //文件名

    private int    lineNumber;       // 调用的行数

// =========构造器======

public StackTraceElement(String declaringClass, String methodName,

                         String fileName, int lineNumber) {

    this.declaringClass = Objects.requireNonNull(declaringClass, "Declaring class is null");

    this.methodName     = Objects.requireNonNull(methodName, "Method name is null");

    this.fileName       = fileName;

    this.lineNumber     = lineNumber;

}

接下来回到fillInStackTrace方法,它还调用了fillInStackTrace(0) 经查看其实调用的是

private native Throwable fillInStackTrace(int dummy);

这是个native方法,也就是个底层本地方法来获取当前线程的堆栈信息。据悉这是一个非常耗时的方法。如果我们仅仅需要用到异常的传播性质,而不关心异常的堆栈信息,那么完全可以在自定义异常类的时候重写fillInStackTrace()方法。

最后我们看一下我们常用的几种打印异常的方法,他们的底层原理

printStackTrace

err是运行期异常和错误反馈的输出流方向。此方法将此对象的堆栈跟踪输出至错误输出流。输出的第一行包含此对象的 toString() 方法的结果。剩余行表示以前由方法 fillInStackTrace() 记录的数据。

    public void printStackTrace() {

        printStackTrace(System.err);

    }

  public void printStackTrace(PrintStream s) {

      printStackTrace(new WrappedPrintStream(s));

  }

private void printStackTrace(PrintStreamOrWriter s) {

    // Guard against malicious overrides of Throwable.equals by

    // using a Set with identity equality semantics.

    Set<Throwable> dejaVu =

        Collections.newSetFromMap(new IdentityHashMap<Throwable, Boolean>());

    dejaVu.add(this);

    synchronized (s.lock()) {

        // Print our stack trace

        s.println(this);

        StackTraceElement[] trace = getOurStackTrace();

        for (StackTraceElement traceElement : trace)

            s.println("\tat " + traceElement);

        // Print suppressed exceptions, if any

        for (Throwable se : getSuppressed())

            se.printEnclosedStackTrace(s, trace, SUPPRESSED_CAPTION, "\t", dejaVu);

        // Print cause, if any

        Throwable ourCause = getCause();

        if (ourCause != null)

            ourCause.printEnclosedStackTrace(s, trace, CAUSE_CAPTION, "", dejaVu);

    }

}

toString

可以看出这里输出了类的信息+定位信息

注意这里的getLocalizedMessage 其实就是 return detailMessage,该值可以自定义字符串,也可以是cause.toString 详见上面的构造器

public String toString() {

    String s = getClass().getName();

    String message = getLocalizedMessage();

    return (message != null) ? (s + ": " + message) : s;

}

getMessage

可以看出返回的仅仅是detailMessage

public String getMessage() {

    return detailMessage;

}

getStackTrace

这个方法返回的是上面介绍的StackTraceElement数组的信息,而且会根据栈的深度去遍历,往数组里放入内容的其实是调用了一个本地方法native StackTraceElement getStackTraceElement(int index); 获得的是栈的信息

public StackTraceElement[] getStackTrace() {

    return getOurStackTrace().clone();

}

private synchronized StackTraceElement[] getOurStackTrace() {

    // Initialize stack trace field with information from

    // backtrace if this is the first call to this method

    if (stackTrace == UNASSIGNED_STACK ||

        (stackTrace == null && backtrace != null) /* Out of protocol state */) {

        int depth = getStackTraceDepth();

        stackTrace = new StackTraceElement[depth];

        for (int i=0; i < depth; i++)

            stackTrace[i] = getStackTraceElement(i);

    } else if (stackTrace == null) {

        return UNASSIGNED_STACK;

    }

    return stackTrace;

}

Error : Error表示程序在运行期间出现了十分严重、不可恢复的错误

======Error======

public class Error extends Throwable { 

static final long serialVersionUID = 4980196508277280342L; 

public Error() { super(); } 

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

public Error(String message, Throwable cause) { super(message, cause); } 

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

protected Error(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); }}

Exception:Exception是应用层面上最顶层的异常类

======Exception======

public class Exception extends Throwable { 

static final long serialVersionUID = -3387516993124229948L; 

public Exception() { super(); } 

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

public Exception(String message, Throwable cause) { super(message, cause); } 

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

protected Exception(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); }}

可以看出error 和 exception 基本上都是大同小异。

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档