Java例外:您处理正确吗?

编程异常表示在程序执行中的某个时刻出现异常情况。当异常条件可以在其他地方而不是遇到的地方更好地处理时,可以使用它。请考虑以下示例:

  • 可以通过使用其他配置文件位置更好地在代码中更好地处理无法打开配置文件的情况。
  • 访问外部的数组项数组的边界表示程序错误。祝您调试愉快!
  • 应该引起用户注意XML解析错误,以便更正XML文件。
  • 程序内存不足(可能是在处理大型程序时)文件)可以通过增加Java进程可用的内存量来纠正。
  • 在所有这些情况下(以及更多情况下),异常应在生成它的位置之外进行处理,以便基础

    异常类型

    下图显示了Java异常层次结构的主要部分。基本类是 Throwable ,它又分为 Exception Error 。类 Exception 用于与程序相关的条件,应用程序可以捕获这些条件以试图挽救这种情况。另一方面,类 Error 用于指示Java运行时环境中的严重错误,应用程序不应捕获这些错误。一些示例是: OutOfMemoryError StackOverflowError

    异常又有两种类型:选中和未选中。已检查的异常必须由调用代码处理。该规则由java编译器强制执行。另一方面,未经检查的异常可以在调用链中传播,而不必显式声明。以下示例将阐明。

    以下方法尝试从文件创建 FileReader 。构造函数将抛出一个已检查的异常 FileNotFoundException ,该异常必须由调用代码处理或声明为抛出。

    以下代码将进行编译,因为它

    private void loadFile(String filename){  FileReader in = new FileReader(filename);}

    获取代码进行编译的一种方法是处理异常(请参见下文)。

    private void loadFile(String filename){  try {    FileReader in = new FileReader(filename)); {  } catch(FileNotFoundException ex) {    // handle exception here  }}

    如果调用者无法直接处理该异常,则必须在方法签名中声明。

    private void loadFile(String filename) throws java.io.FileNotFoundException{  FileReader in = new FileReader(filename)); {}

    未经检查的异常是从 RuntimeException 继承的一个异常,无需直接处理或如上所述声明。例如,以下代码导致 NullPointerException ,它是 RuntimeException 的类型。但是,由于 NullPointerException 是未经检查的异常,因此代码可以编译而不会出错。因为您不必声明或自己处理它们,所以处理未检查的异常更加容易。考虑到这种便利,有时将检查后的异常包装在未检查的异常中可能会很有用。

    下面的代码示例演示了如何包装异常。方法 method_1()在其主体中引发 SQLException 。为了正确编译代码,必须声明抛出异常。

    private void method_1() throws SQLException {  ...  throw new SQLException;}

    从另一个方法( method_2())调用此方法时,该方法可以捕获 SQLException 并将其包装在未经检查的异常中,因此它不必在其方法签名中声明该异常。

    private void method_2() {  try {    method_1();  } catch(java.sql.SQLException ex) {    throw new RuntimeException(ex);  }}

    异常堆栈跟踪

    一个异常堆栈跟踪指的是活动堆栈帧的数组,每个堆栈代表一个方法调用,由JVM在引发异常时捕获。每个堆栈帧都包括方法调用的位置,包括类名称,方法名称,以及文件中的Java源文件名称和行号。这对于追溯导致错误的调用顺序很有用。

    这是一个典型的堆栈跟踪,是在捕获到异常对象后获得的。

    Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 8, Size: 5  at java.util.ArrayList.rangeCheck(ArrayList.java:653)  at java.util.ArrayList.get(ArrayList.java:429)  at sample.sample1.main(sample1.java:24)

    捕获的异常这是 IndexOutOfBoundsException 。它包括有关错误的其他信息。堆栈跟踪包含3个堆栈帧,每个堆栈帧都包含如图所示的位置信息。

    处理异常

    可以通过在 try-catch中捕获异常来处理异常阻止并采取所需的纠正措施。 Exception 对象提供了几种方法来提取有关导致此情况的条件的信息。

    以下代码将错误消息记录到日志文件中。

    private void loadConfig() {  try {    // invoke code which might generate an IOException  } catch(java.io.IOException ex) {    // handle exception here. May be log to a log file.    log.warning(ex.getMessage());  }}

    当一个异常包装在另一个包装中时,您可以检索该包装的异常:

    Throwable cause = ex.getCause();log.warning("Underlying cause: " + cause.getMessage());

    您是否需要访问堆栈跟踪,并可能提取引起该异常的方法的名称?

    StringBuilder sbuf = new StringBuilder("Stack Trace: ");for (StackTraceElement el : ex.getStackTrace()) {  sbuf.append(el.getClassName() + "." + el.getMethodName()).append("\n");}log.warning(sbuf.toString());

    或者也许记录该异常并重新抛出它?

    try {  ...} catch(java.io.IOException ex) {  log.warning(ex.getMessage());  throw ex;}

    Exception 类提供了一个 printStackTrace()方法,该方法可以将堆栈跟踪打印到您自己的 PrintStream (或 PrintWriter )。

    try {  ...} catch(java.io.IOException ex) {  PrintStream out = ...;  out.println(ex.getMessage());  ex.printStackTrace(out);}

    您可以在一个 try 块中捕获多种类型的异常,并对每种类型的异常执行特定的处理。

    try {  // throws some exceptions here} catch(java.io.IOException ex) {  // IOException specific handling here} catch(java.sql.SQLException ex) {  // SQLException specific handling here}

    要捕获多种异常类型但使用相同的处理代码,可以声明一个 catch 块可能有以下几种类型:

    try {  // throws some exceptions here} catch(java.io.IOException | java.sql.SQLException ex) {  // IOException and SQLException specific handling here} catch(SAXException ex) {  // SAXException specific handling here}

    最终清理资源

    在处理可能引发异常的代码时,必须对所有资源(例如打开的文件)进行适当的清理是必不可少的

    InputStream in = null;try {  ...  in = new FileInputStream(filename);  ...} catch(java.io.IOException ex) {  log.warning(ex.getMessage());} finally {  // code here is executed on exiting the try block,  // whether normally or due to an exception  if ( in != null ) in.close();}

    尝试使用资源块

    Java 1.7引入了 try-with-resources 构造,该构造使资源清理更加容易。看起来像这样:

    try( InputStream in = new FileInputStream(..) ) {  // code which uses the InputStream.}

    当代码退出代码块时(无论是干净的还是由于异常), InputStream 变量都会自动清除。

    通过在块的头部中声明所有资源来清理多个资源。

    try( InputStream in = new FileInputStream(..);  Connection con = ...; ) {  // code which uses the InputStream and the Connection.}

    可以使用这种方式清理其类实现 AutoCloseable 接口的任何对象。以下类在 close()方法中执行一些特定的清除操作。

    public class MyClass implements AutoCloseable {  public void close() {    // cleanup code here  }}

    try-with-resources 块中使用此类的实例。

    try( MyClass obj = new MyClass(..) ) {  // code which uses the MyClass object.}

    一些常见的异常

    现在让我们看一些常见的异常。

  • IndexOutOfBoundsException (未选中) :表示正在访问的元素的索引超出了数组,字符串等的范围。
  • SQLException (已选中):由于数据库错误而抛出。
  • IOException (选中):文件访问错误或与输入和输出有关的错误。
  • InterruptedException (选中):当出现以下情况时抛出线程执行被中断。
  • SAXException (选中):由于XML解析错误而抛出。
  • NullPointerException (未选中):在需要对象的地方使用null。
  • 包装

    p是例外Java中错误报告和管理的基本方法。正确使用异常可以提高代码质量,并有助于解决生产中的问题。

    您是否有任何与异常有关的战争故事?如果是这样,请在下面的评论部分中告诉我们。

    图片来源:Dmitry Nikolaev通过Shutterstock.com

    标签: Java 编程