1.异常的限制

当覆盖方法的时候,仅仅能抛出在基类方法的异常说明里列出的那些异常。

这意味着,当基类使用的代码应用到其派生类对象的时候,一样能够工资,异常也不例外。

以下的样例是在编译时施加在异常上面的限制:

public class BaseBallException extends Exception {}
public class Foul extends BaseBallException{}
public class Strike extends BaseBallException{}
public abstract class Inning {
public Inning() throws BaseBallException{}
public void event() throws BaseBallException{}
public abstract void addBat()throws Strike,Foul;
public void walk() {}
}
public class StormException extends Exception{}
public class RainedOut extends StormException{}
public class PopFoul extends Foul{}
public interface Storm {
public void event() throws RainedOut;
public void rainHard() throws RainedOut;
}
public class StromMyInnerings extends Inning implements Storm{
/**
* @throws BaseBallException
*/
public StromMyInnerings() throws RainedOut,BaseBallException {}
public StromMyInnerings(String s)throws RainedOut,BaseBallException{}
/**
* @throws RainedOut
*/
@Override
public void rainHard() throws RainedOut {}
/**
* @throws Strike
*/
@Override
public void addBat() throws PopFoul {}
public void event(){}
public static void main(String[] args) {
StromMyInnerings stromMyInnerings;
try {
stromMyInnerings = new StromMyInnerings();
stromMyInnerings.addBat();
} catch (RainedOut e) {
System.out.println("RainedOut");
} catch (PopFoul e) {
System.out.println("PopFoul");
} catch (BaseBallException e) {
System.out.println("BaseBallException");
}
Inning inning;
try {
inning = new StromMyInnerings();
inning.addBat();
} catch (Strike e) {
System.out.println("Strike");
} catch (Foul e) {
System.out.println("Foul");
} catch (RainedOut e) {
System.out.println("RainedOut");
} catch (BaseBallException e) {
System.out.println("BaseBallException");
}
}
}

在Inning类中,能够看到构造器和event()方法都声明将抛出异常,但实际上没有抛出。

这样的方式使你能强制用户去捕获可能在覆盖后的event()版本号中添加的异常,所以它非常合理。这对于抽象方法相同成立,如addBat()。

接口Storm值得注意,由于它包括了一个在Inning中定义的方法event()和一个不在Inning中定义的方法rainHard()。

这两个方法都抛出新的异常RainedOut。

假设StromMyInnerings类在扩展Inning类的同事又出现了Storm接口,那么Storm里的event()方法就不能改变Inning中的event()方法的异常接口。

否则的话,在使用基类的时候就不能推断是否捕获了正常的异常。当然,假设接口里定义的方法不是来自于基类。比方rainHard(),那么此方法抛出什么样的异常都没有问题。

异常限制对构造器不起作用,你会发现StromMyInnerings的构造器能够抛出不论什么异常。而不必理会基类构造器所抛出的异常。

派生类构造器不能捕获基类构造器抛出的异常。

虽然在继承过程中,编译器会对异常说明做强制要求。但异常说明本身并不属于方法类型的一部分。方法类型是由方法的名字与參数的类型组成的。因此。不能基于异常说明来重载方法。

此外。一个出如今基类方法的异常。不一定会出如今派生类方法的异常说明里。换句话说,在继承和覆盖的过程中。某个特定方法的“异常说明的接口”不是变大了而是变小了–这恰好和类接口在继承时的情形相反。

2.构造器

假设一个类继承了某个类同一时候又实现了某个接口,他们有相同的接口方法,但都抛出了不同的捕获性异常,则该子类实现与重写该方法时,则方法声明处不能抛出不论什么捕获性异常了。

假设调用的父类构造器抛出捕获性异常。则子类对应的构造器也仅仅能抛出。不能在构造器里进行捕获。构造器抛出异常时正确的清理方式:比方在构造器中打开了一个文件,清理动作仅仅有在对象使用完毕而且用户调用了特殊的清理方法之后才干得以清理,而不能直接在构造器里的finally块上关闭,由于finally块是无论是否有异常都会关闭,而构造器执行成功能外界须要这个文件流。

但假设在文件成功打开后才抛出异常,则须要关闭文件,并向外界抛出异常信息:

public class InputFile {
private BufferedReader in;
public InputFile(String fname) throws Exception{
try {
in = new BufferedReader(new FileReader(fname));
} catch (FileNotFoundException e) {
System.out.println("could not open "+fname);
throw e;
}catch (Exception e) {
try {
in.close();
} catch (IOException e1) {
System.out.println("in.close() unsuccessful");
throw e;
}finally{
}
}
}
public String getLine(){
String s;
try {
s=in.readLine();
} catch (IOException e) {
throw new RuntimeException("readLine() failed");
}
return s;
}
public void dispose(){
try {
in.close();
System.out.println("dispose() successful");
} catch (IOException e) {
throw new RuntimeException("in.close() failed");
}
}
public class CleanUp {
public static void main(String[] args) {
try {
InputFile inputFile = new InputFile("D:/workspace/testjava/src/com/test/CleanUp.java");
try {
String s;
int i = 1;
while ((s = inputFile.getLine()) != null) {
}
} catch (Exception e) {
System.out.println("caught Exception in main");
e.printStackTrace(System.out);
}finally{
inputFile.dispose();
}
} catch (Exception e) {
System.out.println("InputFile construction failed");
}
}
}

InputFile 的构造器接受字符串作为參数。该字符串表示所要打开的文件名称。

在try块中,会使用此文件名称建立了FileReader对象。FileReader对象本身用处并不大,但能够用它来建立BufferedReader对象。注意。使用InputFile 的优点就是把两步操作合二为一。这样的通用的清理习惯使用方法在构造器不抛出不论什么异常时也应该运用,其基本规则是:在创建须要清理的对象之后,马上进入一个try-finally语句块。

3.异常匹配

抛出异常的时候。异常处理系统会依照代码的书写顺序找出“近期”的处理程序。找到匹配的处理程序之后,它就觉得异常将得到处理,然后就不再继续查找。

查找的时候并不要求抛出的异常处理程序所声明的异常全然匹配。

派生类的对象也能够匹配其基类的处理程序,样例例如以下:

public class Annoyance extends Exception{}
public class Sneeze extends Annoyance{}
public class Human {
public static void main(String[] args) {
try {
throw new Sneeze();
} catch (Sneeze e) {
System.out.println("catch Sneeze");
}catch (Annoyance e) {
System.out.println("catch Annoyance");
}
try {
throw new Sneeze();
}catch (Annoyance e) {
System.out.println("catch Annoyance exception");
}
}
}

Sneeze异常会被第一个匹配的catch自居补货。也就是程序里的第一个。

然而假设将这个catch字句删掉,仅仅留下Annoyance的catch子句,该横向仍然能正常执行,由于这次补货的是Sneeze的基类。换句话说。catch(Annoyance e)会捕获Annoyance以及全部从它派生的异常。

假设把捕获基类的catch子句放在最前面,以此想把派生类的异常全给“屏蔽”掉,就像这样:

try {
throw new Sneeze(); }catch (Annoyance e) {
//...
} catch (Sneeze e) {
//.....
}

这样编译器就会发现Sneeze的catch子句永远也得不到执行。因此会报错。

4.其它的可选方式

异常处理的一个重要的原则是“仅仅有在你知道怎样处理的情况下才捕获异常”。实际上,异常处理的一个重要目标就是把错误处理的代码同发生错误的地点相分离。

这使你能在一段代码中专注于要完毕的事情。至于怎样处理错误,则放在还有一段代码中。

这样以来,主干代码就不会与错误处理逻辑混在一起,也更easy理解和维护。

“被检查的异常”可能使问题变得复杂,由于它们强制你在可能还没有准备优点理错误的时候被迫加上cacth子名,即使我们不知道怎样处理的情况下,这就导致了异常的隐藏:

try{
//… throw …
}catch(Exception e){//什么都不做
}

把“被检查的异常”转换为“不检查的异常”:当在一个普通方法里调用别的方法时,要考虑到“我不知道该怎样处理这个异常,可是也不能把它‘吞’了,或者仅仅打印一些没用的消息”。

JDK1.4的异常链提供了一种新的思路来解决问题。能够直接把“被检查的异常”包装进RuntimeException里面:

try{
//… throw 检查异常…
}catch(IDontKnowWhatToDoWithThisCheckedException e){
Throw new RuntimeException(e);
}

假设想把“被检查的异常”这样的功能“屏蔽”掉的话,上面是一个好的办法。不用“吞”掉异常,也不必把它放到方法的异常声明里面。而异常链还能保证你不会丢失不论什么原始异常的信息。

你还能够在“知道怎样处理的地方”来处理它,也能够其它上层catch里通过 throw e.getCause(); 再次抛出原始的“被检查的异常”。

5.异常使用指南

应该在以下情况使用异常:

1)在恰当的级别处理问题。(在知道该怎样处理的情况下才捕获异常。



2)解决问题而且又一次调用产生异常的方法。

3)进行少许修补,然后绕过异常产生的地方继续执行。

4)用别的数据进行计算,以取代方法估计会返回的值。

5)把当前执行环境下能做的事情尽量做完,然后把相同的异常重抛到更高层。

6)把当前执行环境下能做的事情尽量做完,然后把不同的异常抛到更高层。

7)终止程序。

8)进行简化。

9)让类库和程序更安全。

6.总结

异常时java程序设计不可切割的一部分。假设不了解 怎样使用它们,那你仅仅能完毕非常有限的工作。异常处理的有点之中的一个就是它使得你能够在某处集中精力处理你要解决的问题,而在还有一边处理你编写的这段代码中产生的错误。

java编程思想读书笔记 第十二章 通过异常处理错误(下)的更多相关文章

  1. 《Java编程思想》笔记 第十二章 通过异常处理错误

    1.异常也是对象 标准异常类都有两个构造器,一个默认,一个接受字符串. 抛异常与方法返回类型不同,但有相似效果使当前方法退出并返回,抛异常可以看作是一种不同的返回机制.(异同点不必深究) Throwa ...

  2. 《Java编程思想》笔记 第十九章 枚举类型

    1.基本enum特征 所有创建的枚举类都继承自抽象类 java.lang.Enum; 一个枚举类,所有实例都要在第一句写出以 ,隔开. 如果只有实例最后可以不加 : 枚举类因为继承了Enum,所以再不 ...

  3. 《Java编程思想》笔记 第十六章 数组

    1 数组 数组和容器比较,数组的优点也只剩访问效率高这一点了. 2 数组是第一级对象 数组也是一个对象,和其他普通对象一样在堆中创建, int[ ] arr  arr是数组的引用. 可以隐式创建数组对 ...

  4. 《Java编程思想》笔记 第十五章 泛型

    1 泛型 “泛型”意思就是适用于许多类型. 使用泛型的目的之一: 指定容器持有什么类型,让编译器确保正确性,而不是在运行期发现错误. 这个容器可以看成是有其他类型对象作为成员的类,而不单单只是JDK中 ...

  5. 《Java编程思想》笔记 第十四章 类型信息

    1.RTTI:在运行时识别一个对象类型 JAVA在运行时 有时要 识别对象和类的信息这个机制叫RTTI.Java提供了两种机制去做这件事.传统的RTTI 和 反射. 传统的RTTI  假定编译时就已经 ...

  6. Java编程思想读书笔记_第7章

    final关键字类似const: import java.util.*; public class FinalData { static Random rand = new Random(47); f ...

  7. Java编程思想读书笔记_第6章

    如何创建一个包: 比如创建一个包名为com.huawei 则在一个目录下如(d:\java)创建目录com/huawei 然后在huawei目录下创建一个文件Assist.java package c ...

  8. Java编程思想读书笔记_第三章

    本章提到的关于==的部分,一个完整的实验如下: class Test { public static void main(String[] args) { Integer i = new Intege ...

  9. Java编程思想读书笔记_第8章

    覆盖私有方法 class Father { private void f() { System.out.println("Father::f()"); } public stati ...

随机推荐

  1. SCP 命令(自己总结)

    \ svn 删除所有的 .svn文件 find . -name .svn -type d -exec rm -fr {} \; linux之cp/scp命令+scp命令详解   名称:cp 使用权限: ...

  2. Git-添加或删除文件

    添加文件 $  git add blash $  git commit "add blash file" $ git push -u origin master 删除文件 $ gi ...

  3. 【linux高级程序设计】(第十四章)TCP高级应用 3

    控制socket文件描述符属性 1.set/getsockopt()修改socket属性 int getsockopt (int __fd, int __level, int __optname, v ...

  4. Appium+python自动化13-native和webview切换【转载】

    前言 现在大部分app都是混合式的native+webview,对应native上的元素通过uiautomatorviewer很容易定位到,webview上的元素就无法识别了. 一.识别webview ...

  5. python cProfile分析程序性能

    转自:http://xianglong.me/article/analysis-python-application-performance-using-cProfile/?utm_source=tu ...

  6. window 10 64bit 安装nodejs v7.0.5 + npm v4.1.2 + Express 4.x及搭建web开发环境

    1.先安装nodejs.npm. 2.然后安装Express (4.0之后需要安装express-generator) npm install -g express npm install -g ex ...

  7. Codeforces 897 A.Scarborough Fair-字符替换

      A. Scarborough Fair   time limit per test 2 seconds memory limit per test 256 megabytes input stan ...

  8. 洛谷 P1739 表达式括号匹配【STL/stack/模拟】

    题目描述 假设一个表达式有英文字母(小写).运算符(+,-,*,/)和左右小(圆)括号构成,以"@"作为表达式的结束符.请编写一个程序检查表达式中的左右圆括号是否匹配,若匹配,则返 ...

  9. Struts2笔记--文件上传

    Servlet 3.0规范的HttpServletRequest已经提供了方法来处理文件上传但这种上传需要在Servlet中完成.而Struts2则提供了更简单的封装. Struts2默认使用的是Ja ...

  10. Map泛型集合-输入名字输出成绩

    package collection; import java.util.HashMap; import java.util.Map; import java.util.Scanner; import ...