javaSE 基础笔记之异常和断言
第八章 异常和断言
学习目标:
² 掌握异常的定义
² 掌握 try、catch 和 finally 语句
² 掌握 throw 和 throws 语句
² 理解异常的分类
² 掌握自定义异常
² 掌握断言的使用
² 运行时屏蔽断言的使用
一:异常的定义
1:异常基础知识
什么是异常?在 Java 编程语言中,异常类定义程序中可能遇到的轻微的错误条件。可
以写代码来处理异常并继续程序执行,而不是让程序中断。
在程序执行中,任何中断正常程序流程的异常条件就是错误或异常。例如,发生下列情
况时,会出现异常:
- 想打开的文件不存在
- 网络连接中断
- 受控操作数超出预定范围
- 非常感兴趣的正在装载的类文件丢失
在Java编程语言中,错误类定义被认为是不能恢复的严重错误条件。在大多数情况下,
当遇到这样的错误时,建议让程序中断。
Java 编程语言实现异常处理来帮助建立弹性代码。在程序中发生错误时,发现错误的
方法能抛出一个异常到其调用程序,发出已经发生问题的信号。然后,调用方法捕获抛出的
异常,在可能时,再恢复回来。这个方案给程序员一个写处理程序的选择,来处理异常。
通过浏览API,可以决定方法抛出的是什么样的异常。
2:异常实例
考虑一下HelloWorld.java 程序版本的简单扩展,它通过信息来循环:
1 public class HelloWorld { 2 3 public static void main (String args[]) { 4 int i = 0; 5 String greetings [] = { 6 "Hello world!", 7 "No, I mean it!", 8 "HELLO WORLD!!" 9 }; 10 while (i < 4) { 11 System.out.println (greetings[i]); 12 i++; 13 } 14 } 15 } 16 17
正常情况下,当异常被抛出时, 在其循环被执行四次之后,程序终止,并带有错误信息,
就象前面所示的程序那样。
1. c:> java HelloWorld
2. Hello world!
3. No, I mean it!
4. HELLO WORLD!!
5. java.lang.ArrayIndexOutOfBoundsException: 3
6. at HelloWorld.main(HelloWorld.java:12)
异常处理允许程序捕获异常,处理它们,然后继续程序执行。它是分层把关,因此,错
误情况不会介入到程序的正常流程中。特殊情况发生时,在与正常执行的代码分离的代码块
中被处理。这就产生了更易识别和管理的代码。
二:异常的处理
Java 提供了一种异常处理模型,它使您能检查异常并进行相应的处理。它实现的是异
常处理的抓抛模型。使用此模型,您只需要注意有必要加以处理的异常情况。Java 提供的
这种异常处理模型,代替了用返回值或参数机制从方法返回异常码的手段。
异常处理的抓抛方法有两大优点:
(1) :异常情况能仅在有必要之处加以处理,而不在其发生处和需要进行处理处之间的
每一级上均进行处理;
(2) :能够编写统一的、可重用的异常处理代码。
应该区别对待程序中的正常控制流和异常处理流。当然,异常处理流也是程序中的控制
流。当异常发生时,抛出一个异常。异常伴随调用链,直到它们被捕获或程序退出为止。
下面是Java语言中的异常处理块的模型:
try{
//放置可能出现异常的代码
}catch(Exception1 el){
//如果try 块抛出异常对象的类型为Exceptionl,那么就在这里进行处理
}catch(Exception2 e2){
//如果try 块抛出异常对象的类型为Exception2,那么就在这里进行处理
}catch(ExceptionN eN){
//如果try 块抛出异常对象的类型为 ExceptionN,那么就在这里进行处理
}finally{
//不管是否有异常发生,始终执行这个代码块
}
在未提供适当异常处理机制的程序中,无论何时发生异常,程序均会异常中断,而之前
分配的所有资源则保持其状态不变。这会导致资源遗漏。要避免这一情况,在适当的异常处
理机制中,我们可以将以前由系统分配的所有资源返还给系统。所以,当异常可能发生时,
要牢记必须对每一异常分别进行处理。
例如我们处理文件I/O,在打开文件时发生 IOException,程序异常中断而没有机会关闭
该文件,这可能会毁坏文件而且分配给该文件的操作系统资源可能未返还给系统。
1:try块
try 块由一组可执行语句组成,在执行它们时可能会抛出异常。try 块后可随一个或多
个catch块,用来捕获在try中抛出的异常。另一方面,try 不可以跟随在 catch块之后。
也就是说:在try语句块中包含可能会产生异常的语句
int demo=0;
try{
System.out.println(20/demo);
}
语句 System.out.println(20/demo);
会抛出一个异常, 原因是试图用0 去除一个数。程序会被成功编译,但当运行该程序时,
程序将会发生异常而中断,异常可在 catch块中被捕获,try 块可以嵌套:
try{
statement 1;
statement 2;
try{
statement 1;
statement 2;
}catch(Exception e){
//异常处理
}
}catch(Exceptione){
//异常处理
}
try 块嵌套时,首先执行内部的 try 块,该块中引发的任何异常在随后的 catch 中被捕
获;如果未发现与该内部块匹配的catch块,则检查外部 try 块的 catch 块;如果发现匹配
的catch块,那么在该块中处理这一异常,否则Java运行时环境(JRE)处理这一异常。
2:catch块
catch 块,从其名称就可以看出,是用来捕获并处理 try中抛出的异常的代码块。没有
try 块,catch 块不能单独存在,我们可有多个 catch 块,以捕获不同类型的异常。下面是
catch块的语法
try{
}catch(异常类型 e){
}
这里,e是异常类型类的对象,利用这一对象,我们可以输出这一异常的详细信息
下面是catch/try块的一个简单示例:
1 class TryClass{ 2 3 public static void main(String args[]) { 4 5 int demO=0; 6 7 try{ 8 9 System.out.println(20/demO); 10 11 }catch(ArithmeticException a){ 12 13 System.out.println(“Can not divided by zero”); 14 15 } 16 17 } 18 19 }
上述程序的输出结果是:Can not devided by zero
注意:当多个catch 块存在的时候,从上往下 catch异常的范围应该从小到大,因为 catch
块的运行机制是找到一个匹配的就进行处理了,如果把范围大的放在前面,那么后面的代码
就没有机会运行了,这会是一个编译异常,示例如下:
比如下面这个是正确的:
1 public class Test { 2 3 public static void main(String[] args) { 4 5 try{ 6 7 int i = 5/0; 8 9 }catch(ArithmeticException e){ 10 11 e.printStackTrace(); 12 13 }catch(Exception err){ 14 15 err.printStackTrace(); 16 17 } 18 19 } 20 21 }
而下面这个就是错误的了,编译都发生了错误:
1 public class Test { 2 3 public static void main(String[] args) { 4 5 try{ 6 7 int i = 5/0; 8 9 }catch(Exception err){ 10 11 err.printStackTrace(); 12 13 }catch(ArithmeticException e){ 14 15 e.printStackTrace(); 16 17 } 18 19 20 21 } 22 23 }
3:finally块
finally 块表示:无论是否出现异常,都会运行的块
通常在finally块中可以编写资源返还给系统的语句,通常,这些语句包括但不限于:
(1) :释放动态分配的内存块:
(2) :关闭文件;
(3) :关闭数据库结果集;
(4) :关闭与数据库建立的连接;
它紧跟着最后一个块,是可选的,不论是否抛出异常,finally 块总会被执行。
finally块的语法如下:
try{
}catch(异常类型1 e){
}catch(异常类型2 e){
}finally{
}
下面的程序显示的是finally块的使用
1 public class Test { 2 3 static String name; 4 5 static int n01, n02; 6 7 public static void main(String args[]) { 8 9 try { 10 11 name = "Aptech Limited"; 12 13 n01 = Integer.parseInt(args[0]); 14 15 n02 = Integer.parseInt(args[1]); 16 17 System.out.println(name); 18 19 System.out.println("Division is" + n01 / n02); 20 21 } catch(ArithmeticException i) { 22 23 System.out.println("Can not be divided by zero!"); 24 25 } finally { 26 27 name = null; 28 29 System.out.println("finally executed"); 30 31 } 32 33 } 34 35 } 36 37
您从下面的命令行执行此程序:
Java Test 20 0
将会得到下面的输出:
Aptech Limited
Can not be divided by zero!
finally executed
现在从下面的命令行执行此程序:
Java Test 20 4
则会得到下面这样的输出:
Aptech Limited
Division is 5
finally executed
说明:当您用不同的命令行参数执行此程序时,均会看见“finally executed”的输出
这意味着,无论try块是否抛出异常,都会执行finally块。
4:try、catch、finally块的关系
(1) :try块不能单独存在,后面必须跟 catch块或者 finally块
(2) :三者之间的组合为:try—catch 、try—catch—finally 、 try—finally 这几种是合法的
(3) :一个 try块可以有多个catch块,从上到下多个 catch块的范围为从小到大
5:throw语句
throw 语句用来从代码中主动抛出异常。throw 的操作数是任一种异常类对象。下面是
throw关键字的一个示例:
try {
int i = 5/0;
} catch (ArithmeticException i) {
throw new Exception("Can not be divided by zero!");
}
6:throws语句
throws用来在方法定义时声明异常。
Java 中对异常的处理有两种方法,一个就是try-catch,然后自己处理;一个就是不做处
理,向外throws,由别人去处理。
Java 语言要求在方法定义中列出该方法抛出的异常:
1 public Class Example 2 3 { 4 5 public static void exceptionExample() throws ExampleException,LookupException 6 7 { 8 9 } 10 11 }
在上面的示例中,exceptionExample()声明包括 throws 关键字,其后列出了此方法可能
抛出的异常列表。在此案例中列出的是ExampleException和 LookupException。
比如前面那个例子写完整如下:
1 public class Test { 2 3 public static void main(String args[])throws Exception { 4 5 try { 6 7 int i = 5/0; 8 9 } catch (ArithmeticException i) { 10 11 throw new Exception("Can not be divided by zero!"); 12 13 } 14 15 } 16 17 }
7:调用栈机制
如果方法中的一个语句抛出一个没有在相应的 try/catch块中处理的异常, 那么这个异
常就被抛出到调用方法中。如果异常也没有在调用方法中被处理,它就被抛出到该方法的调
用程序。 这个过程要一直延续到异常被处理。如果异常到这时还没被处理, 它便回到 main(),
而且,即使main()不处理它,那么,该异常就异常地中断程序。
考虑这样一种情况,在该情况中 main()方法调用另一个方法(比如,first()) ,然后
它调用另一个(比如,second()) 。如果在 second()中发生异常,那么必须做一个检查来看
看该异常是否有一个 catch;如果没有,那么对调用栈(first())中的下一个方法进行检
查,然后检查下一个(main()) 。如果这个异常在该调用栈上没有被最后一个方法处理,那
么就会发生一个运行时错误,程序终止执行。
8:处理或声明规则
为了写出健壮的代码,Java 编程语言要求,当一个方法在栈(即,它已经被调用)上
发生 Exception(它与 Error 或 RuntimeException 不同)时,那么,该方法必须决定如果
出现问题该采取什么措施。
程序员可以做满足该要求的两件事:
第一, 通过将 try{}catch() {}块纳入其代码中,在这里捕获给被命名为属于某
个父类的异常,并调用方法处理它。即使 catch块是空的,这也算是处理情况。
第二, 让被调用的方法表示它将不处理异常,而且该异常将被抛回到它所遇到的调用
方法中。按如下所示通过用 throws 子句标记的该调用方法的声明来实现的:
public void troublesome() throws IOException
关键字 throws 之后是所有异常的列表,方法可以抛回到它的调用程序中。尽管这里只
显示了一个异常,如果有成倍的可能的异常可以通过该方法被抛出,那么,可以使用逗号分
开的列表。
是选择处理还是选择声明一个异常取决于是否给你自己或你的调用程序一个更合适的
候选的办法来处理异常。
三:异常的分类
1:异常的分类
在Java编程语言中,异常有两种分类。java.lang.Throwable类充当所有对象的父类,
可以使用异常处理机制将这些对象抛出并捕获。在Throwable类中定义方法来检索与异常相
关的错误信息, 并打印显示异常发生的栈跟踪信息。 它有 Error 和 Exception两个基本子类。
错误(Error) :JVM系统内部错误、资源耗尽等严重情况;
异常(Exception违例) :其它因编程错误或偶然的外在因素导致的一般性问题,例如:
对负数开平方根、空指针访问、试图读取不存在的文件、网络连接中断等。
当发生Error 时,程序员根本无能为力,只能让程序终止。比如说内存溢出,不可能指
望程序能处理这样的情况。而对于Exception,而有补救或控制的可能,程序员也可以预先
防范,本章主要讨论Exception的处理。
为有效地描述异常状况、传递有关的异常信息,JDK中针对各种普遍性的异常情况定义
了多种异常类型。其层次关系如下图所示:

其中,RuntimeException(运行时异常)是指因设计或实现方式不当导致的问题。也可
以说,是程序员的原因导致的,本来可以避免发生的情况。比如,如果事先检查数组元素下
标保证其不超出数组长度,那么,ArrayIndexOutOfBoundsException 异常从不会抛出;再
如,先检查并确保一个引用类型变量值不为 null,然后在令其访问所需的属性和方法,那
么,NullPointerException 也就从不会产生。
例题中的异常即属于 RuntimeException,出错的原因是数组 friends 中只含有三个元
素,当 for 循环执行到第四次时,试图访问根本不存在的第四个数组元素 friends[3],因
此出错。
包括IOException在内的其它违例,则可以认为是描述运行时遇到的困难,它通常由环
境而并非程序员的原因引起,可以进行处理。例如:文件未找到或无效 URL异常都经常容易
出现。
读者可能的疑问是:既然运行错误经常发生,是不是所有的 Java 程序也都应采取这种
异常处理措施?答案是否定的,Java 程序异常处理的原则为:
(1)对于Error和RuntimeException,可以在程序中进行捕获和处理,但不是必须的;
(2)对于IOException 及其它异常,必须在程序中进行捕获和处理。
ArrayIndexOutOfBoundsException 属于 RuntimeException,一个正确设计和实现的程
序不会出现这种异常, 因此可根据实际情况选择是否进行捕获和处理。现实生活中也采取了
类似的原则:比如火灾(一般属人为因素导致的) ,由于会导致严重后果,因此可以在生产
车间配备灭火器、准备好灭火措施,处理可能发生的火灾。但是,一般家庭不会准备发电机
去应付可能出现的停电事故。
程序运行时,try语句块中的语句产生异常对象后,系统立即将之与后跟的 catch语句
块依次进行匹配性检查,只要异常对象是catch 块中指定的类型或其子类类型,均认为匹配
成功,一旦匹配成功则停止匹配检查、开始执行该 catch块。
如果程序运行时, 某个方法中的一个语句产生了一个没有在相应的 try/catch 块中处理
的异常(肯定是 Error或RuntimeException) ,那么这个异常就被抛出到调用方法中。如果
异常也没有在调用方法中被处理,它就被抛出到该方法的调用方法,直到异常被处理。如果
异常对象一直被传递到main()方法仍为得到捕获处理,则程序将异常终止。
2:预定义异常
Java 编程语言中预先定义好的异常叫预定义异常,下面是可能遇到的更具共同性的异
常中的几种:
- ArithmeticException—整数被 0 除,运算得出的结果。
如:int i =12 / 0;
- NullPointerException—当对象没被实例化时,访问对象的属性或方法的尝试。
如:Date d= null;
System.out.println(d.toString());
- NegativeArraySizeException—创建带负维数大小的数组的尝试。
- ArrayIndexOutOfBoundsException—访问超过数组大小范围的一个元素的尝试。
- SecurityException—典型地被抛出到浏览器中,SecurityManager 类将抛出
applets 的一个异常,该异常企图做下述工作(除非明显地得到允许) :
访问一个本地文件。
打开主机的一个socket,这个主机与服务于 applet的主机不是同一个。
在运行时环境中执行另一个程序。
四:自定义异常
Java 语言允许用户在需要时创建自己的异常类型,用于表述 JDK 中未涉及到的其它异
常状况,这些类型也必须继承Throwable类或其子类。用户自定义异常类通常属Exception
范畴,依据命名惯例,应以Exception结尾。用户自定义异常未被加入 JRE的控制逻辑中,
因此永远不会自动抛出,只能由人工创建并抛出。
看一个用户自定义异常的例子:
程序:Test.java
1 class MyException extends Exception { 2 3 private int idnumber; 4 5 public MyException(String message, int id) { 6 7 super(message); 8 9 this.idnumber = id; 10 11 } 12 13 public int getId() { 14 15 return idnumber; 16 17 } 18 19 }
1 public class Test{ 2 3 public void regist(int num) throws MyException { 4 5 if (num < 0) { 6 7 throw new MyException("人数为负值,不合理",3); 8 9 } 10 11 System.out.println("登记人数"+ num); 12 13 } 14 15 public void manager() { 16 17 try { 18 19 regist(-100); 20 21 } catch (MyException e) { 22 23 System.out.print("登记出错,类别:"+e.getId()); 24 25 } 26 27 System.out.print("本次登记操作结束"); 28 29 30 31 } 32 33 public static void main(String args[]){ 34 35 Test t = new Test(); 36 37 t.manager(); 38 39 } 40 41 } 42 43
五:断言和断言的使用
1:断言
断言用于证明和测试程序的假设,比如“这里的值大于 5” 。
断言可以在运行时从代码中完全删除,所以对代码的运行速度没有影响。
2:断言的使用
断言有两种方法:
一种是assert<<布尔表达式>>;另一种是assert<<布尔表达式>>:<<细节描述>>。
如果布尔表达式的值为 false,将抛出 AssertionError 异常;细节描述是 AssertionError
异常的描述文本
使用javac –source 1.4 MyClass.java 的方式进行编译
示例如下:
1 public class AssertExample { 2 3 public static void main(String[] args) { 4 5 int x = 10; 6 7 if (args.length > 0) { 8 9 try { 10 11 x = Integer.parseInt(args[0]); 12 13 } catch (NumberFormatException nfe) { 14 15 /* Ignore */ 16 17 } 18 19 } 20 21 System.out.println("Testing assertion that x == 10"); 22 23 assert x == 10:"Our assertion failed"; 24 25 System.out.println("Test passed"); 26 27 } 28 29 } 30 31
由于引入了一个新的关键字,所以在编译的时候就需要增加额外的参数,要编译成功,
必须使用 JDK1.4的 javac 并加上参数'-source 1.4',例如可以使用以下的命令编译上面的
代码:
javac -source 1.4 AssertExample.java
以上程序运行使用断言功能也需要使用额外的参数(并且需要一个数字的命令行参数) ,例
如:
java -ea AssertExample 1
程序的输出为:
Testing assertion that x == 10
Exception in thread "main" java.lang.AssertionError:
Our assertion failed
at AssertExample.main(AssertExample.java:20)
由于输入的参数不等于10,因此断言功能使得程序运行时抛出断言错误,注意是错误,
这意味着程序发生严重错误并且将强制退出。断言使用 boolean 值,如果其值不为 true 则
抛出AssertionError并终止程序的运行。
3:断言推荐使用方法
用于验证方法中的内部逻辑,包括:
-内在不变式
-控制流程不变式
-后置条件和类不变式
注意:不推荐用于公有方法内的前置条件的检查
4:运行时屏蔽断言
运行时要屏蔽断言,可以用如下方法:
java –disableassertions 或 java –da
运行时要允许断言,可以用如下方法:
java –enableassertions 或 java –ea
练习实践
本章的主要内容是异常处理,实践重点:
异常处理的两种常用方式; try…catch…finally结构
程序 1
创建异常
需求:主动创建一个异常并抛出;
目标:
深入了解异常的工作机制。
程序: //: ExceptionMethods.java
1 package com.useful.java.part2; 2 3 4 5 public class ExceptionMethods { 6 7 public static void main(String[] args) { 8 9 try { 10 11 throw new Exception("Here's my Exception"); 12 13 } catch(Exception e) { 14 15 System.out.println("Caught Exception"); 16 17 System.out.println( 18 19 "e.getMessage(): " + e.getMessage()); 20 21 System.out.println( 22 23 "e.toString(): " + e.toString()); 24 25 System.out.println("e.printStackTrace():"); 26 27 e.printStackTrace(); 28 29 } 30 31 } 32 33 } 34 35
说明:
1、 try的时候,创建一个异常,没有处理,上抛了出去;
2、 catch时,抓住抛出的异常,并以多种情况显示出来;
3、 运行情况如下图所示:
程序 2:
定义自己的异常类
需求:继承Exception类,定义自己的 Exception类。
目标:
1、 复习Exception类的使用;
2、 了解继承的基本用法。
程序://: Inheriting.java
1 package com.useful.java.part3; 2 3 4 5 class MyException extends Exception { 6 7 public MyException() {} 8 9 public MyException(String msg) { 10 11 super(msg); 12 13 } 14 15 } 16 17 18 19 public class Inheriting { 20 21 public static void f() throws MyException { 22 23 System.out.println( 24 25 "Throwing MyException from f()"); 26 27 throw new MyException(); 28 29 } 30 31 public static void g() throws MyException { 32 33 System.out.println( 34 35 36 37 "Throwing MyException from g()"); 38 39 throw new MyException("Originated in g()"); 40 41 } 42 43 public static void main(String[] args) { 44 45 try { 46 47 f(); 48 49 } catch(MyException e) { 50 51 e.printStackTrace(); 52 53 } 54 55 try { 56 57 g(); 58 59 } catch(MyException e) { 60 61 e.printStackTrace(); 62 63 } 64 65 } 66 67 }说明:
1、 根据异常一章及本章内容,我们自定义了一个异常类;
2、 程序运行情况如下:
3、需要注意的是super 的用法,另外,还有一个 this。super 代表父类,this代表本类。
作业
1:用extends关键字创建自己的例外类;
2:用 main()创建一个类,令其掷出 try 块内的 Exception 类的一个对象。为 Exception 的构
建器赋予一个字串参数。在 catch从句内捕获异常,并打印出字串参数。添加一个 finally从
句,并打印一条消息,证明自己真正到达那里。
3:给定下面代码:
public void example(){
try {
unsafe();
System.out.println("Test 1");
}catch(Exception e}{System.out.println("Test 2");
}finally{System.out.println("Test 3");}
System.out.println("Test 4");
}
如果方法unsafe()运行正常,哪个结果不会被显示出来?
A. Test 1 B. Test 2 C. Test 3 D. Test 4
4:编写应用程序,从命令行传入两个整型数作为除数和被除数。要求程序中捕获
NumberFormatException 异常和ArithmeticException异常,在命令行输入不同的参数时能
输出如下各种结果:
java A
总是被执行
Exception in thread “main” java.lang.ArrayIndexOutofBoundsException at
A.main(A.java:7)
java A 1 2
0
总是被执行
java A 1 3a
java.lang.NumberFormatException: 3a
at java.lang.Integer.parseInt(Integer.java:435)
at java.lang.Integer.parseInt(Integer.java:476)
at A.main(A.java:8)
总是被执行
java A 1 0
java.lang.ArithmeticException: / by zero at A.main(A.java:9)
总是被执行

浙公网安备 33010602011771号