一.异常的概述
指的是程序在执行过程中,出现的非正常情况,如果不处理最终会导致JVM的非正常停止。
1.异常的抛出机制
Java吧不同的异常用不同的类来表示,一旦发生某种异常,就创建该异常类型的对象,并且抛出throw。然后程序员可以捕获catch到这个异常对象,并处理;如果没有捕获catch这个异常对象,那么这个异常对象将会导致程序终止
2.如何对待异常
对于程序出现的异常,一般有两种解决方法:一是遇到错误就终止程序的运行。另一种方法是程序员在编写程序时,就充分考虑到各种可能发生的异常和错误,极力预防和避免。实在无法避免的,要编写相应的代码进行异常的检测,以及异常的处理,保证代码的健壮性
二.异常的体系结构及常见的异常
java.lang.Throwable:异常体系的根父类
|---java.lang.Error:错误。Java虚拟机无法解决的严重问题。如:JVM系统内部错误,资源耗尽等严重情况。
一般不编写针对性的代码进行处理
|---java.lang.Exception:异常。我们可以编写针对性的代码进行处理。
|---编译时异常:(受检异常)在执行javac.exe命令时,出现的异常。
|--- ClassNotFoundException
|--- FileNotFoundException
|--- IOException
|---运行时异常:(非受检异常)在执行java.exe命令时,出现的异常。
|--- ArrayIndexoutOfBoundsException
|--- NullPointerException
|--- InputMismatchException
|--- ArithmeticException
三.异常处理的方式
过程1:“抛”
>"自动抛":程序在执行的过程中,一旦出现异常,就会在出现异常的代码处,自动生成对应异常类的对象,并将此对象抛出
>"手动抛":程序在执行过程中,不满足指定条件的情况下,我们主动的使用“throw + 异常类的对象”方式抛出异常对象
过程2:“抓”
狭义上讲:try - catch的方式捕获异常,并处理。
广义上讲:把“抓“理解为”处理”。则此时对应着异常处理的两种方式:①try - catch - finally②throws
3.1 try - catch - finally
1.基本结构:
try
{
…………//可能产生异常的代码
}
catch(异常类型1 e)
{
…………//当产生异常类型1型异常时的处置措施
}
catch(异常处理2 e)
{
…………//当产生异常类型2型异常时的处置措施
}
finally
{
…………//无论是否发生异常,都无条件执行的语句
}
2.使用细节:
>将可能出现异常的代码声明在try语句中。一旦代码出现异常,就会自动生成一个对应异常类的对象。并将此对象抛出。
>针对于try抛出的异常类对象,使用之后的catch语句进行匹配。一旦匹配上,就进入catch语句块进行处理。
>如果生命了对各catch结构,不同的异常类型在不存在子父类关系的情况下,谁声明在上面,谁声明在下面都可以。如果多个异常类型满足子父类的关系,则必须将子类声明在父类结构上面。否则报错。
>catch中异常处理的方式:
①自己编写的输出语句
②printStackTrace();打印异常的详细信息。(推荐)
③getMessage():获取发生异常的原因。
>try中声明的变量,出了try结构以后,就不可以进行调用了。
>try - catch结构是可以嵌套使用的。
3.finally的使用说明:
①finally的理解
>我们将一定要执行的代码声明在finally结构中
>更深刻的理解:无论try中或catch中是否存在仍未被处理的异常,无论try中或catch中是否存在return语句等,finally
中声明的变量一定要被执行。
>finally语句和catch语句是可选的,但是finally不能单独使用
②什么样的代码我们一定要声明在finally中呢?(我觉得是一定要执行的代码,不管什么情况)
>我们在开发中,有一些资源(比如:输入流,输出流,数据库连接,Socket连接等资源),在使用完以后,必须显式的进行关闭操作,否则GC不会自动的回收这些资源,进而导致内存的泄漏
为了保证这些资源在使用完以后,不管是出现了违背处理的异常的情况下,这些资源能被关闭。我们必须将这些操作声明在finally中
3.2 throws
1.格式:在方法的声明处,使用“throws 异常类型1,异常类型2,....”
2.举例:
public void test()throws异常类型
{
//可能存在编译时异常
}
3.是否真正处理了异常?
>从编译上是否通过的角度看,看成是给出了异常,万一要是出现时候的解决方案。此方案就是往上抛出(throws)
>但是,此throws的方式,仅是将可能出现的异常抛给了此方法的调用者。此调用者依然需要考虑如何处理相关异常。从这个角度看,throws的方式不算真正意义处理了异常
4.方法的重写的要求:(针对于编译时异常来说的)
子类重写的方法抛出的 异常类型可以与父类被重写的方法抛出的异常类型相同,或是父类被重写的方法抛出的异常类型的子类
5.开发中的经验之谈
开发中,如何选择异常处理的两种方式?(重要、经验之谈)
- 如果程序代码中,涉及到资源的调用(流、数据库连接、网络连接等),则必须考虑使用try-catch-finally来处理,
保证不出现内存泄漏。
- 如果父类被重写的方法没有throws异常类型,则子类重写的方法中如果出现异常,只能考虑使用try-catch-finally
进行处理,不能throws。
- 开发中,方法a中依次调用了方法b,c,d等方法,方法b,c,d之间是递进关系。此时,如果方法b,c,d中有异常,
我们通常选择使用throws,而方法a中通常选择使用try-catch-finally。
四.手动throw异常现象
在方法的内部,满足指定条件的情况下使用“throw 异常类对象”的方式抛出
五.如何自定义异常类
1.继承于现有的异常体系。通常继承于RuntimeException / Exception
2.通常提供几个重载的构造器
3.提供一个全局常量,声明为 static final long serialVersionUID();后面要加一个值,相当于是每个异常都有的一个标签,与以前的异常不一样就行
为什么需要自定义异常类?
我们其实更关心的是,通过异常的名称就能直接判断此异常出现的原因。既然如此,我们就有必要在实际开发场景中,
不满足我们指定的条件时,指明我们自己特有的异常类。通过此异常类的名称,就能判断出具体出现的问题。