八宝书库 > 文学其他电子书 > Java编程思想第4版[中文版](PDF格式) >

第76部分

Java编程思想第4版[中文版](PDF格式)-第76部分

小说: Java编程思想第4版[中文版](PDF格式) 字数: 每页4000字

按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!





…………………………………………………………Page 276……………………………………………………………

  // but you must deal with the base constructor  

  // exceptions:  

  StormyInning() throws RainedOut;   

    BaseballException {}  

  StormyInning(String s) throws Foul;   

    BaseballException {}  

  // Regular methods must conform to base class:  

//! void walk() throws PopFoul {} //pile error  

  // Interface CANNOT add exceptions to existing  

  // methods from the base class:  

//! public void event() throws RainedOut {}  

  // If the method doesn't already exist in the  

  // base class; the exception is OK:  

  public void rainHard() throws RainedOut {}  

  // You can choose to not throw any exceptions;  

  // even if base version does:  

  public void event() {}  

  // Overridden methods can throw   

  // inherited exceptions:  

  void atBat() throws PopFoul {}  

  public static void main(String'' args) {  

    try {  

      StormyInning si = new StormyInning();  

      si。atBat();  

    } catch(PopFoul e) {  

    } catch(RainedOut e) {  

    } catch(BaseballException e) {}  

    // Strike not thrown in derived version。  

    try {  

      // What happens if you upcast?  

      Inning i = new StormyInning();  

      i。atBat();  

      // You must catch the exceptions from the  

      // base…class version of the method:  

    } catch(Strike e) {  

    } catch(Foul e) {  

    } catch(RainedOut e) {  

    } catch(BaseballException e) {}  

  }  

} ///:~  

  

在 Inning 中,可以看到无论构建器还是event()方法都指出自己会“掷”出一个违例,但它们实际上没有那 

样做。这是合法的,因为它允许我们强迫用户捕获可能在覆盖过的event()版本里添加的任何违例。同样的 

道理也适用于abstract 方法,就象在 atBat()里展示的那样。  

 “interface Storm”非常有趣,因为它包含了在 Ining 中定义的一个方法——event(),以及不是在其中 

定义的一个方法。这两个方法都会“掷”出一个新的违例类型:RainedOut。当执行到“StormyInning  

extends”和“implements Storm”的时候,可以看到Storm 中的event()方法不能改变 Inning中的event() 

的违例接口。同样地,这种设计是十分合理的;否则的话,当我们操作基础类时,便根本无法知道自己捕获 

的是否正确的东西。当然,假如interface 中定义的一个方法不在基础类里,比如rainHard(),它产生违例 

时就没什么问题。  

对违例的限制并不适用于构建器。在StormyInning 中,我们可看到一个构建器能够“掷”出它希望的任何东 

西,无论基础类构建器“掷”出什么。然而,由于必须坚持按某种方式调用基础类构建器(在这里,会自动 



                                                                                          275 


…………………………………………………………Page 277……………………………………………………………

调用默认构建器),所以衍生类构建器必须在自己的违例规范中声明所有基础类构建器违例。  

StormyInning。walk()不会编译的原因是它“掷”出了一个违例,而 Inning。walk()却不会“掷”出。若允许 

这种情况发生,就可让自己的代码调用 Inning。walk(),而且它不必控制任何违例。但在以后替换从 Inning 

衍生的一个类的对象时,违例就会“掷”出,造成代码执行的中断。通过强迫衍生类方法遵守基础类方法的 

违例规范,对象的替换可保持连贯性。  

覆盖过的event()方法向我们显示出一个方法的衍生类版本可以不产生任何违例——即便基础类版本要产生 

违例。同样地,这样做是必要的,因为它不会中断那些已假定基础类版本会产生违例的代码。差不多的道理 

亦适用于atBat(),它会“掷”出PopFoul——从 Foul 衍生出来的一个违例,而Foul 违例是由 atBat()的基 

础类版本产生的。这样一来,假如有人在自己的代码里操作 Inning,同时调用了atBat(),就必须捕获Foul 

违例。由于 PopFoul 是从Foul 衍生的,所以违例控制器(模块)也会捕获PopFoul。  

最后一个有趣的地方在 main()内部。在这个地方,假如我们明确操作一个StormyInning 对象,编译器就会 

强迫我们只捕获特定于那个类的违例。但假如我们上溯造型到基础类型,编译器就会强迫我们捕获针对基础 

类的违例。通过所有这些限制,违例控制代码的“健壮”程度获得了大幅度改善(注释③)。  

  

③:ANSI/ISO C++施加了类似的限制,要求衍生方法违例与基础类方法掷出的违例相同,或者从后者衍生。 

在这种情况下,C++实际上能够在编译期间检查违例规范。  

  

我们必须认识到这一点:尽管违例规范是由编译器在继承期间强行遵守的,但违例规范并不属于方法类型的 

一部分,后者仅包括了方法名以及自变量类型。因此,我们不可在违例规范的基础上覆盖方法。除此以外, 

尽管违例规范存在于一个方法的基础类版本中,但并不表示它必须在方法的衍生类版本中存在。这与方法的 

 “继承”颇有不同(进行继承时,基础类中的方法也必须在衍生类中存在)。换言之,用于一个特定方法的 

 “违例规范接口”可能在继承和覆盖时变得更“窄”,但它不会变得更“宽”——这与继承时的类接口规则 

是正好相反的。  



9。6 用 finally 清除  



无论一个违例是否在try 块中发生,我们经常都想执行一些特定的代码。对一些特定的操作,经常都会遇到 

这种情况,但在恢复内存时一般都不需要(因为垃圾收集器会自动照料一切)。为达到这个目的,可在所有 

违例控制器的末尾使用一个finally 从句(注释④)。所以完整的违例控制小节象下面这个样子:  

  

try {  

// 要保卫的区域:  

// 可能“掷”出A;B;或C 的危险情况  

} catch (A a1) {  

// 控制器 A  

} catch (B b1) {  

// 控制器 B  

} catch (C c1) {  

// 控制器 C  

} finally {  

// 每次都会发生的情况  

}  

  

④:C++违例控制未提供finally 从句,因为它依赖构建器来达到这种清除效果。  

  

为演示 finally 从句,请试验下面这个程序:  

  

//: FinallyWorks。java  

// The finally clause is always executed  

  

public class FinallyWorks {  

  static int count = 0;  

  public static void main(String'' args) {  



                                                                              276 


…………………………………………………………Page 278……………………………………………………………

    while(true) {  

      try {  

        // post…increment is zero first time:  

        if(count++ == 0)  

          throw new Exception();  

        System。out。println(〃No exception〃);  

      } catch(Exception e) {  

        System。out。println(〃Exception thrown〃);  

      } finally {  

        System。out。println(〃in finally clause〃);  

        if(count == 2) break; // out of 〃while〃  

      }  

    }  

  }  

} ///:~  

  

通过该程序,我们亦可知道如何应付Java 违例(类似 C++的违例)不允许我们恢复至违例产生地方的这一事 

实。若将自己的try 块置入一个循环内,就可建立一个条件,它必须在继续程序之前满足。亦可添加一个 

static计数器或者另一些设备,允许循环在放弃以前尝试数种不同的方法。这样一来,我们的程序可以变得 

更加“健壮”。  

输出如下:  

  

Exception thrown  

in finally clause  

No exception  

in finally clause  

  

无论是否“掷”出一个违例,finally 从句都会执行。  



9。6。1  用 finally 做什么  



在没有“垃圾收集”以及“自动调用破坏器”机制的一种语言中(注释⑤),finally 显得特别重要,因为 

程序员可用它担保内存的正确释放——无论在 try 块内部发生了什么状况。但Java 提供了垃圾收集机制,所 

以内存的释放几乎绝对不会成为问题。另外,它也没有构建器可供调用。既然如此,Java 里何时才会用到 

finally 呢?  

  

⑤:“破坏器”(Destructor)是“构建器”(Constructor)的反义词。它代表一个特殊的函数,一旦某个 

对象失去用处,通常就会调用它。我们肯定知道在哪里以及何时调用破坏器。C++提供了自动的破坏器调用机 

制,但Delphi 的Object Pascal 版本 1及 2 却不具备这一能力(在这种语言中,破坏器的含义与用法都发生 

了变化)。  

  

除将内存设回原始状态以外,若要设置另一些东西,finally 就是必需的。例如,我们有时需要打开一个文 

件或者建立一个网络连接,或者在屏幕上画一些东西,甚至设置外部世界的一个开关,等等。如下例所示:  

  

//: OnOffSwitch。java  

// Why use finally?  

  

class Switch {  

  boolean state = false;  

  boolean read() { return state; }  

  void on() { state = true; }  

  void off() { state = false; }  

}  



                                                                                           277 


…………………………………………………………Page 279……………………………………………………………

  

public class OnOffSwitch {  

  static Switch sw = new Switch();  

  public static void main(String'' args) {  

    try {  

      sw。on();  

      // Code that can throw exceptions。。。  

      sw。off();  

    } catch(NullPointerException e) {  

      System。out。println(〃NullPointerException〃);  

      sw。off();  

    } catch(IllegalArgumentException e) {  

      System。out。println(〃IOException〃);  

      sw。off();  

    }  

  }  

} ///:~  

  

这里的目标是保证main()完成时开关处于关闭状态,所以将 sw。off()置于 try 块以及每个违例控制器的末 

尾。但产生的一个违例有可能不是在这里捕获的,这便会错过 sw。off()。然而,利用finally,我们可以将 

来自 try 块的关闭代码只置于一个地方:  

  

//: WithFinally。java  

// Finally Guarantees cleanup  

  

class Switch2 {  

  boolean state = false;  

  boolean read() { return state; }  

  void on() { state = true; }  

  void off() { state = false; }  

}  

  

public class WithFinally {  

  static Switch2 sw = new Switch2();  

  public static void main(String'' args) {  

    try {  

      sw。on();  

      // Code that can throw exceptions。。。  

    } catch(NullPointerException e) {  

      System。out。println(〃NullPointerException〃);  

    } catch(IllegalArgumentException e) {  

      System。out。println(〃IOException〃);  

    } finally {  

      sw。off();  

    }  

  }  

} ///:~  

  

在这儿,sw。off()已移至一个地方。无论发生什么事情,都肯定会运行它。  

即使违例不在当前的catch 从句集里捕获,finally 都会在违例控制机制转到更高级别搜索一个控制器之前 

得以执行。如下所示:  



                                                                                             278 


…………………………………………………………Page 280……………………………………………………………

  

//: AlwaysFinally。java  

// Finally is always executed  

  

class Ex extends Exception {}  

  

public class AlwaysFinally {  

  public static void main(String'' args) {  

    System。out。println(  

      〃Entering first try block〃);  

    try {  

      System。out。println(  

        〃Entering second try block〃);  

      try {  

        throw new Ex();  

      } finally {  

        System。out。println(  

          〃finally in 2nd try block〃);  

      }  

    } catch(Ex e) {  

      System。out。println(  

        〃Caught Ex in first try block〃);  

    } finally {  

    

返回目录 上一页 下一页 回到顶部 0 1

你可能喜欢的