【笔记】Java核心技术卷1-第七章_异常、断言和日志

7.1.1 异常分类

所有异常由Throwable继承而来,下一层分解为两个:Error和Exception。
Error描述Java运行时内部错误和资源耗尽错误。少见。
Exception分解为RuntimeException(程序错误)和其他异常(程序本身没错)。

7.1.2 声明受查异常

一个方法必须声明所有可能抛出的受查异常,而非受查异常要么不可控制(Error), 要么就应该避免发生( RuntimeException)。如果方法没有声明所有可能发生的受查异常, 编译器就会发出一个错误消息。

7.1.3 如何抛出异常

1 ) 找到一个合适的异常类。 2 ) 创建这个类的一个对象。 3 ) 将对象抛出。

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

创建异常类

在程序中,可能会遇到任何标准异常类都没有能够充分地描述清楚的问题。 在这种情 况下,创建自己的异常类就是一件顺理成章的事情了。我们需要做的只是定义一个派生于 Exception 的类,或者派生于 Exception 子类的类。例如, 定义一个派生于 IOException 的类。 习惯上, 定义的类应该包含两个构造器, 一个是默认的构造器;另一个是带有详细描述信息的构造器。

class FileFormatException extends IOException {
    public FileFormatException(){}
    public FileFormatException(String gripe){
        super(gripe);//构造一个带描述信息的异常
    }
}

//抛出自己定义的异常类型
String readDate(BufferedReader in) throws FileFormatException {
    ...
    while (...) {
        if (ch == -1) { //EOF encounteered
        	if (n < len) throw new FileFormatException();
        }
        ...
    }
    return s;
}

7.2.1 捕获异常

如果某个异常发生的时候没有在任何地方进行捕获,那程序就会终止执行,并在控制台上打印出异常信息, 其中包括异常的类型和堆栈的内容。对于图形界面程序(applet 和应用程序),在捕获异常之后,也会打印出堆桟的信息,但程序将返回到用户界面的处理循环中。
要想捕获一个异常,必须设置 try/catch语句块。

try
{
	code
}
catch (ExceptionType e) 
{ 
	handlerfor this type
} 

若try语句块任何代码抛出catch子句中说明的异常类,则
1)程序将跳过 try 语句块的其余代码。
2)程序将执行 catch 子句中的处理器代码。
如果方法中的任何代码拋出了一个在 catch 子句中没有声明的异常类型,那么这个方法就会立刻退出。

7.2.2 捕获多个异常

在一个 try语句块中可以捕获多个异常类型,并对不同类型的异常做出不同的处理。每个异常类型使用一个单独的 catch 子句。
JAVA SE 7中,同一个catch可以捕获多个异常类型(当异常动作一样时,合并catch)。

7.2.3 再次抛出异常与异常链

在 catch 子句中可以抛出一个异常,这样做的目的是改变异常的类型。如果开发了一个供其他程序员使用的子系统, 那么,用于表示子系统故障的异常类型可能会产生多种解释。 ServletException 就是这样一个异常类型的例子。执行 servlet 的代码可能不想知道发生错误的细节原因,但希望明确地知道 servlet 是否有问题。下面给出了捕获异常并将它再次抛出的基本方法:

try 
{ 
	access thedatabase 
} 
catch (SQLException e) 
{ 
	throw new ServletException("database error: " + e.getMessage()); 
} 

这里,ServleException 用带有异常信息文本的构造器来构造。 不过,可以有一种更好的处理方法,并且将原始异常设置为新异常的“ 原因”:

try {
	accessthedatabase
}
catch(SQLException e) 
{
	Throwable se = new ServletException("database error");
	se.initCause(e); 
	throw se;
}

当捕获到异常时, 就可以使用下面这条语句重新得到原始异常:Throwable e = se.getCause();强烈建议使用这种包装技术。这样可以让用户抛出子系统中的高级异常,而不会丢失原始异常的细节。

7.2.4 finally子句

不管是否有异常被捕获,finally子句中的代码都被执行。
try语句可以只有finally子句,没有catch子句。

7.2.5 带资源的try语句

带资源的 try语句(try-with-resources) 的最简形式为:

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

try块退出时,会自动调用 res.close()。下面给出一个典型的例子, 这里要读取一个文件 中的所有单词:

try (Scanner in = new Scanner(new FileInputStream("/usr/share/dict/words"), "UTF-8") ;
{ 
	while (in.hasNext()) 
		System.out.println(in.next()); 
} 

这个块正常退出时, 或者存在一个异常时, 都会调用 in.close()方法, 就好像使用了 finally块一样。

7.2.6 分析堆栈轨迹元素

import java.util.*;

public class StackTraceTest {
    public static int factorial(int n) {
        System.out.println("factorial(" + n + "):");
        Throwable t = new Throwable();
        StackTraceElement[] frames = t.getStackTrace();
        for (StackTraceElement f : frames)
            System.out.println(f);
        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) {
        Scanner in = new Scanner(System.in);
        System.out.println("Enter n :");
        int n = in.nextInt();
        factorial(n);
    }
}

7.3 使用异常机制的技巧

  1. 异常处理不能代替简单的测试(捕获异常时间太长)。
  2. 不要过分细化异常。
  3. 利用异常层次结构。
    不要只抛出 RuntimeException 异常。应该寻找更加适当的子类或创建自己的异常类。 不要只捕获 Thowable 异常, 否则,会使程序代码更难读、更难维护。
  4. 不要压制异常。
    如果编写了一个调用另一个方法的方法,而这个方法有可能 100 年才抛出一个异常, 那么, 编译器会因为没有将这个异常列在 throws 表中产生抱怨。而没有将这个异常列在 throws 表中主要出于编译器将会对所有调用这个方法的方法 进行异常处理的考虑。因此,应该将这个异常关闭。即catch(Exception e){}//使用{}通过编译
  5. 检测错误时,苛刻比放任好。
    我们认为:在出错的地方抛出一个 EmptyStackException 异常要比在后面抛出一个 NullPointerException异常更好。
  6. 传递异常可能比捕获异常更好。
    注:5.6归结为“早抛出,晚捕获”

7.4.1 断言的概念

断言机制允许在测试期间向代码中插入一些检査语句。当代码发布时,这些插人的检测语句将会被自动地移走。
两种形式:

assert 条件;
assert 条件:表达式;

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

要想断言x是一个非负数值, 只需要简单地使用下面这条语句
assert x >= 0;或者将 x 的实际值传递给 AssertionError 对象,从而可以在后面显示出来。 assert x >= 0 : x;

7.5 记录日志

优点
  1. 可以很容易地取消全部日志记录,或者仅仅取消某个级别的日志,而且打开和关闭这个操作也很容易。
  2. 可以很简单地禁止日志记录的输出,因此,将这些日志代码留在程序中的开销很小
  3. 日志记录可以被定向到不同的处理器,用于在控制台中显示,用于存储在文件中等。
  4. 日志记录器和处理器都可以对记录进行过滤。过滤器可以根据过滤实现器制定的标准丢弃那些无用的记录项。
  5. 日志记录可以采用不同的方式格式化,例如,纯文本或 XML。
  6. 应用程序可以使用多个日志记录器, 它们使用类似包名的这种具有层次结构的名字,例如,com.mycompany.myapp 。
  7. 在默认情况下,日志系统的配置由配置文件控制。如果需要的话, 应用程序可以替换这个配置。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值