先假设你是java异常家族中的一个自定义的非检查异常,你的父亲正是大名鼎鼎的RunTimeException
首先认识一下你的老祖Throwable
,这可是你们家族唯一的老祖
你的祖宗生了你的爷爷Exception和你的堂爷爷Error
你堂爷爷犯了严重错误,导英年早逝 (Error 表示严重错误,通常是指 JVM 本身出现的内部错误,例如 java.lang.StackOverflowError 或 java.lang.OutOfMemoryError。Error 类型的错误通常是无法修复的,程序必须终止执行。)
你爷爷Exception 子孙满堂 子孙主要分两个分支检查异常和非检查异常 (Exception 表示应用程序出现的错误,它又分为两种类型:受查异常(Checked Exception)和非受查异常(Unchecked Exception)。
在 Java 中,受查异常(Checked Exception)是指在方法中可能会抛出的异常,编译器会强制要求在方法签名中声明它们或在方法内部进行显式的处理。这样,调用该方法的代码就必须通过 try-catch 块捕获和处理这些异常,或者在方法签名中声明抛出它们。
与受查异常相反,非受查异常(Unchecked Exception),也称为运行时异常(RuntimeException),是指在方法中可能会发生但不需要在方法签名中声明或显式处理的异常。这些异常通常是由程序员的编程错误导致的,例如空指针引用(NullPointerException)、数组越界(ArrayIndexOutOfBoundsException)等。这些异常在编译时不会强制要求捕获或声明,可以在运行时由程序直接抛出。)
其实你爷爷Exception本身就属于检查异常,只是从你父亲(RunTimeException )这一脉开辟了非检查异常的先河 从你父亲(RunTimeException )这一脉开始往下都是隶属于非检查异常,当然也包括你,然后你父亲的兄弟姐妹都是继承了你爷爷的检查异常
有一天你开始疑惑为啥是你父亲(RunTimeException )开创非检查异常,你反复看你父亲履历(源码)也没发现什么特殊的地方,然后你开始往上翻你的家族史,终于在你的老祖履历中发现了
public synchronized Throwable fillInStackTrace() {if (stackTrace != null ||backtrace != null /* Out of protocol state */ ) {fillInStackTrace(0);stackTrace = UNASSIGNED_STACK;}return this;}
你发现如果你有一个自定义的堂爷爷重写这个方法
// 重写 fillInStackTrace 方法@Overridepublic synchronized Throwable fillInStackTrace() {return null;}
结果也会是一个非检查异常(不建议这种写法)
事实上,RuntimeException
之所以成为非检查异常是由 Java 语言规范定义的。Java 规范中将异常分为两类:检查性异常和非检查性异常。其中,检查性异常在编译期强制进行检查,必须在方法签名中声明或捕获;而非检查性异常不需要在编译期进行检查,可以在代码中选择性地捕获或声明。RuntimeException
及其子类被定义为非检查性异常,因此可以在代码中随意抛出而不进行强制检查。
简单来说,Java 规范将 RuntimeException
定义为非检查异常,这是一个约定俗成的规则。虽然源码中并没有特殊操作,但这个规则在编译器和 JVM 中得到了支持,使得我们可以在编写程序时针对 RuntimeException
以非检查异常的方式进行处理。