向用户通知错误 保存所有工作 允许用户妥善地退出程序
处理错误
异常处理的任务就是将控制权从产生错误的地方转移到能够处理这种情况的一个错误处理器。传统方法返回错误码,但是不能明确区分合法数据与非法数据
1.异常分类
异常对象都是派生与Throwable类的一个类的实例,下一层:Error、Exception
Error类:Java运行时系统的内部错误和资源耗尽问题。非检查型异常
RuntimeException:由编译错误导致的异常,非检查型异常
IOException:程序本身没有问题,由I/O错误之类的问题导致的异常,检查型异常
2.声明检查型异常
遇到无法解决的情况,方法抛出一个异常
public FileInputStream(String name) throws FileNotFoundException
下面四种情况会抛出异常:
调用了一个抛出检查型异常的方法
检测到一个错误,并利用throw语句抛出一个检查型异常
程序出现错误
Java虚拟机或运行时库出现内部错误
有些方法包含再对外提供的类中,对于这些方法,应该通过方法首部的异常规范声明这个方法可能抛出的异常,但是不与要声明Java的内部错误。一个方法必须声明所有可能抛出的检查型异常。如果子类中覆盖了超类的一个方法,子类方法中声明的检查型异常不能比超类方法中声明的异常更通用。超类方法没有抛出任何异常,子类也不能抛出。
3.如何抛出异常
如果一个已有的异常类能够满足你的要求,抛出这个异常非常容易,这种情况下:找到一个合适的异常类 创建这个类的一个对象 将对象抛出。Java中只能抛出Throwable的子类的对象。
4.创建异常类
遇到已有异常类无法描述清楚的问题,创建自己的异常类,定义一个派生于Exception的类或者子类,包含两个构造器,默认构造器和包含详细描述信息的构造器。
捕获异常
1.概述
try/catch语句块
如果try语句块中的任何代码跑出来catch子句中指定的一个异常类,程序跳过try语句块的其余代码,程序将执行catch子句中的处理器代码。try语句块没有抛出异常,则跳过catch子句。
捕获那些你知道怎么处理的异常,继续抛出你不知道怎么处理的异常。如果编写一个覆盖超类方法的方法,而超类方法没有抛出异常,就必须处理你的方法代码中出现的异常。
2.捕获多个异常
在一个try语句块中可以捕获多个异常类型,并对不同类型的异常做出不同的处理,要为每个异常类型使用一个单独的catch子句。
使用e.getMessage()获得更多信息
只有当捕获的异常类型彼此之间不存在子类关系时才需要这个特性。捕获多个异常时,异常变量隐含为final。
3.再次抛出异常与异常链
可以在catch子句中抛出一个异常,改变异常的类型,可以不原始异常设置为新异常的原因
捕获到这个异常时,可以获取原始异常
如果在一个方法中出现检查型异常,但该方法不允许抛出,可以把这个异常包装成一个运行时异常。
也可以只记录异常
4.finally子句
代码抛出异常时,会停止处理这个方法中剩余的代码。不管是否捕获到异常,finally子句中的代码都会执行。finally子句用于清理资源,不要把改变控制流的语句放在其中。
5.try-with-Resources
资源属于一个实现了AutoCloseable接口的类
try退出,自动调用res.close()
只要需要关闭资源,尽可能使用try-with-resources语句,可以有catch和finally
6分析栈轨迹元素
栈轨迹是程序执行过程中某个特定点上所有挂起的方法调用的一个列表。
StackWalker.StackFrame类有一些方法可以得到正在执行的代码行的文件名和行号、类对象、方法名。
使用异常的技巧
1.异常处理不能代替简单的测试
2.不要过分地细化异常
3.合理利用异常的层次结构
4.不要压制异常
5.在检测错误时,苛刻比放任好
6.不要羞于传递异常
7.使用标准方法报告null指针和越界异常
8.不要向最终用户显示栈轨迹
使用断言
1.概念
断言机制允许你在测试期间在代码中插入一些检查,而在生产代码中自动删除这些检查
计算条件(condition)结果为false,则抛出AssertionError异常,expression将传入AssertionError对象的构造器,并转化成一个消息字符。
2.启用和禁用断言
默认情况下断言是禁用的,在运行程序时用-enableassertions或-ea启动
java -enableassertions MyApp
启用或禁用断言是类加载器的功能。可以在特定的类或整个保重启用断言
java -ea:MyClass -ea:com.mycompany.mylib MyApp
用选项-disableassertion或-da禁用断言
断言开关不能应用于哪些没有类加载器的”系统类“,需要使用-enablesystemassertions/-esa
3.使用断言完成参数检查
断言失败是致命的、不可恢复的错误
断言检查只在开发和测试阶段打开
不应该使用断言向程序的其他部分通知发生了可恢复性的错误,不应该利用断言与程序用户沟通问题,断言只应该用于在测试阶段确定程序内部错误的位置。前置条件
4.使用断言提供假设文档
日志
用于程序整个生命周期的战略性工具
优点
1.基本日志
使用全局日志记录器(global logger)并调用其info方法。
2.高级日志
定义自己的日志记录器
未被任何变量引用的日志文件可能会被垃圾回收,用静态变量存车处日志记录器的一个引用。
日志记录器的父子之间会共享某些属性,对父设置了日志级别,子会继承。
使用Level.ALL开启所有级别的日志记录,使用Level.OFF关闭所有日志
3.修改日志管理器的配置
通过配置文件来修改日志系统的各个属性。
4.过滤器
实现Filter接口并定义以下方法
一次最多只能有一个过滤器
5.日志技巧
对一个简单的应用,选择一个日志记录器
默认的日志配置会把级别等于或高于INFO的所有消息记录到控制台
想要调用System.out.println时可以换成
6.调试技巧
打印变量的值
在每一个类中放置main方法
日志代理
Java虚拟机提供对Java应用的监控和管理支持