1.使用try...catch捕捉异常
如果执行try块里的业务逻辑代码时出现异常,系统自动生成一个异常对象,这个异常对象会被提交给Java的运行时环境,把这个过程叫做“抛出异常”
java的运行时环境受到异常对象后,就会寻找这个异常对象的处理catch语句,如果找到合适的,就把这个异常对象交给catch语句块处理,这个过程被称为“捕获异常” 要是捕获不到异常,运行时环境被终止,退出程序
无论处在哪里的代码块,只要出现异常,都会产生异常对象。
2.异常类的继承体系
try{
statement1
statement2
}
catch(ExceptionClass1 e1){
....
}
catch(ExceptionClass2 e2){
....
}
...
每个catch语句里都是处理该异常类及其子类的异常实例
try语句里面的语句出现异常,生成异常对象,运行时环境接收到异常对象后,以此判断该异常对象是否是catch块后异常类或其子类的实例,如果是,将调用该catch块来处理该异常,不会向下进行,否则再次拿该异常对象和下一个catch块里的异常类进行比较
try块里声明的变量时代码块内局部变量,它只在try块内有效,在catch块里不能访问该变量。
异常类的继承树
Java把所有的非正常情况分为两种:异常(Exception)和错误(Error),他们都继承Throwable 父类
Error错误,一般是指与虚拟机相关的问题,如系统崩溃、虚拟机错误、动态连接失败等,这种错误无法恢复或不可能捕捉,将导致应用程序中断。通常应用程序无法处理这些错误,因此应用程序不应该试图用catch块来捕获Error对象,在定义该方法时,也无需再其throws子句中声明该方法可能跑出Error及其任何子类
4.多异常捕获
自从java7开始,一个catch块可以捕获多种类型的异常:
1.捕获多种类型的异常时,多种异常类型之间用竖线(|)隔开。
2.捕获多种类型的异常时,异常变量有隐式的final修饰,因此程序不能对异常变量重新赋值
5.访问异常信息
d当java运行时决定使用某个catch块来处理该异常对象时,会将异常对象赋给catch块后的异常参数。所有的异常对象都包括了几个方法
getMessage()异常的详细描述字符串
printStackTrace()将该异常的跟踪栈信息输出到标准错误输出
printStackTrace(PrintStream s)将该异常的跟踪栈信息输出到指定输出流
getStackTrace() 返回该异常的跟踪栈信息
6.使用finally回收资源
有些时候,在try块打开了一些系统物理资源(数据库连接、网络连接、磁盘文件等),这些物理资源必须显示回收
为了保证一定能回收try块中打开的资源,异常处理机制提供了finally块,finally块一定会被执行,即使前面出现了return语句,如果在异常处理代码中使用了system.exit(1)(关闭虚拟机)语句,那么finally就失去了执行的机会。
异常处理语句结构里面try是必须的,没有try,就不能有catch和finally,catch和finally至少出现其中之一。
注意:尽量不要在finally块中使用return或throw等导致方法终止的语句。一旦使用,将会使try、catch里面的return、throw失去意义
当java程序执行try、catch时遇到了return或者throw,就会找有没有finally语句,如果有,就会执行finally语句里面的,如果finally里面有return或throw,那么方法就会终止,try、catch里面的语句就失去了执行的机会
7.自动关闭资源的try语句
为了避免代码的臃肿,java7增强了try语句的功能--允许在try关键字后面紧跟一对括号,括号内可以声明、初始化一个或多个资源(必须显示关闭的).try语句在语句结束后自动关系这些资源。这些资源实现类必须实现AutoCloseable或Closeable接口,实现这两个接口必须实现close()方法。
8.Checked异常和Runtime异常
1.RuntimeException类及其子类的实例被称为Runtime异常。
2.不是RuntimeException类及其子类的实例成为Checked异常(只有java提供)
java认为Checked异常都是可以被修复的异常,所以必须被显示处理,如果没有处理,编译就会发生错误。
对Checked的处理方式有两种:
1)、当前方法明确知道如何处理该异常,程序应该使用try。。。catch语句,在对应的catch块中修复该异常
2)、当前方法不知道该如何处理这种异常,在定义该方法时声明跑出该异常
8.1 使用throws声明抛出异常
使用throws声明抛出异常的思路是,当前方法不知道如果处理这种类型的异常,由上一级调用者处理;如果main方法也不知道如何处理,也使用throws声明抛出异常。这个异常将交给jvm处理。JVM对异常的处理方式是,打印异常的跟踪栈信息,并终止程序的运行。throws声明抛出只能在方法签名中使用,一旦使用了throws抛出了异常,就不需要使用try...catch块来捕获该异常了
也就是说,如果某段代码调用了一个带throws声明的方法,该方法声明抛出了checked异常,则表明该方法希望它的调用者来处理该异常(调用该方法时要么放在try块里显示捕获该异常,要么放在另一个带throws声明跑出的方法中)子类方法声明跑出的异常不允许比父类方法声明跑出的异常多
大部分时间下推荐使用Runtime异常,一旦发生了自定义错误,程序只管抛出Runtime异常即可
9.使用throw抛出异常
当程序出现错误时,系统会自动抛出异常;java也允许程序自行抛出异常,自行抛出异常使用throw语句来完成
9.1抛出异常
同样一件事情,可能对你来说是好的,对别人来说是坏的。异常也一样,很多时候都要根据应用的业务要求来决定是否要抛出异常。
由于业务需求不同产生的异常,必须由程序员来决定抛出,系统无法抛出这种异常。这时如果需要在程序中自行抛出异常,应使用throw语句,throw语句可以单独使用,throw抛出的不是异常类,而是一个异常实例
如果是Checked异常,则该throw语句要么处于try块里,显示捕捉该异常,要么放在一个带throws声明抛出的方法里,把该异常交给调用者处理,该方法签名中也该用throws显示抛出该异常而Runtime异常,则该语句无须放在try块里,也无须放在带throws声明抛出的方法中。可以完全不理会。
9.2自定义异常类
通常情况下,程序很少会自行抛出系统异常, 因为异常的类名通常也包含了该异常的有用信息。所以抛出异常时,应该选择合适的异常类,从而可以明确的描述该异常情况。在这种情况下,应用程序常常需要抛出自定义异常
9.3 catch和throw同时使用
前面介绍的异常处理有两种方式:
1.在出现异常的方法内捕获并处理该异常,该方法的调用者将不会再次捕捉该异常
2.该方法签名中声明抛出该异常,由方法调用者处理
但有的时候该方法中,catch块仅仅能处理捕获到的异常的一部分,剩下的处理不了得交给他的调用者处理,这时就可以使用catch和throw相结合
10.增强的throw语句
实际上try块可能只抛出了FileNotFoundException异常,当时在java7以前,由于在捕获该异常时声明e的类型是Exception,因此编译器认为这段代码可能抛出Exception,所以包含这段代码的方法通常需要声明抛出Exception异常
11.异常连
中间层:业务逻辑处理
持久层:保存数据
当业务逻辑层访问持久层出现SQLException异常的时候,程序不应该把底层的SQLExcePtion传给用户,一是用户不想看到,另外把异常暴露给用户也不安全
最常用的做法是:程序先捕获异常,然后抛出一个新的业务异常,新的业务异常对用户进行提示,这种处理方式叫做异常转移
两种写法
12.异常跟踪栈
13.异常处理规则
1.使程序代码混乱最小化
2.捕获并保留诊断信息
3.通知合适的人员
4.采用合适的方式结束异常活动