2

我无法理解在 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.Exceptionthrows子句中指定异常。因此,我的问题是:

为什么不管throws方法的子句runException()throws ExceptionA, ExceptionBor ,下面的代码输出都一样throws Exception

我在 Eclipse 中使用 Oracle JVM-12。提前致谢!

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();
        }
    }
}

输出:

In main(), 'catch(ExceptionA e){}', caught exception: class ExceptionA
In main(), 'catch(ExceptionB e){}', caught exception: class ExceptionB
4

5 回答 5

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){}块时,异常几乎可以是任何东西,但您只有一个通用代码块来处理它。

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

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
}
于 2019-11-14T16:36:14.663 回答
0

作为一项规则,你永远不应该catch (Exception ex)。因为这也会捕获 RuntimeExceptions。有时捕获 (Throwable t) 或使用 Thread.setDefaultUncaughtExceptionHandler 自定义未捕获的异常处理程序以捕获异常并将其显示给用户是有意义的。有时我会捕获一个异常,将它包装在一个 RuntimeException(或一个错误)中并抛出它

当涉及到异常时,你真的应该只在你可以对它们做某事时捕获它们,或者当你想确保异常不会导致方法的其余部分无法处理时。

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

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

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

于 2019-11-14T16:25:20.307 回答
0

引用@Carlos Heuberger,我的代码输出相同,无论throws方法的子句runException()throws ExceptionA, ExceptionB还是throws Exception因为:

异常的运行时类型用于选择 catch 子句:见14.20.1。执行try-catch

无论用于引用方法抛出的异常对象的异常引用类型(在本例ExceptionA中为ExceptionBor ) ,此类方法都将抛出or类型的对象。这些对象的赋值将与方法的前两个 catch 的 catch 参数兼容。ExceptionrunException()ExceptionAExceptionBmain()

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

于 2019-11-22T12:29:45.567 回答
0

这是因为,你一直在扔子类,

try{
        if(what == 'A') 
            throw new ExceptionA();
        else if (what == 'B')
            throw new ExceptionB();
    }

依次被抛出的“异常类”,

catch(Exception e){
        throw e;
    }

分配给“Exception class(at Exception e)”后,如果你指定 throwing a Superclass type throws objectReference at

public static void runException(char what) throws Exception){

或子类类型将objectReferences 抛出

public static void runException(char what) throws ExceptionA, ExceptionB){
  • 由于 java 编译器允许您指定throws ObjectReference,如果它是在 try 语句中抛出的对象的超类。
于 2021-09-15T22:25:52.183 回答