文章目录
- 第七章 异常,断言和日志
- 处理错误
- 异常的分类
- 声明检查型异常
- 如何抛出异常
- 创建异常类
- 捕获异常
- 捕获单个异常
- 捕获多个异常
- 再次抛出异常和异常链
- `finally`子句
- `try-with-Resource`语句
- 分析堆栈轨迹元素
- 使用异常的技巧
- 使用断言
- 使用断言完成参数检查
- 日志
- 基本日志
- 高级日志
- 调试技巧
第七章 异常,断言和日志
处理错误
异常的分类
- 异常对象都是派生于
Throwable
类的一个类实例 - 下一层:
Error
:描述java运行时系统内部错误或者资源耗尽错误Exception
: 其他异常(IOException
),RuntimeException
(编程错误导致的异常) RuntimeException:
错误的强制类型转换,数组访问越界,访问null指针- 非检查型异常:
Error, RuntimeException
, 其他的异常称为检查型异常
声明检查型异常
- 一个方法必须声明所有可能抛出的检查型异常,而非检查型异常:
Error
在控制之外,RuntimeException
一开始就应该避免的情况 - 子类中覆盖了超类的一个方法,子类中声明的检查型异常必须特殊性大于等于超类中的(不抛出或者抛出更加特殊的异常)
如何抛出异常
-
方法: 找到一个合适的异常类–>创建这个类的一个对象–>将对象抛出
-
throw new EDFException
-
var e = new EDFException(); throw e;
-
throw new EOFException(string)
创建异常类
-
定义一个派生于
Exception
的类或者其子类,包含两个构造器:默认构造器和一个包含详细描述信息的构造器 -
class FileFormatException extends IOException {public FileFormatException(){}public FileFormatException(Strig gripe){super(gripe);} }
捕获异常
捕获单个异常
-
try{codemore code } catch(ExceptionType e){handle for this type }
-
捕获知道怎么处理的异常, 传播不知道如何处理的异常
-
public void read(String filename){try{var in = new FileInputStream(filename);int b;while((b = in.read())!= -1){...}}catch(IOException exception){exception.printStackTrace();} }public void read(String filename) throws IOException {var in = new FileInputStream(filename);int b;while((b = in.read())!= -1){...} }
-
子类方法覆盖超类,超类没有抛出异常,子类必须捕获
捕获多个异常
-
try{codemore code } catch(FileNotFoundException e){emergency action for missing files } catch(UnknowHostException e){emergency action for unknow hosts } catch(IOException | ExceotionType e){emergency action for all i/o problems }
-
e.getMessage()
获取错误信息 -
e.getClass().getName()
获得异常对象实际类型
再次抛出异常和异常链
- 将原始异常设置为新异常的原因
- 子系统中抛出高层异常,而不会丢失原始异常的细节
- 可以将检查型异常包装为非检查型异常
finally
子句
-
try{.. } catch{... } finally{... }
-
finally
子句中的代码一定会执行,可以没有catch
子句 -
不要把改变控制流的语句
return , throw, continue, break
放在finally
子句中
try-with-Resource
语句
-
try(Resource res= . . .){work with res }
try块自动退出时,自动调用
res,close()
分析堆栈轨迹元素
-
package chapter7_exception_assertion_logger.stackTrace;import java.util.Scanner;public class StackTraceTest {//阶乘public static int factorial(int n) {System.out.println("factorial(" + n + "):");var walker = StackWalker.getInstance();walker.forEach(System.out::println);int r;if (n <= 1) {r = 1;} else {r = n * factorial(n - 1);}System.out.println("return " + r);return r;}public static void main(String[] args) {try (var in = new Scanner(System.in)){System.out.println("Enter n: ");int n = in.nextInt();factorial(n);}} }
使用异常的技巧
- 异常处理不能代替简单测试
- 不要过分细化异常
- 充分利用异常层次结构
- 不要压制异常
- 在检查错误时,"苛刻"要比放任好
- 不要羞于传递异常
使用断言
- 断言:允许在测试期间在代码中插入一些检查,而在生产代码中会自动删除这些检查
assert condition;
assert condition: expression;
使用断言完成参数检查
-
断言使用规则: 断言失败是致命的,不可恢复的错误; 断言只是在开法和测试阶段打开
-
assert a!= null;
-
3种处理系统错误的机制: 抛出异常,断言,日志
日志
基本日志
-
生成简单的日志,使用全局日志记录器
Logger.getGlobal().info("File->Open menu item selected");
9月 05, 2021 11:24:59 上午 chapter7_exception_assertion_logger.test.log main 信息: File->Open menu item selected.
-
Logger.getGlobal().setLevel(Level.OFF)
将会取消所有的日志
高级日志
- P306
调试技巧
- 用
System.out.println("x=" + x); ``Logger.getGlobal().info("x=" + x);``Logger.getGlobal().info("this=" + this);
打印或者记录任意变量的值 - 每一个类中单独放置
main
方法 - 日志代理:一个子类对象,截获方法调用,记录日志,调用超类中的方法.
var generator = new Random(){public double nextDouble(){double result = super.nextDouble();Logger.getGlobal().info("nextDouble: " + result);return result;}
};System.out.println(generator.nextDouble());
/*
9月 05, 2021 11:48:19 上午 chapter7_exception_assertion_logger.test.log$1 nextDouble
信息: nextDouble: 0.8803807382089267
0.8803807382089267
*/
- 利用
Throwable
类的printStackTrace
方法,可以从任意异常对象获得堆栈轨迹
try{...
}
catch(Throwable t){t.printStackTrace();throw t;
}或者:
Thread.dumpStack();
-
堆栈轨迹一般显示在
System.err
上.想要记录或者显示堆栈轨迹,可以捕获进入一个字符串import java.io.*;
var out = new StringWriter();
new Throwable().printStackTrace(new PrintWriter(out));
String description = out.toString();
//System.out.println(description);