Java异常处理之try...catch...语句的使用进阶

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

Java异常处理之try...catch...语句的使⽤进阶
try就像⼀个⽹,把try{}⾥⾯的代码所抛出的异常都⽹住,然后把异常交给catch{}⾥⾯的代码去处理。

最后执⾏finally之中的代码。

⽆论try中代码有没有异常,也⽆论catch是否将异常捕获到,finally中的代码都⼀定会被执⾏。

虽然 Java 执⾏时期系统所提供的预设处理器对除错很有⽤,你通常想要⾃⼰处理例外。

这样做有两个优点:第⼀,它让你修正错误。

第⼆,它可以避免程式⾃动终⽌。

每当错误发⽣时,如果你的程式就停⽌⽽且列印出堆叠追踪,⼤多数的使⽤者都会感到很困惑。

很幸运,你很容易就能避免这种情形。

要防备并且处理执⾏时期错误,只要将你要监视的程式码放在 try 区块⾥即可。

在 try 区块之后紧接著在 catch ⼦句⾥指定你希望捕捉的例外型态
错误捕捉例⼦:
try
{
code; //将⾃⼰的代码放在其中;
} catch(e) //如果上⾯的代码有错误,这⾥就捕获
{
alert(e.number); //获得错误信息
}
例如:
import java.io.*;//调⽤io包
public class SimpleCharInOut
{
public static void main(String args[])
{
char ch=' ';//定义个字符ch初始为‘ '
System.out.println(" Enter a character please");//在屏幕上输出Enter a character please
try {//你要监视的程式码放在 try 区块⾥即可。

在 try 区块之后紧接著在 catch ⼦句⾥指定你希望捕捉的例外型态
ch=(char)System.in.read();//将从键盘输⼊的字符赋给ch
}
catch(IOException e) //如果上⾯的代码有错误,这⾥就捕获
{ } ;//错误后不进⾏操作
System.out.println("You're entered character:" + ch);// 在屏幕上输出You're entered character:
//和ch的值
}
}
我们在写Java的try..catch的时候,往往需要在最后加上finally⼦句关闭⼀些IO资源,⽐如
InputStream is;
try{
is=openInputStream();
// do something
}catch(IOException e){
e.printStaceTrace(e);
}finally{
try{
is.close();
}catch(IOException e ){
}
}
但是在使⽤这种模式时,即使是Java⽼⼿,偶尔也会犯⼀些错误。

⽐如上⾯这段代码,当openInputStream()函数在执⾏过程中抛出异常,那么变量is的值仍为null,此时执⾏is.close()会抛出NullPointerException. 由于NullPoiterException不是IOException的⼦类,因此它不能被catch块捕获,⽽是直接往调⽤层抛出去. ⼀种改进的写法就是在关闭流的时候先进⾏⾮空判断,但这样代码会显得很啰嗦。

个⼈认为⽐较优雅的写法是直接调⽤commons-io包提供的IOUtils.closeQuitely()⽅法关闭流(或者⾃⼰封装⼀个closeQuitely()⽅法)。

使⽤这种写法还有⼀种好处,就是当遇到关闭多个IO资源时不容易出错,⽐如下⾯这段代码:
InputStream is;
OutputStream os ;
try{
is=openInputStream();
// do something
}catch(IOException e){
e.printStaceTrace(e);
}finally{
try{
if (is != null ) is.close();
if (os != null ) os.close();
}catch(IOException e ){
}
}
当is.close()发⽣错误的时候,os.close()就⽆法被执⾏,从⽽导致os所引⽤的资源没有被释放。

也许Oracle也觉得这种try .. catch ... finally的样板代码太没必要,因此在JDK 7中对try ⼦句进⾏了⼀些改造,免去编写⼀些⼿动关闭资源的代码,让代码看起来更紧凑更简洁。

⽐如上⾯的代码在JDK 7下可以改成:
try(
InputStream is = openInputStream();
OutputStream os = openOutStream();
){
// do something
}catch(IOException e){
e.printStaceTrace(e);
}
Oracle把这⾥的try(..)语句叫做try-with-resource语句。

需要注意的是,try(.. )中变量所引⽤的对象都必须是实现了
java.io.AutoClosable接⼝的实例,当退出try ..catch块时,JDK会⾃动调⽤close()⽅法。

也就是说,try-with-resource语句中的resource(资源)不仅限于IO资源。

这⾥有必要对try-with-resource语句的⼀些细节进⾏补充说明:
JDK会确保所有资源的close()⽅法被调⽤,不管close()⽅法是否抛出异常,⽽调⽤的顺序和资源声明的顺序相反
try-with-resource语句中所有抛出的异常都会被捕获。

如果多个异常被抛出,后⾯所抛出的异常会被suppress(抑制)在前⼀个异常中,catch块最终只拿到最先抛出的那个异常。

可以依次通过调⽤Throwable类定义的getSuppressed()获得被suppressed(抑制)的异常。

还是上⾯那个例⼦,
当退出try .. catch.块的时候,JDK会先调⽤os.close(),然后是is.close(),如果两次close()都抛出IOException,那么is.close()所抛出的异常会被suppress(抑制)在os.close()所抛出的异常中,最终catch块只捕获到os.close()所抛出的异常。

可以通过getSuppressed()⽅法拿到is.close()所抛出的异常。

如果调⽤openInputStream()的时候就发⽣IOException,那么openOutputStream()就不会被调⽤,os.close()和is.close()也不会被调⽤, catch块捕捉到调⽤openInputStream()时所抛出的异常。

如果调⽤openOutputStream()发⽣IOException(⽤记号 e1表⽰), 那么is.close()还是会被调⽤,如果此时is.close()⼜抛出IOException(⽤记号 e2表⽰),那么e2会被suppress到e1中,⽽catch块捕捉到的异常是 e1.
除了对try块做了改造,JDK 7还对catch部分进⾏了简化,允许把多个catch⼦句合并。

⽐如:
try(
InputStream is = openInputStream();
OutputStream os = openOutStream();
){
// do something
}catch(IOException | XMLParseException | XPathException e){
e.printStaceTrace(e);
}
此外,当你重新抛出多个异常时,不再需要详细定义异常类型了,编译器已经知道你具体抛出的是哪个异常了。

你只需在⽅法定义的时候声明需要抛出的异常即可。

⽐如
// 虽然这⾥⽤Exception匹配抛出的IOException,到编译器知道实际上抛给上层的异常是IOException
public void doIO() throws IOException {
try{
throw new IOException();
}catch(Exception e){
throw e;
}
}
PS : 这个特性我想不到会带来什么好处
JDK 7还有其他有趣的语法新特性,⽐如⼆进制字⾯量,⽤下划线分割长数字,泛型参数的类型推断,switch⽀持字符串匹配等等。

现在JDK 8⼜引⼊了⼀些有⽤的特性。

在不需要考虑向后兼容的前提下,适当并灵活运⽤⼀些语法特性,可以让我们
的代码在⼀定程度上显得更清晰,更简洁。

相关文档
最新文档