Java核心技术:异常、断言和日志——捕获异常


异常的捕获比较简单,只列出需要注意的地方

捕获异常

使用try/catch语句块捕获一个异常。try/catch
编译器严格地执行throws说明符,如果调用了一个抛出受查异常的方法,就必须对它进行处理,或者继续传递。
通常,应该捕获那些知道如何处理的异常,而将那些不知道怎样处理的异常继续进行传递。
这个规则也有一个例外:如果编写一个覆盖超类的方法,而这个方法又没有抛出异常,那么这个方法就必须捕获方法代码中出现的每一个受查异常。不允许在子类的throws说明符中出现超过超类方法所列出的异常类范围。

捕获多个异常

可以按照下列方式为每个异常类型使用一个单独的catch字句:
try/catch多个异常
要想获得对象的更多信息,可以试着使用e.getMessage()。得到详细的错误信息(如果有的话),或者使用e.getClass().getName()
在Java SE 7中,同一个catch字句中可以捕获多个异常类型。
try/catch合并多个异常
***注释:***捕获多个异常时,异常变量隐含为final变量。不能在字句中为e赋不同的值。捕获多个异常不仅会让你的代码看起来更简单,还会更高效。生成的字节码只包含一个对应公共catch字句的代码块。

再次抛出异常与异常链

在catch字句中可以抛出一个异常。
catch中抛出异常
这里,ServleException用带有异常信息文本的构造器来构造。不过一种更好的处理方法是将原始异常设置为新异常的“原因”
设置新异常的原因
当捕获到异常时,就可以使用Throwable e = e.getCause();重新得到原始异常。
**强烈建议使用这种包装技术。**这样可以让用户抛出子系统中的高级异常,而不会丢失原始异常的细节。
如果再一个方法中发生了一个受查异常,而不允许抛出它,那么包装技术就十分有用。我们可以捕获这个受查异常,并将它包装成一个运行时异常。

finally字句

当代码抛出一个异常时,就会终止方法中剩余代码的处理,并退出这个方法的执行。如果方法获得了一些本地资源,并且只有这个方法自己知道,又如果这些资源在退出方法之前必须被回收,那么就会产生资源回收问题。一种解决方案是捕获并重新抛出所有异常。但这种解决方案比较乏味,因为需要在两个地方清楚所分配的资源。一个在正常的代码中;另一个在异常代码中。
Java提供了finally字句作为一种更好的解决方案。不管是否有异常被捕获,finally字句中的代码都被执行。

InputStream in = new FileInputStream(...);
try
{
	//1
	code that might throw exceptions
	//2
}
catch(IOException e)
{
	//3
	show error message
	//4
}
finally
{
	// 5
	in.close();
}
//6

在上面这段代码中,有下列3中情况会执行finally字句:

  1. 代码没有抛出异常。执行标注的1、2、5、6处。
  2. 抛出一个在catch字句中捕获的异常。执行标注的1、3、4、5、6处的字句。
  3. 代码抛出一个异常,但不由catch字句捕获。执行标注1、5处的语句。
    try语句可以只有finally字句,而没有catch字句。例如:
InputStream in = ...;
try
{
	code that might throw exceptions
}
finally
{
	in.close();
}

在需要关闭资源时,用这种方式使用finally子句是一种不错的选择。
提示:强烈建议解耦合try/catch和try/finally语句块。这样可以提高代码的清晰度。例如:

InputStream in = ...;
try
{
	try
	{
		code that might throw exceptions
	}
	finally
	{
		in.close();
	}
}
catch(IOException e)
{
	show error message
}

警告当finally子句包含return语句时,会出现一种想不到的结果。例如:

public static in f(int n)
{
	try
	{
		int r = n * n;
		return r;
	}
	finally
	{
		if(n == 2) return 0;
	}
}

如果调用f(2),那么finally子句使得方法返回0,覆盖了try语句块中原始的返回值4。
有时候,finally子句也会带来麻烦。例如,清理资源时的方法也有可能抛出异常。

InputStream in = ...;
try
{
	code that might throw exceptions
}
finally
{
	in.close();
}

finally语句块中的close方法本身也有可能抛出IOException异常,出现这种情况时,原始的异常将会丢失,转而抛出close方法的异常。
如果想做适当的处理,重新抛出原来的异常,代码会变得极其繁琐。如下所示:

InputStream in = ...;
Exception ex = null;
try
{
	try
	{
		code that might throw exceptions
	}
	catch(Exception e)
	{
		ex = e;
		throw e;
	}
}
finally
{
	try
	{
		in.close();
	}
	catch (Exception e)
	{
		if(ex == null) throw e;
	}
}

Java SE 7 中关闭资源的处理会容易得多。

带资源的try语句

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

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

资源属于一个实现了AutoCloseable接口的类,Java SE 7 为这种代码模式提供了一个很有用的快捷方式。AutoCloseable接口有一个方法:

	void close() throws Exception

try块退出时,会自动调用res.close()。
可以指定多个资源。例如:

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

不论这个块如何退出,in和out都会关闭。原来的异常会重新抛出,而close方法抛出的异常会“被抑制”。这些异常将自动捕获,并由addSuppressed方法增加到原来的异常。可以调用getSuppressed方法,它会得到从close方法抛出并被抑制的异常列表。

分析堆栈轨迹元素

**堆栈轨迹(stack trace)**是一个方法调用过程的列表,它包含了程序执行过程中方法调用的特定位置。可以调用Throwable类的printStackTrace方法访问堆栈轨迹的文本描述信息。

Throwable t = new Throwable();
StringWriter out = new StringWriter();
t.printStackTrace(new PrintWriter(out));
String description = out.toString();

一种更灵活的方法是使用getStackTrace方法,它会得到StackTraceElement对象的一个数组。例如:

Throwable t = new Throwable();
StackTraceElement[] frames = t.getStackTrace();
for (StackTraceElement frame : frames)
	analyze frame

StackTraceElement类有能够获得文件名和当前执行的代码行号的方法,同时,还含有能偶获得类名和方法名的方法。toString方法将产生一个格式化的字符串。
静态的Thread.getAllStackTrace方法,它可以产生所有线程的堆栈轨迹。

Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces();
for (Thread t : map.keySet())
{
	StackTraceElement[] frames = map.get(t);
	analyze frames
}

Throweable
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值