首先,什么是异常?
我们先来看jdk1.8的API对异常的定义
异常:程序执行中的非正常事件,程序无法再按预想的流程执行,就是Java程序在运行过程中出现的错误。
异常Exception这个东西常常和error混淆,但是他们有本质的区别:
error表示应用程序运行时出现的重大错误,例如用户输入错误 ,设备错误 , 物理限制 等
例如JVM运行时出现的OutOfMemoryError以及Socket编程时出现的端口占用等程序无法处理的错误,会导致JVM直接崩溃退出。
而异常Exception是运行中产生的可控的错误,如果能够正确的抛出异常以及捕获异常,JVM完全可以继续运行,只有在找不到异常处理程序,整个系统才会完全退出
JVM是如何处理异常的
main函数收到这个问题时,有两种处理方式:
- a:自己将该问题处理,然后继续运行
- b:自己没有针对的处理方式,只有交给调用main的jvm来处理
– jvm有一个默认的异常处理机制,就将该异常进行处理.
– 并将该异常的名称,异常的信息.异常出现的位置打印在了控制台上,同时将程序停止运行
后者简而言之就是将错误信息传递给上层调用者,并报告“案发现场”的信息
异常到底是个什么东西?
在Java编程语言中,异常对象始终是从Throwable派生的类的实例
而异常层次结构也分为两个分支:
- 运行时异常,由程序员在代码里处理不当造成
- 其他异常:由外部原因造成 例如 I/O 错误 服务器宕机,数据库崩溃等
通过API查看Throwable 类 我们都可以看到
Throwable类是java语言中的所有错误和异常的父类。
(只有在Throwable的实例对象(或其子类)才能由java虚拟机或者可以通过java throw声明抛出。同样的,这类只或子类才可以作为 catch的参数类型。)
这么多子类中只有 RuntimeException一个类是运行时异常,这类异常在代码编写的时候不会被编译器所检测出来,是可以不需要被捕获,其他的异常在编译时编译器会提示需要捕获,如果不进行捕获则提示编译错误。
我们看一下 RuntimeException的定义:
RuntimeException是异常,可以把java虚拟机正常运行时的Exception。
RuntimeException及其子类是未检查(unchecked)的异常。未检查的异常不需要在方法或构造函数的throws条款宣称如果他们可以采用的方法或构造函数的执行和宣传外抛的方法或构造函数的边界。
所以我们可以理解为:运行时异常RuntimeException,是程序源代码中引入的故障所造成的 ,如果在代码中提前进行验证,这些故障就可以避免,而其他的非运行时异常,是程序员无法完全控制的外在问题所导致的
而在RuntimeException中又派生出了很多的子类,许多都是我们常见的异常
Checked与unchecked异常
这是从异常处理机制的角度所做的分类:异常会被谁check?——编译器(checked)、程序员(unchecked)
下面这张图可以帮助我们很好的理解Java中的异常分类
总的说一下,体现在编程上:
在编程的时候,Unchecked exceptions不需要在编译的时候用try…catch等机制处理,但是执行时出现了就导致程序崩溃,是程序中的潜在bug,程序员可以通过对代码的修改,提高健壮性来完全避免Unchecked exception
但是Unchecked异常也可以使用throws声明或try/catch进行捕获,通产是对于RuntimeException,但大多数时候是不需要的,也不应该这么做——掩耳盗铃,对发现的编程错误充耳不闻,不如直接修改代码让其不可能抛出异常
例如
- NullPointerException
- ArrayIndexOutOfBoundsException
而Checked exceptions必须捕获并指定错误处理器handler,否则编译无法通过,类似于编程语言中的静态类型检查
checked异常必须使用throws声明或try/catch进行捕获,否则无法通过编译
例如
- IllegalArgumentException
- IllegalStateException
- NoClassDefFoundError
如果用客户端的思维进行分析:
-
如果客户端可以通过其他的方法恢复异常,那么采用checked exception,例如读文件的时候发现文件不存在了,可以让用户选择其他文件
-
如果客户端对出现的这种异常无能为力,例如系统损毁,那么采用unchecked exception;
-
而对于一些编程错误,可以使用unchecked exception来处理,例如调用某方法时传入了错误的参数,则无论如何都无法在不中止执行的前提下进行恢复。
-
若为了代码的简洁性更高,开发者应更倾向于使用 unchecked 异常(不必须try-catch处理,只需要祈祷不会出现)
-
若为了程序的健壮性更高,则 应倾向于使用 checked 异常 (必须try-catch处理)
如何处理异常?
常见的处理异常的方式:
- try, catch, finally 捕获并处理XX异常
- throw 抛出XX异常
- throws 声明“本方法可能会发生XX异常”
具体怎么处理异常方法有很多本文不再赘述
异常与继承之间的关系:子类型中重写的方法不能抛出额外的异常
- 如果子类型中override了父类型中 的函数,那么子类型中方法抛出的异常不能比父类型抛出的异常类型 更宽泛
- 子类型方法可以抛出更具体的异 常,也可以不抛出任何异常
- 如果父类型的方法未抛出 异常,那么子类型的方法也不能抛出异常。
public class Test {
public boolean readFromFile() throws IOException {
...
}
}
class SubType extends Test {
@Override public boolean readFromFile() throws FileNotFoundException {
...
}
}
因为FileNotFoundException是IOException的子类型,所以子类重新方法中可以将异常类型改为FileNotFoundException
如何自定义一个异常?
首先还是那句话,看一下Exception的API文档
我们有三种选择:
- 继承Throwable
- 继承Exception
- 继承RuntimeException
通常情况下都会选择继承Exception和RuntimeException,如果不要求调用者一定要处理抛出的异常,可以继承RuntimeException,而对RuntimeException通常是是逻辑上的错误,比如数组越界,空指针异常。
如果是需要调用者处理的异常,通常情况之下都选择继承 Exception
创建异常类后,通常需要一个构造方法
首先看一下父类Exception中的几种构造方法
public Exception() {
}
构造一个新的异常,其详细信息(message)为null。 产生异常的原因尚未初始化,并且随后可以通过调用Throwable.initCause(java.lang.Throwable)进行初始化。
public Exception(String message) {
super(message);
}
使用指定的message构造一个新的异常。 原因尚未初始化,并且随后可以通过调用Throwable.initCause(java.lang.Throwable)进行初始化。
参数:
message-详细消息。 将保存详细消息,以供以后通过调用父类中Throwable.getMessage()方法检索。
public Exception(String message, Throwable cause) {
super(message, cause);
}
使用指定的message和cause构造一个新异常。
请注意,与原因关联的详细消息不会自动合并到此异常的详细消息中。
参数:
message-详细消息(保存此消息以供Throwable.getMessage()方法以后检索)。
cause-产生异常的原因(保存该原因,以便以后通过Throwable.getCause()方法进行检索)。 (允许使用空值,并指示原因不存在或未知。)
protected Exception(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
使用指定的message,cause,启用或禁用抑制以及可写的堆栈跟踪启用或禁用,构造一个新异常。
参数:
message-详细消息。
cause-原因。 (允许使用null,表示cause不存在或未知。)
enableSuppression-是否启用或禁用抑制
writableStackTrace-堆栈跟踪是否应该写入
那么我们在编写异常类的时候同样可以参考这些构造方法进行产生异常后的操作
package Exception;
public class MyException extends Exception{
private String message;
public MyException() {
}
public MyException(String message) {
super(message);
}
public MyException(String message, Throwable cause) {
super(message, cause);
}
public MyException(Throwable cause) {
super(cause);
}
protected MyException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
在编写完异常类后,我们可以编写测试代码对其进行测试,在一些方法总检测条件是否满足并用throw抛出异常,用其调用者检测并处理异常或是不处理交给JVM并输出在控制台。