异常概述
- 依赖于try catch finally throw throws五个关键字
- Java把一场分为两种:Checked Runtime,Checked异常可以在编译阶段被发现,所以强制程序处理所有的Checked异常,Runtime异常无需处理
- 无法穷举所有的异常情况,错误处理代码和业务实现代码混杂,影响可读性,增加维护难度,使用Java异常处理机制就可以解决这个问题
- Java把错误分为Exception和Error,Error一般指与虚拟机相关的问题,通常程序无法处理这些错误,所以不应该使用catch捕捉Error对象
异常处理机制
使用try...catch捕获异常
try
{
//业务实现代码
}
catch (Excption e)
{
//处理异常
}
-
当try中业务逻辑代码出现异常时,系统会自动生成一个异常对象交给Java运行时环境,抛出(throw)异常
-
抛出异常后,Java运行时环境收到异常对象时寻找对应的catch块交给其处理,如果找不到捕获异常的catch块,则运行时环境种植,程序退出
-
会依次判断该异常对象是否为catch块后异常类或其子类的实例,如果是则交由其处理
-
try块后的花括号不能省略,catch也不能省略
-
catch应该先处理小异常再处理大异常,把父类异常的catch块排在子类的后面,否则会出现编译错误
-
Java7提供多异常捕获,一个catch可以捕获多种类型的异常,用|隔开,捕获多种类型的异常时,异常变量有隐式的final修饰,程序不能对其重新赋值
catch (...|...|... e){}
-
异常对象包含的访问信息的方法:getMessage printStackTrace getStackTrance
finally回收资源
- try块内打开的物理资源(数据库连接、网络连接、磁盘文件等),这些物理资源必须显式回收
try{}
catch(.. e){}
catch(.. e){}
...
finally
{
//回收资源
}
- 只有try块时必须的,catch和finally至少出现其中之一,finally位于catch之后,catch位于try之后
- 除非再try或catch中调用了退出虚拟机的方法System.exit(1);,不管怎么样finally块总会被执行
- 通常不要在finally块中使用return或throw方法终止语句,会导致try catch中的return throw语句失效
- 层次太深的潜逃异常处理没有太大的必要,会导致程序可读性降低
- Java7自动给关闭资源的try语句,资源的实现类必须实现AutoCloseable或Closeable(AutoCloseable子接口,只能抛出IOException或其子类)接口,也就是实现了close()方法
try(//声明初始可关闭的资源){}
- 自动关闭资源的try语句相当于包含了隐式的finally块,所以这个try可以既没有catch块也没有finally块
Checked和Runtime异常体系
- 不是RuntimeException类及其子类的异常实例称为Checked异常
- 对于Checked异常的处理方式
- 当明确知道如何处理时使用try...catch捕获修复该异常
- 当前方法不知道如何处理时应该在定义该方法时抛出该异常
- Runtime异常无需显式声明抛出,如果需要捕获,也可以使用try...catch
- throws声明抛出异常,
throws EC1,EC2...
public static void main(String[] args) throws IOException {}
- 当前方法不知如何处理该异常,则抛出有上一级调用者处理,如果main方法也不知道,也可以使用throws抛出交给JVM处理。
- JVM处理异常:打印异常跟踪栈信息,终止程序运行。
- 重写使用throws声明抛出异常的方法时,子类方法声明抛出的异常类型应该时父类的相同类或子类,并且子类方法声明抛出的异常不能比父类声明的多
throw抛出异常
- Java允许自行抛出异常,使用throw语句
throw ExceptionInstance;
- 如果抛出Checked异常,需要处于try块或有throws声明抛出的方法中,如果是Runtime异常,则不需要,自行抛出Runtime异常比Checked异常灵活
自定义异常类
- 需要继承Exception基类,如果自定义Runtime异常则可以继承RuntimeException基类
- 通常需要两个构造器,一个无参,一个字符串参数(描述异常对象的信息),getMessage()返回值
public myException(String msg){super(msg);}
catch和throw同时使用
- 在出现异常的方法内捕获并处理异常,该方法的调用者将不能再次捕获该异常
- 方法签名中声明抛出该异常,改异常将完全交给方法的调用者处理
- 在实际应用中,完全处理一个异常必须由几方法写作才可以,比如在出现异常的方法中捕获并处理部分,还需要再次抛出,让调用者也捕获到异常处理剩下的部分
- 在catch块中结合throw语句完成
catch (Exception e)
{
e.printStackTrace();//在本方法中只是进行一个打印异常信息的操作
throw new AuctionException("想传递的信息");//再次抛出自定义异常
}
- 企业级应用对异常处理通常分为两部分
- 应用后台需要通过日志记录异常发生的详细情况
- 向应用的使用者传达提示
- Java7增强的throw语句,编译器会检查throw语句抛出异常的实际类型,而不是仅根据catch后的类型判断,因此方法声明中只需要声明抛出实际类型的异常即可
异常链
- 不应该把底层的异常传到用户界面,对于用户,此举没有任何帮助,且对于恶意用户,暴露异常是不安全的
- 应该把底层原始异常捕获,抛出一个新的业务异常,这种处理方式叫做异常转译,完全符合面向对象的封装原则
- 捕获一个异常接着抛出另一个异常,并把原始已异常信息保存下来是一种典型的链式处理,职责链模式,也称为异常链
- Java1.4后Throwable的子类在构造器中可以接受一个cause对象作为参数,表示原始异常,这样可以通过异常链追踪到异常最初发生的位置,获取原始异常信息
异常跟踪栈
- 只要异常没有被完全捕获,从发生异常的方法逐渐向外传播,打印异常的跟踪栈信息
- printStackTrace()方法用于调试,最终还是应该避免使用它,应该对捕获的异常进行处理,而不是简单的打印
异常处理规则
- 成功处理异常的四个目标
- 是程序代码混乱最小化
- 捕获并保留诊断信息
- 通知合适的人员
- 采用合适的方式结束异常活动
- 异常只应该用于处理非正常的情况,不要去代替正常的流程控制
- 不要使用过于庞大的try块,应该分割成单独的try块,分别捕获并处理异常
- 避免使用Catch All语句,catch(Throwable t){...}
- 不要忽略捕获到的异常,去处理异常,重新抛出新异常,再在合适的层处理异常
网友评论