方法签名中的抛出与Java中的抛出语句之间的区别

时间:2013-10-05 03:06:40

标签: java throw throws

我试图清楚地说明Java中方法签名中的抛出 Throw Statements 之间的区别。 方法签名中的引发如下:

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

投掷声明如下:

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

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

但是,以下代码似乎没有这样做。代码来自库。我的问题是它为什么会发生?我理解错误的概念吗?

这段代码是java.util.linkedList的副本。 @author Josh Bloch

 /**
 * 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;
}

答案更新:

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

4 个答案:

答案 0 :(得分:28)

你非常正确。除了一件事,我稍后会提到。

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

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

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

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

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

如果您可以通过所有AOP内容,this就如何有效地使用已检查和未检查的异常进行了很好的讨论。

答案 1 :(得分:5)

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

最重要的词是&#34;最后一点是,当异常是一个经过检查的异常时,你只需要在抛出中声明一个异常&#34;

只是为了向您展示一个示例代码,这意味着什么。想象一下,我们想使用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
    ...
  }
}

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

答案 2 :(得分:3)

方法签名中的

throw属性,就像你正确猜到的那样,是对编译器提示该方法引发必须被调用者捕获的异常的提示。这种异常,即称为检查异常,调用者必须总是再次捕获或调度给它的调用者。这是编译器级别的东西,签名指定方法能够抛出的异常:这在调用者中强制执行try-catch或重新调度,在方法内部的某处使用throw语句,是​​开发人员放置的约束指定有关方法行为的内容。

另一方面,其他例外,即未经检查运行时异常,(NoSucheElementException就是一个例子)是您不必强制指定的例外情况它们来自不同的情况。

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

答案 3 :(得分:2)

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