【问题标题】:Difference between Throws in method signature and Throw Statements in JavaJava 中方法签名中的 Throw 和 Throw 语句之间的区别
【发布时间】:2013-10-12 04:17:42
【问题描述】:

我试图弄清楚Java中方法签名中的抛出抛出语句之间的区别。 方法签名中的抛出如下:

public void aMethod() throws IOException{
    FileReader f = new FileReader("notExist.txt");
}

抛出语句如下:

public void bMethod() {
    throw new IOException();
}

根据我的理解,方法签名中的throws 是一个通知,该方法可能会抛出这样的异常。 throw 语句是在相应情况下实际抛出创建的对象。 从这个意义上说,如果方法中存在 throw 语句,则方法签名中的 throws 应始终出现。

但是,以下代码似乎没有这样做。代码来自图书馆。我的问题是为什么会这样?我对概念的理解有误吗?

这段代码是 java.util.linkedList 的副本。 @作者乔什·布洛赫

 /**
 * Returns the first element in this list.
 *
 * @return the first element in this list
 * @throws NoSuchElementException if this list is empty
 */
public E getFirst() {
    final Node<E> f = first;
    if (f == null)
        throw new NoSuchElementException();
    return f.item;
}

更新答案:

update 1 : 上面的代码和下面的代码一样吗?

// as far as I know, it is the same as without throws
public E getFirst() throws NoSuchElementException {
    final Node<E> f = first;
    if (f == null)
        throw new NoSuchElementException();
    return f.item;
}

更新 2:检查异常。我需要在签名中有“投掷”吗?是的。

// has to throw checked exception otherwise compile error
public String abc() throws IOException{
    throw new IOException();
}

【问题讨论】:

  • 只是一个小小的修正:throw 语句不会创建可抛出对象;它只是抛出一个已经创建的对象。创建对象的是new 关键字。将throw new MyException() 视为throw (new MyException())。你也可以有MyException e = new MyException(); throw e。看到不同? throw 抛出,new 创建一个实例。

标签: java throw throws


【解决方案1】:

你说的很对。除了我稍后会提到的一件事。

throws 与名称和参数一样是方法 API 的一部分。客户知道如果他们调用了那个方法,他们需要处理那个异常——通过简单地也抛出它或者捕获它并处理它(这实际上可能需要抛出另一个包装原始异常的异常)。 throws 在编译时处理。

throw 是让运行时知道发生了不好的事情的实际行为——我们担心的异常情况实际上已经发生了。所以需要在运行时处理。

但是当您说“如果方法中存在 throw 语句,则应始终出现方法签名中的抛出”时,您并不完全正确。这通常是正确的,但并非总是如此。我还可以调用另一个在我的方法中抛出异常的方法,如果我没有捕获它,我的方法需要抛出它。在那种情况下,我没有明确抛出相同的异常。

最后一点是,当异常是checked异常时,你只需要在throws中声明一个异常——意味着它来自Exception的另一端RuntimeException 的类层次结构。常见的检查异常是 IOException 和 SQLException。如果您不自己处理检查的异常,则必须在方法签名的 throws 部分中列出它们。任何继承 RuntimeException 的东西——比如你的例子中的 NoSuchElementException 以及讨厌的 NullPointerException——都是未经检查的异常,不必被捕获或抛出或任何东西。

通常,您对可恢复问题使用检查异常(客户端知道会发生什么并且可以优雅地处理问题并继续前进)和对灾难性问题(例如无法连接到数据库)使用未检查异常。

如果您能通过所有 AOP 知识,this 是关于如何有效使用检查和未检查异常的精彩讨论。

【讨论】:

  • 如果我们不处理它们,那么在“抛出”中列出检查的异常有什么用?他们会自动捕获吗?在 NullPointerException 的情况下,为什么不必被捕获?我可以在运行时得到它,我们可以捕获它并给出一个简单的错误消息?
  • NullPointerException 意味着您的代码中存在错误。你永远不应该让它们以理想的方式发生。
【解决方案2】:

Vidya 为您的问题提供了很好的答案。

最重要的一句话是“最后一点是你只需要在异常是checked exception时在throws中声明一个异常”

只是向您展示一个示例代码这是什么意思。想象一下,我们想使用 FileOutputStream 来传递一些数据。该函数如下所示:

public void saveSomeData() throws IOException {
  FileInputStream in = null;
  FileOutputStream out = null;

  try {
    in = new FileInputStream("input.txt");
    out = new FileOutputStream("output.txt");
    int c;

    while ((c = out.read() != -1) {
      in.write(c);
    }
  } catch (Exception e) {
    e.printStackTrace();
  } finally {
    // Close in
    if (in != null) {
      in.close(); // <-- If something bad happens here it will cause runtime error!
    }
    // Close out
    ...
  }
}

现在想象一下,如果你不提供 throws IOException 并且在 finally{} 语句中发生了一些不好的事情 - 它会导致错误。

【讨论】:

    【解决方案3】:

    throw 方法签名中的属性,就像您正确猜到的那样,是对编译器的提示,即该方法引发了一个必须由调用者捕获的异常。这种称为已检查异常的异常是调用者必须总是再次捕获或分派给其调用者的东西。这是编译器级别的东西,签名指定该方法能够抛出哪个异常:这会在调用者中强制执行try-catch 或重新调度,并在方法内部的某处使用 throw 语句,这是开发人员指定的约束关于方法行为的一些事情。

    另一方面,其他异常,即 unchecked运行时异常,(NoSucheElementException 是一个例子)是您不必指定的异常,因为它们会出现来自不同的情况。

    概念上的区别在于,已检查的异常通常用于警告开发人员应以某种方式处理的异常情况(想想IOException),而未检查的则是真正的错误(例如NullPointerException 或您的示例中的@ 987654326@)

    【讨论】:

      【解决方案4】:

      RuntimeExceptions 不必在 try-catch 块中处理,因此不必将它们声明为已抛出,NoSuchElementExceptionRuntimeException,因为它扩展了它。

      【讨论】:

      • 那么 RunTimeExceptions 是如何处理的。我们不应该处理 NullPointerException 和 RunTimeException 吗?
      • @Diffy 您可以使用 try-catch 处理这类异常,但大多数情况下最好让它们停止应用程序,因为它们很可能代表代码中需要更正的错误。
      猜你喜欢
      • 2020-08-03
      • 2018-08-18
      • 2016-12-21
      • 2018-05-30
      • 2011-04-17
      • 2018-02-19
      • 2010-12-22
      • 2014-10-26
      相关资源
      最近更新 更多