JAVA-异常-知识点总结
本篇博客内容如题目所示,在题主进一步学习与复习JAVA知识时,进行了知识点总结,相当于个人笔记,大多数是个人理解后的大白话,有不妥之处请各位大佬留言交流~
另外,学习参考资料为《Java核心技术卷I》和毕向东老师的Java视频学习。
##异常概述
简单地说,异常就是在程序运行期间产生的错误,个人理解为:程序没有按照自己的想法进行而产生的错误。Java的异常处理机制就是来解决错误的。Java的异常层次结构如下图所示:
根据层次结构我们可以看出,所有的异常或错误类均继承自Throwable父类,异常在Java中又被分为两种:
1、受查异常:编译时受JVM检查的异常。图中橘色部分是除了RuntimeException外的异常类及其子类,例如:IOException,SQLException,TimeoutException等。受查异常就是自身代码允许出现的一些问题,需要在自身的代码中对这些问题进行捕获并处理。
2、非受查异常:编译时不受JVM检查的异常。图中蓝色部分的Error和RuntimeException及其子类(常见有:NullPointerException,ArithmeticException,ArrayIndexOutOfBoundsException,ClassNotFoundException)。非受查异常要么就是应该尽量避免的、自身代码导致的一些逻辑错误,发生这些问题时,我们需要对已有代码本身进行修改,而不是在代码中对这些问题捕获并处理;要么是代码不可控制的Java运行时系统的内部错误和资源耗尽错误(Error)。
在Throwable类中java已经定义了许多常见的受查异常和非受查异常,具体可以参见文档:https://docs.oracle.com/javase/8/docs/api/index.html?java/lang/Throwable.html
异常的好处就是:将问题进行封装并且将正常流程代码和问题处理代码分离,有种现实生活中各司其职的感觉。
##异常处理过程
处理过程对于受查异常来说分两种:调用该异常抛出方法时,若能处理,则对异常进行捕获并处理;若不能处理,则调用方法需要声明并继续向上抛出该异常,直至能捕获并处理该异常为止。
注意:
1)如果异常一直被抛出,没有被处理,最终会被main方法抛出,JVM进行捕获并处理,JVM的处理方式就是调用该异常的printStackTrace()方法。
2)对于受查异常,编译器编译时会进行检查,一个方法必须对其声明抛出,或者在内部对其捕获并处理,否则编译失败。
3)一般情况下,对于受查异常,我们需要用代码捕获并处理。
处理过程对于非受查异常来说:函数内抛出时,函数可以不声明;函数上声明时,调用者可以不处理。
注意:
1)对于非受查异常,编译器编译时不会进行检查,一个方法可以对其声明抛出也可以不声明抛出。
2)由于非受查异常要么不可控制(Error)要么就应该避免发生(RuntimeException),所以我们可以处理也可以不处理,但一般情况下,非受查异常发生后,我们希望它能使程序停止(JVM进行处理),而不用代码对它进行捕获并处理。
在本小节中,我们反复用了异常中的专用词:抛出和捕获,在下两节中我们对其进行解释。
##抛出异常-throw和throws关键字
首先要说的是异常体系的可抛性质:Throwable中的所有类可以被和throws关键字操作,这些类建立的对象都可以被throw关键字操作。而其他类并不具备可抛性质。
这两个关键字都用于抛出异常,不同的是:
1、throw用于在函数内新建并抛出异常对象。
格式为:throw 异常类名称 = new 异常类名称();
如下例:
法1:
{
……
throw new IOException();
……
}
法2:
{
……
IOException e = new IOException();
throw e;
……
}
2、throws用于在函数头上声明抛出异常类,可以声明并抛出多个异常类,用逗号隔开。
格式为:访问修饰符 返回值类型 函数名称(参数类型 参数名称) throws A异常,B异常
如下例:
public static void printNum(int num) throws IOException,SQLException
{
……
}
注意:
1)若在子类中覆盖了超类的一个方法,子类方法中throws声明的受查异常必须为超类方法中声名的受查异常类,或者其子类异常;若超类方法没有抛出任何受查异常,那么子类也不能抛出任何受查异常;若子类确实有超类中没有抛出的异常,那么必须在子类中自行捕获并解决。
2)throw语句和return语句一样,会使函数停止,所以其后面不应该有代码,执行不到。
##捕获异常-try、catch、finally关键字
如果某个异常发生的时候没有在任何地方捕获,那程序就会终止执行,并在控制台上打印异常信息(即:JVM调用了该异常的printStackTrace()方法进行了异常处理)。
对于受控异常,我们常需要其进行捕获并进行处理。而对于非受控异常,则不必需。Java中的try、catch、finally关键字就用于异常捕获与处理。
try代码块中为可能会产生异常的代码。
catch代码块中为针对try中某个异常产生后需要针对该异常进行处理的代码。
finally代码块中为不管try中是否有异常产生,不管catch中是否进行了异常捕获,都会执行的代码。其中通常是关闭资源代码,因为资源必须释放。
共有以下几种异常捕获处理方式:
法1:
try
{
需要被检测的代码
}
catch (某异常类 异常名称)
{
处理异常的代码
}
法2:
try
{
需要被检测的代码
}
finally
{
一定会执行的代码
}
法3:
try
{
需要被检测的代码
}
catch (某异常类 异常名称)
{
处理异常的代码
}
finally ()
{
一定会执行的代码
}
注意:
1)由于try中代码可能产生多个异常,所以可以在异常捕获时写多个catch语句来捕获处理多个不同异常。
2)在catch语句中也可以抛出异常,这样做的目的是改变异常类型,通常用于实际编程过程。在本层中,只捕获该层能处理的异常,把不能处理的异常继续抛出,或者转换异常类型后抛出。
3)多个catch语句时,将父类的异常捕获catch语句放在最下面。
4)finally只有一种情况是不被执行的:当执行到 System.exit(0); 语句时finally不会执行。该语句使系统退出,JVM结束。
##自定义异常
当Java提供的异常机制中没有我们需要的问题描述时,我们也可以封装并自定义异常。自定义异常类只需要继承于Exception即可(不考虑Error)。
如下例:
class MyException extends Exception
{
MyException(String msg);
{
super(msg);
}
}
我们创建异常对象时,将异常的描述信息传给Exception父类的构造函数即可。我们还可以使用父类已经定义好的方法,或者覆盖父类方法。
##异常机制使用原则
1)catch语句中尽量放一些实质性解决问题的代码,而不要总简单的调用printStackTrace()方法或者输出语句,而不要什么都不写(尽管可以)。
2)使用有层次性的异常结构。在不同的类中定义该类能处理的异常,并把不能完全处理的异常转化为其他异常(本类处理过,只是不能完全处理)抛出,并把完全不能处理的继续抛出。
3)早抛出,晚捕获。