Java异常处理

来源:互联网 发布:淘宝买彩票在哪里 编辑:程序博客网 时间:2024/06/07 18:08

Java异常处理

Java 的基本原理就是“形式错误的代码不会运行”。(编译错误<=>”形式错误的代码”)
捕获错误最理想的是在编译期间,最好在试图运行程序以前。然而,并非所有错误都能在编译期间侦测到。有些问题必须在运行期间解决,让错误的缔结者通过一些手续向接收者传递一些适当的信息,使其知道该如何正确地处理遇到的问题。
摘自 thinking in java 4th

异常基本介绍

“违例”(Exception)表示”例外”情况。当一个Exception产生时,要么解决这个问题(catch),要么将与错误有关的信息发送到更高一级的处理(throw),在更恰当的地方解决。

违例的构造

和 Java 的其他任何对象一样,需要用 new 在内存堆里创建违例,并需调用一个构建器。在所有标准违例中,存在着两个构建器:第一个是默认构建器,第二个则需使用一个字串自变量,使我们能在违例里置入相关信息。我们可根据需要掷出任何类型的“可掷”对象。通常情况下,我们要为每种不同类型的错误“掷”出一类不同的违例。我们的思路是在违例对象以及挑选的违例对象类型中保存信息。(通常,唯一的信息是违例对象的类型,而违例对象中保存的没什么意义)。

违例的捕获

“警戒区”代表一个特殊的代码区域,有可能产生违例,并在后面跟随用于控制那些违例的代码。

try 块

    try{        // 可能产生Exception的代码    }

违例控制(catch)

生成的Exception 必须在某处中止(违例控制器 => catch)。针对想捕获的每种违例类型,都必须有一个相应的违例控制器。违例控制器紧接在 try 块后面,且用 catch(捕获)关键字标记。

try{        // 可能产生异常的代码}catch(type1 id1){// 处理类型为type1的Exception}catch(type2 id2){// 处理类型为type2的}

若“掷”出一个违例,违例控制机制就会搜寻自变量与违例类型相符的第一个控制器。随后,它会进入那个 catch 从句,并认为违例已得到控制(一旦 catch 从句结束,对控制器的搜索也会停止)。只有相符的 catch 从句才会得到执行。

注意:一个衍生异常类对象可与基础异常类的一个控制器相配。

//异常匹配class Annoyance extends Exception {} class Sneeze extends Annoyance {} public class Human {   public static void main(String[] args) {     try {       throw new Sneeze();     } catch(Annoyance a) {       System.out.println("Caught Annoyance");     }  catch(Sneeze s) {     System.out.println("Caught Sneeze");     }  } }//---------------------------------------------------------------------------------//output://Caught Annoyance

在违例控制理论中,共存在两种基本方法。

  • 在“中断”方法中,我们假定错误非常关键,没有办法返回违例发生的地方。无论谁只要“掷”出一个违例,就表明没有办法补救错误,而且也不希望再回来。
  • 另一种方法叫作“恢复”。它意味着违例控制器有责任来纠正当前的状况,然后取得出错的方法,假定下一次会成功执行。若使用恢复,意味着在违例得到控制以后仍然想继续执行。

违例规范

方法声明时在自变量列表的后面,加上“违例规范”。

void f() throws Exception1,Exception2 { }//throws 后面跟随全部潜在的违例类型。

捕获所有违例

catch(Exception e){ }

Exception 类是常见违例的基础类,所以实际使用时,应该将其放在控制器列表的末尾,防止其他控制器失效。
捕获到Exception 后可以调用:

  • 获得详细消息 getMessage()
  • 获得调用堆栈路径 printStackTrace()

重新抛出违例

  • 重新抛出捕获的异常
catch(Exception e){  throw e; //重新抛出捕获的句柄}

若只是简单地重新掷出当前违例,在调用printStackTrace()方法时,会输出违例真正的产生位置,而不是重新掷出的位置。若想更新堆栈信息(将当前的堆栈信息填充到原来的违例对象里),可以调用fillInStackTrace()方法.

catch(Exception e){    throw e.fillInStackTrace();} 
  • 从捕获的违例中重新抛出一个不同的违例

这样做,会得到与使用fillInStackTrace()类似的效果:与违例起源地有关的信息会全部丢失,我们留下的是与新的 throw 有关的信息。

标准Java 违例

Java 异常

在 Java 中,所有的异常都有一个共同的祖先 Throwable(可抛出)。Throwable 指定代码中可用异常传播机制通过 Java 应用程序传输的任何问题的共性。Throwable: 有两个重要的子类:Exception(异常)和 Error(错误),二者都是 Java 异常处理的重要子类,各自都包含大量子类。

  • Error(错误):是程序无法处理的错误,表示运行应用程序中较严重问题。
    大多数错误与代码编写者执行的操作无关,而表示代码运行时 JVM(Java 虚拟机)出现的问题。例如,Java虚拟机运行错误(Virtual MachineError),当 JVM 不再有继续执行操作所需的内存资源时,将出现 OutOfMemoryError。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。。 这些错误表示故障发生于虚拟机自身、或者发生在虚拟机试图执行应用时,如Java虚拟机运行错误(Virtual MachineError)、类定义错误(NoClassDefFoundError)等。这些错误是不可查的,因为它们在应用程序的控制和处理能力之 外,而且绝大多数是程序运行时不允许出现的状况。对于设计合理的应用程序来说,即使确实发生了错误,本质上也不应该试图去处理它所引起的异常状况。在 Java中,错误通过Error的子类描述。

  • Exception(异常):是程序本身可以处理的异常。
    Exception 类有一个重要的子类 RuntimeException。RuntimeException 类及其子类表示“JVM 常用操作”引发的错误。例如,若试图使用空值对象引用、除数为零或数组越界,则分别引发运行时异常(NullPointerException、 ArithmeticException)和 ArrayIndexOutOfBoundException。

注意:异常和错误的区别:异常能被程序本身可以处理,错误是无法处理。

Exception

Exception 这种异常分两大类运行时异常和非运行时异常(编译异常)。

  • 运行时异常:
    都是RuntimeException类及其子类异常,如NullPointerException(下标越界异常)等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。运行时异常的特点是Java编译器不会检查它,也就是说,当程序中可能出现这类异常,即使没有用try-catch语句捕获它,也没有用throws子句声明抛出它,也会编译通过。

  • 非运行时异常 (编译异常):
    是 RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。当程序中可能出现这类异常,要么用try-catch语句捕获它,要么用throws子句声明抛出它,否则编译不会通过。

自定义违例

自定义异常Example

class MyException extends Exception {   public MyException() {}   public MyException(String msg) {     super(msg);   } } class SimpleException extends Exception { }

继承中的违例限制

  • 基类方法没有抛出异常,衍生类覆盖的方法也不能抛出异常。
  • 若基类方法抛出异常,衍生类方法可不抛出异常,或抛出同样的异常(基类抛出的异常及其子类)
  • 衍生类的构造器可以增加抛出的异常种类,但必须处理基类构造器的异常。注意:衍生类上溯造型到基础类型时,编译器就会强迫我们捕获针对基础类的违例。

“违例规范接口”可能在继承和覆盖时变得更“窄”,但它不会变得更“宽”——这与继承时的类接口规则(衍生类方法是对基类接口的扩展)是正好相反的。

摘自 thinking in java

finally

try-catch语句还可以包括第三部分,就是finally子句。它表示无论是否出现异常,都应当执行的内容。try-catch-finally语句的一般语法形式为:

try {          // 可能会发生异常的程序代码      } catch (Type1 id1) {          // 捕获并处理try抛出的异常类型Type1      } catch (Type2 id2) {          // 捕获并处理try抛出的异常类型Type2      } finally {          // 无论是否发生异常,都将执行的语句块      }  

小结:

  • try 块:用于捕获异常。其后可接零个或多个catch块,如果没有catch块,则必须跟一个finally块。
  • catch 块:用于处理try捕获到的异常。
  • finally 块:无论是否捕获或处理异常,finally块里的语句都会被执行。当在try块或catch块中遇到return语句时,finally语句块将在方法返回之前被执行。
    在以下3种特殊情况下,finally块不会被执行完:
    • JVM 过早终止(调用 System.exit(int));
    • 在 finally 块中抛出一个未处理的异常;
    • 计算机断电、失火、或遭遇病毒攻击。

try、catch、finally语句块的执行顺序:

  • 当try没有捕获到异常时:
    try语句块中的语句逐一被执行,程序将跳过catch语句块,执行finally语句块和其后的语句;
  • 当try捕获到异常,catch语句块里没有处理此异常的情况:
    当try语句块里的某条语句出现异常时,而没有处理此异常的catch语句块时,此异常将会抛给JVM处理,finally语句块里的语句还是会被执行,但finally语句块后的语句不会被执行;
  • 当try捕获到异常,catch语句块里有处理此异常的情况:
    在try语句块中是按照顺序来执行的,当执行到某一条语句出现异常时,程序将跳到 catch语句块,并与catch语句块逐一匹配,找到与之对应的处理程序,其他的catch语句块将不会被执行,而try语句块中,出现异常之后的语句 也不会被执行,catch语句块执行完后,执行finally语句块里的语句,最后执行finally语句块后的语句;

finaly 缺点:

try -finally 中的丢失:

  • 返回值的覆盖:
public class testfinally {    private String test(){        try{            System.out.println("try starts...");            return "success";        }        finally{            System.out.println("finally starts...");            return "false";        }    }    public static void main(String[] argvs){        String result = new testfinally().test();        System.out.println("result is: "+result);    }}//----------------------------------------------------------------------------//output://try starts...//finally starts...//result is: false
  • 返回值的副本操作:
public class testfinally {    private String test(){        String flag = "";        try{            System.out.println("try starts...");            flag = "success";            return flag;        }        finally{            System.out.println("finally starts...");            flag = flag + "-> return after finally";            System.out.println("flag is: "+flag);        }    }    public static void main(String[] argvs){        String result = new testfinally().test();        System.out.println("result is: "+result);    }}//------------------------------------------------------------------------------------------------//output://try starts...//finally starts...//flag is: success-> return after finally//result is: success

说明:

public int inc(){    int x;    try {        x = 1;        return x;    } catch (Exception e) {        x =2;        return x;    } finally{        x = 3;        // System.out.println("x:" + x);        // return x;    }}

这是《深入理解Java虚拟机-JVM高级特性与最佳实践》第二版书中的例子(P187~P188)。出现这种情况的原因是:在没有出线异常的情况下,先 执行了x=1;然后执行return x;时,首先是将x的一个副本保存在本地变量表中,执行return之前必须执行finally中的操作:x=3;将x的值设置为了3,但是return 时是将本地变量表中保存的x的那个副本拿出来放到栈顶返回。故没出异常时,返回值为1;出Exception异常或其子类异常时,返回值是2;如果出现非 Exception异常,则执行完x=3之后,抛出异常,没有返回值。

catch -finally 中的丢失:

  • 抛出异常覆盖:
public class testfinally {    private String test() throws Exception {        String flag = "";        try{            System.out.println("try starts...");            throw new Exception();        } catch (Exception e) {            System.out.println("catch starts...");            throw e;        }        finally{            System.out.println("finally starts...");            flag = "-> return in finally";            return flag;        }    }    public static void main(String[] argvs){        try {            String result = new testfinally().test();            System.out.println("result is: "+result);        }catch(Exception e){            e.printStackTrace();        }    }}//--------------------------------------------------------------------------------------------------//output://try starts...//catch starts...//finally starts...//result is: -> return in finally
public class testfinally {    private String test() throws Exception {        String flag = "";        try{            System.out.println("try starts...");            throw new Exception("from try");        } catch (Exception e) {            System.out.println("catch starts...");            throw e;        }        finally{            System.out.println("finally starts...");            throw new Exception("from finally");        }    }    public static void main(String[] argvs){        try {            String result = new testfinally().test();            System.out.println("result is: "+result);        }catch(Exception e){            e.printStackTrace();        }    }}//---------------------------------------------------------------------------//output://try starts...//catch starts...//finally starts...//java.lang.Exception: from finally//at mytest1.testfinally.test(testfinally.java:16)//at mytest1.testfinally.main(testfinally.java:22)
  • 返回值的副本操作
public class FinallyTest {      public static int method(){          int a=1;          try{              throw new Exception();          }catch(Exception e){              System.out.println("catch:a="+a);              return a;          }finally{              a=2;              System.out.println("finally:a="+a);          }      }      public static void main(String[] args) {          // TODO Auto-generated method stub          System.out.println(method());      }  }  //---------------------------------------------------------------------------------------------//output://catch:a=1//finally:a=2//1
原创粉丝点击