本文介绍异常的编程处理,并应用在聊天系统的设计中。
一个异常就是程序的执行过程中所发生问题的迹象。Java的可扩展性可以增加可能发生的错误的数目和类型。每一个新类可以添加自己的错误的可能性。在此展示的特性使程序员能够编写更清晰、更强大、以及更加容错的程序。使用Java的异常处理使程序员可以从程序的执行的“主线”移除错误处理代码,这提高了程序的清晰性并增强其可修改性。
异常处理是为了使程序能够捕获和处理错误,而不是让它们发生并导致严重的后果。异常处理被设计为处理同步错误,而不是处理异步事件。
编写健壮的聊天系统程序,需要引入异常控制流程设计,实现异常的捕获与处理。
在前面情景的控制结构的讨论中,简单的描述了异常处理的执行流程,并在前面的例子或任务中使用了异常处理,本情景中将阐述异常的概念和处理方式。
一个异常情况的出现,例如,内存用完、资源分配错误、找不到文件或网络连接故障,表明在程序执行期间发生了问题,这种问题有可能导致程序突然终止并将控制交给操作系统。可将发生的问题分为两种类型,一种是代表严重内部错误问题的Error类型,它是在程序执行期间发生时不期望被捕获和处理的,定义了正常情况下不会出现的条件,一旦出现,程序设计者无能为力,只能让用户按照系统提示关闭程序;另一种是代表一般问题的Exception类型,它是在程序执行期间发生时应当被捕获和处理的,用于捕获程序中的异常条件。这两个类型的类的直接父类是Throwable,代表所有的异常情况。如图所示:
在Java中定义了许多描述和处理异常的类,这些异常类的继承层次结构如图所示。
每个异常类代表一类问题,包含了异常信息的属性与对异常处理的方法。当程序运行期间发生了异常,系统将产生并抛出对应的异常类对象,由相应的机制捕获并处理。
常见的异常类如下:
异常类 说明
Exception 异常层次结构的根类。
RuntimeException 是那些可能在 Java 虚拟机正常运行期间抛出的异常的超类。
ArithmeticException 算术错误情形,如以零作除数。
IllegalArgumentException 方法接收到非法参数。
ArrayIndexOutOfBoundException 数组大小小于或大于实际的数组大小。
ArrayStoreException 当程序试图存储数组中错的类型数据时产生。
NullPointerException 尝试访问 null 对象成员。
ClassNotFoundException 不能加载所需的类。
NumberFormatException 数字转化格式异常,比如字符串到 float 型数字的转换无效。
OutOfMemoryException 当没有足够的内存来分配新对象时产生。
IOException I/O 异常的根类,由于一般I/O故障而引起的。
FileNotFoundException 找不到文件,试图访问不存在的文件时产生。
EOFException 文件结束。
InterruptedException 线程中断。
SecurityException 当applet试图执行由于浏览器的安全设置而不允许的动作时产生。
StackOverflowException 当系统的堆栈空间用完时产生。
StringIndexOutOfBoundsException 当程序试图访问字符串中不存在的字符位置时产生。
SQLException 提供关于数据库访问错误或其他错误的信息。
异常处理使得程序能够捕获并处理Exception类及其子类类型的异常问题,异常处理被设计为处理同步错误,例如程序执行时的除法指令而除数为零的错误。异常处理并不是设计为处理异步事件,例如磁盘的I/O操作、网络信息到达、鼠标点击、键盘敲击等,它们是通过事件侦听器来处理。
异常处理使得一个程序能够捕获所有的Exception类型的异常,有助于改进程序的荣错性。当异常发生时,Java运行环境产生相应异常类型的异常对象,它包含关于错误类型和异常发生时的程序状态的信息。Java通过引发异常机制(throwing an exception)将其传递给异常处理程序(exception-handle)。异常处理机制的流程如图所示。
从图中可看出,要监控的程序语句包含在try块中,判断是否有异常,如果有异常对象产生,则通过catch块进行异常类型匹配并处理,最终由finally块释放资源。如果产生的异常对象在catch块没有匹配成功,则显示缺省的错误信息,然后由finally块释放资源。
异常的引发有两种方式,一种是通过throw手动引发异常,一种是通过throws指定由方法引发的异常。
包裹在try块里面的程序代码可以产生一个异常,任何一行代码产生异常的时候就停止后面代码的执行,try块后面可以跟零个或多个catch块,每个catch块指定捕获的特定异常类型,并且包含一个异常处理程序。在最后一个catch块之后,是一个可选的finally块,由此块提供的代码总是要执行,不管是否有异常发生,可以看出finally块是一个理想的场所,在它里面的代码可以释放资源以阻止“资源泄露”,finally块根据需要选择,并不是强制的。如图所示,当try块引发一个异常时,catch块会立即捕获并由其中的异常处理程序进行处理。结构如下:
结构如下:
try {
可以引发异常的代码行;
}
catch (异常类型 异常引用变量) {
处理异常的代码行;
}
finally {
释放资源的代码行;
}
需要注意的是在catch块的异常处理程序中,不能访问由try块定义的对象,这是因为try块在异常处理程序开始执行之前已过期。
清单 7-1 异常处理
1: public class ExecutiveCollection {
2: CustomerCareExecutive exObjects[];
3: public ExecutiveCollection(){
4: try{
5: exObjects=new CustomerCareExecutive[3];
6: }
7: catch(NullPointerException e){
8: System.out.println("Memory not allocated for the array");
9: }
10: for(int ctr=0;ctr != 3;ctr++){
11: exObjects[ctr] = new CustomerCareExecutive();
12: }
13: try{
14: exObjects[0].executiveName="PYP Inc";
15: exObjects[0].rating=Integer.valueOf("30+40");
16: }
17: catch(NumberFormatException nfe){
18: System.out.println("输入到方法的不是一个整数");
19: exObjects[1].executiveName="HotDon Inc";
20: exObjects[1].rating=85;
21: exObjects[2].executiveName="HotMail Inc";
22: exObjects[2].rating=60;
23: }
24: }
25: public void displayCollection(){
26: for(int ctr=0;ctr!=3; ctr++){
27: exObjects[ctr].displayDetails();
28: }
29: }
30: public static void main(String args[]){
31: ExecutiveCollection collectionObj;
32: collectionObj = new ExecutiveCollection();
33: collectionObj.displayCollection();
34: System.out.println("所有结果已显示");
35: }
36:}
37:class CustomerCareExecutive {
38: String executiveName;
39: int rating;
40: public void displayDetails(){
41: System.out.println(executiveName);
42: System.out.println(rating);
43: }
44:}
单个try
块引发的异常对象可有不同的异常类型,针对想捕获的每种异常类型,都必须有一个相应的异常控制器,即catch
块,采用多个catch
块来匹配对应的异常类型,结构如下:
try {
可以引发异常的程序;
}
catch (异常类型1 异常引用变量1) {
异常处理程序1;
}
catch (异常类型2 异常引用变量2) {
异常处理程序2;
}
…
catch (异常类型n 异常引用变量n) {
异常处理程序n;
}
finally {
释放资源的程序;
}
try
块有多行代码语句,每行语句都有可能引发一个异常,接在try
块后的多个catch
块,每个catch
块只能处理一种类型的异常。如果可能引发的多个异常类对象之间没有继承关系,则多个catch块
之间的顺序可以随意,但如果它们之间有继承关系,则应该将子异常类的catch
块放于父异常类的catch
块之前,否则将会出现编译错误。异常处理程序包含在catch
块中,在伴随catch
的园括号中,含有一个指定异常类型的类名和一个引用变量参数名,异常处理程序通过这个参数能够引用引发的异常对象。
清单 7-2 多个catch块
1: public class TryCatch {
2: public static void main(String args[]){
3: int array[]={0,0};
4: int num1=100;
5: int num2=0;
6: int result=0;
7: try{
8: result=num1/num2;
9: System.out.println(num1/array[2]);
10: }
11: catch(ArithmeticException ex){
12: System.out.println("错误...除数为零");
13: }
14: catch(ArrayIndexOutOfBoundsException ex){
15: System.out.println("错误...数组越界");
16: }
17: catch(Exception ex){
18: System.out.println("其它错误...");
19: }
20: System.out.println("结果是:"+result);
21: }
22:}
以上代码的Exception
类是所有异常的基类,处理引发的所有异常,如果将带Exception
类的catch
块放在前面,其它的catch
块将不会被执行。javac
编译器将给出还未达到特定捕获的错误信息。另外9
行的语句没有被执行到,这是因为当异常引发时,程序流被中断,执行特定catch
块中的语句。
也有可能try
块没有对应的catch
块来匹配一个引发的特定异常对象,在这种情况下,字符界面的应用就会终止,而applet
和图形界面的应用返回它们规则的事件处理过程,虽然能够继续执行,但不正确。
在try
、catch
和finally
的异常处理结构中,有时需要在它们的块中嵌入一个try-catch
块,这是因为在catch
块的异常处理程序和finally
块的释放资源程序中也有可能引发异常,使用try-catch
块捕获异常。而在try
块中嵌套try-catch
块,如果内层的try-catch
块没有匹配的catch
块来处理异常,则Java将检查外层的try
块,看是否有catch
块将处理此异常,依次类推,向外搜索后续的嵌套层次。如果最终还是没有找到处理异常的catch
块,异常对象将传递给默认异常处理器。
清单 7-3 嵌套try-catch块
1: public class NestedTry {
2: public void test(String[] argumnet) {
3: try {
4: int num = Integer.parseInt(argumnet[1]);
5: try {
6: int numValue = Integer.parseInt(argumnet[0]);
7: System.out.println(argumnet[0] + "的平方是 " + numValue * numValue);
8: } catch (NumberFormatException ex) {
9: System.out.println("不是一个数字! ");
10: }
11: }
12: catch (ArrayIndexOutOfBoundsException ex) {
13: System.out.println("请输入数字!!!");
14: }
15: }
16: public static void main(String[] args) {
17: NestedTry nestedtry = new NestedTry();
18: nestedtry.test(args);
19: }
20:}
异常处理程序可以采用多种方式编写,可以重新引发一个异常,通过引发不同类型的异常,可将一种类型的异常转换为另一种,在最后的异常处理之后,它们能够完成任何必要的异常恢复,能够看到引起错误的情况,消除错误的原因,并且通过调用引起异常的原来的方法重新尝试。能够返回它们的环境状态值,但在catch
块的处理程序中不可能返回到异常的引发点,这是因为控制流程到catch
块的处理程序后,try
块的异常引发点已经失效,return
语句无法感知。
聊天室类DushiChatRoom的结构如图所示:
在其方法log()的实现中,使用了try、catch的异常处理结构,实现代码如下:
1:protected void log(String logText) throws IOException {
2: if (chatLogStream != null) {
3: try {
4: synchronized (chatLogStream) {
5: byte[] bytes = logText.getBytes();
6: chatLogStream.write(bytes);
7: }
8: } catch (IOException e) {
9: chatLogStream.close();
10: chatLogStream = null;
11: throw (e);
12: }
13: }
14:}
在第1行的方法定义中,使用了thorws
子句声明了该方法可能引发IOException
异常。在第3行到第12行代码中,进行了异常的捕获与处理,其中在catch
的处理结构中,第11行代码使用throw
关键字引发捕获的异常。
清单 7-4 DushiChatRoom.java
聊天服务器类ChatServer的结构如图所示:
在该类的方法GetDBProperties()
的实现中,使用了try、catch与finally的异常处理结构,用于加载资源属性文件,其代码为:
1: private Properties GetDBProperties() {
2: Properties DBProperties = new Properties();
3: try {
4: InputStream inputstream = this.getClass().getClassLoader()
5: .getResourceAsStream("server.properties");
6: DBProperties.load(inputstream);
7: inputstream.close();
8: } catch (IOException _IOExc) {
9: } finally {
10: return (DBProperties);
11: }
12: }
代码行3-11行实现了异常捕获与处理,以及加载资源属性文件。
清单 7-5 ChatServer.java
有时根据需要,不一定非要使用Java提供的异常类型,而要创建自己的异常类型,以表示一个特殊的错误或约束,定义自己的异常类时必须继承Exception
类或其子类。样例代码如下:
1: public class IllegalValueException extends Exception {
2: public String getMessage(){
3: return "提供的错误值";
4: }
5: }
在面向对象的设计中,虽然一个类的设计是其设计的基本单元,但在该单元内,方法的定义与实现至关重要。在一个方法定义中,throws子句指定方法可能抛出的异常,而该异常也许是方法体内的语句引发的,语句引发通过throw实现。或者是方法体内的方法调用引发的。在聊天系统的类中的方法定义与实现中,善于利用throws与throw的运用。
使用throw语句能够明确的引发一个异常,通过catch块的引用变量参数名或new运算符获得一个异常类实例的句柄。格式如下:
throw new 异常类名(信息);
执行throw
语句后,程序执行流程将立即停止,throw
的下一条语句暂停执行,系统控制将转向内层的try-catch
块检查是否有catch
块子句能匹配“异常类”的实例。如果找到相匹配的实例,系统转向该子句。如果没有找到,则转向上一层的try
块,这样逐层往上,直到最外层的异常处理程序中止程序并打印出堆栈调用情况。异常类名后的括号中的信息是可选部分,如果提供了信息,toString()
方法的返回值中将增加该信息内容。
清单 7-6 throw引发异常
1: public class ThrowDemo {
2: static void demoproc(){
3: try{
4: throw new NullPointerException("演示");
5: }
6: catch(NullPointerException ex){
7: System.out.println("在demoproc内部捕获");
8: throw ex;
9: }
10: }
11: public static void main(String args[]){
12: try{
13: demoproc();
14: }
15: catch(NullPointerException ex){
16: System.out.println("重新捕获:"+ex.toString());
17: }
18: }
19:}
运行结果为:
在demoproc内部捕获
重新捕获:java.lang.NullPointerException: 演示
一个throws子句列出被一个方法引发的相关异常,格式如下:
[public | protected | private ] [static][final | abstract] [native] [synchronized]
返回类型 方法名([参数列表])[throws 异常列表]{
代码语句;
}
其中,[public | protected | private ] [static][final | abstract] [native] [synchronized]
已在情景4中作了说明,异常列表表示的是用逗号分隔的Exception类型的类或其子类。
如果一个方法可能引发某个异常,但它自己并不处理,那么应该在方法定义中标明这些潜在的异常类型,让其调用者捕获并处理由方法引发的一个潜在异常,而throws
关键字后面列出的是所有可能引发的异常。
有些异常能够在程序执行期间的任意点发生,许多这样的异常能够通过合理的编码可以避免。这些是运行时异常,它们继承自RuntimeException
异常类,例如你的程序试图访问一个数组中的元素,而数组索引已超过其边界,则会发生ArrayIndexOutOfBoundsException
的异常类型,而它是RuntimeException
的子类,编程可以避免这样的问题,因此,它是一个运行时异常。另一种运行时异常类型是NullPointerExption
,当在程序中声明一个对象的引用变量,但还没有实例化一个对象指定到这个引用变量,如果试图使用这个引用变量的时候,将会引发NullPointerExption
异常,同样,合理编程可以避免这种情况发生。
需要明确一下,并不是所有的错误和异常需要在一个方法的throws
子句列出,其中包括类型为Error
或RuntimeException
或它们的子类。Error
代表随时发生的严重系统级错误,大多数程序不可能恢复它们。针对RuntimeException
及其子类,一个方法应当在方法内直接处理它们,而不是将它们传递给另一个程序组件。因此,对于非RuntimeException
的潜在异常类型,必须在方法的throws
子句指定。
Java将RuntimeException
和Error
区分为非检验类型,将所有的非RuntimeException
的Exception区分为检验类型,检验类型的异常需要在方法体的try-catch
块中捕获或在方法的throws
子句中声明。将检验类型的异常放在throws
子句中,将会强迫调用此方法的其它方法也处理这个检验类型的异常。如果感觉某个特定检验异常不像会发生,那么选择try-catch
块捕获它而不做任何事情,可以避免后续强迫处理它。
以下列出了包java.lang
,java.util
, java.io,java.awt
和java.net
中的Error
和Exception
层次:
java.lang包中的Errors:
java.lang.Object
java.lang.Throwable
java.lang.Error
java.lang.LinkageError
java.lang.ClassCircularityError
java.lang.ClassFormatError
java.lang.ExceptionInInitializerError
java.lang.IncompatibleClassChangeError
java.lang.AbstractMethodError
java.lang.IllegalAccessError
java.lang.InstantiationError
java.lang.NoSuchFieldError
java.lang.NoSuchMethodError
java.lang.NoClassDefFoundError
java.lang.UnsatisfiedLinkError
java.lang.VerifyError
java.lang.ThreadDeath
java.lang.VirtualMachineError
java.lang.InternalError
java.lang.OutOfMemoryError
java.lang.StackOverflowError
java.lang.UnknownError
java.awt.AWTError
程序员应当忽略这些错误,它们虽然是严重错误,但很少出现。
java.lang包中的Exceptions:
java.lang.Object
java.lang.Throwable
java.lang.Exception
java.lang.ClassNotFoundException
java.lang.CloneNotSupportedException
java.lang.IllegalAccessException
java.lang.InstantiationException
java.lang.InterruptedException
java.lang.NoSuchFieldException
java.lang.NoSuchMethodException
java.lang.RuntimeException
java.lang.ArithmeticException
java.lang.ArrayStoreException
java.lang.ClassCastException
java.lang.IllegalArgumentException
java.lang.IllegalThreadStateException
java.lang.NumberFormatException
java.lang.IllegalMonitorStateException
java.lang.IllegalStateException
java.lang.IndexOutOfBoundsException
java.lang.ArrayIndexOutOfBoundsException
java.lang.StringIndexOutOfBoundsException
java.lang.NegativeArraySizeException
java.lang.NullPointerException
java.lang.SecurityException
以上列出了很多Java的RuntimeException
类型的异常,虽然Java程序员不需要在方法的throws
子句中声明,但它们通常在Java程序中的try-catch
块中将被捕获和处理。
java.util包中的Exceptions:
java.lang.Object
java.lang.Throwable
java.lang.Exception
java.lang.RuntimeException
java.util.EmptyStackException
java.util.MissingResourceException
java.util.NoSuchElementException
java.util.TooManyListenersException
以上列出了Java另外三个RuntimeException
类型的子类,在后续情景中讲到Vector
类时将会遇到这些异常。
java.io包中的Exceptions:
java.lang.Object
java.lang.Throwable
java.lang.Exception
java.io.IOException
java.io.CharConversionException
java.io.EOFException
java.io.FileNotFoundException
java.io.InterruptedIOException
java.io.ObjectStreamException
java.io.InvalidClassException
java.io.InvalidObjectException
java.io.NotActiveException
java.io.NotSerializableException
java.io.OptionalDataException
java.io.StreamCorruptedException
java.io.WriteAbortedException
java.io.SyncFailedException
java.io.UnsupportedEncodingException
java.io.UTFDataFormatException
以上列出了Java的IOException,它们是输入/输出和文件处理时发生的所有检验类型的异常。
java.awt包中的Exceptions:
java.lang.Object
java.lang.Throwable
java.lang.Exception
java.awt.AWTException
java.lang.RuntimeException
java.lang.IllegalStateException
java.awt.IllegalComponentStateException
以上列出了java.awt包中的唯一的检验类型的异常类AWTException,它是通过各种不同的窗口工具方法引发的。
java.net包中的Exceptions:
java.lang.Object
java.lang.Throwable
java.lang.Exception
java.io.IOException
java.net.SocketException
java.net.BindException
java.net.MalformedURLException
java.net.ProtocolException
java.net.SocketException
java.net.ConnectException
java.net.NoRouteToHostException
java.net.UnknownHostException
java.net.UnknownServiceException
以上列出了java.net包中的IOException异常类,这些表示所有的各种网络问题的检验类型的异常。
如果想查阅所有的Exception
和Error
,请参考Java的API文档。
清单 7-7 throws子句引发异常
1: public class ThrowsDemo {
2: static void procedure() throws IllegalAccessException {
3: System.out.println("在方法Procedure内");
4: throw new IllegalAccessException("演示");
5: }
6: public static void main(String[] args) {
7: try{
8: procedure();
9: }
10: catch(IllegalAccessException ex){
11: System.out.println("引发异常:"+ex);
12: }
13: }
14:}
运行结果如下:
在方法Procedure内
引发异常:java.lang.IllegalAccessException: 演示
类DushiFileTransfer的结构如图所示:
在这个类的getFileBlock()方法实现中,使用throw引发异常,代码如下:
1:protected byte[] getFileBlock(int blockNumber)
2: throws IndexOutOfBoundsException, IOException {
3: byte[] data = null;
4: if (transFile == null)
5: throw (new IOException("未指定传输文件"));
6: if (blockNumber >= totalBlocks)
7: throw (new IndexOutOfBoundsException("块号" + blockNumber
8: + "超出范围"));
9: if (blockNumber == (totalBlocks - 1))
10: data = new byte[(int) (totalSize % blockSize)];
11: else
12: data = new byte[blockSize];
13: synchronized (transFile) {
14: transFile.seek((long) (blockNumber * blockSize));
15: transFile.read(data, 0, data.length);
16: }
17: return (data);
18: }
在代码行5和7行,使用throw
语句引发IOException
类型的异常和IndexOutOfBoundsException
两类型的异常。
同样在上图所示类的方法getFileBlock()
的定义中,使用throws
子句声明了该方法可能引发的异常IOException
类型和异常IndexOutOfBoundsException
类型。在该类的另一方法putFileBlock()
的定义中,使用throws
子句声明了同类的异常类型。
清单 7-8 DushiFileTransfer.java
当自定义了异常类IllegalValueException后,要引发自定义的异常,可以使用下列语法:
throw new IllegalValueException();
通常在引发异常的同一行实例化该异常,因为异常可以携带被创建时添加的行号信息。如果该异常在某一个方法中,则在该方法的throws子句中必须声明。若在其它代码处调用该方法,则需要try catch语句捕获该异常并进行处理。样例代码如下:
1: class UserTrial{
2: int val1,val2;
3: public UserTrial(int a,int b){
4: val1=a;
5: val2=b;
6: }
7: void show() throws IllegalValueException{
8: if ((val1<0)||(val2>0)){
9: throw new IllegalValueException();
10: }
11: System.out.println("值1="+val1);
12: System.out.println("值2="+val2);
13: }
14:}
15:public class TestCatchException{
16: public static void main(String args[]){
17: UserTrial values=new UserTrial(-1,1);
18: try{
19: values.show();
20: }
21: catch(IllegalValueException ex){
22: System.out.println(ex.getMessage());
23: }
24: }
25:}
通常认为只要程序通过编译,能够运行就是正确的,这种程序的正确性是相对于程序的需求说明而言的,是指程序的实现是否与其需求说明相一致。为了在程序中体现这样的一致性,在程序中使用表达式加入需求说明的描述,而这样的表达式被称为断言(assert)。断言语句有两种语法形式,它们是:
assert <boolean_expression>;
assert <boolean_expression>:<detail_expression>;
两种形式中,如果布尔表达式的值为false,则将引发AssertionError异常指示某个断言失败,不要捕获该错误,程序会异常终止。如果使用第二种形式,则其第二个表达式(可以是任意类型)被转换为String类型,并且补充报告断言时打印的信息。
断言可以提供有关程序员假设和期望的有价值文档。基于该点,断言对于其他工作于同一代码的程序员的维护工作显得尤其有意义。断言通常用于验证某一方法或某一小组紧密耦合的方法的内部逻辑。一般而言,断言不应用来验证代码是否使用正确,而是验证是否满足内部的预期结果。
断言的比较好的用法是内在不变式、控制流程不变式、后置条件和类不变式。
⑴ 内在不变式
当您确信某种情况总是存在或者总是不存在,并且按照相应的情况进行编码时,即存在一个内在不变式。考虑一下代码:
1:if(x>0){
2: //做这个
3: }else{
4: //做那个
5: }
如果假定x的值为0,并且永远不会为负。但是x仍然出现负值时,代码将产生非预期结果,出现这种情况几乎肯定是不正确的。而代码不会停止并报告问题,您发现不了它,直到产生更大的破坏。这时问题的最初来源变得越发难以确定。
在此种情况下,可以在else语句块开始处加上断言语句来帮助确保假设的正确性,并可在发生错误或在其他人维护代码期间更改了代码,从而违反假设时,快速定位错误。增加了断言的代码如下:
1: if(x>0){
2: //做这个
3: }else{
4: assert (x == 0);
5: //做那个,除非x为负值
6: }
⑵ 控制流程不变式
控制流程不变式所描述的假设与内在不变式的假设相同,只是它们依赖于执行的流程的方式,而不是变量的值或关系。若在多分支结构流程中,switch语句内已经枚举了控制变量的所有可能出现值,因此,default语句永远不会执行。这可以通过加入断言语句来验证:
1:switch(suit){
2: case Suit.CLUBS:
3: System.out.println("梅花");
4: break;
5: case Suit.DIAMONDS:
6: System.out.println("方块");
7: break;
8: case Suit.HEARTS:
9: System.out.println("红心");
10: break;
11: case Suit.SPADES:
12: System.out.println("黑桃");
13: break;
14: default:
15: assert false:"不知道显示哪种扑克牌?";
16: }
⑶ 后置条件和类不变式
后置条件是指一个方法结束后变量的值或关系的假设。一个简单的后置条件的样例是在堆栈的弹出方法执行后的测试。当弹出方法被调用后,除非堆栈已空,否则至少还有一个元素。弹出方法样例代码片段如下:
1:public Object pop(){
2: int size = this.getElemrntCount();
3: if(size == 0){
4: throw new RuntimeException("试图从空栈中弹出");
5: }
6: <取栈中元素>;
7: assert (this.getElemrntCount() == size-1);
8: return <元素对象>;
9:}
类不变式是指对类中的每个方法调用结束后都可以进行测试。针对堆栈类,其中一个不变式条件是堆栈的元素计数器永远不为负。
⑷ 断言的不当使用
①不要使用断言来检查public方法的参数。
②在断言检查中不要使用可引起副作用的方法。
以上是因为断言检查可以在运行时关闭和禁用。
博文最后更新时间: