7.java标准异常
Throwable这个java类用来表示任何可以作为异常被抛出的类.Throwable对象可以分成两种, 一种时error, 用来表示编译时和系统错误, 一般用户不需要关心, 需要关心得是另一种Exception, 表示可以被抛出的基本类型, 在java类库, 用户方法,以及运行时故障可能抛出Exception行异常, 所以java一般更关心Exception.
7.1 特例: RuntimeException
如果调用了null的对象, 就会抛出NullPointerException, 这不需要你亲自动手去抛出, 而是java会自动抛出, 而NullPointerException属于运行时异常, 是继承自RuntimeException, 运行时异常不需要亲自去动手捕获, java会自动帮你捕获;
public class NeverCaught{
static void f() {
throw new RuntimeException("From f()");
}
static void g() {
f();
}
public static void main(String[] args) {
g();
}
}
对于这种异常类型, 不需要异常说明, 其输出就被报告给了System.err
Exception in thread "main" java.lang.RuntimeException: From f()
at NeverCaught.f(NeverCaught.java:3)
at NeverCaught.g(NeverCaught.java:6)
at NeverCaught.main(NeverCaught.java:9)
[Finished in 1.0s with exit code 1]
记住只有RuntimeException及其子类是可以在代码中忽略的, 其他类型的异常处理有编译器强制执行.
8.使用finally进行处理
对于一些代码, 无论try块是否有异常抛出, 都能得到处理, 比如对文件操作, 执行完了总是应该把文件关闭掉, 这时候就可以在异常处理最后再加上finally字句
class ThreeException extends Exception{}
public class FinallyWorks {
static int count = 0;
public static void main(String[] args) {
while(true) {
try {
if (count++ == 0) {
throw new ThreeException();
}
System.out.println("No Exception");
} catch (ThreeException e) {
System.out.println("ThreeException");
} finally {
System.out.println("In finally clause");
if (count == 2) break;
}
}
}
}
ThreeException
In finally clause
No Exception
In finally clause
如果try中有return呢? 是否还是能总是被执行呢?
public class MultipleReturn {
public static void f(int i) {
System.out.println("Initialization that requires cleanup");
try {
System.out.println("point 1");
if (i == 1) return;
System.out.println("point 2");
if (i == 2) return;
System.out.println("point 3");
if (i == 3) return;
System.out.println("End");
return;
} finally {
System.out.println("Performing cleanup");
}
}
public static void main(String[] args) {
for (int i = 1; i <= 4; i++) {
f(i);
}
}
}
结果是肯定的当然是能的
Initialization that requires cleanup
point 1
Performing cleanup
Initialization that requires cleanup
point 1
point 2
Performing cleanup
Initialization that requires cleanup
point 1
point 2
point 3
Performing cleanup
Initialization that requires cleanup
point 1
point 2
point 3
End
Performing cleanup
遗憾: 异常丢失, 异常作为程序出错的标志, 应该不能被忽略, 然而还是有可能被轻易的忽略的, 用某些特殊的方式使用finally子句, 就会发生这种情况:
class VeryImportantException extends Exception {
public String toString() {
return "A very important exception!";
}
}
class HoHumException extends Exception {
public String toString() {
return "A trivial exception";
}
}
public class LostMessage {
void f() throws VeryImportantException{
throw new VeryImportantException();
}
void g() throws HoHumException {
throw new HoHumException();
}
public static void main(String[] args) {
try {
LostMessage lost = new LostMessage();
try {
lost.f();
} finally {
lost.g();
}
} catch (Exception e) {
System.out.println(e);
}
}
}
结果只输出了A trivial exception, VeryImportantException咋被吃掉了? 被finally子句中抛出HoHumException给取代了.
更加粗暴的一种异常丢失情形:
try {
throw new Exception();
} finally {
return;
}
9. 异常限制
- 当子类覆盖父类的方法时, 只能抛出基类方法异常说明中列出的异常
- 异常限制对构造器不起作用,派生类的构造器的异常说明必须不少于基类构造器的异常说明。且派生类构造器不能捕获基类构造器抛出的异常。
- 不抛出性。派生类方法可以不抛出任何异常,即使它是基类所定义的异常。
- 尽管在继承过程中,编译器对异常说明所强制要求,但是异常说明本身不是方法的一部分,方法是由方法的名字与参数的类型组成的。
- 异常限制与继承规则不同。某个方法的异常说明 不能变大,只能变小或者不变。
10 构造器
如果异常发生了, 所有东西总是能被正确的清理吗?
大多数情况下是可以的, 但是遇到构造器就会有各种问题, 比如在构造器中打开了一个文件,
进行异常捕获, 应该在finally中关闭文件吗? 万一如果文件都还没有打开就抛出异常, 那么关闭文件就没有意义了, 最安全方法就是嵌套使用try
import java.io.*;
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 e2) {
System.out.println("in.close() unsucessful");
}
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 e2) {
throw new RuntimeException("in.close() failed");
}
}
}
public class Cleanup {
public static void main(String[] args) {
try {
InputFile in = new InputFile("Cleanup.java");
try {
String s;
while((s = in.getLine()) != null) {
// System.out.println(s);
}
} catch (Exception e) {
System.out.println("Caught Exception in main");
} finally {
in.dispose();
}
} catch (Exception e) {
System.out.println("InputFile construction failed");
}
}
}
11 异常匹配
抛出异常的时候, 异常处理系统会按照代码的书写顺序找出”最近”的处理程序, 找到匹配的处理程序后, 就认为异常将得到处理, 然后就不在继续查找.
查找的时候并不要求异常同处理程序所声明的异常完全匹配, 派生类的对象也可以匹配基类的处理程序:
class Annoyance extends Exception {}
class Sneeze extends Annoyance {}
class Human {
public static void main(String[] args) {
try {
throw new Sneeze();
} catch (Sneeze s) {
System.out.println("Caught Sneeze");
} catch (Annoyance a) {
System.out.println("Caught Annoyance");
}
try {
throw new Sneeze();
} catch (Annoyance a) {
System.out.println("Caught Annoyance");
}
}
}
输出是
Caught Sneeze
Caught Annoyance
可以看到如果捕获了sneeze异常, 那么下一句就不会再执行了, 而且也可以将这一句去掉也是能运行的, 但是如果交换这两句的话, 就会出现错误, 以为这样第二个catch子句就永远无法访问了.