代码如下:class Exception1 extends Exception {}
class Exception2 extends Exception1 {}
class Exception3 extends Exception2 {}
class A {
void f() throws Exception1 {
throw new Exception1();
}
}class B extends A {
void f() throws Exception2 {
throw new Exception2();
}
}class C extends B {
void f() throws Exception3 {
throw new Exception3();
}
}public class E25_Exception {
public static void main(String[] args) {
A c= new C();
try {
c.f();
} catch(Exception1 e) {
}
}
}
为什么在E25_Exception类中,在捕获异常时,为什么需要用Exception1,我觉得f方法被覆盖了,c.f()用的应该是C类的f方法,捕获的应该是Exception3啊,为什么只能捕获Exception1呢?
class Exception2 extends Exception1 {}
class Exception3 extends Exception2 {}
class A {
void f() throws Exception1 {
throw new Exception1();
}
}class B extends A {
void f() throws Exception2 {
throw new Exception2();
}
}class C extends B {
void f() throws Exception3 {
throw new Exception3();
}
}public class E25_Exception {
public static void main(String[] args) {
A c= new C();
try {
c.f();
} catch(Exception1 e) {
}
}
}
为什么在E25_Exception类中,在捕获异常时,为什么需要用Exception1,我觉得f方法被覆盖了,c.f()用的应该是C类的f方法,捕获的应该是Exception3啊,为什么只能捕获Exception1呢?
修改后如下:package com.learnpattern.util;class Exception1 extends Exception {
}class Exception2 extends Exception1 {
}class Exception3 extends Exception2 {
public Exception3(){
StackTraceElement[] st={new StackTraceElement("异常三","F方法","本文件",5)};
this.setStackTrace(st);
System.out.println("先构造本异常对象,再抛出异常!");
}
}class A {
void f() throws Exception1 {
throw new Exception1();
}
}class B extends A {
void f() throws Exception2 {
throw new Exception2();
}
}class C extends B {
void f() throws Exception3 {
throw new Exception3();
}
}public class E25_Exception {
public static void main(String[] args) {
A c = new C();
try {
c.f();
} catch (Exception1 e) {
e.printStackTrace();
}
}
}
运行结果如下:
先构造本异常对象,再抛出异常!
com.learnpattern.util.Exception3
at 异常三.F方法(本文件:5)
支持编译器只检查编译时的类型,而无法确定它实际运行时的类型
楼主的代码中,变量 c 的类型是 A, 编译器就认为它必须要捕捉的异常是 A.f 声明抛出的异常
class Exception1 extends Exception {
Exception1(){
System.out.println("Exception1");
}
private static final long serialVersionUID = 1L;}
class Exception2 extends Exception1 {
Exception2(){
System.out.println("Expection2");
}
private static final long serialVersionUID = 1L;}
class Exception3 extends Exception2 {
Exception3(){
System.out.println("Exception3");
}
private static final long serialVersionUID = 1L;}
class A {
void f() throws Exception1 {
System.out.println("catch1");
throw new Exception1();
}
}class B extends A {
void f() throws Exception2 {
System.out.println("catch2");
throw new Exception2();
}
}class C extends B {
void f() throws Exception3 {
System.out.println("catch3");
throw new Exception3();
}
}public class ExceptionTest {
public static void main(String[] args) {
A c= new C();
try {
c.f();
} catch(Exception1 e) {
}
}
}
你这么看就知道他匹配异常3
在catch块里面用 调用 e.printStackTrace();
不就可以知道是捕获那个异常了吗?
try {
c.f();
} catch(Exception1 e) {
}
java在程序编译期是看代码左边的执行类,不会去考虑父类对象指向子类引用的,所以c.f()调用的是A类中的f()方法,只有当程序在运行期的时候才会真正知道调用的是C类的f()方法,这样解释你应该能明白了,为什么在你贴的程序里捕获的异常必须是Exception1了吧。
A obj = ......
try{
obj.f();
}catch(Exception1 ex){
}在这里,obj 可能是A类,也可能是A类的子类,子孙类
对于 A.f(),它只声明抛出了 Exception1,则上面的代码应该是可以编译通过的运行时也不会有问题,因为类 B、C 重写的 f() 方法声明抛出的都是 Exception1 的子类或子孙类,因此无论上面的 obj.f() 抛出的是 Exception2 还是 Exception3 都会被当作 Exception1 对象而捕捉
那么假设如果 obj 引用的是 A 的子类对象:obj = new C();
而在 C.f() 中又声明抛出了别的异常:IOException
那么这个新冒出来的异常在上面的代码中就不会被捕捉到了,这就违反了 java 中的异常处理原则
new BufferedReader(new InputStreamReader(System.in));
String line = reader.readLine().toLowerCase();A c = null;
if("a".equals(line)){
c = new A();
}else if("b".equals(line)){
c = new B();
}else if("c".equals(line)){
c = new C();
}try{
if(c != null){
c.f();
}
}catch(Exception1 ex){
}这样一来,c 实际引用的对象的类型是不可能在编译时就确定的,它是在程序运行时由人在 dos 窗口输入的字符决定的
try {
c.f();
} catch(Exception1 e) {
}改成:
BufferedReader reader =
new BufferedReader(new InputStreamReader(System.in));
String line = reader.readLine().toLowerCase();A c = null;
if("a".equals(line)){
c = new A();
}else if("b".equals(line)){
c = new B();
}else if("c".equals(line)){
c = new C();
}try{
if(c != null){
c.f();
}
}catch(Exception1 ex){
}
这样一来,程序运行的时候,变量 c 引用的对象到底是 A、B 还是 C,或者是的哪些未知的继承自 A 的子类,编译器就不可能确定得了,它要看用户在运行程序时,在 dos 窗口输入的是什么字符串而定现在从这段代码来看,编译器可以确定,变量 c 所引用的是一个 A 类的类型,而 A.f() 方法声明抛出了一个异常 Exception1,并且编译器发现程序中已经对 A.f() 所抛出的异常进行了捕捉,因此从编译的角度看这段程序应该是没有问题的。而由于 Exception2、Exception3 是Exception1 的子类,因此在程序运行时,无论变量 c 引用的是 B 还是 C 类的对象,由于子类的 f() 方法声明抛出的是 Exception1 的子类异常,因此在上面的代码中,如果子类实现中抛出了Exception2或是Exception3,都会被当作 Exception1 而进行捕捉。比如
c = new B();try{
c.f();
}catch(Exception1 ex){
}这时捕捉到的异常其实应该是 Exception2 的一个对象。而由于 Exception2 是 Exception1 的子类,因此它被当作一个 Exception1 而捕捉,这是合理的,符合多态的原理。
现在假设在 C.f() 方法中,声明抛出了 java.io.IOException,那么在下面的程序段中:
try{
c.f();
}catch(Exception1 ex){
}由于 c 的类型是 A,并且 A.f() 仅仅声明了抛出 Exception1,因此编译应该是没有问题的然而在实际运行时,c 却可能是 C 的一个对象,那么在执行 c.f(); 语句时,程序就会抛出 IOException。而 IOException 并不是 Exception1 的子类,它不会被当作 Exception1 的对象而被捕捉到。这样一来,这个 IOException 就没有被捕捉到了,但是编译器却认为它捕捉到了。这样一来就违反了 java 的异常捕捉原则:所有的非 RuntimeException 异常必须被捕捉或声明抛出。
try{
//......
}catch(Exception ex){
}因为 Exception 是所有异常类的基类,在 try 语句块中无论抛出哪一个异常,都将被当作 Exception 对象而被捕捉。
这也可以解释 java 的另一个异常捕捉原则:当有多个异常需要捕捉时,不能把 Exception 放在第一个要捕捉的异常,也就是说也不能把父类的异常放在子类异常的捕捉声明之前,下面的代码是编译不过的:try{
// ......
}catch(Exception ex){
}catch(Exception1 ex1){
}catch(Exception2 ex2){
}catch(Exception3 ex3){
}因为 Exception2 是 Exception1 的子类,当try语句块内抛出 Exception2 时,它会被当作 Exception1 而被捕捉,这样一来,捕捉 Exception2 的语句就永远不会被执行到,javac.exe 可以检测到不可能被执行的代码而报错
try{
//......
}catch(Exception ex){
}因为 Exception 是所有异常类的基类,在 try 语句块中无论抛出哪一个异常,都将被当作 Exception 对象而被捕捉。
这也可以解释 java 的另一个异常捕捉原则:当有多个异常需要捕捉时,不能把 Exception 放在第一个要捕捉的异常,也就是说也不能把父类的异常放在子类异常的捕捉声明之前,下面的代码是编译不过的:try{
// ......
}catch(Exception ex){
}catch(Exception1 ex1){
}catch(Exception2 ex2){
}catch(Exception3 ex3){
}因为 Exception2 是 Exception1 的子类,当try语句块内抛出 Exception2 时,它会被当作 Exception1 而被捕捉,这样一来,捕捉 Exception2 的语句就永远不会被执行到,javac.exe 可以检测到不可能被执行的代码而报错