【问题标题】:What is the value of letting an exception move up the call stack?让异常向上移动调用堆栈的价值是什么?
【发布时间】:2015-05-17 23:24:22
【问题描述】:

我只是 reading about try-catch blocks 和异常处理,我在“传播 IOExceptions”部分读到一些让我有点困惑的东西。 Jenkov 说,如果方法 C 抛出 IOException - public void MethC() throws IOException{}- 但没有 try-catch 来处理它,它会将调用堆栈汇总到可能有 try-catch 来处理异常的调用方法

try-catch-finally 块的全部意义似乎是处理一个异常,该异常会在未处理的情况下使您的程序崩溃,对吗?那么,为什么让异常将调用堆栈回滚到链上某处的try-catch?价值是什么?如果有的话,在我看来,让异常向上滚动而不是立即处理它只会损害程序并且是错误的形式并导致效率低下或效率低下。

【问题讨论】:

  • 如果MethC 没有正确处理异常的选项,那么它应该传播到它的调用方法,以便它可以处理它。例如,通过记录异常或使用 UI 向用户显示通知。这最终得到了更好的代码MethCs 唯一的责任是使用 I/O 操作做某事,它不应该关心打开日志文件或启动与用户的对话。 “妥协程序” 为什么以及如何? “坏形式” 怎么样? “导致效率低下或效率低下” 如何?你能详细说明你的陈述吗?
  • @Tom 我在课堂上处理异常方面做得并不多,我正在尝试自学一些可能对我正在从事的项目有用的东西。当我阅读有关传播异常的信息时,在我看来,处理异常的最佳位置(例如IOException)就是它发生的地方。关于 "compromise" 部分,在我看来,让异常向上移动是没有意义的,并且可能对程序的运行或其他东西造成危险。同样,这是我试图自学,所以我的担忧可能是没有根据的。
  • “再说一次,这是我试图自学,所以我的担忧可能是没有根据的。” 但是您应该考虑编辑您的问题以解释您为什么这么认为(妥协,低效,糟糕的形式),因此其他人可以解释为什么这是对或错的。
  • 有些人确实同意你的观点。例如“去”语言的人。而且很多人不喜欢检查异常(java 是唯一拥有它的语言)。 Java 是一种非常古老的语言。新语言从 Java 提供的真实用户体验中吸取了很多教训(好的或坏的)。尽管他们随后嘲笑 java 并不酷 :(

标签: java exception try-catch


【解决方案1】:

try-catch-finally 块的全部意义似乎是处理一个异常,该异常会在未处理的情况下使您的程序崩溃,对吗?

不完全是。异常并不总是导致程序崩溃,它可能“只是”导致它的某些部分无法正常工作。确实try-catch-finally是用来处理异常的。

为什么让异常将调用堆栈回滚到链上某处的 try-catch?价值是多少?

因为有时发生异常的方法不适合决定如何处理它。

我举个例子:看Integer.parseInt(String s)方法,它接受String,并尝试根据其内容将其转换为int。由于这是在运行时完成的,并且您无法提前知道字符串是什么,因此您必须考虑该字符串不是数字,例如,如果它来自用户输入。如果它不是可解析的字符串,您希望该方法如何处理它?为什么它要决定它并非设计为执行的操作的结果?

这就是该方法抛出NumberFormatException - if the string does not contain a parsable integer 的原因。调用该方法的人有权决定应该做什么,因为该方法仅用于将字符串转换为数字。它可以请求一个新字符串,因为最后一个字符串无效,或者只使用默认数字作为占位符。

现实生活中的类比:老板告诉他的秘书安排在给定日期与员工会面,秘书发现员工在该日期休假。秘书应该自己处理吗?可能不是。相反,他们会按照“InvalidMeetingTime”的方式向老板“抛出异常”,让老板决定做什么。

在我看来,让异常向上滚动而不是立即处理它只会损害程序并且是错误的形式并导致效率低下或效率低下。

这就是调用范围有责任正确委派事件的原因。要么自己处理它,要么向上传递,直到有东西处理它。

在Lesson: Exceptions查看更多信息。

【讨论】:

  • 类比说的很清楚。我想一个更好的问题是为什么不将所有内容都包装在try-catch 中以处理每个调用堆栈的每个级别的异常,但似乎已经存在一个问题。非常感谢
【解决方案2】:

只要您的设计需要,让异常渗透到调用堆栈中是完全可以的。一个简单的示例是,如果您的 main 方法在调用周围有一个 try/catch 块,该调用可能具有非常深的调用堆栈。如果在任何时候发生错误,它可以throw 一个异常,并且程序可以优雅地结束,main 中的catch 说“对不起,发生了以下异常,所以程序正在结束”。

从不捕获异常并让程序异常终止是不好的设计,但是如果在实例中有意义的话,从实际异常发生的地方捕获它的多个级别并没有错。

问题经常发生在代码深处,处理它的正确方法(显示对话框?打印错误?安静地处理?)可能因调用路径不同而不同,并且可以选择要做什么调用堆栈中的其他位置。

【讨论】:

    【解决方案3】:

    一般来说,异常应该在调用堆栈中能够处理它的地方处理。

    例如,假设您有一个库方法,它采用文件路径并以byte[] 形式返回文件的全部内容:

    public static byte[] readFile(final String filePath) throws IOException {
        final InputStream inputStream = new FileInputStream(filePath);
        ...
    }
    

    这个函数不可能“处理”一个不存在的文件;如果new FileInputStream(filePath) 引发FileNotFoundException(它是IOException 的子类型),那么这个函数可以做的最好的事情就是通知它的调用者。调用者有更多关于filePath 来自何处以及文件丢失意味着什么的信息;例如,如果filePath 是由用户输入的,那么这可能意味着程序需要向用户显示一条消息,让他/她知道可能是错字。关键是,readFile 没有任何这些信息,也无法做出正确的决定。所以它应该让异常传播到可以传播的调用者。

    【讨论】:

    • 您的回答在逻辑上非常有意义,因为调用者方法比您作为示例包含的方法更了解需要处理的内容和正确的响应。谢谢。
    【解决方案4】:

    有时让异常回滚堆栈会很有帮助,原因有很多(服务、Restfull api 等)。假设您有一个显示或解析文件并将文件名作为参数的方法,您不知道谁将使用您的方法,也不知道为什么会使用您的方法,因此通过抛出异常您给了开发人员另一个机会来显示他的消息或句柄如他所愿。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-11-28
      • 2015-08-26
      • 1970-01-01
      • 2014-04-23
      • 2018-08-29
      • 2014-06-21
      • 1970-01-01
      相关资源
      最近更新 更多