《Java核心技术》笔记:第7章 异常、断言和日志
1. 异常
(P 280)异常处理需要考虑的问题:
- 用户输入错误
- 设备错误
- 物理限制
- 代码错误
(P 280)传统的处理错误的方法是:返回一个特殊的错误码,常见的是返回-1或者
null
引用(P 280)在Java中,方法出现错误时,它会立即退出,不返回任何值,而是抛出一个封装了错误信息的对象
(P 280)Java中所有的异常都是由
Throwable
继承而来,它下面又分解为两个分支:Error
和Exception
Error
:描述了Java运行时系统的内部错误和资源耗尽错误(对于处理这种错误,你几乎无能为力)Exception
:又分解为两个分支:RuntimeException
(由编程错误导致,如果出现该异常,那一定是你的问题)和其他异常(诸如IO错误这类问题)- 派生于
RuntimeException
的异常:- 错误的强制类型转换
- 数组访问越界
- 访问
null
指针
- 不是派生于
RuntimeException
的异常:- 试图超越文件末尾继续读取数据
- 试图打开一个不存在的文件
- 试图根据特定的字符串查找
Class
对象,而这个字符串表示的类并不存在
- 派生于
graph TD
Throwable[Throwable]-->Error[Error]
Throwable[Throwable]-->Exception[Exception]Error[Error]-->OtherError1[...]
Error[Error]-->OtherError2[...]
Error[Error]-->OtherError3[...]Exception[Exception]-->RuntimeException[RuntimeException]
Exception[Exception]-->IOException[IOException]
Exception[Exception]-->OtherException[...]IOException[IOException]-->OtherIOException1[...]
IOException[IOException]-->OtherIOException2[...]
IOException[IOException]-->OtherIOException3[...]RuntimeException[RuntimeException]-->OtherRuntimeException1[...]
RuntimeException[RuntimeException]-->OtherRuntimeException2[...]
RuntimeException[RuntimeException]-->OtherRuntimeException3[...](P 281)派生于
Error
和RuntimeException
的所有异常称为非检查型异常,所有其他异常称为检查型异常(P 282)如果没有处理器捕获异常对象,那么当前执行的线程就会终止
(P 283)必须在方法的首部列出所有检查型异常类型,但是不需要声明从
Error
继承的异常,也不应该声明从RuntimeException
继承的那些非检查型异常(P 283)如果在子类中覆盖了超类的一个方法,子类方法中声明的检查型异常不能比超类方法中声明的异常更通用(子类方法可以抛出更特定的异常,或者根本不抛出任何异常)。如果超类方法没有抛出任何检查型异常,子类也不能抛出任何检查型异常
(P 288)同一个
catch
子句中可以捕获多个异常类型,如果一些异常的处理逻辑是一样的,就可以合并catch
子句。只有当捕获的异常类型彼此之间不存在子类关系时才需要这个特性try {
...
} catch (FileNotFoundException | UnknownHostException e) {
...
} catch (IOException e) {
...
}
(P 289)可以在catch子句中抛出一个异常,此时,可以把原始异常设置为新异常的“原因”
try {
...
} catch (SQLException original) {
var e = new ServletException("database error");
e.initCause(original);
throw e;
}
捕获异常时,获取原始异常
Throwable original = caughtException.getCause();
(P 292)一种推荐的异常捕获写法:内层
try
语句块只有一个职责,就是确保释放资源,外层try
语句块也只有一个职责,就是确保报告出现的错误try {
try {
...
} finally {
// 释放资源
}
} catch (Exception e) {
// 报告错误
}
(P 293)Java 7中,对于实现了
AutoCloseable
接口的类,可以使用带资源的try
语句(try-with-resources):try (Resources res = ...) {
// Work with res
...
}
Java 9中,可以在
try
首部中提供之前声明的事实最终变量(effectively final variable):try (res) {
// Work with res
...
} // res.close() called here
(P 294)在try-with-resources语句中,如果
try
块抛出一个异常,而且close
方法也抛出一个异常,则原来的异常会重新抛出,而close
方法抛出的异常会“被抑制”(可以通过getSuppressed
方法得到这些被抑制的异常)(P 294)可以通过
StackWalker
类处理堆栈轨迹var walker = StackWalker.getInstance();
walker.forEach(frame -> ...); // 例如:walker.forEach(System.out::println);
(P 298)使用异常的一些技巧:
- 异常处理不能代替简单的测试(捕获处理异常的成本很高,只在异常情况下使用异常)
- 不要过分的细化异常(有必要将整个任务包在一个
try
语句块中,将正常处理与错误处理分开) - 充分利用异常层次结构
- 不要压制异常(异常非常重要时,应该适当地进行处理)
- 在检测错误时,“苛刻”要比放任更好
- 不要羞于传递异常(最好继续传递异常,而不是自己捕获)
2. 断言
(P 301)Java中引入了关键字
assert
,其有如下两种形式:assert condition;
assert condition : expression;
这两个语句都会计算条件,如果结果为
false
,则抛出一个AssertionError
异常。在第二个语句中,表达式将传入AssertionError
对象的构造器,并转换为一个消息字符串(P 301)默认情况下,断言是禁用的,可以使用
-enableassertions
或者-ea
选项启用断言:java -enableassertions MyApp
禁用断言可以使用
-disableassertions
或-da
(P 302)断言只应该用于在测试阶段确定程序内部错误的位置
3. 日志
(P 305)基本日志的使用:
生成简单的日志记录
Logger.getGlobal().info("hello world!");
取消所有日志
Logger.getGlobal().setLevel(Level.OFF);
(P 305)高级日志的使用:
创建或获取日志记录器
private static final Logger myLogger = Logger.getLogger("className"); // className是全限定类名
设置日志级别
logger.setLevel(Level.FINE); // FINE以及更高级别的日志都会被记录
记录日志
// 调用相应级别的日志记录方法
logger.warning(message);
logger.fine(message); // 使用log方法并指定级别
logger.log(Level.FINE, message); // 跟踪执行流的方法
logger.entering("className", "methodName", new Object[]{ params... });
logger.exiting("className", "methodName", result); // 在日志记录中包含异常的描述
logger.throwing("className", "methodName", exception);
logger.log(Level.WARNING, message, exception);
(P 305)7个日志级别:
- SEVERE
- WARNING
- INFO
- CONFIG
- FINE
- FINER
- FINEST
(P 307)可以通过配置文件修改日志系统的各个属性,默认情况下,配置文件位于:
conf/logging.properties
指定特定位置的配置文件:
java -Djava.util.logging.config.file=configFile MainClass
指定日志记录器的日志级别:在日志记录器名后面追加后缀.level,例如
com.mycompany.myapp.level=FINE
(P 313)日志技巧
- 对一个简单的应用,选择一个日志记录器,可以把日志记录器命名为与主应用包一样的名字
- 默认的日志配置会把级别等于或高于INFO的所有消息记录到控制台,用户可以覆盖这个默认配置,最好在你的应用中安装一个更合适的默认配置
- 所有级别为INFO、WARNING和SEVERE的消息都将显示到控制台上
- 只将对程序用户有意义的消息设置为以上这几个级别
- 将程序员想要的日志消息设定为FINE级别是一个很好的选择
(P 321)调试技巧
打印或日志记录变量的值
在每一个类中放置一个单独的
main
方法,以便独立地测试类使用
JUnit
日志代理,它是一个子类的对象,可以截获方法调用,记录日志,然后调用超类中的方法
var generator = new Random() {
public double nextDouble() {
double result = super.nextDouble();
Logger.getGlobal().info("nextDouble: " + result);
return result;
}
}
利用
Throwable
类的printStackTrace
方法,可以从任意的异常对象获得堆栈轨迹一般来说,堆栈轨迹显示在
System.err
上。如果想要记录或显示堆栈轨迹,可以将它捕获到一个字符串中var out = new StringWriter();
new Throwable().printStackTrace(new PrintWriter(out));
String description = out.toString();
通常,将程序错误记入一个文件会很有用:
java MyProgram > errors.txt # 错误,错误被发送到System.err而不是System.out
java MyProgram 2> errors.txt # 正确,只输出System.err
java MyProgram 1> errors.txt 2>&1 # 同时捕获System.out和System.err
将未捕获的异常的堆栈轨迹记录到一个文件中,而不是直接输出到
System.err
,可以使用静态方法Thread.setDefaultUncaughtExceptionHandler
改变未捕获异常的处理器Thread.setDefaultUncaughtExceptionHandler(
new Thread.UncaughtExceptionHandler() {
public void uncaughtException(Thread t, Throwable e) {
// save information in log file
}
}
)
要想观察类的加载过程,启动Java虚拟机时可以使用
-verbose
标志-Xlint
选项告诉编译器找出常见的代码问题javac -Xlint sourceFiles
Java虚拟机增加了对Java应用程序的监控和管理支持,允许在虚拟机中安装代理来跟踪内存消耗、线程使用、类加载等情况。jconsole工具可以显示有关虚拟机性能的统计结果
Java任务控制器:一个专业级性能分析和诊断工具
《Java核心技术》笔记:第7章 异常、断言和日志的更多相关文章
- [core java学习笔记][第十一章异常断言日志调试]
第11章 异常,断言,日志,调试 处理错误 捕获异常 使用异常机制的技巧 使用断言 日志 测试技巧 GUI程序排错技巧 使用调试器 11.1 处理错误 11.1.1异常分类 都继承自Throwable ...
- 20145330《Java学习笔记》第一章课后练习8知识总结以及IDEA初次尝试
20145330<Java学习笔记>第一章课后练习8知识总结以及IDEA初次尝试 题目: 如果C:\workspace\Hello\src中有Main.java如下: package cc ...
- 【马克-to-win】学习笔记—— 第五章 异常Exception
第五章 异常Exception [学习笔记] [参考:JDK中文(类 Exception)] java.lang.Object java.lang.Throwable java.lang.Except ...
- [core java学习笔记][第六章接口与内部类]
接口域内部类 接口 描述类具有什么功能,不给出具体实现. 内部类 用于设计协作关系的类集合 代理 实现任意接口的对象. 6.1 接口 接口声明 public interface Comparable ...
- 初读"Thinking in Java"读书笔记之第二章 --- 一切都是对象
用引用操纵对象 Java里一切都被视为对象,通过操纵对象的一个"引用"来操纵对象. 例如, 可以将遥控器视为引用,电视机视为对象. 创建一个引用,不一定需要有一个对象与之关联,但此 ...
- java核心技术笔记
1.类和对象 第四章:面向对象 日历的作用是提供某个时间点的信息 查询设置信息:GregorianCalendar now = new GregorianCalendar() int month = ...
- Java 学习笔记 ------第三章 基础语法
本章学习目标: 认识类型与变量 学习运算符的基本使用 了解类型转换细节 运用基本流程语法 一.类型(基本类型) 所谓基本类型,就是在使用时,得考虑一下数据用多少内存长度存比较经济,利用程序语法告诉JV ...
- Java 学习笔记 ------第四章 认识对象
本章学习目标: 区分基本类型与类类型 了解对象与参考的关系 从打包器认识对象 以对象观点看待数组 认识字符串的特性 一."=" 和 "==" 当=用于基本类型时 ...
- Java 学习笔记 ------第五章 对象封装
本章学习目标: 了解封装的概念与实现 定义类.构造函数与方法 使用方法重载与不定长度自变量 了解static方法 一.Java封装概念 在面向对象程式设计方法中,封装(英语:Encapsulation ...
- Java 学习笔记 ------第六章 继承与多态
本章学习目标: 了解继承的目的 了解继承与多态的关系 知道如何重新定义方法 认识java.lang.object 简介垃圾回收机制 一.继承 继承是java面向对象编程技术的一块基石,因为它允许创建分 ...
随机推荐
- parrot os安装drozer
dz需要支持 大部分parrot都装好了,只有Protobuf未安装 apt install Protobuf 安装dz 下面下载https://labs.f-secure.com/tools/dro ...
- 如何开启远程桌面连接功能?windows的远程桌面连接功能使用步骤
由于远程桌面的诞生,为电脑工作者提供了极大的便利.首先,推荐1款比较适合服务器管理的远程桌面: 可以管理1000+服务器/vps的远程桌面:IIS7远程桌面管理 开启远程桌面功能步骤: 1.右键点击电 ...
- 附021.Traefik-ingress部署及使用
一 Helm部署 1.1 获取资源 [root@master01 ~]# mkdir ingress [root@master01 ~]# cd ingress/ [root@master01 ing ...
- Rocket - tilelink - CrossingHelper
https://mp.weixin.qq.com/s/y432EkLcBvVn2u_U3tPWeA 简单介绍CrossingHelper的实现. 1. 基本介绍 为节点生成一个跨 ...
- Java实现蓝桥杯 算法提高 线段和点
算法提高 线段和点 时间限制:1.0s 内存限制:256.0MB 提交此题 问题描述 有n个点和m个区间,点和区间的端点全部是整数,对于点a和区间[b,c],若a>=b且a<=c,称点a满 ...
- Java实现第八届蓝桥杯外星日历
外星日历 题目描述 某星系深处发现了文明遗迹. 他们的计数也是用十进制. 他们的文明也有日历.日历只有天数,没有年.月的概念. 有趣的是,他们也使用了类似"星期"的概念, 只不过他 ...
- Python 爬虫之request+beautifulsoup+mysql
一.什么是爬虫?它是指向网站发起请求,获取资源后分析并提取有用数据的程序:爬虫的步骤: 1.发起请求使用http库向目标站点发起请求,即发送一个RequestRequest包含:请求头.请求体等 2. ...
- 【工作Vlog】Jmeter响应结果乱码解决方案
资料:https://blog.51cto.com/ydhome/1864340 方法一:使用后置控制器"Beanshell PostProcessor"(动态修改,灵活) 添加后 ...
- 【个人博客 hexo】一个小时就搭好属于自己的博客
对于经常需要发博客的小伙伴来说,拥有一个属于自己的博客网站,听起来是不是很酷. 今天我就来告诉大家,怎么搭建一个属于自己的博客网站,我们需要的就是使用hexo+github来搭建我们自己博客系统. 你 ...
- 宇宙第一IDE是谁?
更多精彩文章,尽在码农翻身 微服务把我坑了 如何降低程序员的工资? 程序员,你得选准跑路的时间! 两年,我学会了所有的编程语言! 一直CRUD,一直996,我烦透了,我要转型 字节码万岁! 上帝托梦给 ...