第7章 异常、断言和日志

处理错误

异常分类

所有的异常都是由Throwable继承而来,但是在下一层会被分解为两个分支:Error和Exception。

Error

Error类层次结构描述了Java运行时系统的内部错误和资源耗尽错误。
如果出现了Error,除了通知用户,并尽力妥善地终止程序以外,你几乎无能为力。

Exception

RuntimeException:由编程错误导致的异常

  • 错误的强制类型转换
  • 数组访问越界
  • 访问null指针

其他异常:程序本身没有问题,但由于像I/O错误这类问题导致的异常

  • 试图超越文件末尾继续读取数据
  • 试图打开一个不存在的文件
  • 试图根据给定的字符串查找Class对象,而这个字符串表示的类并不存在

Java语言规范将派生于Error类或RuntimeException类的所有异常称为非检查型(unchecked)异常,所有其他的异常称为检查型(checked)异常。编译器将检查你是否为所有的检查型异常提供了异常处理器。

声明检查型异常

一个方法必须声明所有可能抛出的检查型异常,非检查型异常要么再你的控制之外(Error),要么是由一开始就应该避免的情况导致的(RuntimeException)。如果你的方法没有声明所有可能发生的检查型异常,编译器就会发出一个错误信息。

如何抛出异常

  1. 找到一个合适的异常类
  2. 创建这个类的一个对象
  3. 将对象抛出
    例如:
String gripe="Content-length:  "+len+",Received:  "+n;
throw new EOFException(gripe);

一旦方法抛出了异常,这个方法就不会返回到调用者。

创建异常类

我们需要做的只是定义一个派生于Exception的类,或者派生于Exception的某个子类。
习惯做法是:自定义的这个类应该包含两个构造器,一个是默认的构造器,另一个是包含详细描述信息的构造器。

捕获异常

try/catch语句块

try
{
    code
}
catch(ExceptionType e)
{
    handler for this type
}

异常处理的一般经验:
捕获那些你知道如何处理的异常,而继续传播那些你不知道怎样处理的异常。
如果想传播一个异常,就必须再方法的首部添加一个throws说明符,提醒调用者这个方法可能会抛出异常。
如果编写一个方法覆盖超类的方法,而这个超类方法没有抛出异常,你就必须捕获你的方法代码中出现的每一个检查型异常。不允许在子类的throws说明符中出现超类方法未列出的异常类。

再次抛出异常与异常链

可以在catch子句中抛出一个异常。通常,希望改变异常的类型时会这样做。
这样可以在子系统中抛出高层异常,而不会丢失原始异常的细节。

finally子句

为了解决在代码抛出异常时,方法的本地资源释放问题,引入了finally子句。
不管是否有异常被捕获,finally子句中的代码都会执行。
finally子句的体要用于清理资源。不要把改变控制流的语句(return,throw,break,continue)放在finally子句中。

try-with-Resources语句(带资源的try语句)

try(Resource res=...)
{
    work with res
}

try块退出时,会自动调用res.close()。还可以同时指定多个资源。不论块如何退出,指定的多个资源都会关闭。
try-with-resources语句自身也可以有catch子句,甚至还可以有一个finally子句。这些子句会在关闭资源之后执行。
只要需要关闭资源,就要尽可能使用try-with-resources子句。

分析堆栈轨迹元素

两种方法

  1. 调用Throwable类的printStackTrace方法来访问堆栈轨迹的文本描述信息。
        var t=new Throwable();
        var out=new StringWriter();
        t.printStackTrace(new PrintWriter(out));
        String description=out.toString();
        System.out.println(description);

这个方法效率不高,因为它要得到整个堆栈,即使调用者可能只需要几个栈帧。另外它只允许访问挂起方法的类名,而不能访问类对象。

  1. 使用StackWalker类,它会生成一个StackWalker.StackFrame实例流,其中每个实例分别描述一个栈帧(stack frame)。
        var walker = StackWalker.getInstance();
        walker.forEach(System.out::println);

使用异常的技巧

  1. 异常处理不能代替简单的测试(只在异常情况下使用异常)
  2. 不要过分地细化异常
    不要将每一条语句都分装在一个独立的try语句块中。
  3. 充分利用异常层次结构
  4. 不要压制异常
  5. 在检测错误时,“苛刻”要比放任更好
  6. 不要羞于传递异常

使用断言

断言机制允许在测试期间向代码中插入一些检查,而在生产代码中会自动删除这些检查。
选择使用断言的时机

  • 断言失败是致命的,不可恢复的错误
  • 断言检查只是在开发和测试阶段打开
    因此,不应该使用断言向程序的其他部分通知发生了可恢复性的错误,断言只应该用于在测试阶段确定程序内部错误的位置。

日志

稍微看了一下,具体细节等日后若有需要的话再来学习。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。