【问题标题】:relax exception catch necessity放宽异常捕获的必要性
【发布时间】:2011-08-12 08:00:12
【问题描述】:

Java 中是否有可能摆脱捕获非RuntimeException 异常的必要性?也许编译器标志?

我知道推广捕获的原因,但想做简单而直接的工具来执行他们的要求。因此,如果出现问题,我不想赶上,而是退出应用程序,并因有意义的异常而崩溃。通常这会像这样结束:

try {
     connection.close();
} catch (IOException e) {
    throw new RuntimeException(e);
}   

其中引入了 4 行代码混乱,并在错误输出中引入了包装 RuntimeException mess。有时它甚至会促使人们将大的 try ... catch (Throwable ..) 块包裹在任何东西上,这可能是我们心爱的“发生未知错误”警告框的原因......

【问题讨论】:

  • 也许会抛出一个更有意义的未经检查的异常?如果你能把这四行封装成一个类或静态方法,那还不错。
  • 听起来您可能想建立一个约定来处理应用程序中的异常。我读这个问题的次数越多,似乎有些地方需要真正处理异常,以及应用程序必须终止的其他地方。
  • 对于关闭的异常,您不太可能对异常执行任何操作,人们倾向于编写类似于 IOUtils.closeQuietlycloseQuietly 方法,但用于连接。
  • 我喜欢 Java 异常的意义,所以我认为没有必要添加任何内容。每个自定义异常都会混淆或以轻微的形式使调试输出中应用程序故障的真正根源复杂化。

标签: java checked-exceptions


【解决方案1】:

您可以在方法原型中使用throws 关键字来避免try-catch 阻塞。它最终将异常抛出给 JVM 的默认异常处理程序,如果您的代码中没有指定 catch 块来处理引发的异常,该处理程序将停止应用程序。

【讨论】:

  • 如果方法覆盖了非throws 方法,例如Thread.run(),则不会工作
【解决方案2】:

在第一次看到异常时使应用程序崩溃是非常不好的做法。尤其是当某些工作未保存并且应用程序正在使用一些在应用程序终止执行之前需要释放和清理的资源时。一些非常流行的软件曾经这样做......而不是“修复”问题,他们在应用程序重启时引入了数据恢复功能。然而,诀窍是,这不是好的软件工程。

至少,您的应用程序不应该在遇到第一个异常/错误时崩溃,而是通过有意义的消息恢复。将所有内容都包装在 RuntimeException(甚至是 Throwable)中,尤其是不要对它做任何事情,这是一种懒惰。

Java 不支持任何类型的标志,因为有 1) 一种解决方法,以及 2) 处理这种情况的更好方法。例如:

1.在调用方法中处理异常

您可以在方法声明中添加 throws 关键字,直到您的 static public void main 方法,如果不处理异常,最终将导致应用程序崩溃并出现堆栈跟踪。

class Foo {
   public void someMethod(....) throws IllegalArgumentException, IOException {
      ...
   }

   static public void main(String...args) throws Throwable {
      new Foo().someMethod();
   } 
}

此方法不提供任何可恢复性方法,并且可能会让您的用户不满意(如果他们从控制台运行应用程序,则会出现大量无意义的 stachtrace,或者如果他们从快捷方式或 GUI 启动应用程序,则根本什么都没有)。另外,如果你有一些获取的资源,当异常发生时你将无法清理它们。至少,你的main 应该catch (Throwable e) 并在抛出上面的异常之前输出一些东西。类似的东西:

class Foo {
   public void someMethod(....) throws IllegalArgumentException, IOException {
      ...
   }

   static public void main(String...args) {
      try {
         new Foo().someMethod();
      } catch (...) {
         // output or log exception here and, optionally, cleanup and exit
      }
   } 
}

** 编辑 **

考虑这种情况:一个程序正在初始化一些资源以处理一些数据,然后在处理过程中发生一些运行时异常(或错误),应用程序崩溃,但资源没有释放或释放。然而,在 Java 中,可以做到这一点

public E doSomething() throws RuntimeException {
   // declare a bunch of resources

   try {
      // process resources with unchecked exceptions
   } finally {
      // free resources
   }

   // return some result
}

并在错误或成功时干净地退出该方法,甚至可能为“posterity”记录运行时错误。

2。记录错误并返回一些有意义的值

记录是一种非常好的做法。您可以向您的用户显示一些消息,告诉他们在不使整个事情崩溃的情况下无法执行该操作,并为您提供一些用户在做什么和在哪里做的痕迹。一个简单的日志系统可能是:

class Foo {
   static private final Logger LOG = Logger.getLogger(Foo.class.getName());

   public boolean doSomethingImpl(...) {
      boolean result = true;
      try {
        ...
      } catch (SomeException e) {
         LOG.log(Level.SEVERE, "meaningful message why method could not do something!", e);
         result = false;
      }
      return result;
   }

   public void doSomething() {
      if (!doSomethingImpl(...)) {
          // handle failure here
      }
   }
}

默认情况下,Logger 会将所有内容输出到err 输出流,但您可以添加自己的处理程序:

// loggers are singletons, so you can retrieve any logger at anytime from
// anywhere, as long as you know the logger's name
Logger logger = Logger.getLogger(Foo.class.getName());

logger.setUseParentHandlers(false);  // disable output to err
logger.addHandler(new MyHandler());  // MyHandler extends java.util.logging.Handler

Java 已经附带了一些默认的日志记录处理程序,其中之一是 writes to file

等等

【讨论】:

  • “一看到异常就使应用程序崩溃是非常糟糕的做法。尤其是当某些工作未保存时。”好的。但是在编写小型命令行应用程序或类似插件的可执行文件时,没有工作可以松动,每一行不必要的代码或无聊的调试输出对我来说都是不好的做法。带有代码位置的 java 异常错误输出对我来说很好。其他一些可靠的工具就是这样工作的,例如rsync,它会在失败时记录代码文件和行号。
  • 我理解小工具/插件/等,也许。但是我仍然认为,即使不是真的必要,也应该养成良好的习惯。对于糟糕的代码,没有任何程序如此之小。无论如何,IMO。
  • 好吧,我在寻找好代码的过程中遇到了这个问题......我偏爱简约的小代码,因此我不喜欢 Java 对某些 Exceptions 的预定义检查强制执行。
  • @dronus,那我相信你没有完全理解有例外的意义; Java 有例外是有充分理由的。其中之一是An exception is an event, which occurs during the execution of a program, that disrupts the normal flow of the program's instructions. Exceptions 使得在不同的、可能的事件上分支成为可能;不像简单地返回 null(或 0)或依赖调用另一个方法来找出什么/为什么/在哪里。我建议你阅读整个部分,更具体地说是download.oracle.com/javase/tutorial/essential/exceptions/…
  • 嗯,这正是我想要的。出现问题时,程序指令的正常流程应该被打乱,在我的情况下,程序应该立即终止,将问题描述给stderr。但是 Java 强制我捕获一些异常并自己进行特殊处理。因此,如果我决定在这些程序上退出程序,我必须编写额外的重新抛出代码,并且生成的stderr 输出通常与未捕获的RuntimeException 的输出不同。这就是我不喜欢并想要摆脱的整个事实,这似乎是不可能的。
【解决方案3】:

在 Java 中是否有可能摆脱捕获非 RuntimeException 异常的必要性?

对于已检查的异常,您可以在捕获异常和在方法标头中将其声明为已抛出之间进行选择。

也许是编译器标志?

没有。没有编译器标志可以放松这一点。它是语言设计的基本部分。通过编译器开关放宽检查异常规则会导致严重的库互操作性问题。

【讨论】:

    【解决方案4】:

    我认为 JVM 没有办法解决这个问题。你最好的办法是让你的方法重新抛出异常,这样可以消除代码中的“混乱”,然后让你的主程序抛出异常。这应该会将错误传播到程序的顶部。

    但是请记住,异常实际发生的地方是让用户知道发生了什么的更好的地方(即,当这个特定的 IOException 发生时它正在做什么)。如果所有错误都简单地传播到顶层,您将失去此解决方案。

    【讨论】:

    • '你最好的办法是让你的方法重新抛出异常'如果重写框架方法通常是不可能的。例如Thread.run() 方法不允许抛出异常。
    【解决方案5】:

    您确实有能力将您的异常提高一个级别。这是一个例子

    public class Foo {
        public Foo() {
            super();
        }
        public void disconnect(connection) throws IOException {
            connection.close();
        }
    }
    

    【讨论】:

    • 如果覆盖 Thread.run() 这样的框架方法,这是不可能的
    • 啊,谢谢,我不知道。过去没有真正接触过线程管理。
    【解决方案6】:

    使用“Throws”来避免错误..但这不是好的编程习惯

    【讨论】:

      猜你喜欢
      • 2017-06-19
      • 2023-04-10
      • 2015-11-29
      • 1970-01-01
      • 2022-07-07
      • 1970-01-01
      • 1970-01-01
      • 2014-11-24
      相关资源
      最近更新 更多