JDK源码解析之 java.lang.Throwable

在 Java 中,所有的异常都有一个共同的祖先 Throwable(可抛出)。Throwable 指定代码中可用异常传播机制通过 Java 应用程序传输的任何问题的共性。

一、类定义

public class Throwable implements Serializable {}
  • Serializable:可被序列化的标志接口

二、成员变量

//静态变量
//这两个变量主要用于序列化
private static class SentinelHolder {public static final StackTraceElement STACK_TRACE_ELEMENT_SENTINEL =new StackTraceElement("", "", null, Integer.MIN_VALUE);public static final StackTraceElement[] STACK_TRACE_SENTINEL =new StackTraceElement[] {STACK_TRACE_ELEMENT_SENTINEL};
}
//一个空的StackTraceElement[]数组,用来初始化或者作为返回值。
private static final StackTraceElement[] UNASSIGNED_STACK = new StackTraceElement[0];
//一个空的只读List,同样用于初始化
private static final List<Throwable> SUPPRESSED_SENTINEL =Collections.unmodifiableList(new ArrayList<Throwable>(0));
//前两个用于作为错误信息,后两个作为printStackTrace方法的说明前缀使用
private static final String NULL_CAUSE_MESSAGE = "Cannot suppress a null exception.";
private static final String SELF_SUPPRESSION_MESSAGE = "Self-suppression not permitted";
private static final String CAUSE_CAPTION = "Caused by: ";
private static final String SUPPRESSED_CAPTION = "Suppressed: ";
//用作getSuppressed方法的返回值(当suppressedExceptions没有元素时)
private static final Throwable[] EMPTY_THROWABLE_ARRAY = new Throwable[0];//实例变量
//用来保存栈信息的轨迹
private transient Object backtrace;	
//描述这个异常的信息
private String detailMessage;		
//描述这个异常由哪个Throwable导致,默认是this。
private Throwable cause = this;			
//异常抛出位置的栈信息,每个StackTraceElement代表一个栈信息,默认指向静态常量UNASSIGNED_STACK,代表栈信息为空。
private StackTraceElement[] stackTrace = UNASSIGNED_STACK;		
//JDK 1.7引入的新特性。该List用来保存被屏蔽的异常对象,在try-catch语句中,如果try中抛出了异常,在执行流程转移到方法栈上一层之前,finally语句块会执行,但是,如果在finally语句块中又抛出了一个异常,那么这个异常会覆盖掉之前抛出的异常,这点很像finally中return的覆盖。
private List<Throwable> suppressedExceptions = SUPPRESSED_SENTINEL;		

三、构造方法

public Throwable() {fillInStackTrace();
}
/*** @param message 异常描述信息,该参数直接赋值给实例变量detailMessage*/
public Throwable(String message) {fillInStackTrace();detailMessage = message;
}
/*** @param message 异常描述信息,该参数直接赋值给实例变量detailMessage* @param cause 描述当前异常由哪个异常引发*/
public Throwable(String message, Throwable cause) {fillInStackTrace();detailMessage = message;this.cause = cause;
}
/*** @param cause 描述当前异常由哪个异常引发*/
public Throwable(Throwable cause) {fillInStackTrace();detailMessage = (cause==null ? null : cause.toString());this.cause = cause;
}
/*** @param message 异常描述信息,该参数直接赋值给实例变量detailMessage* @param cause 描述当前异常由哪个异常引发* @param enableSuppression 是否支持Suppress异常消息* @param writableStackTrace 是否调用fillInStackTrace使堆栈信息可以写入*/
protected Throwable(String message, Throwable cause,boolean enableSuppression, boolean writableStackTrace) {if (writableStackTrace) {fillInStackTrace();} else {stackTrace = null;}detailMessage = message;this.cause = cause;if (!enableSuppression)suppressedExceptions = null;
}

Throwable提供了4个public构造器和1个protected构造器(该构造器由JDK1.7引入)。4个public构造器共同点就是都调用了fillInStackTrace方法。

fillInStackTrace会首先判断stackTrace是不是为null,如果不为null则会调用native方法fillInStackTrace获取当前堆栈信息。那么什么时候为null呢,答案是上面的protected构造器可以指定writableStackTracefalse,这样stackTrace就为null了,就不会调用fillInStackTrace获取堆栈信息。如果你不需要异常的栈信息,你也可以重写这个方法,让它直接返回this,毕竟异常的爬栈是一个开销比较大的操作。

四、常用方法

1、printStackTrace方法

printStackTrace把传入的输入流用内部类WrappedPrintStreamWrappedPrintWriter包装,主要用来实现printStackTrace方法在打印堆栈信息时的线程安全。

public void printStackTrace() {printStackTrace(System.err);
}public void printStackTrace(PrintStream s) {printStackTrace(new WrappedPrintStream(s));
}public void printStackTrace(PrintWriter s) {printStackTrace(new WrappedPrintWriter(s));
}private void printStackTrace(PrintStreamOrWriter s) {Set<Throwable> dejaVu = Collections.newSetFromMap(new IdentityHashMap<Throwable, Boolean>());dejaVu.add(this);synchronized (s.lock()) {s.println(this);StackTraceElement[] trace = getOurStackTrace();for (StackTraceElement traceElement : trace)s.println("\tat " + traceElement);for (Throwable se : getSuppressed())se.printEnclosedStackTrace(s, trace, SUPPRESSED_CAPTION, "\t", dejaVu);Throwable ourCause = getCause();if (ourCause != null)ourCause.printEnclosedStackTrace(s, trace, CAUSE_CAPTION, "", dejaVu);}
}
}

printStackTrace把传入的输入流用内部类WrappedPrintStreamWrappedPrintWriter包装,主要用来实现printStackTrace方法在打印堆栈信息时的线程安全。

2、getMessage()和getLocalizedMessage()

默认条件下可以调用getMessagegetLocalizedMessage方法获取detailMessage

public String getMessage() {return detailMessage;
}
public String getLocalizedMessage() {return getMessage();
}

3、getCause()和initCause()

通过构造器自定义cause。构造完成后,可以通过getCause方法访问获取,如果没有指定cause,则返回null

在构造完成后,也可通过initCause方法修改:修改cause的前提是必须在构造方法中没有指定别的cause(即默认条件下causethis),否则会抛出IllegalStateException异常。另外也不能修改causethis

public synchronized Throwable getCause() {return (cause == this ? null : cause);
}public synchronized Throwable initCause(Throwable cause) {if (this.cause != this)throw new IllegalStateException("Can't overwrite cause with " +Objects.toString(cause, "a null"), this);if (cause == this)throw new IllegalArgumentException("Self-causation not permitted", this);this.cause = cause;return this;
}

五、拓展

1.处理异常机制

在 Java 应用程序中,异常处理机制为:抛出异常,捕捉异常。

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

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

​ 对于运行时异常、错误或可查异常,Java技术所要求的异常处理方式有所不同。

​ 由于运行时异常的不可查性,为了更合理、更容易地实现应用程序,Java规定,运行时异常将由Java运行时系统自动抛出,允许应用程序忽略运行时异常。

​ 对于方法运行中可能出现的Error,当运行方法不欲捕捉时,Java允许该方法不做任何抛出声明。因为,大多数Error异常属于永远不能被允许发生的状况,也属于合理的应用程序不该捕捉的异常。

​ 对于所有的可查异常,Java规定:一个方法必须捕捉,或者声明抛出方法之外。也就是说,当一个方法选择不捕捉可查异常时,它必须声明将抛出异常。

​ 能够捕捉异常的方法,需要提供相符类型的异常处理器。所捕捉的异常,可能是由于自身语句所引发并抛出的异常,也可能是由某个调用的方法或者Java运行时 系统等抛出的异常。也就是说,一个方法所能捕捉的异常,一定是Java代码在某处所抛出的异常。简单地说,异常总是先被抛出,后被捕捉的。

​ 任何Java代码都可以抛出异常,如:自己编写的代码、来自Java开发环境包中代码,或者Java运行时系统。无论是谁,都可以通过Java的throw语句抛出异常。

​ 从方法中抛出的任何异常都必须使用throws子句。

​ 捕捉异常通过try-catch语句或者try-catch-finally语句实现。

​ 总体来说,Java规定:对于可查异常必须捕捉、或者声明抛出。允许忽略不可查的RuntimeException和Error。

try {  // 可能会发生异常的程序代码  
} catch (Type1 id1){  // 捕获并处置try抛出的异常类型Type1  
}  
catch (Type2 id2){  //捕获并处置try抛出的异常类型Type2  
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/535751.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JDK源码解析之 java.lang.Error

java.lang.Error 错误。是所有错误的基类&#xff0c;用于标识严重的程序运行问题。这些问题通常描述一些不应被应用程序捕获的反常情况。 一、源码部分 //继承了java.lang.Throwable public class Error extends Throwable {//适用于java序列化机制,过判断类的serialVersionU…

linux命令之有关网络的操作命令

1&#xff0e;hostname 命令&#xff08;1&#xff09;一般格式&#xff1a;hostname [选项] [主机名]&#xff08;2&#xff09;说明&#xff1a;显示或设置系统的主机名&#xff1b;如果无任何选项和主机名&#xff0c;则用于显示系统的主机名。&#xff08;3&#xff09…

JDK源码解析之 java.lang.Exception

异常。是所有异常的基类&#xff0c;用于标识一般的程序运行问题。这些问题通常描述一些会被应用程序捕获的反常情况。 一、源码部分 //继承了java.lang.Throwable public class Exception extends Throwable {//适用于java序列化机制,过判断类的serialVersionUID来验证的版本…

linux命令之有关关机和查看系统信息的命令

shutdown 正常关机 reboot 重启计算机 ps 查看目前程序执行的情况top 查看目前程序执行的情景和内存使用情况kill 终止一个进程date 更改或查看目前时间 一&#xff0e;查看系统的进程 要管理进程&#xff0c;首先要知…

HDFS-文件读写过程

一、文件读取 Client向NameNode发起RPC请求&#xff0c;来确定请求文件block所在的位置&#xff1b;NameNode会视情况返回文件的部分或者全部block列表&#xff0c;对于每个block&#xff0c;NameNode 都会返回含有该 block 副本的 DataNode 地址&#xff1b; 这些返回的 DN 地…

linux命令复习之有关磁盘空间的命令

1&#xff0e;mount 命令&#xff08;1&#xff09;一般格式&#xff1a;mount 文件系统类型 [选项] 挂接设备&#xff08;2&#xff09;说明&#xff1a;将某个文件系统挂载到某个目录上。当这个命令执行成功后&#xff0c;直到使用 umount 将这个文件系统移除为止。&…

HDFS-常用API操作

一、Maven <dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>RELEASE</version> </dependency> <dependency><groupId>org.apache.logging.log4j</groupId><artifactId>…

linux命令之-管理文件和目录的命令

一. 创建和删除目录的命令 1&#xff0e;mkdir 命令 &#xff08;1&#xff09;一般格式&#xff1a;mkdir [选项] 目录名 &#xff08;2&#xff09;说明&#xff1a;该命令创建由目录名命名的目录。 &#xff08;3&#xff09;举例1&#xff1a; 在目录 /usr/fedora 下建…

Hive-简介入门

Hive简介 Hive最初是Facebook为了满足对海量社交网络数据的管理和机器学习的需求而产生和发展的。互联网现在进入了大数据时代&#xff0c;大数据是现在互联网的趋势&#xff0c;而hadoop就是大数据时代里的核心技术&#xff0c;但是hadoop的mapreduce操作专业性太强&#xff0…

Hive-原理解析

一、Hive 架构 下面是Hive的架构图。 Hive的体系结构可以分为以下几部分 1、用户接口&#xff1a;CLI&#xff08;hive shell&#xff09;&#xff1b;JDBC&#xff08;java访问Hive&#xff09;&#xff1b;WEBUI&#xff08;浏览器访问Hive&#xff09; 2、元数据&#x…

linux命令之history命令

在Linux系统上输入命令并按下Enter后&#xff0c;这个命令就会存放在命令记录表 ( ~/.bash_history )中&#xff0c;预定的记录为1000条&#xff0c;这些都定义在环境变量中。列出所有的历史记录&#xff1a;#history 只列出最近10条记录&#xff1a;#history 10 (注,history和…

Hive-配置安装

一、HDFS安装 1、解压到指定位置tar -zxvf apache-hive-3.1.2-bin.tar.gz -C /usr/local/apps/ 2、改名mv apache-hive-3.1.2-bin/ hive-3.1.2 3、在conf目录下添加Hadoop安装路径mv hive-env.sh.template hive-env.sh # 配置HADOOP_HOME路径 export HADOOP_HOME/opt/module/…

linux文件系统概念目录结构

文件系统概念一. 文件与目录的定义1. 文件系统&#xff1a;它是磁盘上有特定格式的一片区域&#xff0c;操作系统通过文件系统可以方便地查寻和访问其中所包含的磁盘块&#xff1b;2. 文件&#xff1a;文件系统中存储数据的一个命名的对象。3. 目录&#xff1a;其中包含许多文件…

JDK源码解析之 java.lang.Class

Java程序在运行时&#xff0c;Java运行时系统一直对所有的对象进行所谓的运行时类型标识。 这项信息纪录了每个对象所属的类。虚拟机通常使用运行时类型信息选准正确方法去执行&#xff0c;用来保存这些类型信息的类是Class类。Class类封装一个对象和接口运行时的状态&#xff…

Linux Vi常用技巧

VI常用技巧VI命令可以说是Unix/Linux世界里最常用的编辑文件的命令了&#xff0c;但是因为它的命令集众多&#xff0c;很多人都不习惯使用它&#xff0c;其实您只需要掌握基本命令&#xff0c;然后加以灵活运用&#xff0c;就会发现它的优势&#xff0c;并会逐渐喜欢使用这种方…

JDK源码解析之 java.lang.ClassLoader

Class代表它的作用对象是类&#xff0c;Loader代表它的功能是加载&#xff0c;那么ClassLoader就是把一个以.class结尾的文件以JVM能识别的存储形式加载到内存中。 一、核心方法 1、loadClass方法 protected Class<?> loadClass(String name, boolean resolve) throws…

Linux Vi的使用

一、插入文本┌──┬────────────┐│命令│描述 │├──┼────────────┤│i │在当前字符前插入文本 │├──┼────────────┤│I │在行首插入文本 │├──┼────────────┤│a │在当前字符后添加文本 │├──┼──…

Hive-beeline服务

Hive客户端工具后续使用了Beeline 替代HiveCLI &#xff0c;并且后续版本也会废弃掉HiveCLI 客户端工具,Beeline是 Hive 0.11版本引入的新命令行客户端工具,它是基于SQLLine CLI的JDBC客户端。 Beeline支持嵌入模式(embedded mode)和远程模式(remote mode)。在嵌入式模式下&am…

用户账号管理基本概念

什么是用户账号管理用户账号一般包括普通用户账号、管理账号和系统账号。为了鉴别用户身份以及加强系统安全&#xff0c;系统为每个使用它的人分配了一个账号&#xff0c;这就是普通用户账号。每个人拥有一个独立的普通用户账号&#xff0c;每个账号有不同的用户名和密码。用户…

JDK源码解析之 Java.lang.Compiler

Compiler类提供支持Java到本机代码编译器和相关服务。在设计上&#xff0c;它作为一个占位符在JIT编译器实现。 一、源码部分 public final class Compiler {private Compiler() {} // dont make instancesprivate static native void initialize();private st…