ASE265

《第7章 异常、断言和日志》

异常

1.异常分类

在Java中,异常对象都是派生于Throwable类的一个实例。
如果Java内置的异常类不能够满足需求,用户可以创建自己的异常类

需要注意的是所有异常都是由Throwable继承而来,但在下一层立即分解为两个分支
ErrorException
Error类层次结构描述了Java运行时系统的内部错误和自愿耗尽错误,应用程序不应该抛出这种类型的对象。
在Java程序设计时,需要关注Exception层次结构
Exception层次结构又分解为两个分支:
一个分支派生于RuntimeException
另一个分支包含其他异常

划分这两个分支的规则为:
由程序错误导致的异常属于RuntimeException
程序本身没有问题,但由于像I/O错误这类问题导致的属于其它异常

一般来说,属于RuntimeException的有:

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

属于其它异常的有:

  • 试图在文件尾部后面读取数据
  • 试图打开一个不存在的文件
  • 试图根据给定的字符串查找class对象,而这个字符串表示的类并不存在

Java将派生于Error类或RuntimeException类的所有异常称为非受查异常
所有其他的异常称为受查异常
而编译器将核查是否为所有的受查异常提供了异常处理器

2.声明受查异常

如果遇到了无法处理的情况,那么Java的方法可以抛出一个异常
方法应该在其首部声明所有可能抛出的受查异常
不需要声明可能抛出的非受查异常
因为非受查异常要么不可控制(Error),要么应该避免发生(RuntimeException)
例如,我们读取文件常用到的类FileInputStream的read函数
其read构造函数的定义如下:

public int read() throws IOException {
    return read0();
}

这个read函数的声明表示如执行成功将返回int类型的数据
但也有可能抛出一个IOException异常
同时由于该异常为受查异常,所以必须为其提供异常处理器

怎么调用一个可能会抛出受查异常的方法呢?
调用的方式一般有两种:以FileInputStream打开文件为例
一种是在方法中直接使用throws语句来继续传递异常

public void readFile() throws FileNotFoundException{
    FileInputStream fileInputStream = new FileInputStream("hello.txt");
    ...
}

特别需要说明的是,如果在子类中覆盖了超类的一个方法
如果超类方法没有抛出任何受查常
子类也不能抛出任何受查异常

另一种则是使用try-catch语句将其捕获并处理

try {
    FileInputStream fileInputStream = new FileInputStream("hello.txt");
    ...
}
catch (FileNotFoundException e){...}
3.如何抛出异常

一个简单的例子:一个名为readData的方法读取一个文件
当读取的字符数(n)小于文件的总字符数(len)时抛出异常

String readData(Scanner in) throws EOFException{
    ...
    while(...){
        if(!in.hasNext()){
            if(n<len) throw new EOFException();
        }
        ...
    }
}

其中EOFException是已经存在的异常类型,若是我们想要抛出属于我们自己的异常类型
可以新建一个类继承于Exception类然后再使用throw关键词抛出即可

4.捕获异常

要想捕获一个异常,必须设置try/catch语句
一般的try/catch语句如下:

try {
    ...
}
catch (ExceptionType e){
    ...
}

这里需要注意的是:
如果方法中的任何代码抛出了一个在catch子句中没有声明的异常类型
那么这个方法将立即退出

在try语句块中可以捕获多个异常类型
并对不同的异常类型做出不同的处理

try{
    ...
}
catch(FileNotFoundException e){...}
catcg(UnkownHostException e){...}
catch(IOException e){...}

可以使用e.getMessage()得到更为详细的信息
可以使用e.getClass().getName()得到异常对象的实际类型

当然,我们也可以选在在catch语句中再次抛出异常
这样做的目的一般是为了改变异常的类型

try/catch语句一般也搭配finally使用
不管有无异常被捕获,finally子句的代码都会被执行

5.使用异常机制的技巧:
  • 异常处理不能代替简单的测试
  • 不要过分地细化异常;如不要将一条语句封装在一个独立的try语句中
  • 利用异常层次;
  • 不要压制异常;
  • 在检查错误时,“苛刻”要比放任好
  • 不要羞于传递异常

断言

断言机制允许在测试期间想代码插入一些检查语句。当代码发布时,这些插入的测试语句将被自动地移走
Java引入了关键字assert,对应的有两种形式
assert 条件;
assert 条件:表达式;

这两种形式都会对条件进行检测,若结果为false,则抛出一个AssertError异常。
在第二种形式中,表达式将会被传入AssertError的构造器,并转换成一个消息字符串

在默认情况下,断言被禁用,需要开启。

什么时候使用断言呢?

  • 断言失败是致命的,不可恢复的错误
  • 断言检查只用于开发和测试阶段。断言只应该用于在测试阶段确定程序内部的错误。

记录日志

每个Java程序员都很熟悉在有问题的代码中插入System.out.println方法调用来帮助观察程序运行的过程,
而一旦发现问题的根源,就要将这个打印语句删除。若其他地方还有问题,则需要重复这种写打印语句又删除的行为。
而记录日志则提供了一种更为科学的方法。

要生成简单的日志记录,可以使用全局日志记录器并调用其info方法:
Logger.getGlobal().info("File->Open menu item seleceted");
Logger.getGlobal().info("x"+x);//输出某个变量的值
但是在适当的地方(如main开始)调用
Logger.getGlobal.setLevel(Level.OFF);
就会取消所有的日志。

在一个专业的应用程序中,不要将所有的日志都记录到一个全局日志记录器中,而是可以自定义日志记录器
可以调用getLogger方法来创建或者获取记录器
private static final Logger myLogger = Logger.getLogger("com.mycompany.myapp");
用一个静态变量存储日志记录器的引用

记录日志的常见用途是记录那些不可预料的异常

分类:

技术点:

相关文章:

猜你喜欢
相关资源
相似解决方案