需要Java“更精确地重新抛出异常”

时间:2019-11-14 16:21:16

标签: java exception rethrow

我无法理解Java 7和更高版本中精确重新抛出的工作原理。正如https://www.theserverside.com/tutorial/OCPJP-Use-more-precise-rethrow-in-exceptions-Objective-Java-7中指出的那样,在Java 7和更高版本中,我们可以在方法声明中使用throws子句,并用逗号分隔该方法可能抛出的特定异常列表。如果所有这些异常都是常规异常java.lang.Exception的子类型,我们将能够在捕获该超类的catch块中捕获它们中的任何一个,同时让客户端代码(例如调用方方法)知道哪个异常。实际发生了可能的子类型异常。

最初,我认为为了让客户代码知道实际发生了哪种异常,我们需要在throws子句中指定特定异常的列表。但是,在下面的示例中,即使我们仅在被调用方法的main()子句中指定异常java.lang.Exception,客户端代码(throws方法)似乎也能够检索该信息。因此,我的问题是:

无论方法throws的{​​{1}}子句是runException()还是throws ExceptionA, ExceptionB,为什么以下代码输出的结果相同?

我在Eclipse中使用Oracle JVM-12。预先感谢!

throws Exception

输出:

class ExceptionA extends Exception{}
class ExceptionB extends Exception{}

public class RethrowingAndTypeChecking{
    public static void runException(char what) throws Exception{
    //public static void runException(char what) throws ExceptionA, ExceptionB{
        try{
            if(what == 'A') 
                throw new ExceptionA();
            else if (what == 'B')
                throw new ExceptionB();
        }
        catch(Exception e){
            throw e;
        }
    }

    public static void main (String args[]){
        char ch;
        for (int i=0;i<2;i++) {
            if(i==0) ch='A';
            else ch = 'B';

            try{
                runException(ch);
            }
            catch(ExceptionA e){
                System.out.print("In main(), 'catch(ExceptionA e){}', caught exception: " + e.getClass());
            }
            catch(ExceptionB e){
                System.out.print("In main(), 'catch(ExceptionB e){}', caught exception: " + e.getClass());
            }
            catch(Exception e){
                System.out.print("In main(), 'catch(Exception e){}', caught exception: " + e.getClass());
            }               
            System.out.println();
        }
    }
}

4 个答案:

答案 0 :(得分:1)

您缺少的是需要以不同方式处理那些可能的异常的情况。您的代码正在捕获个别异常,但是,粗略地说,它正在执行相同的操作。

如果您处理ExceptionA的方式与处理ExceptionB的方式大不相同,那么抓住广泛的Exception将不允许您这样做:

catch(Exception e){
    // something unexpected happened
    // e could be an ExceptionA problem
    // e could be an ExceptionB problem
    // e could be any other unchecked exception
}

输入catch(Exception e){}块时,几乎可以是任何异常,但是您只有一个通用代码块来处理它。

此外,如果您要调用的方法声明了特定的 checked 异常,则编译器可以帮助您仅处理这些异常,从而增加了代码的可预测性

try{
    runException(ch);
} catch(ExceptionA e){
    // code specific to handling ExceptionA problems
} catch(ExceptionB e){
    // code specific to handling ExceptionB problems

} catch(ExceptionC e){ //will not compile, because not declared by runException
    // code specific to handling ExceptionB problems
}

答案 1 :(得分:0)

这些throws声明是为了让您更明确地列出方法中发生的事情。否则,这就是普通的多态性:您使用基类来组合多个子类,但是您绝对不更改实例,这就是为什么在两种情况下都在运行时将异常解析为它们的具体类的原因。

答案 2 :(得分:0)

通常,您绝对不要catch (Exception ex)。因为这也会捕获RuntimeExceptions。捕获(Throwable t)或使用Thread.setDefaultUncaughtExceptionHandler定制未捕获的异常处理程序以捕获异常,然后将其显示给用户有时是有意义的。有时我会捕获一个异常,将其包装在RuntimeException(或错误)中并抛出该异常

当涉及到异常时,您实际上应该只在可以对它们执行某些操作时,或者在您要确保异常不会导致该方法的其余部分无法处理时才捕获它们。

我个人将异常分为3种类型

  1. 代码中的问题:这是您需要解决的
  2. 用户存在的问题:例如,如果您告诉他们输入数字,然后输入“ a”,那是用户的错误
  3. “朋友”异常:例如,SocketException就是一个例子。如果套接字关闭并且您有一个线程在等待输入,它将抛出此异常,释放线程并让您对套接字进行清理。

答案 3 :(得分:0)

引用@Carlos Heuberger,无论方法throws的{​​{1}}子句是runException()还是throws ExceptionA, ExceptionB,我的代码都会输出相同的内容,因为:

  

异常的运行时类型用于选择catch子句:请参见14.20.1. Execution of try - catch

无论哪种异常引用类型(在这种情况下为throws ExceptionExceptionAExceptionB)都用于通过方法{{1 }},该方法将抛出类型为ExceptionrunException()的对象。这些对象的分配与ExceptionA方法的前两个catch的catch参数兼容。

在Java语言规范的8.4.611.2.314.20.1段之后,我了解到我们在方法签名的ExceptionB子句中实际指定的是列表的异常引用类型将与该方法抛出的任何可能的异常对象进行赋值兼容(给定一个类引用类型,我们可以使其指向其自身的实例对象或其子类而不是超类的实例对象)。这告诉其他任何调用方方法在使用throws子句调用该方法时可能必须处理哪些异常。在我的代码示例中,使用子句main()的优点在于,我无需在throws中捕获java.lang.Exception。实际上,如果我在方法throws ExceptionA, ExceptionB中选择子句main(),并从throws Exception中删除runException()块,则会收到编译时错误。这是因为即使我们在运行时抛出cath(Exception)main()对象,编译器也会理解方法ExceptionA可能抛出类型为ExceptionB的异常对象,其分配将与runException()中的任何catch参数不兼容(Exceptionmain()Exception的超类)。