代码如下: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呢?

解决方案 »

  1.   

    捕获的应该是Exception3啊,为什么只能捕获Exception1呢????????你从哪知道是捕获的Exception1呢??这个程序捕获的一定是Exception3,你的理解是正确的,把你的程序修改一下就可以验证了:
    修改后如下: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)
      

  2.   

    楼上的,我想知道既然捕获的是Exception1,那为什么把catch内的参数改为Exception3后就出错了呢?
      

  3.   

     编译器就检查下类型 不知道对象具体的具体类型。 所以只能Exception1
      

  4.   

    支持,虽然看不懂但学习气氛很浓,good
      

  5.   


    支持编译器只检查编译时的类型,而无法确定它实际运行时的类型
    楼主的代码中,变量 c 的类型是 A, 编译器就认为它必须要捕捉的异常是 A.f 声明抛出的异常
      

  6.   


    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
      

  7.   

    你在捕获异常后,
    在catch块里面用 调用 e.printStackTrace();
    不就可以知道是捕获那个异常了吗?
      

  8.   

    A c= new C();//A类对象指向C类引用
    try {
        c.f();
    } catch(Exception1 e) {
                                  
    }
    java在程序编译期是看代码左边的执行类,不会去考虑父类对象指向子类引用的,所以c.f()调用的是A类中的f()方法,只有当程序在运行期的时候才会真正知道调用的是C类的f()方法,这样解释你应该能明白了,为什么在你贴的程序里捕获的异常必须是Exception1了吧。
      

  9.   

    我不认为你说的是正确的子类重写父类方法时,可以声明抛出的异常,和它是不是一层一层向外抛出没有直接关系,而是因为根据多态原理,子类是比父类更精确的类型,因此子类的一个对象必须是其父类的一个对象。对于这类规则,它最重要的一点是,必须满足非 RuntimeException 或它的子孙类异常必须处理的问题:要么捕捉,要么声明抛出。在楼主的例子中,如果 C.f() 方法声明抛出的是 A.f() 声明抛出的异常的子类,则在编译和运行时都不会有问题:
    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 中的异常处理原则
      

  10.   

    顶一个在这段代码中,可能还不是很明显的看出运行时和编译时来,因为在人看来,变量 c 它就是指向一个 C 的对象,如果改成这样: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 实际引用的对象的类型是不可能在编译时就确定的,它是在程序运行时由人在 dos 窗口输入的字符决定的
      

  11.   

    谢谢大家的帮助啊,我说下我自己的看法,在C类中抛出一个Excepion3的对象,throw new Exception3(),由于子类在调用其构造方法时,必然首先会调用其父类的所有构造方法,所以在throw new Exception3()时,同时得到一个Exception2和一个Exception1对象,因此在捕获异常时,必须用Exception1类型,不知道这样理解对不对?
      

  12.   

    不是你理解的这么回事,把你的代码:A c= new C();
    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 异常必须被捕捉或声明抛出。
      

  13.   

    从上面的分析中,也应该可以分析出一个原因:为什么所有的语句,只需要捕捉 Exception 就可以捕捉所有的异常:
    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 可以检测到不可能被执行的代码而报错
      

  14.   

    从上面的分析中,也应该可以分析出一个原因:为什么所有的语句,只需要捕捉 Exception 就可以捕捉所有的异常:
    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 可以检测到不可能被执行的代码而报错