方法签名中的抛出与Java中的抛出语句之间的区别
我试图弄清楚方法签名中的Throws和Java中的Throw Statements之间的区别。 方法签名中的抛出如下:
public void aMethod() throws IOException{ FileReader f = new FileReader("notExist.txt"); }
抛出语句如下:
public void bMethod() { throw new IOException(); }
根据我的理解,方法签名中的throws
是该方法可能抛出此类exception的通知。 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 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 f = first; if (f == null) throw new NoSuchElementException(); return f.item; }
更新2:检查exception。 签名中是否需要“抛出”? 是。
// has to throw checked exception otherwise compile error public String abc() throws IOException{ throw new IOException(); }
你非常正确。 除了一件事我会稍微提一下。
throws与方法API一样,也是名称和参数的一部分。 客户知道他们是否调用该方法,他们需要处理该exception – 通过简单地抛出它或者通过捕获它并处理它(实际上可能需要抛出包装原始的另一个exception)。 抛出在编译时解决。
throw是让运行时知道发生了不好事情的实际行为 – 我们担心的特殊情况实际上已经发生了。 所以它需要在运行时处理。
但是当你说“如果方法中存在throw语句时,应该总是出现方法签名中的抛出”,你就不太对了。 这通常是正确的,但并非总是如此。 我还可以调用另一个在我的方法中抛出exception的方法,如果我没有捕获它,我的方法需要抛出它。 在这种情况下,我没有明确抛出同一个例外。
最后一点是,当exception是一个经过检查的exception时,您只需要在throws中声明一个exception – 这意味着它来自RuntimeException的Exception类层次结构的另一端。 常见的已检查exception是IOException和SQLException。 如果您不自行处理,则必须在方法签名的throws部分中列出已检查的exception。 任何inheritanceRuntimeException的东西 – 比如你的例子中的NoSuchElementException以及讨厌的NullPointerException – 都是一个未经检查的exception,不必被捕获或抛出或任何东西。
通常,您使用已检查的exception来解决可恢复的问题(客户端知道会发生什么,可以优雅地处理问题并继续前进)和未经检查的灾难性exception(例如无法连接到数据库)。
如果你能够通过所有AOP的东西, 这是一个很好的讨论如何有效地使用已检查和未经检查的exception。
Vidya为您的问题提供了很好的答案。
最重要的一句是“最后一点是,当exception是一个经过检查的exception时,你只需要在throws中声明一个exception”
只是为了向您展示示例代码,这意味着什么。 想象一下,我们想使用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并且在最终{}语句中发生了一些不好的事情 - 它会导致错误。
RuntimeException
不必在try-catch块中处理,因此它们不必被声明为抛出, NoSuchElementException是RuntimeException
因为它扩展了它。
方法签名中的throw
属性,正如您正确猜到的那样,是对编译器提示该方法引发必须由调用者捕获的exception的提示。 这种exception,即称为检查exception ,是调用者必须始终捕获或再次发送给其调用者的东西。 这是编译器级别的东西,签名指定方法能够抛出的exception:这在调用者中强制执行try-catch
或重新调度,在方法内部的某处执行throw语句,是开发人员指定的约束关于方法行为的一些事情。
另一方面,其他exception,即未经检查或运行时exception ( NoSucheElementException
是一个示例)是您不必强制指定的exception,因为它们来自不同的情况。
概念上的区别在于,检查exception通常用于警告开发人员应以某种方式(想想IOException
)处理的exception情况,而未选中则是真正的错误(如NullPointerException
或类似于NoSuchElementException
)