提示:以下是本篇文章正文内容,下面案例可供参考
一、异常是什么?
我们在使用计算机语言进行项目开发的过程中,即使程序员把代码写得尽善尽美,然而在系统的运行过程中仍然会遇到一些问题,因为很多问题并不是靠代码能够避免的,比如: 客户输入数据的格式问题,读取文件是否存在,网络是否始终保持通畅,jvm是否内存溢出等等。
异常: 指的是程序在执行过程中,出现的非正常的情况,如果不处理最终会导致JVM的非正常停止。
异常指的并不是语法错误,语法错了,编译不通过,不会产生字节码文件,根本不能运行.
异常也不是指因为逻辑代码错误而没有得到想要的结果,例如: 求a与b的和,你写成了a-b
演示如下
二、异常类的层次
我们在平时进行Java程序的开发过程中,遇到的绝大数异常信息都会继承Exception类。
如下所示
public void test05(){ System.out.println(2/0); }
而在Java中,Exception类更是向上继承异常类型的根类:Java.lang.Trowable。
其Exception类的具体继承关系如下所示
注意:
1.Throwable 类是 Java 语言中所有错误或异常的超类。
只有当对象是此类(或其子类之一)的实例时,才能通过 Java 虚机或 Java throw 语句抛出。
类似地,只有此类或其子类之一才可以是 catch 子句中的参数类型。2.Throwable有两个大子类:
Error: 错误,用于指示合理的应用程序不应该试图捕获的严重问题
例如: VirtualMachineError (虚拟机错误) ,OutofMemoryError(堆内存溢出),StackOverfLowError (栈内存溢出)这种错误,要么进行硬件升级,要么进行JVM调优,要么进行系统优化。Exception: 异常,指出了合理的应用程序想要捕获的条件。
3.Exception:又可以分为两大类:
运行时异常: RuntimeException及其子类,非受检异常编译时异常:除了运行时异常,剩下的都是编译时异常。
如何区分是编译时异常类型还是运行时异常类型 ?
(1) 看类的继承关系图
IDEA中快捷键:选中类 Ctrl+Alt+Shift+U(新开窗口显示),Ctrl+Alt+U(弹出窗口显示)
(2) 看编译器的态度
如果编译器在还未运行之前报红了,说明时存在编译时异常,反之则是运行时异常
三、如何对待异常?
1.尽量核实代码,保证数组下标、空指针、类型转换不出现异常,加判断处理。避免异常。
2.加异常处理代码。例如: try...catch处理
四、异常的抛出机制
Java中是如何表示不同的异常情况,又是如何让程序员得知,并处理异常的呢?
java中把不同的异常用不同的类表示,一旦发生某种异常,就通过创建该异常类型的对象,并且抛出,然后程序员可以catch到这个异常对象,并处理,如果无法catch到这个异常对象,那么这个异常对象将会导致程序终止。
五、怎样处理异常?
5.1 try...catch...
第一种处理方式,也是最彻底的方式,最根本的方式: try...catch
5.1.1 语法格式
try{
可能发生异常的代码
}catch(异常的类型 异常对象名称){//异常对象名称都是用e表示处理这个异常的代码。
/*处理方式: (1) 输出打印异常的信息(2) 进行相应的逻辑处理 */
catch(异常的类型 异常对象名称){
}
5.1.2 注意
(1) 多个catch,异常的类型如果是有父子类关系的话,一定是子类在上,父类在下
(2) catch() 中一定是异常的类型,不能是其他的类型只有是Throwable及其子类类型才能作为catch()的参数类型
(3) 名个catch如果处理方式都一样,那么在JDK1.7之后,允许这么写
演示案例如下
try.....catch....抛出异常的详细图解:try中会在要出问题的代码语句中停下,产生一个异常对象,和catch括号里声明的异常一一对比,若存在多个catch分支,先找小的比【小的异常类型】,如果不是,就找下面的,如果都不满足,异常会往上抛。若在main中,程序会直接挂掉。
5.1.3 try...catch 执行特点
(1)如果try的分中没有发生异常,那么一个catch都不会去匹配,相当于catch没有用上,直接执行try..catch结构下面的代码即可
(2)如果try的中发生了异常,try}中剩下的代码就不执行了,在发生异常的位置会产生一个异常对象,并抛出
A: catch分支会从上往下依次去检查,依次去尝试捕获这个异常对象,如果可以捕获到,那么就继续执行try..catch结构下面的代码
B:所有catch分支都无法匹配,如果不是main方法,就会把异常接着往上抛,如果是main,程序就挂了。
附注:如何获取异常对象的信息?
异常的信息包含:
(1)异常的类型
(2)异常的原因message
(3) 异常的堆栈跟踪信息如下样例所示
在catch分支中捕获了一个异常对象之后,可以获取异常的信息。
方法如下:
(1) e.printstackTrace();//打印异常的详细信息,包括上面的异常信息中的(1) (2) (3)部分
(2) e.getMessage()方法。配合System.err.println()方法
(3) e.getClass()方法,配合System.err.println()方法
(4)e.getStackTrace()方法,配合System.err.println()方法
tips:我们平时开发一般首选第(1)个方法,次选第(2)个方法。
5.1.4 finally
注意看,在Java中有以下三个很相似的单词
final, finalize, finally
第一个单词是成员的修饰词,意为最终的,不可修改的、
第二个单词是一种方法的名字,回收垃圾内存的方法
第三个单词是抛出异常的关键字
1)finally块表示
(1) 无论try}中代码是否发生异常,都要执行的代码
(2) 无论catch()分支是否可以捕获异常,都要执行的代码
(3) 无论try和catch分支中是否有return语句,都要执行的代码(4)唯一一种finally不会执行情况是:
在try后catch{}中有一句 System.exit(0); 退出VM。
通常都是资源释放或关闭代码写到finally中。
2)finally 与 return
finally中写了return语句,那么trv和catch中的return语句就失效了,最终返回的是finally块中的
①从try中回来
②在catch中回来
③从finally中回来
5.1.5 部分代码分析题勘误分析
1.
分析过程如下所示
2.
import java.io.IOException; public class Test03 { public static void main(String[] args) { int a = -1; try{ if(a>0){ throw new RuntimeException("");//这里的空字符串是打印在异常的详细信息里异常的原因处,调用了RuntimeException对象 // 的有参构造。 // 而异常详细信息则是要catch里调用 // printStackTrace()方法 //打印出来,不调用就不会打印异常的详细信息 }else if(a<0){ throw new IOException(""); }else{ return ; } }catch(IOException ioe){ System.out.println("IOException"); }catch(Throwable e){ System.out.println("Throwable"); }finally{ System.out.println("finally"); } } } /* 分析结果如下 IOException finally 分析结果第一处错误 分析在代码第10行处 */
正确运行结果如下
5.2 throws(中文释义:投,抛,扔)
功能释义:转换异常处理位置
5.2.1 throws编译时异常
如果在编写方法体的代码时,某句代码可能发生某个编译时异常,不处理编译不通过,但是在当前方法体中可能不适合处理或无法给出合理的处理方式,就可以通过throws在方法签名中声明该方法可能会发生xx异常,需要调用者处理。
声明异常格式:
修饰符 返回值类型 方法名(参数) throws 异常类名1,异常类名2 { }
tips:在throws后面可以写多个异常类型,用逗号隔开。【是英文的逗号,不是中文的】
5.2.2 throws运行时异常
当然,throws后面也可以写运行时异常类型,只是运行时异常类型,写或不写对于编译器和程序执行来说都没有任何区别。如果写了,唯一的区别就是调用者调用该方法后,使用try...catch结构时,IDEA可以获得更多的信息需要添加什么catch分支。
如果方法签名【方法名】中有throws,对于方法重写有什么影响?
(1)如果父类或父接口被重写方法,没有throws“编译时异常”,重写方法时,就不能throws编译时异常,但是可以throws运行时异常
(2) 如果父类或父接口被重写方法,有throws“编译时异常”,
A: 重写方法时,可以不throws编译时异常,
B:重写方法时,可以throws编译时异常,但是要求 <= 被重写方法的异常类型C: 对于运行时异常来说,没有限制。
总结:1)运行时异常,编译器不管。
2)编译器异常: <=
5.2.3 方法重写对于throws要求
5.3 Throw(手动抛出异常对象)
ava程序的执行过程中如出现异常,会生成一个异常类对象,该异常对象将被提交给Java运行时系统,这个过程称为抛出(throw)异常。
异常对象的生成有两种方式:
- 由虚拟机自动生成:程序运行过程中,虚拟机检测到程序发生了问题,就会在后台自动创建一个对应异常类的实例对象并抛出一一自动抛出。
- 由开发人员手动创建: new 异常类型([实参列表]),如果创建好的异常对象不抛出对程序没有任何影响,和创建一个普通对象一样,但是一旦throw抛出,就会对程序运行产生影响了。
使用格式:
throw new 异常类名(参数);
tips:参数会被打印成异常信息中的原因Message在控制台显示
throw语句抛出的异常对象,和JVM自动创建和抛出的异常对象一样
- 如果是编译时异常类型的对象,同样需要使用throws或者try...catch处理,否则编译不通过
- 如果是运行时异常类型的对象,编译器不提示。
- 但是无论是编译时异常类型的对象,还是运行时异常类型的对象,如果没有被try..catch合理的处理,都会导致程序崩溃。
5.4 Throw与Throws区别
区别:
- throws只能出现在方法(后面,表示当前方法没有处理这个异常,需要调用者处理这个异常.
- throw只能出现在方法体里面,表示在xx条件下手动抛出一个异常的对象。只要有throw语句,就表示当前方法可能发生xx异常。如果这个异常对象的类型是运行时异常类型,那么编译器不会提示你要做什么处理。但是如果这个异常对象是编译时异常类型,那么编译器会强制你处理这个异常,处理方式有两种,一种是throws甩给调用者处理,一种是try...catch处理,编译才会通过。
六、可能遇到的面试题
演示如下
七、自定义异常
要求:
(1)自定义异常是一个“类class”,必须继承Throwable或它的子类。一般是继承如下两个类:
- RuntimeException: 如果是继承它,那么说明你这个自定义异常是运行时异常
- Exception: 如果是继承它,那么说明你这个自定义异常是编译时异常。如果是这种,一旦方法体中有thrownew 自定义异常类型(的代码,就必须加throws或try...catch,否则编译不通过。
(2)建议自定义异常编写两个或以上的构造器
(3) 自定义异常的对象,必须使用throw语句抛出。
八、方法重写总结