异常
异常是指由于各种不期而至的情况,导致程序中断运行的一种指令流,如:文件找不到、非法参数、网络超时等。为了保证正序正常运行,在设计程序时必须考虑到各种异常情况,并正确的对异常进行处理。
异常也是一种对象,java当中定义了许多异常类,并且定义了基类java.lang.Throwable作为所有异常的超类。Java语言设计者将之划分为两类:Error和Exception,其体系结构大致如下图所示:
Throwable:有两个重要的子类:Exception(异常)和Error(错误),两者都包含了大量的异常处理类。
1、Error(错误):是程序中无法处理的错误,表示运行应用程序中出现了严重的错误。此类错误一般表示代码运行时JVM出现问题。通常有Virtual MachineError(虚拟机运行错误)、NoClassDefoundError(类定义错误)等。比如说当jvm耗完可用内存时,将出现OutOfMemoryError。此类错误发生时,JVM将终止线程。
这些错误是不可查的,非代码性错误。因此,当此类错误发生时,应用不应该去处理此类错误。
2、Exception(异常):程序本身可以捕获并且可以处理的异常。
Exception这种异常又分为两类:运行时异常和编译异常。
1)运行时异常(不受检异常或不可查异常):RuntimeException类及其子类表示JVM在运行期间可能出现的错误。编译器不会进行检查并且不要求必须处理的异常,也就说当程序中出现此类异常时,即使我们没有try-catch捕获它,也没有使用throws抛出该异常,编译也会正常通过。
比如说试图使用空值对象的引用(NullPointerException)、数组下标越界(ArrayIndexOutBoundException)。此类异常属于不可查异常,一般是由程序逻辑错误引起的,在程序中可以选择捕获处理,也可以不处理。
2)编译异常(受检异常或可查异常):Exception中除RuntimeException及其子类之外的异常,正确的程序在运行过程中,经常容易出现的、符合预期的异常情况,编译器要求必须处理的异常。编译器会检查此类异常,也就是说当编译器检查到应用中的某处可能会此类异常时,将会提示你处理本异常——要么使用try-catch捕获,要么使用throws语句抛出,否则编译不通过。通常不会自定义该类异常,而是直接使用系统提供的异常类。
异常处理机制
1、通过try-catch语句块捕获异常
try{
语句
}catch(){
语句
}
(在报错的地方按alt+enter,可以动态生成),catch()括号内可以用Exception进行接收,因为Exception是所有可处理异常的最大父类,缺点是不精确。
将语句放入try语句块中,try语句块会查找是否有异常,如果有,catch()会进行捕获。catch后的括号内是捕获异常的对象。比如填入空指针异常(NullPointerException e),那么就会用NullPointerException e 来接收捕获到的异常。如下图
此时能够打印出异常信息,但程序没有继续进行,一般我们需要程序在遇到异常后能够继续运行且能够打印出异常信息。
此时可以用到e.printfStackTrace();异常信息打印。如下图
可以看到,异常信息被打印出,且程序没有中断,在程序后添加了一句输出“程序已执行到此”来进行验证。
运行时异常是继承自RunTimeException,没有继承RunTimeException的异常都是编译时异常。
throws关键字
后面会跟一个异常名,出现在方法中时,一是出现该异常时抛出,二是可以提醒开发人员注意可能出现该异常。
2、抛出异常:throws。
创建异常对象,并抛出。(抛给方法的调用处)
比如主函数中调用一个方法,该方法出现异常,将异常抛给主函数,而主函数也是抛出机制,则主函数将异常抛给jvm虚拟机,jvm虚拟机没有处理机制,就会终止程序,打印异常信息。
这种套娃式是不建议使用的,所以一般用try-catch
什么情况时用throws呢,想要方法调用处知晓被调用方法有这个异常。
什么情况时用try-catch呢,想要自己处理异常。
注意:try语句块中捕获到异常时,try语句块中捕获到异常的语句后面的语句不会执行
finally语句块
在一般情况下,finally语句块是无论如何都会执行的语句块,除非让jvm虚拟机停掉(即System.exit(0))。
使用时可以和try-catch配合使用:
try{
}catch(){
}finally{
}
这里提一下,IO流对象和数据库对象在使用后要关闭掉,避免造成过多资源损耗。此时就可以用finally语句块进行关闭。
如果将关闭的语句xxx.close()放在try语句块中,出现异常后,出现异常语句后面的语句是不会被执行的,如下图中调用myMethod02后的close()是不会被执行的,所以用finally语句块。使用finally语句块时要注意,被关闭对象要在一个公共的区域中。
即上图中划上红线的一句,如果没有这一句将对象放在一个公共区域,finally语句块是无法关闭该资源的。
自定义异常:
当Java本身定义的异常不够使用时,可以自定义异常。
怎么自定义?去继承一个异常类,然后写一个无参构造和一个有参构造。
异常信息会一层层的传递给最顶层的父类,把值赋给message,最顶层父类有getMessage()方法,那就可以通过getMessage()方法获得异常信息。