zoukankan      html  css  js  c++  java
  • 异常机制及其处理

    一、异常

      异常就是一个表示阻止执行正常进行的错误或者情况。比如数组下标越界访问异常:ArrayIndexOutOfBoundsException;文件不存在异常:FileNotFoundException等等。

     

    抛出异常与异常处理的例子:除数为0异常。

    package demo;
    
    import java.util.Scanner;
    
    /**
     * Created by luts on 2015/11/28.
     */
    public class QuotientWithException {
        public static void main(String[] args){
            Scanner input = new Scanner(System.in);
    
            System.out.println("输入两个数:");
            int num1 = input.nextInt();
            int num2 = input.nextInt();
    
            try {
                if (num2 == 0)
                    throw new ArithmeticException("除数不能为0");
                System.out.println(num1 + " / " + num2 + " = " + (num1 / num2));
            }
            catch (ArithmeticException ex){
                System.out.println("除数不能为0");
            }
    
            System.out.println("除法运算结束");
        }
    }

    输出:

    输入两个数:
    3
    0
    除数不能为0
    除法运算结束
    输入两个数:
    6
    3
    6 / 3 = 2
    除法运算结束

    上述例子中,try语句是正常执行情况下的代码,如果出现异常则抛出异常。catch语句是对异常情况的处理。当程序中抛出一个异常的时候,程序的正常流程就被破坏了。throw语句用于抛出一个异常,类似于方法的调用,其调用的是catch块。catch块处理完异常后,不返回throw语句,而是执行catch块的下一条语句。

    一个try-throw-catch块的模板为

      try {
            Code to try;
            Throw an exception with a throw statement or from method if necessary;
            more code to try;
            }
        catch (type ex){
            Code to process the exception;
            }

    1.1. 异常的类型

      

    throwable类是所有异常类的根。所有异常类都直接或间接的继承自throwable类。可以通过Exception 或者Exception 的子类来创建自己的异常类。异常类大体分为三类:系统错误、异常和运行时异常。

      · 系统错误(system error):有java虚拟机抛出,用Error类表示。Error类描述的是内部系统错误,无法对错误进行处理。

      · 异常(exception):用Exception类表示,描述的是程序和外部环境所引起的错误,这些错误是能被程序捕获和处理的。

      · 运行时异常(runtime exception):用RuntimeException类表示,描述的是程序设计的错误。

    Error类的子类例子:

    可能引起异常的原因
    LinkageError 一个类对另一个类由某种依赖性,编译前者后,后者变得不兼容
    VirtualMachineError Java虚拟机中断或者没有继续运行所必需的资源可用

    Exception类的子类例子:

    可能引起异常的原因
    ClassNotFoundException 企图使用一个不存在的类。例如试图使用Java命令来运行一个不存的类或者程序要调用三个类文件,而只能找到两个,都会发生这种异常。
    IOException 同输入/输出相关的操作,例如,无效的输入、读文件时超出文件尾、打开一个不存在的文件等。

    RuntimeException类子类的例子:

    可能引起异常的原因
    ArithmetricException 一个整数除以0.  注意:浮点运算不抛出异常
    NullPointerException 企图通过一个null引用变量访问一个对象
    IndexOutOfBoundsException

    数组下标越界

    IllegalArgumentException 传递给方法的参数非法或者不合适

    通常情况下,免检异常都会反映出程序设计上不可恢复的逻辑错误。为了避免过多使用try-catch块,java语言不允许编写代码捕获或声明免检异常

    1.2. 异常的处理

      java的异常处理模型包括:声明一个异常、抛出一个异常和捕获一个异常

    1.2.1 声明异常

      声明异常:每个方法都必须声明它可能抛出的必检异常的类型。因为任何代码都可能在程序中发生系统错误或者运行时错误,所以不要求显示声明Error和RuntimeException(免检异常)。声明异常的语法:

    抛出一个异常:
    public void myMethod() throws IOException
    
    抛出多个异常:
    public void myMethod() throws IOException1,throws IOException2, ..., throws IOExceptionN

    注意:如果在父类中没有声明异常,那么在子类中就不能对其进行覆盖来声明异常。

    1.2.2. 抛出异常

      检测一个错误的程序可以创建一个正确的异常类型的实例并抛出它,这就称为抛出异常。

    例如程序调用方法时传递的参数不符合方法的签名,可以创建一个IllegalArgumentException的实例并抛出它:

    IllegalArgumentException ex = new IllegalArgumentException("Wrong Arguments");
    throw ex;
    
    
    或者使用下列语句:
    throw new IllegalArgumentException("Wrong Arguments");

    注意:声明异常使用关键字throws,抛出异常使用关键字throw。

    1.2.3. 捕获异常

    使用try-catch块来捕获异常:

    try{
        statements; //statemenrs that may throw exceptions
    }
    catch(Exception1 exVar1){
        handle for Exception1 ;
    }
    catch(Exception2 exVar2){
        handle for Exception2 ;
    }
    
    ...
    
    catch(ExceptionN exVarN){
        handle for ExceptionN ;
    }

      · 如果在try块中没出现异常,则跳过catch块语句。

       · 如果try块中的某条语句抛出了一个异常,java就会跳过try块中剩下的语句,然后开始查找处理这个异常的代码的过程。处理这个异常的代码称为异常处理器;可以从当前方法开始,沿着调用方法的调用链,按照异常的反向传播方向找到这个处理器。从第一个到最后一个逐一检查catch块,判断是catch块中的异常实例是否是该异常对象的类型。如果是,就将该异常对象赋值给所声明的变量,然后执行catch块中的代码。如果没有发现异常处理器,java会退出这个方法,把异常传递给调用方法的方法,继续同样的过程来查找处理器。如果在调用的方法链中都找不到处理器,程序就会终止并且在控制台上打印出错信息。

       需要注意的是:

        ·  一旦某个catch捕获到匹配的异常类型,将进入异常处理代码。一经处理结束,就意味着整个try-catch语句结束。其他的catch子句不再有匹配和捕获异常类型的机会。

        · 一个通用父类可以派生各种异常类。如果一个catch块可以捕获一个父类的异常对象,那么它就能捕获那个父类的所有子类的异常对象。

        · 子类的catch块必须出现在父类的catch块之前,否则会导致编译错误。正确的例子:

    try{
       ....
    }
    catch (RuntimeException ex){
       ....
    }
    catch (Exception ex){
      ....
    }

         · java强迫程序员必须处理必检异常。如果一个方法中声明了一个必检异常,就必须在try-catch块中调用它,或者在调用方法中声明要抛出的异常。

    假设方法P1调用方法P2,P2可能会抛出一个IOException异常,那么代码编写有两种方式:
    
    捕获异常:
    void p1(){
        try{
            p2();
        }
        catch (IOException ex){
        ......
        }
    }
    
    
    
    或者抛出异常:
    void p1() throws IOException {
        p2();    
    }

    一个完整的声明、抛出、捕获异常的例子:

     1 package demo;
     2 
     3 /*
     4  * 利用Circle类的setRadius方法操作如何声明、抛出和捕获异常
     5  * Created by luts on 2015/11/29.
     6  */
     7 public class CircleWithException {
     8 
     9     private double radius;  //圆的半径
    10 
    11     private static int numberOfObjects = 0;     //记录实例对象的个数
    12 
    13     //构建一个半径为1的圆
    14     public CircleWithException(){
    15         this(1.0);
    16     }
    17 
    18 
    19     //构建一个指定半径的圆
    20     public CircleWithException(double newRadius){
    21         setRadius(newRadius);
    22         numberOfObjects++;
    23     }
    24 
    25     //访问器
    26     public double getRadius(){
    27         return radius;
    28     }
    29 
    30     //设置器,声明异常、抛出异常
    31     public void setRadius(double newRadius) throws IllegalArgumentException{
    32         if (newRadius >= 0)
    33             radius = newRadius;
    34         else throw new IllegalArgumentException("Radius cannot be negative");
    35     }
    36 
    37     public static int getNumberOfObjects(){
    38         return numberOfObjects;
    39     }
    40 
    41     public double findArea(){
    42         return radius * radius * Math.PI;
    43     }
    44 }
     1 package demo;
     2 
     3 /**
     4  * Created by luts on 2015/11/29.
     5  */
     6 public class TestCircleWithException {
     7     public static void main(String[] args){
     8 
     9         //捕获异常
    10         try {
    11             CircleWithException c1 = new CircleWithException(5);
    12             CircleWithException c2 = new CircleWithException(-5);
    13             CircleWithException c3 = new CircleWithException(0);
    14         }
    15         catch (IllegalArgumentException ex){
    16             System.out.println(ex);
    17         }
    18 
    19         System.out.println("Number of objects created: " + CircleWithException.getNumberOfObjects());
    20     }
    21 }

    输出结果:

    java.lang.IllegalArgumentException: Radius cannot be negative
    Number of objects created: 1

    1.2.4. 从异常中获取信息

      异常对象包含关于异常信息的有用信息,这些信息可以通过java.lang.Throwable类中的实例方法获得:

      · printStackTrace()方法在控制台上输出栈跟踪信息。

      · getStackTrace()方法提高方案来访问由 printStackTrace()打印输出的栈跟踪信息。

    例如上述例子,将测试类修改如下:

     1 package demo;
     2 
     3 /**
     4  * Created by luts on 2015/11/29.
     5  */
     6 public class TestCircleWithException {
     7     public static void main(String[] args){
     8 
     9         //捕获异常
    10         try {
    11             CircleWithException c1 = new CircleWithException(5);
    12             CircleWithException c2 = new CircleWithException(-5);
    13             CircleWithException c3 = new CircleWithException(0);
    14         }
    15         catch (IllegalArgumentException ex){
    16             ex.printStackTrace();
    17             System.out.println("
    " + ex.getMessage());
    18             System.out.println("
    " + ex.toString());
    19 
    20 
    21             System.out.println("
    Trace Info Obtained from getStackTrace");
    22             StackTraceElement[] traceElements = ex.getStackTrace();
    23             for (int i = 0; i < traceElements.length; i++) {
    24                 System.out.print("method " + traceElements[i].getMethodName());
    25                 System.out.print("(" + traceElements[i].getClassName() + ":");
    26                 System.out.print( traceElements[i].getLineNumber() + ")");
    27             }
    28 
    29 
    30            // System.out.println(ex);  //打印一个有关异常信息的短信息ex.toStrin()
    31         }
    32 
    33         System.out.println("Number of objects created: " + CircleWithException.getNumberOfObjects());
    34     }
    35 }

    控制台输出:

     1 java.lang.IllegalArgumentException: Radius cannot be negative    <-------printStackTrac()
     2 
     3     at demo.CircleWithException.setRadius(CircleWithException.java:34)   <--------getMessage() 
     4 Radius cannot be negative
     5     at demo.CircleWithException.<init>(CircleWithException.java:21)
     6 
     7     at demo.TestCircleWithException.main(TestCircleWithException.java:12)  <--------toString()
     8 java.lang.IllegalArgumentException: Radius cannot be negative
     9     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    10 
    11     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)  <------using getStackTrace()
    12 Trace Info Obtained from getStackTrace                                 
    13     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    14 method setRadius(demo.CircleWithException:34)method <init>(demo.CircleWithException:21)method main(demo.TestCircleWithException:12)method invoke0(sun.reflect.NativeMethodAccessorImpl:-2)method invoke(sun.reflect.NativeMethodAccessorImpl:62)method invoke(sun.reflect.DelegatingMethodAccessorImpl:43)method invoke(java.lang.reflect.Method:483)method main(com.intellij.rt.execution.application.AppMain:144)Number of objects created: 1
    15     at java.lang.reflect.Method.invoke(Method.java:483)
    16     at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)

    1.2.5 finally子句

      finally子句:不管是否发生异常,都会执行的语句,其语法如下:

    try {
       .....
    }
    catch (TheException ex){
      .......
    }
    finally {
      fianlStatements;
    }

      任何情况下,finally块中的代码都会执行,不管try块中是否出现异常或是否捕获异常:

      · 如果try块中没有出现异常,执行finalStatements,然后执行try语句的下一条语句。

      · 如果try块中有一条语句会引起异常,并被catch块捕获,然后跳过try块中的其他语句,执行catch块和finally子句。执行try语句之后的下一条语句。

      · 如果try块中有一条语句引起异常,但是没有被任何catch块捕获,就会跳出try块中其他的语句,执行finally子句,并且将异常传递给该方法的调用者。

      即使在到达finally块之前有一个return语句,finally语句还是会执行。

    finally例子:

     1 public class TestException {  
     2     public static void main(String args[]) {  
     3         int i = 0;  
     4         String greetings[] = { " Hello world !", " Hello World !! ",  
     5                 " HELLO WORLD !!!" };  
     6         while (i < 4) {  
     7             try {  
     8                 // 特别注意循环控制变量i的设计,避免造成无限循环  
     9                 System.out.println(greetings[i++]);  
    10             } catch (ArrayIndexOutOfBoundsException e) {  
    11                 System.out.println("数组下标越界异常");  
    12             } finally {  
    13                 System.out.println("--------------------------");  
    14             }  
    15         }  
    16     }  
    17 }

    输出结果:

     Hello world !
    --------------------------
     Hello World !! 
    --------------------------
     HELLO WORLD !!!
    --------------------------
    数组下标越界异常
    --------------------------

    小结:

      ·  try 块:用于捕获异常。其后可接零个或多个catch块,如果没有catch块,则必须跟一个finally块。

      ·  catch 块:用于处理try捕获到的异常。
      ·  finally 块:无论是否捕获或处理异常,finally块里的语句都会被执行。当在try块或catch块中遇到return语句时,finally语句块将在方法返回之前被执行。在以下4种特殊情况下,finally块不会被执行:
          1)在finally语句块中发生了异常。
          2)在前面的代码中用了System.exit()退出程序。
          3)程序所在的线程死亡。
          4)关闭CPU。

     try-catch-finally 规则(异常处理语句的语法规则):

      1)  必须在 try 之后添加 catch 或 finally 块。try 块后可同时接 catch 和 finally 块,但至少有一个块。

      2) 必须遵循块顺序:若代码同时使用 catch 和 finally 块,则必须将 catch 块放在 try 块之后。
      3) catch 块与相应的异常类的类型相关。
      4) 一个 try 块可能有多个 catch 块。若如此,则执行第一个匹配块。即Java虚拟机会把实际抛出的异常对象依次和各个catch代码块声明的异常类型匹配,如果异常对象为

      某个异常类型或其子类的实例,就执行这个catch代码块,不会再执行其他的 catch代码块
      5) 可嵌套 try-catch-finally 结构。
      6) 在 try-catch-finally 结构中,可重新抛出异常。
      7) 除了下列情况,总将执行 finally 做为结束:JVM 过早终止(调用 System.exit(int));在 finally 块中抛出一个未处理的异常;计算机断电、失火、或遭遇病毒攻击。

     

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

      1) 当try没有捕获到异常时:try语句块中的语句逐一被执行,程序将跳过catch语句块,执行finally语句块和其后的语句;

      2)当try捕获到异常,catch语句块里没有处理此异常的情况:当try语句块里的某条语句出现异常时,而没有处理此异常的catch语句块时,此异常将会抛给JVM处理,finally语句块里的语句还是会被执行,但finally语句块后的语句不会被执行;

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

     1.2.6 重新抛出异常

      如果异常处理器没有处理某种异常,或者处理器只是希望它的调用者注意到该异常,java就允许异常处理器重新抛出异常。语法如下:

    try{
       .....
    }
    catch(TheException ex){
      perform operations before exits;
      throw ex;  //重新抛出异常,以便调用者的其他处理器获得处理异常ex的机会。
    }

    异常链

     1 package demo;
     2 
     3 public class TestException {  
     4     public static void main(String args[]) {  
     5         try {
     6             method1();    //捕获method1中的异常
     7         }
     8         catch (Exception ex){
     9             ex.printStackTrace();  //打印输出异常,首先显示method1 中抛出的异常,然后显示method2中抛出的异常。
    10         }
    11     }  
    12     
    13     public static void method1() throws Exception{
    14         try{
    15             method2();   //捕获method2中的异常
    16         }
    17         catch(Exception ex){
    18             throw new Exception("New info from method1", ex);  //抛出一个新的异常
    19         }
    20     }
    21     
    22     public static void method2() throws Exception{
    23         throw new Exception("New info from metho2");  //抛出一个异常,该异常被method1 的catch块捕获
    24     }
    25 }

    输出:

    1 java.lang.Exception: New info from method1
    2     at demo.TestException.method1(TestException.java:18)
    3     at demo.TestException.main(TestException.java:6)
    4 Caused by: java.lang.Exception: New info from metho2
    5     at demo.TestException.method2(TestException.java:23)
    6     at demo.TestException.method1(TestException.java:15)
    7     ... 1 more

    关于异常处理知识的深入知识,可以参阅:[深入理解java异常处理机制]

  • 相关阅读:
    破解 inode 校园网多网卡限制方法
    更改 eclipse的 workplace路径
    VMware Network Adapter VMnet1和VMnet8 未识别的网络的解决方法
    eclipse更改xml文件,txt文件,property文件等文件编辑器的字体设置
    Lua中数组全排序
    Lua尾调用
    C++ 调用Lua简单例子
    linux生成core dump
    vc获取系统日期
    C++培训第一天
  • 原文地址:https://www.cnblogs.com/luts/p/5003595.html
Copyright © 2011-2022 走看看