JavaCoreNote–异常
如果由于出现错误而使得某些操作没有完成,程序应该:
• 返回到一种安全状态,并能够让用户执行一些其他的命令;或者
• 允许用户保存所有操作的结果,并以妥善的方式终止程序
异常
for:将控制权从错误产生的地方转移给能够处理这种情况的错误处理器
程序错误分类:
- 用户输入
- 物理限制
- 设备
- 代码
处理方式:返回一个特殊的错误码;不返回任何值,而是抛出一个封装了异常信息的对象,寻找对应的错误处理器。
语法
分类
-
非受查异常(unchecked)
Error、RuntimeException
Error类层次结构描述了 Java 运行时系统的内部错误和资源耗尽错误;
RuntimeException:程序错误导致的异常,包括情况:- 错误的类型转换
- 数组访问越界
- 访问null指针
如果出现RuntimeException异常,一定是程序员的问题
-
受查异常(checked)
非RuntimeException表示程序本身没有问题,但由于像IO错误这类的异常
何时声明
- 调用一个抛出受査异常的方法
- 程序运行过程中发现错误,并且利用throw语句抛出一个受查异常
- 程序出现错误抛出非受查异常
- 虚拟机和运行时库出现的内部错误
注:不应声明从Error继承来的错误、从RuntimeException继承来的异常
一个方法必须声明所有可能抛出的受查异常,而非受查异常要么不可控制(Error),要么就应该避免发生(RuntimeException)
抛出异常
- 找到合适异常类
- 创建异常类对象
- 将对象抛出
定义异常类:派生于Exception或其子类的类,提供两个构造器
注:不允许子类的throws说明符中出现超过超类方法所列出的异常类范围
捕获异常
- try/catch语句块
- 何时捕获,何时抛出?——捕获哪些知道如何处理的异常
- 捕获多个异常时,异常变量隐含为final变量
- catch语句中可再次抛出异常,为了转换异常的类型,可设置异常原因initCause,getCause可重新获得
finally子句
for:资源回收问题,如果没有finally子句,一样的代码将在两个地方出现。
finally子句中包含return语句将覆盖try中的return
解耦合try/catch和try/finally语句块,内层负责关闭资源,外层负责确保报告出现的异常
InputStrean in = . . .;
try
{
try
{
code that might throwexceptions
}
finally
{
in.cose();
}
}
catch (IOException e) {
show error message
}
finally子句可能产生异常,覆盖原始的异常信息。
带资源的try语句
- 资源属于实现AutoClosable接口的类,会自动调用close方法。
- 带资源的try语句也可以有自己的catch、finally子句,这些子句会在调用了关闭资源之后执行。实际中,避免加入过多内容,很少这样使用这种情况。
对于finally子句产生异常覆盖原始异常的问题,会自动调用addSuppressd方法添加到原始异常的抑制异常中,抛出原始异常,处理器可调用getSuppressed获得异常列表。
堆栈轨迹
- Throwable.printStackTrace、getStackTrace
- Thread.getAllStackTrace
使用技巧
- 异常处理不能代替简单测试
- 不要过分细化异常,使代码膨胀
- 利用异常层次结构
- 不要压制异常
- 检测错误时,苛刻比放任更好(早抛出)
- 不要羞于传递异常(晚捕获)
断言
for:有选择的启用检测
断言机制允许在测试期间向代码中插入一些检査语句。当代码发布时,这些插人的检测语句将会被自动地移走
使用
-
assert 条件;
assert 条件:表达式;
条件不成立时抛出AssertionError异常,第二种形式中表达式生成一个说明字符串。 -
启用禁用
不需要重新编译,是类加载器的功能。
启用:-enableassertions 或 -ea
可在某个类或包中启用:
java -ea:MyClass -ea:com.mycompany.mylib… MyApp
禁用:-disableassertions 或 -da
对于系统类(没有类加载器),使用:enablesystemassertions/-esa
程序中也可以控制,参看API
- 何时使用
- 断言失败是致命的、不可恢复的错误;
- 断言检查只用于开发和测阶段
日志
日志API优势:
- 很容易开启、取消全部或某个级别日志记录;
- 可以被定向到不同处理器;
- 记录器和处理器可以对记录过滤;
- 不同方式格式化;
- 可以用多个日志记录器;
- 默认用配置文件控制,也可用程序替换配置。
基本日志
- 调用全局记录器(global logger)
- 取消:Logger.getGlobal().setLevel(Off)
高级日志
- 记录器具有层次结构,子继承父的属性
- 7个级别:SEVERE、WARNING、INFO、CONFIG、FINE、FINER、FINEST,默认前三个
默认记录类名、方法名信息,但VM可能进行优化得不到准确调用信息,可使用logp方法获得调用类和方法的确切信息
作用:
- 用来跟踪执行流的方法:entring、exiting
- 记录不可预料的异常:throwing、log
修改日志管理器配置
默认:jre/lib/1ogging.properties
修改:java -Djava.util.logging.config.file=configFile MainClass
修改日志级别 名称.level=FINE
默认日志管理器:java.util.logging.LogManager
可通过系统属性修改:java.util.logging.manager
处理器
- 默认使用ConsoleHandler
- 默认INFO级java.uti1.1ogging.ConsoleHandler.level
- 默认发送到处理器及父处理器
其他处理器:FileHandler、SocketHandler
可修改文件处理器的默认行为
过滤器
实现Filter,记录器或处理器中setFilter方法使用
格式化
实现Formatter,处理器中setFormatter方法使用
常用操作
- 日志记录器命名为主程序包名
- 程序中安装默认配置
- 只将对用户有用的日志设置为前3个级别,显示在控制台。
调试技巧
- 打印变量;
- 在类中放置main方法,对每个类做单元测试;
- 使用JUnit组织测试用例
- 使用日志代理(logging proxy)截获方法调用,记录日志
- 打印堆栈轨迹Thread.dumpStack()
- 捕获堆栈轨迹到字符串
- 捕获错误信息到文件java MyProgram 2> errors.txt
java MyProgram 1> errors.txt 2>&1 - 非捕获异常的堆栈轨迹保存到文件中,可以调用静态的 Thread.setDefaultUncaughtExceptionHandler方法改变非捕获异常的处理器
- 观察类的加载过程,可以用-verbose标志启动Java虚拟机
- -Xlint选项告诉编译器对一些普遍容易出现的代码问题进行检査
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vg4mJ7vS-1582984683851)(evernotecid://1520493E-927F-420A-8EE1-BA6F74088A9D/appyinxiangcom/11767354/ENResource/p3065)] - 利用JVM对Java应用程序进行监控(monitoring)和管理 (management)的支持
jconsole processID - jmap实用工具获得一个堆的转储
jmap -dump:format=b,file=dumpFileName processID
jhat dumpFileName
进人localhost:7000 - 使用 -Xprof 标志运行 Java 虚拟机, 就会运行一个基本的剖析器来跟踪那些代
码中经常被调用的方法
小结
本文整理了程序错误的分类,除了返回特殊的返回值外,Java语言中可通过抛出异常对象、捕获异常的方式处理程序中出现的问题。通过不同日志级别记录程序运行中的业务、程序信息,通过断言实现程序开发、测试阶段的程序检测。