异常
异常指不期而至的各种状况,异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的。
比如说,你的代码少了一个分号,那么运行出来结果是提示是错误 java.lang.Error ;如果你用 System.out.println(11/0) ,那么你是因为你用0做了除数,会抛出 java.lang.ArithmeticException 的异常。
要理解Java异常处理是如何工作的,你需要掌握以下三种类型的异常:
检查性异常: 最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。运行时异常: 运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。错误: 错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译也检查不到的。异常都是从 Throwable 类派生出来的,而 Throwable 类是直接从 Object 类继承而来。
异常发生的原因
用户输入了非法数据。要打开的文件不存在网络通信时连接中断,或者JVM内存溢出异常分类
Error:系统内部错误,这类错误由系统进行处理,程序本身无需捕获处理。Exception:可以处理的异常。RuntimeException:可以捕获,也可以不捕获的异常。继承 Exception 的其他类:必须捕获,通常在 API 文档中会说明这些方法抛出哪些异常。所有的异常类是从 java.lang.Exception 类继承的子类。而 Exception 异常下又主要分为两大类异常,一个是派生于 RuntimeExcption 的异常,一个是除了RuntimeExcption 体系之外的其他异常。
RuntimeExcption 异常(运行时异常)通常有以下几种:
错误的类型转换数组访问越界访问 null 指针算术异常
一般来说,RuntimeException 都是代码逻辑出现问题。非 RuntimeException(受检异常,Checked Exception)一般有:
打开一个不存在的文件没有找到具有指定名称的类操作文件异常
受检异常是编译器要求必须处理的异常,必须使用 try catch 处理,或者使用 throw 抛出,交给上层调用者处理。
异常的定义及抛出
throw抛出异常
当程序运行时数据出现错误或者我们不希望发生的情况出现的话,可以通过抛出异常来处理。
异常抛出语法:
throws声明异常
throws 用于声明异常,表示该方法可能会抛出的异常。如果声明的异常中包括 checked 异常(受检异常),那么调用者必须捕获处理该异常或者使用 throws 继续向上抛出。throws 位于方法体前,多个异常之间使用 , 分割。
throw 和 throws的区别
throw 用于抛出异常对象,后面跟的是异常对象;throw 用在函数内。throws 用于抛出异常类,后面跟的异常类名,可以跟多个,用逗号隔开。throws 用在函数上捕获异常
通常抛出异常后,还需要将异常捕获。使用 try 和 catch 语句块来捕获异常,有时候还会用到 finally。对于上述三个关键词所构成的语句块,try 语句块是必不可少的,catch 和 finally 语句块可以根据情况选择其一或者全选。你可以把可能发生错误或出现问题的语句放到 try 语句块中,将异常发生后要执行的语句放到 catch 语句块中,而 finally 语句块里面放置的语句,不管异常是否发生,它们都会被执行。
捕获异常对于系统而言,其开销非常大,所以应尽量减少该语句块中放置的语句。
注意下面事项
catch 不能独立于 try 存在。在 try/catch 后面添加 finally 块并非强制性要求的。try 代码后不能既没 catch 块也没 finally 块。try, catch, finally 块之间不能添加任何代码。finally 很有用,主要用户关闭资源。无论是否发生异常,资源都必须进行关闭捕获多个异常
在一段代码中,可能会由于各种原因抛出多种不同的异常,而对于不同的异常,我们希望用不同的方式来处理它们,而不是笼统的使用同一个方式处理,在这种情况下,可以使用异常匹配,当匹配到对应的异常后,后面的异常将不再进行匹配。
在处理异常时,并不要求抛出的异常同 catch 所声明的异常完全匹配,子类的对象也可以匹配父类的处理程序。比如异常 A 继承于异常 B,那么在处理多个异常时,一定要将异常 A 放在异常 B 之前捕获,如果将异常 B 放在异常 A 之前,那么将永远匹配到异常 B,异常 A 将永远不可能执行,并且编译器将会报错。
父类的异常捕获语句不可以写在子类异常捕获语句前面
自定义异常
自定义一个异常类非常简单,只需要让它继承 Exception 或其子类就行。在自定义异常类的时候,建议同时提供无参构造方法和带字符串参数的构造方法,后者可以为你在调试时提供更加详细的信息。在 Java 中你可以自定义异常。编写自己的异常类时需要记住下面的几点。
所有异常都必须是 Throwable 的子类。如果希望写一个检查性异常类,则需要继承 Exception 类。如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。
异常堆栈
当异常抛出后,我们可以通过异常堆栈追踪程序的运行轨迹,以便我们更好的 DEBUG 。
通过上面的异常堆栈轨迹,在对比我们方法的调用过程,可以得出异常信息中首先打印的是距离抛出异常最近的语句,接着是调用该方法的方法,一直到最开始被调用的方法。 从下往上看,就可以得出程序运行的轨迹。
什么时候定义try,什么时候定义throws呢?
功能内部如果出现异常,如果内部可以处理,就用 try如果功能内部处理不了,就必须声明出来,让调用者处理总结
异常的捕获、抛出和异常处理是维持代码健壮性的重要条件。灵活使用异常及处理,不仅能最大限度的避免出错,也能增加软件的容错机制。