异常指不期而至的各种状况,如:文件找不到、网络连接失败、非法参数等。异常是一个事件,它发生在程序运行期间,干扰了正常的指令流程。Java通 过API中Throwable类的众多子类描述各种不同的异常。因而,Java异常都是对象,是Throwable子类的实例,描述了出现在一段编码中的 错误条件。当条件生成时,错误将引发异常。
1 Error
Error(错误)表示系统级的错误和程序不必处理的异常,是Java运行环境中的内部错误或者硬件问题。比如:内存资源不足等。对于这种错误,程序基本无能为力,除了退出运行外别无选择,它是由Java虚拟机抛出的。
2 Exception
Exception 表示需要捕捉或者需要程序进行处理的异常,它处理的是因为程序设计的瑕疵而引起的问题或者在外的输入等引起的一般性问题,是程序必须处理的。
注意:异常与错误的区别,异常能被程序本身处理,错误无法处理。
3 Java 异常分类
分为非检异常(unchecked),检查异常(checked)。
(1) 非检异常(RuntimeException),表示无法让程序恢复的异常,导致的原因通常是因为执行了错误的操作,建议终止程序,因此,编译器不检查这些异常。
Error和RuntimeException及其子类为非检查异常(unchecked)。
(2) 检查异常(Checked Exception),是表示程序可以处理的异常,也即表示程序可以修复(由程序自己接受异常并且做出处理), 所以称之检查异常。除了Error和RuntimeException及其子类,其他的都称为检查异常。
4 Exception 分类
Exception 异常分两大类运行时异常和非运行时异常(编译异常)。程序中应当尽可能去处理这些异常。
(1) 运行时异常:都是RuntimeException类及其子类异常,如NullPointerException(空指针异常)、IndexOutOfBoundsException(下标越界异常)等,这些异常是非检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。
(2) 非运行时异常 (编译异常):是RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常。
5 Exception特点
(1) Error类体系描述了Java运行系统中的内部错误以及资源耗尽的情形。应用程序不应该抛出这种类型的对象(一般是由虚拟机抛出)。如果出现这种错误,除了尽力使程序安全退出外,在其他方面是无能为力的。所以,在进行程序设计时,应该更关注Exception体系。
(2) RuntimeException 与 非RuntimeException
① RuntimeException:RuntimeException体系包括错误的类型转换、数组越界访问和试图访问空指针等等。处理RuntimeException的原则是:如果出现RuntimeException,那么一定是程序员的错误。例如,可以通过检查数组下标和数组边界来避免数组越界访问异常。
② 其他非RuntimeException(IOException等等):这类异常一般是外部错误,例如试图从文件尾后读取数据等,这并不是程序本身的错误,而是在应用环境中出现的外部错误。
6 异常处理机制
在 Java 应用程序中,异常处理机制为:抛出异常,捕捉异常。
(1) 抛出异常:当一个方法出现错误引发异常时,方法创建异常对象并交付运行时系统,异常对象中包含了异常类型和异常出现时的程序状态等异常信息。运行时系统负责寻找处置异常的代码并执行。
(2) 捕获异常:在方法抛出异常之后,运行时系统将转为寻找合适的异常处理器(exception handler)。潜在的异常处理器是异常发生时依次存留在调用栈中的方法的集合。当异常处理器所能处理的异常类型与方法抛出的异常类型相符时,即为合适 的异常处理器。
7 常见异常
(1) RuntimeException
1)java.lang.ArrayIndexOutOfBoundsException
数组索引越界异常。当对数组的索引值为负数或大于等于数组大小时抛出
2)java.lang.ArithmeticException
算术条件异常。譬如:整数除零等
3)java.lang.NullPointerException
空指针异常。当应用试图在要求使用对象的地方使用了null时,抛出该异常。譬如:调用null对象的实例方法、访问null对象的属性、计算null对象的长度、使用throw语句抛出null等等
4)ClassCastException 类型转换异常类
5)java.lang.IllegalArgumentException 非法参数异常
6) NumberFormatException 数字格式异常
7)java.lang.NegativeArraySizeException 数组长度为负异常
8)java.lang.ArrayStoreException 数组中包含不兼容的值抛出的异常
9)java.lang.SecurityException 安全性异常
(2) IOException
IOException:操作输入流和输出流时可能出现的异常
EOFException:文件已结束异常;
FileNotFoundException:文件未找到异常;
(3) 其他异常
ArrayStoreException 数组中包含不兼容的值抛出的异常
SQLException 操作数据库异常类
NoSuchFieldException 字段未找到异常
NoSuchMethodException 方法未找到抛出的异常
NumberFormatException 字符串转换为数字抛出的异常
StringIndexOutOfBoundsException 字符串索引超出范围抛出的异常
IllegalAccessException 不允许访问某类异常
InstantiationException 当应用程序试图使用Class类中的newInstance()方法创建一个类的实例,而指定的类对象无法被实例化时,抛出该异常
ClassNotFoundException 找不到类异常。当应用试图根据字符串形式的类名构造类,而在遍历CLASSPAH之后找不到对应名称的class文件时,抛出该异常
8 非RuntimeException与RuntimeException使用场景
继承Exception还是继承RuntimeException是由异常本身的特点决定的,而不是由是否是自定义的异常决定的。例如我要写一个java api,这个api中会调用一个极其操蛋的远端服务,这个远端服务经常超时和不可用。所以我决定以抛出自定义异常的形式向所有调用这个api的开发人员周知这一操蛋的现实,让他们在调用这个api时务必考虑到远端服务不可用时应该执行的补偿逻辑(比如尝试调用另一个api)。此时自定义的异常类就应继承Exception,这样其他开发人员在调用这个api时就会收到编译器大大的红色报错:【你没处理这个异常!】,强迫他们处理。又如,我要写另一个api,这个api会访问一个非常非常稳定的远端服务,除非有人把远端服务的机房炸了,否则这个服务不会出现不可用的情况。而且即便万一这种情况发生了,api的调用者除了记录和提示错误之外也没有别的事情好做。但出于某种不可描述的蛋疼原因,我还是决定要定义一个异常对象描述“机房被炸”这一情况,那么此时定义的异常类就应继承RuntimeException,因为我的api的调用者们没必要了解这一细微的细节,把这一异常交给统一的异常处理层去处理就好了。