我试图清楚地说明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();
}
答案 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块中处理,因此它们不必被声明为抛出而NoSuchElementException是RuntimeException
因为它扩展了它。