java核心技术-I-7-异常、断言和日志

异常分类

在Java程序设计语言中,所有异常对象都是派生于Throwable类的一个类实例。

继承关系图如下:

错误类继承

可以看到Throwable异常被分为两大类:

  • Error:表示Java运行时系统的内部错误和资源耗尽错误。
  • Exception:程序运行的异常,一般我们能操作的都是这种错误。

RuntimeException 一般是编程错误导致的。

派生于RuntimeException的异常包括以下问题:

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

IOException的异常包括以下问题:

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

如果出现RuntimeException,那么就一定是你的问题。

所以这句话有一定的根据(当然错误也可能出现在你引用的库中)

Java语言规范将派生于Error类或RuntimeException类的所有异常称为非检查型(unchecked)异常,所有其他的异常称为检查型(check)异常。

抛出异常

可以通过throw关键字来抛出异常。

Java在库中定义了很多错误类型,我们可以通过查阅文档来获得相应的错误类型。(当然我们也可以定义自己的错误类型)

示例如下:

1
2
3
4
public void method1(){
///do something
throw new EOFExcepiton();
}

创建异常类

我们可以通过继承Exception类或者其子类来创建单独的异常类。

基本示例如下:

1
2
3
4
public class MyException extends Exception{

}

Throwable类中定义了很多成员,其中主要有message:Stringcause:Throwable。而Exception也根据其提供了4个构造方法。

  • 无参构造器
  • 只含message的构造器
  • 只含cause的构造器
  • 包含messagecause的构造器

所以我们也可以这样定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package test.mw.ExceptionTest;

public class MyException extends Exception{

public MyException() {
super();
// TODO Auto-generated constructor stub
}

public MyException(String message, Throwable cause) {
super(message, cause);
// TODO Auto-generated constructor stub
}

public MyException(String message) {
super(message);
// TODO Auto-generated constructor stub
}

public MyException(Throwable cause) {
super(cause);
// TODO Auto-generated constructor stub
}

}

捕获异常

如果发生了某个异常,但没有任何地方捕获这个异常,程序就会停止,并且会在控制面板上打印一个消息:包括这个异常的信息和调用堆栈。

捕获异常

可以通过

1
2
3
4
5
6
7
try{
// normal code
}catch(Exception e){
// code for handling the exception
}finaly{
// code need to be execeuted anyway
}

来捕获异常,其含义如下:

  1. 如果normal code发生错误,则错误后的代码都不会被执行。
  2. 将错误传递到catch块中进行处理
  3. finally中的代码无论如何都会执行(normal code发生错误或不发生错误都会执行)

如果normal code中可能发生的错误不只一个。我们可以通过以下两种方式来进行处理:

  • 添加多个catch块来分别处理
  • 使用catch(Exception1 | Exception2 | Exception3 e)的格式来捕获多个错误。
  • 使用catch(Exception e)来通过多态来捕获所有可能的异常。(这里即使发生错误,也会在控制台打印出具体的类名,而不是Exception)。

再次抛出异常

一般来说,我们可以再次抛出异常。而在最外围来捕获所有的异常。其有两种形式:

  • catch中捕获到后再次抛出。

    1
    2
    3
    4
    5
    try{
    // code
    }catch(Exception e){
    throw e;
    }
  • 直接在方法上抛出错误。

    1
    2
    3
    public void String save()  throws Exception{
    // code
    }

try…with-Resource语句

有时我们需要在try语句中打开一些资源,并且最后必须关闭,我们通常会使用finally语句来实现。如:

1
2
3
4
5
6
7
try{
source.open();
}catch(Exception e){
///handle
}finally{
source.close();
}

在Java7之后,可以用try...with-Resource语句来关闭资源,前提是这个资源实现了AutoCloseable接口。格式如下:

1
2
3
try(Resource res = ...){
//res is accessible in the scope
}

在try语句退出之后,会自动调用res.close()

也可以指定多个资源:

1
2
3
4
5
try(Resource res1 = new Resource();
Resource res2 = new Resource();
){
//res1 and res2 is include in the scope
}

使用异常的技巧

  1. 异常处理不能代替简单的判断:即能用if判断是否出现异常的,就不直接用异常。
  2. 不要过分的细化异常。
  3. 不要只捕获Exception或者Throwable,因为这对代码阅读并不友好,无法直接判断可能出现的错误。
  4. 不要对异常进行静默处理。即catch必须做点什么,而不是仅捕获,什么都不做。
  5. 并不一定要捕获所有异常,有时候抛出到外层是更好的选择(可以在外层做统一处理)。

断言

有时候我们需要在测试期间加入一些判断,如果条件不成立,则退出程序。但是在正式版本中,将会删除这部分代码,这是很麻烦的。

所以引入了断言机制,其格式如下:

  1. assert conditon;:如果condition为false,则退出程序,并抛出AssertionError
  2. assert condition : expresssion:如果condition为false,则退出程序,并抛出AssertionError,并且信息为expression。

启用断言

断言默认是被禁用的。。

启用断言需要加上vm关键字:-ea或者-enableassertions

也可以使用-desableassertions-da来禁止断言。

idea或eclipse中可以在环境参数中配置。

启用或禁用断言是类加载器的功能。断言被禁用时,类加载器会去除断言代码,因此不会降低程序运行时的代码。

日志

在Java程序中,我们经常需要打印中间信息。

我们通常使用System.out.print来进行打印。

但是其具有很多缺点。相较于直接输出,日志API有以下 优点:

  • 日志分级。并且可以分级打印特定日志级别下的信息。
  • 可以很简单的禁止日志记录。
  • 可以将日志信息打印到控制台或保存在文件中。
  • 可以对所有日志进行过滤。
  • 可以单独的格式化日志信息。
  • 可以单独的配置日志设置。

Java从1.5开始,在java.util下增加了Logger类。但是这个类已经很少有人使用。使用Log4j的更多,所以这里不详细介绍。

Powered by Hexo and Hexo-theme-hiker

Copyright © 2019 - 2024 My Wonderland All Rights Reserved.

UV : | PV :