第8章 Java异常处理

第8章 Java异常处理
主要内容
8.1 异常处理的基础知识
      即使是有经验的程序员,也难免出现编程错误。
  编程错误的分类
8.1 异常处理的基础知识
  运行时错误(runtime error)
      在程序运行时出现的一些非正常的现象被称为运行时错误,如除数为0、数组下标越界、文件不存在、内存不够用等等。 
分类:根据错误性质将运行时错误分为两类
致命性的错误
非致命性的异常
8.1 异常处理的基础知识
  异常处理的类层次
Java中预定义了很多异常类,每个异常类代表一种运行错误。
8.1 异常处理的基础知识
常用Exception类的子类:
8.1 异常处理的基础知识
未被捕获“异常”
“异常”对象是Java运行时对某些“异常”情况作出反应而产生的。如果不处理“异常”会有什么样的情况发生?
例8.1 被0除异常。
class TestException1{
      public static void main(String args[]){
            int d=0;
            int a=42/d;
      }
}
8.1 异常处理的基础知识
程序的输出结果如图所示:
8.1 异常处理的基础知识
class TestException2{
      static void subRoutine(){
    int d=0;
    int a=10/d;
      }
      public static void main(String args[]){
       TestException2.subRoutine();
      }
}
8.2 异常处理机制
      Java提供了异常处理机制,通过面向对象的方法来处理异常。
在程序运行的过程中,如果发生了异常,则该程序(或Java虚拟机)生成一个代表该异常类的对象(包含该异常的详细信息),并把交给运行时系统;
运行时系统从生成对象的代码开始,沿方法的调用栈逐层回溯查找,直到找到包含相应处理代码的方法,并把异常对象交给该方法,来处理这一异常。
8.2 异常处理机制
使用try-catch-finally语句捕获和处理异常
格式:try{
                  //产生异常的语句
             }catch(异常类1 变量){
                  //异常处理代码
             }catch(异常类2 变量){
                 //异常处理代码
             }[finally{
             }]
8.2 异常处理机制
try语句块
将程序中可能出现异常的代码放入try块中。
当try块中有语句引发异常时,系统将不再执行try块中未执行的语句,而执行匹配的catch块。
如果try块中没有语句引发异常,则程序执行完try块中的语句后不执行catch块中的语句,即跳过catch语句,继续执行后面的程序。
catch块
每个try语句后面必须伴随一个或多个catch语句,用于捕捉try语句块所产生的异常并作相应的处理。
8.2 异常处理机制
catch子句的目标是解决“异常”情况,并像没有出错一样继续运行。 
例如:try{  
                           int d=0;
                           int a=42/d;  
               }catch(ArithmeticException e){
                        System.out.println(e.toString());
             }
8.2 异常处理机制
例8.3 try-catch语句的执行顺序。
        int a = 0,b = 0,c = 0;       Random r=new Random();
        for (int i = 0; i < 5; i++){
            try{
                b = r.nextInt();       c = r.nextInt();
                a = 12345 / ( b / c );
            }catch(ArithmeticException e){
                a = 0;
                System.out.print("有被0除异常 "); 
            }
            System.out.println("a:" + a);
        }
8.2 异常处理机制
注意:一个try和它的catch语句组成了一个单元。catch子句的范围限制于try语句块中的语句。一个catch语句不能捕获另一个try声明所引发的异常(除非是嵌套的try语句情况)。被try保护的语句声明必须在一个大括号之内。try语句块不能单独使用。
8.2 异常处理机制
多个catch块 一个catch块只能处理一类异常,当try块中的语句组可能抛出多种异常时,就需要有多个catch块来分别处理各种异常。
try{
      int a=args.length;   System.out.println("a=" + a);
      int b=42/a;      int c[]={1};      c[42]=99;
}catch(ArithmeticException e){
      System.out.println("div by 0:" + e);
}catch(ArrayIndexOutOfBoundsException e){
      System.out.println("array index oob:" + e);
}
8.2 异常处理机制
一个异常对象能否被一个catch块接收主要看该异常对象与catch块中声明的异常类的匹配情况,当它们满足下面条件中的任一条时,异常对象将被接受:
异常对象是catch块中声明的异常类的实例;
异常对象是catch块中声明的异常类的子类的实例;
异常对象实现了catch块中声明的异常类的接口。
当使用多个catch块时,需注意catch子句排列顺序--先特殊到一般,也就是子类在父类前面。如果子类在父类后面,子类将永远不会到达。 
例8.5 catch语句块顺序的示例。
8.2 异常处理机制
try语句的嵌套 一个try语句可以在另一个try块内部----try语句的嵌套。
每次进入try语句,异常的前后关系都会被压入堆栈。如果一个内部的try语句不含特殊异常的catch处理程序,堆栈将弹出,下一个try语句的catch处理程序将检查是否与之匹配。
这个过程将继续直到一个catch语句匹配成功,或者是直到所有的嵌套try语句被检查耗尽。
如果没有catch语句匹配,Java的运行时系统将处理这个异常。 
8.2 异常处理机制
例8.6 运用嵌套try语句的示例。 
分析:
(1)当在没有命令行参数的情况下运行程序,外面的try块将产生一个被零除的异常。
(2)程序在有一个命令行参数条件下运行,内部的try块将产生一个被零除的异常。因为与内部的catch块不匹配,它将把该异常传给外部的catch块来处理。
(3)如果在具有两个命令行参数的条件下执行该程序,由内部try块产生一个数组下标越界异常,由内部的catch块处理。 
8.2 异常处理机制
finally语句块
某些情况下,不管异常是否发生,都需要处理某些语句,那么就将这些语句放到finally语句块中。finally语句所包含的代码在任何情况下都会被执行。
一个try语句至少有一个catch语句或finally语句与之匹配,但匹配的catch可以有多个,而finally语句只能有一个,并且finally语句并非必须有的。
例8.7 finally的用法示例。main()方法中的语句为:
String[] friends={"Tom","葫芦娃","孙悟空"};
8.2 异常处理机制
try{
 for(int i=0;i<5;i++){
  System.out.println(friends[i]);
 }
}catch(ArrayIndexOutOfBoundsException e){
 System.out.println("index err");
 return;
}finally{
 System.out.println("in finally block!");
}
System.out.println("this is the end");
8.2 异常处理机制
 抛出异常
                           Java运行时系统引发的异常
异常的抛出
                           根据需要人工创建并抛出
人工抛出异常
语法格式:throw  异常类对象;
例如:IOException e = new IOException();
           throw e;
8.2 异常处理机制
例8.7 throw语句的使用。
分析:
(1)main()调用方法demoproc()的过程中捕获并处理空指针异常。
(2)demoproc()方法也进行了相同的异常处理并且捕获该异常后将该异常抛出。
(3)创建异常对象:throw new NullPointerException(); 或   throw new NullPointerException(“demo”);
8.2 异常处理机制
 声明抛弃异常
如果一个方法中的代码在运行时可能生成某种异常,但是在本方法中不必要,或者不能确定如何处理此类异常时,则可以使用throws声明抛弃异常;
表明该方法中将不对此类异常进行处理,而由该方法的调用者负责处理;
即系统将在调用该方法的上层方法体内寻找合适的异常处理代码,而不再继续执行该方法的正常处理流程。
8.2 异常处理机制
声明抛弃异常的格式 类型  方法名([参数表]) throws 异常类型,…{   //方法体; } 例8.8 声明抛弃异常。 static void procedure()  {       System.out.println("inside procedure");       throw new IllegalAccessException("demo"); } public static void main(String args[])  {       procedure(); }
8.2 异常处理机制
static void procedure() throws IllegalAccessException{
          System.out.println("inside procedure");
          throw new IllegalAccessException("demo");
}
public static void main(String args[]){
      try{
               procedure();
      }catch(IllegalAccessException e){
               System.out.println("caught" + e);
      }
}
8.3 自定义异常类
      虽然Java的内置异常处理能够处理大多数常见错误,但用户仍需建立自己的异常类型来处理特殊情况。这时可以通过创建Exception的子类来定义自己的异常类。
格式:class 类名 extends Exception{
                     … …
            }
例8.10 自定义异常类。
分析:Exception类自己没有定义任何方法。但它继承了Throwable提供的一些方法。 例如,
public String getMessage();   public void printStackTrace(); 
8.5 异常处理应注意的问题
在Java中,对异常进行处理需要考虑以下因素:
如果异常事件是在运行时产生的,并且在JDK API中没有与该异常事件相对应的异常对象,则应创建用户自定义类型的异常对象。
如果可以预测异常对象的类型并且该异常是在程序运行时发生的,则建议应用JDK API中定义的系统异常类型,并且可以抛出这种类型的异常对象由JVM处理。
如果不能确定异常对象的类型以及异常发生的时机,则应该采用系统类型异常对象并由JVM处理。
8.5 异常处理应注意的问题
对应用程序设计失误导致的数组越界、非法变量等类型的异常,如果要全部捕获所有类型的异常对象,会增加系统开销,导致程序的运行效率降低,建议应用程序可以不对此类异常进行捕获,而交由JVM进行处理。
对于实现输入/输出处理、网络通讯和数据库访问功能的代码,必须进行异常对象的捕获和处理 。
8.6 断言
      从JDK1.4版本开始,Java语言引入了断言(assert)机制。目的:程序调试
8.6 断言
      如果没有断言机制,Java程序通常使用if-else或switch语句进行变量状态检查。缺点:
由于检查的数据类型不完全相同,这样的语句形式不会统一。
因为检查仅仅是应用在测试阶段,而if-else或switch语句在发布以后仍然将起作用,如果消除这些代码就意味着要注释或者删除这些代码,如果这些代码量很大就意味着工作很繁重并存在风险。
      使用断言的优点: 
Java程序员用统一的方式处理状态检查问题;
断言只需在发行的时候关闭该功能即可。
8.6 断言
断言的开启和关闭
在默认情况下断言是关闭的,因此在使用断言以前,需要先开启断言功能,方法: java –ea MyClass                           或者 java –enableassertions MyClass
关闭断言功能的方法: java –da MyClass                            或者 java –disableassertions MyClass
注意:断言检查通常在开发和测试时开启。为了提高性能,在软件发布后,断言检查通常是关闭的。 
8.6 断言
断言的使用
     Java中使用关键字assert标记断言,语法格式为:
assert  Expression1 
assert  Expression1:Expression2
8.6 断言
例8-12 断言的使用。
public class TestAssertion1{
      public static void main(String args[]){
            int x=10;
            System.out.println("Testing Assertion that x==100");
            assert x==100:"Our assertion failed!";
            System.out.println("Test passed!");
      }
}
8.6 断言
什么时候使用断言
通常来说,断言用于检查一些关键的值,并且这些值对整个程序,或者局部功能的完成有很大的影响。
断言表达式应该短小、易懂,如果需要评估复杂的表达式,应该使用函数计算。
使用断言的情况
检查控制流:在if-else和switch语句中,可以在不应该发生的控制支流上加上assert语句。如果这种情况发生了,assert能够检查出来。 
8.6 断言
在私有方法计算前,检查输入参数是否有效 对于一些private的方法,要求输入满足一些特定的条件,可以在方法开头使用assert进行参数检查;对于公共方法,通常不使用断言检查
在方法计算后,检查方法结果是否有效 
检查程序不变量 private boolean isBalance() {       …… } // assert isBalance():"balance is destoried";
上机实践
1.编写应用程序,从键盘输入10个学生的数学成绩,统计及格人数、不及格人数、平均分。要求输入的数学成绩在0~100之间(设计一个自定义异常类NumberRangeException,当输入的成绩不在0~100之间时,抛出该异常类对象,程序中捕获这个异常并作出相应的处理)。
2.用户输入密码,要求密码满足的条件是:长度大于6且包含数字、大写字母和小写字母,如果不满足条件,则抛出UnSafePasswordException自定义异常类对象。

你可能感兴趣的:(java,exception,String,Random,Class,subroutine)