Java异常
相关概念
异常:异常指的是在程序运行过程中发生的异常事件,通常是由外部问题(如硬件错误、输入错误)所导致的。
异常情形:异常情形是阻止当前方法或作用域继续执行的问题,异常情形特点是在当前环境无法获得必要信息来解决问题。
抛出异常:抛出异常是指面对异常情形,从当前环境跳出,把问题交给上一级环境。
捕获异常:如果某方法内部抛出了异常或方法内部调用的其他人方法抛出了异常,这个方法将在抛出异常的过程中结束。当不希望该方法就此结束时,可在方法内设置一个特殊块来捕获异常并交由异常处理程序进行处理。
异常处理程序:抛出的异常必须在某处得到处理,这个地点就是异常处理程序,针对于每一个捕获的异常,需要有相应的处理程序。
异常分类
Throwable类被用来表示任何可以作为异常被抛出的类。它分为两种类型:Error表示编译时和系统错误;Exception是可以被抛出的基本类型。在Java类库中,用户方法和运行时故障都有可能抛出Exception,设计程序时需要关注它。
Exception类分为RuntimeExceprion--程序错误导致的异常;其他异常--例如I/O错误。
RuntimeException情形包括:
1)错误类型转换
2)数组访问越界
3)访问null指针
其他异常情形包括:
1)试图在文件尾部后面读取数据
2)试图打开一个不存在的文件
3)试图根据给定的字符串查找Class对象,而该字符表示的类不存在。
不受检查异常:运行时异常会自动被Java虚拟机抛出,所以不必在异常说明中罗列出来,这些异常被称为不受检查异常,即从RuntimeException继承的异常。
受检查异常:而Exception下的除RuntimeException其他异常都被称为受检查异常,编译器会检查此类异常,如果不将异常捕获或抛出,将不能通过编译。
抛出异常
对于对象引用,使用对象引用调用其方法前,会对引用进行检查,可以创建一个代表错误信息的对象,并将其从当前环境中抛出,类似于:
if(t == null) throw new NullPointException();
在堆上会创建一个异常对象,之后当前执行路径被终止,从当前环境弹出对异常对象的引用,异常处理程序接管程序。
异常对象会传递给throw,故从效果看,可以将异常处理看做一种不同的返回机制。
String readData(Scanner in) throws EOFException { //code while(//code) { if(in.hasNext()) //EOF encountered { if(n < len) throw new EOFException(); } //code } return s; }
throws是可能抛出的异常类的什么声明,写在方法参数列表后,若有多个类,则用逗号隔开。当某个方法可能会抛出某种异常时,用throws 声明可能抛出的异常,然后交给上层调用它的方法程序处理。
throw抛出的是对象,写在方法体里面。
捕获异常
使用try/catch来捕获异常,如果在try语句块中任何代码抛出了在catch语句中说明的异常类,那么程序将1)跳过try语句块的剩余代码 2)执行catch语句块的代码
try{ //code that might generate exception }catch(TypeOfException e){ //Handle }
可以用多个catch语句来捕获多个异常
try{ //code that might generate exception }catch(TypeOfException xxx){ //Handle }catch(Exception yyy){ //Handle }catch(Exception zzz){ //Handle }
异常对象有多个方法可以获取详细信息:getMessage(),getLocalizedMessage(),toString(),printStackTrace()等。
public class ExceptionMethods { public static void main(String[] args) { try { throw new Exception("myException"); }catch (Exception e){ System.out.println("Caught Exception"); System.out.println("getMessage" + e.getMessage() ); System.out.println("getLocalizeMessage" + e.getLocalizedMessage()); System.out.println("toString" + e.toString()); System.out.println("getStacktrace"); e.printStackTrace(System.out); } } }
结果:
Caught Exception
getMessagemyException
getLocalizeMessagemyException
toStringjava.lang.Exception: myException
getStacktrace
java.lang.Exception: myException
at ExceptionMethods.main(ExceptionMethods.java:6)
再次抛出异常与异常链
catch子句中可以再次抛出异常,再次抛出异常会将异常交给上一级的异常处理程序,同一个try块的后续catch子句将被忽略,该异常对象信息得到保存。
再次抛出异常的目的一般是为了改变异常类型。
try{ access the database }catch(SQLException e){ throw new servletException("databaseError" + e.getMessage()); }
然而,这种方法抛出的异常,将覆盖原异常的细节,而需要保留原异常的细节并仍然抛出新异常,需要使用initCause()将异常原因描述为原异常,通过将原异常传递给新异常,即使在当前位置创建并抛出了新异常,也能通过异常链追踪到异常最初发生的位置。
try{ access the database }catch(SQLException e){ throwable se = new servletException("databaseError"); se.initCause(e); throw se; }
之后可以用throwable e = se.getCause()获取原始异常.
finally子句
当代码抛出一个异常时, 就会终止方法中剩余代码的处理,并退出这个方法的执行。如果方法获得了一些本地资源,并且只有这个方法自己知道,又如果这些资源在退出方法之前必须被回收,那么就会产生资源回收问题。一种解决方案是捕获并重新抛出所有的异常。但是,这种解决方案比较乏味,这是因为需要在两个地方清除所分配的资源。一个在正常的代码中;另一个在异常代码中。Java提供了finally子句解决这个问题。
不管是否有异常被捕获,finally子句中的代码都会被执行,可以在finally子句中进行资源清理。
以下例子分析程序可能执行情况:
1)代码没有异常,程序按照1,2,5,6顺序执行
2)try子句中抛出io异常,catch子句中没有再次抛出异常,程序按照1,3,4,5,6顺序执行
3)try子句中抛出io异常,catch子句中再次抛出异常,程序按照1,3,5,6,顺序执行
4)try子句抛出catch子句中没有捕获的异常,程序按照1,5,6顺序执行
InputStream in = new FileInputStream(...) try { //1 code that might throw exception //2 } catch(IOException e) { //3 show error message //4 } finally { //5 in.close(); } //6
事实上,资源清理过程中,也可能出现异常,可以使用带资源的try语句解决问题。
带资源的try语句最简形式如下,try块退出时,会自动执行res.close()
try (Resource res = . . .) { work with res }
如下示例,这个块无论正常或异常,结束后都会调用in.close()
try (Scanner in = new Scanner(new FileInputStream("xxx")) , "UTF-8") { while (in.hasNextO) System.out .println(in.next ()) ; }