Java中的异常
前两天粗浅的学习了java中的反射和注释。今天再粗浅的学习下java中的异常。
在java程序中,经常可以见到如下形式的语句。
try{
//捕获可能的异常
}catch(异常类 异常对象){
//异常处理语句
}[finally{
//一定会执行到的程序代码
}]
流程图如下所示。
其中finally中的语句是一定会执行到的。一般在此处进行各种输入输出流的关闭操作。
下面的demo展示一个简单的异常捕获
int a = 1; int b = 0; try { System.out.println(a/b);
System.out.println("java"); } catch (java.lang.ArithmeticException e) { // TODO: handle exception System.out.println(e); }
我们都知道,除数不能为0。在java中如何除数为0,则抛出 java.lang.ArithmeticException 异常,在catch中捕获这个异常并输出
输出结果: zerojava.lang.ArithmeticException: / by zero //除数为0
注:一般在输出异常的时候可直接输出 System.out.println(e); 也可使用Exception中提供的方法,e.printStackTrace();
以上的代码就是简单的try{}catch(){}异常处理结构,当在try中捕获异常后,产生异常后的代码将不会再执行,而是跳转到相应的cath语句中,用于处理异常。
异常类的继承结构:
在整个java的异常处理结构中,有如下两个最常用的类Exception 和Error 这两个类实际上都是Throwable的子类。
Exception:
1.可以是可被控制(checked) 或不可控制的(unchecked)
2.表示一个由程序员导致的错误
3.应该在应用程序级被处理
Error:
1.总是不可控制的(unchecked)
2.经常用来用于表示系统错误或低层资源的错误
3.如何可能的话,应该在系统级被捕捉
因此现在我们只专注于Exception。java中的所有异常都是继承自Exception类的,都是Exception的子类。
如下图所示:
上图中红色框中所有的异常都是Exception的直接子类。
实际上在异常的处理结构中,也是按照面向对象的方式进行处理的,处理的步骤如下:
1、一旦产生异常,则会产生一个异常类的实例化对象
2、在try语句中对此异常对象进行捕获
3、产生的异常对象与catch中的各个类型相匹配,匹配成功,则执行catch中的语句。
由于java有多态性,因此子类的对象可以使用父类的对象直接接收,在异常处理中同样适用。因此实际上
只需要在catch的时候 catch(Exception e) ,由于Exception是所有异常的父类,因此必然能捕获到所有异常。(向上转型)
当然在比较细致的程序中,是不建议这样捕获异常的,所有的异常最好分别捕获。
另外在捕获异常的时候,只能先捕获异常子类的异常,才能捕获其异常父类的异常。
throws与throw关键字
1、throws
在定义一个方法的时候可以使用throws关键字声明,使用throws声明的方法,表明此方法不处理异常,而交给方法的调用者去处理异常。
那么如果在main方法上使用throws抛出异常,交给谁处理呢。答案是JVM。由JVM来处理所有异常。
2、throw
不但可以由程序抛出异常,也可以人为的抛出一个异常,throw关键字的作用就是在程序中抛出一个异常,抛出的时候抛出的是一个异常类的实例化对象。
try{ throw new Exception("自己抛着玩的"); }catch(Exception e){ System.out.println(e); }
输出结果: java.lang.Exception: 自己抛着玩的
区别:
1、throws出现在方法函数头;而throw出现在函数体。
2、throws表示出现异常的一种可能性,并不一定会发生这些异常;throw则是抛出了异常,执行throw则一定抛出了某种异常。
3、两者都是消极处理异常的方式(这里的消极并不是说这种方式不好),只是抛出或者可能抛出异常,但是不会由函数去处理异常,真正的处理异常由函数的上层调用处理。
在java面试中经常有人问到Exception和RuntimeException有什么区别:
Exception: (检查型)在程序中必须使用 异常处理块
RuntimeException :(非 检查型)可以不使用 异常处理块,如果有异常产生,将由 JVM 进行处理。
常见的RuntimeException:
ClassCastException
NullPointerException
ArrayIndexOutOfBoundsException
IllegalArgumentException
NumberFormatException
自定义异常类,只需要继承Exception或者继承Exception的子类就可以完成自定义异常类。
Java中提供的都是标准的异常类,有时候需要使用自定义的异常类。
@SuppressWarnings("serial") class MyException extends Exception{ //一个简单的自定义异常类 public MyException(String msg){ super(msg); } } public class Test{ public static void main(String[] args) throws Exception{ try{ throw new MyException("自己抛着玩的"); }catch(Exception e){ System.out.println(e); } } }
注:
在catch语句中可以抛出一个异常,这样做的目的是改变异常的类型。
如果开发了一个供其他程序员使用的子系统,那么,用于表示子系统故障的异常类型可能会产生多种解释。ServletException就是这样一个异常类型的例子。执行servlet的代码可能不想知道发生错误的细节原因,但希望明确知道servlet是否有问题。
下面给出捕获异常并再次将它抛出的基本方法。
try{ //access the database }catch(SQLException e){ throw new ServletException("database error" + e.getMessage()); }
不过还有一种更好的处理方法。可以从包装的异常,重新获得原异常
示例代码如下。
class MyException extends Exception{ //一个简单的自定义异常类 public MyException(){} public MyException(String msg){ super(msg); } } public class Test{ public static void main(String[] args) { try { test(); } catch (Throwable e) { // TODO: handle exception Throwable se = e.getCause(); //获得原异常 System.out.println(se); System.out.println(e.getMessage()); } } public static void test() throws Throwable{ try{ //access the database throw new MyException("抛着玩的"); }catch(MyException e){ // 对异常进行包装 Throwable es = new Exception("database error: " + e.getMessage()); es.initCause(e); throw es; } } }
建议使用这种包装技术,这样可以抛出高级异常,而不会丢失原始异常的细节。
今天关于异常的学习就到这了,在实际的开发中,肯定会遇到而各种各样的问题,因此需要考虑到所有情况,才能保证程序的健壮性。