@author:posper
@version:v 1.0
@date:2021/7/3-2021/7/4
本文档为第二遍看 《Java 卷1》整理,第一遍看的时候画的思维导图。
异常处理的任务:就是将控制权从错误产生的地方转移给能够处理这种情况的错误处理器。
在 Java 中,如果某个方法无法正常执行并采用了异常处理机制,则该方法将会立刻退出,并不返回正常值(任何值),而是抛出(throw)一个封装了错误信息的对象。然后,该方法出错地方之后的代码将不会再执行,此时异常处理机制开始搜索能够处理这样异常状况的异常处理器(exception handler)。
Java 中,所有的异常对象都是都是派生于 Throwable 类的一个类实例。
Error
Exception
检查型异常 & 非检查型异常
“如果出现RuntimeException 异常, 那么就一定是你的问题”。此时,不应该抛出/捕获异常,而是通过修改自己的代码来使得程序正常运行。比如,出现 NullPointerException 时,不应该抛出该类异常,而是应该在变量只用前判断其是否为 null。
抛出异常
捕获异常
使用 try … catch 捕获异常
也可以使用 try … catch … finally
try … with … resources
上一级不知道这个异常(相当于自己扛下所有)
举个栗子:相当于账目亏空,自掏腰包处理,瞒着上级
如何选择抛出异常还是捕获异常?
throws 关键字
在方法声明的位置上(方法首部),使用 throws 关键字,抛出所有可能的检查型异常;否则,编译器报错。
注意:
抛出异常的步骤(3步):
一旦方法抛出了异常, 这个方法就不可能返回到调用者。也就是说, 不必建立默认值的返回或错误代码。
// 抛出异常 String readData(Scanner in) throws EOFException { // 1、找到一个合适的异常类,在方法首部 throws 之; while (…) { if (in.hasNext()) { // EOF encountered if (n < len) { throw new EOFException(); // 2、创建这个类的一个对象; // 3、将对象抛出。(这里这两部合并了) } } } }
语法
try { code more code } catch (ExceptionType e) { handler for this type }
try - catch 语句执行顺序:
捕获多个异常
在 Java 7 中,同一个 catch 子句中可以捕获多个异常类型。
catch (FileNotFoundException | UnknownHostException e) { ... } // 等价于 catch (FileNotFoundException e) { ... } catch (UnknownHostException e) { ... }
只有当捕获的异常类型彼此之间不存在子类关系时才需要这个特性。
捕获多个异常时, 异常变量隐含为 final 变量;
捕获多个异常时,生成的字节码只包一个对应公共 catch 子句的代码。
再次抛出异常
如何选择使用抛出异常/捕获异常?
编译器严格地执行throws 说明符。如果调用了一个抛出受查异常的方法,就必须对它进行处理, 或者继续传递。
注意:
语法:
try { // 1 code that might throw exceptions // 2 } catch (IOException e) { // 3 show error message // 4 } finally { // 关闭资源 // 5 in.dose(); } // 6
finally 语句:
try - catch - finally 语句执行顺序:(3 种情况)
可以只有 try - finally.
注意:不要把改变控制流的语句(return,throw,break,continue)放在 finally 子句中。
public static int test() { try { return 123; } finally { return 0; // error // 会覆盖 try 中合法的 return 结果 } }
Java 7 之后,支持 try - with - resources 语句,比 finall 子句更常用、更方便。
语法:
try (Resource res = ...) { work with res }
Java 9 中,可以在 try 首部中提供之前声明的事实最终变量。
Resource res = ... try (res) { // 事实最终变量 ... }
// 创建异常类 class FileFormatException extends IOException { // 1、定义一个派生于 Exception 子类的类 // 2、定义两个构造器 // 1)无参构造器 public FileFormatException() {} // 2)带有详细描述信息的构造器 public FileFormatException(String gripe) { super(gripe); } } // 3、抛出自定义异常类型 String readData(BufferedReader in) throws FileFormatException { while (. . .) { if (ch == -1) { // EOF encountered if (n < len) { throw new FileFormatException(); } ... } return s; } }
异常处理不能代替简单的测试
不要过分地细化异常
充分利用异常层次结构
不要压制异常
在检测错误时,“ 苛刻” 要比放任更好
不要羞于传递异常
规则 5、6 归纳为“早抛出,晚捕获”。
断言机制允许在测试期间向代码中插入一些检査语句。当代码发布时,这些插入的检测语句将会被自动删除。
断言语法:
Java 中处理系统错误的 3 中机制:
抛出一个异常
日志
使用断言
断言注意事项(2点)
断言失败是致命的、不可恢复的错误
断言检查只用于开发和测阶段
断言只应该用于在测试阶段确定程序内部的错误位置
断言是一种测试和调试阶段所使用的战术性工具; 而日志记录是一种在程序的整个生命周期都可以使用的策略性工具。
自定义日志记录器
// 未被任何变量引用的日志记录器可能会被垃圾回收,所以用一个静态变量存储日志记录器的一个引用。 private static final Logger myLogger = Logger.getLogger("com.mycompany.myapp");
可以修改日志级别(7 个)
默认只记录前 3 个级别,也可以利用 setLevel 方法设置不同的级别。
logger.setLevel(Level.FINE);
子日志记录器会继承父日志记录器的日志级别
记录日志的常见用途是:使用日志记录那些不可预料的异常:
// 1、throwing 方法 if (...) { Exception e = new IOException("..."); logger.throwing("com.mylib.Reader", "read", e); // 使用日志记录异常的描述 throw e; } // 2、log 方法 try { ... } catch (IOException e) { Logger.getLogger("com.myapp").log(Level.WARNING, "Reading image", e); // 使用日志记录异常的描述 }
默认是按照日志记录的级别进行过滤
也可以自定义过滤器,来过滤掉不想要的日志记录
具体步骤:(2 步)
定义一个过滤器,实现 Filter 接口
重写 isLoggable 方法
注意:同一个时刻最多只能有一个过滤器。
ConsoleHandler 类和 FileHandler 类可以生成 XML 格式的日志记录。
可以自定义日志记录的格式:
具体步骤:(2 步)
扩展 Formatter 类
重写 format 方法
为一个简单的应用程序, 选择一个日志记录器,并把日志记录器命名为与主应用程序包一样的名字
默认的日志配置将级别等于或高于 INFO 级别的所有消息记录到控制台
现在,可以记录自己想要的内容了
打印变量的值
单元测试
日志代理
打印异常对象和堆栈轨迹
…告辞,还有好多老夫没看…