异常(Exception)

异常(Exceptions)

java编程语言使用异常来处理错误(error)和其他的异常事件。本节课描述何时、如何使用异常。

概览

什么是异常?

执行一个程序时,打断正常的指令流程的事件就是异常。

The Catch or Specify Requirement

本节涵盖如何捕获(Catch)或解决异常。讨论包含try,Catchfinally块,以及异常链(chain)和logging

如何抛出异常

本章节包括throw语句和Throwable类以及其子类。

try-with-resource语句

本章节描述try-with-resource语句,即一个声明一个或多个资源的try语句。资源是一个必须在程序结束之后关闭的对象。语句try-with-resources保证在语句结束后每个资源都会被释放掉。

Unchecked Exception- 争议

RuntimeException的子类阐述了UncheckedException的正确与错误用法。

异常的优势

与传统的错误管理技术相比,使用异常来管理错误占据更多的优势。

什么是异常

异常是特殊事件(exceptional event)的简称。异常是在程序执行过程中所发生的打断程序指令流程的事件。

当错误发生在方法的内部,方法会创建一个对象并把它交给系统(runtime system)。这个对象叫做异常对象exception object,它包含关于错误的信息。包括错误发生时程序的状态和类型。创建一个异常对象并将它递交给系统的过程称之为抛出一个异常(throwing an exception)。

方法抛出一个异常以后,运行时系统尝试着找到某种东西来处理它。可能处理异常的东西的集合是有序的方法列表,它们被调用以进入错误发生的方法内部。有序的方法列表被称为调用栈(call stack)。

exceptions-callstack

运行时系统搜索调用栈以找出可以处理异常拥有代码块的方法。这个代码块被称为异常处理器(exception Handler)。搜素开始的时机是错误发生的时候,搜索的顺序是调用栈的反顺序。当合适的处理器被发现,运行时系统会递交异常给该处理器。当被抛出的异常对象的类型与可处理异常的处理器类型想匹配时,我们认为该异常处理器是合适的

异常处理器被选择的过程被称为捕获异常。如果运行时系统在调用栈搜索了一遍没有找到合适的异常处理器,如下图所示,则运行时系统停止。

exceptions-errorOccur

The Catch or Specify Requirement

有效的java编程语言代码必须遵循Catch或者Specity requirement。这意味着代码中抛出的异常必须以以下两种方式之一结束:

  1. try语句捕获异常。try必须为异常提供一个处理器。
  2. 一种可以指定它抛出异常的方法。方法必须提供throws语句以列出异常。

代码如果没有遵循Catch或者Specity requirement,则编译不通过。

并非所有的异常都被 Catch或者Specity requirement约束住。为了了解为什么,我们需要学习三个基本的异常分类,仅其中之一需要遵循这种约束。

三种类型的异常

第一种异常类型被称为受查异常(checked exception)。一个编写良好的程序应该预知特殊的异常,并从中恢复。例如,以下程序在正常情况下用户应该输入一个存在的文件名称,但是如果文件不存在,好的程序应该捕获这个异常并通知用户这种错误。

//在一个实际的文件上创建一个链接connection   
public FileInputStream(File file) throws FileNotFoundException {
        String name = (file != null ? file.getPath() : null);
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkRead(name);
        }
        if (name == null) {
            throw new NullPointerException();
        }
        if (file.isInvalid()) {
            throw new FileNotFoundException("Invalid file path");
        }
        fd = new FileDescriptor();
        fd.incrementAndGetUseCount();
        this.path = name;
        open(name);
    }

受查异常是遵循Catch或者Specity requirement约束的。除了ErrorRuntimeException以及它们的子类,所有的异常都是受查异常。

第二种异常是Error。这种异常条件是程序外部引起的,程序通常不能从中预知并恢复。例如程序打开读取一个文件,但因硬件故障或者系统故障导致文件不可读。

第三种异常是运行时异常runtime exception。这些异常条件是程序内部的,程序通常可以从中预知并恢复。这种异常通常表现为程序bugs,例如逻辑错误或者使用不恰当的API。例如前面所举例的程序接收用户输入的文件名,然后打开读取里面的内容,这时,如果用户传入的文件名是null,则程序抛出空指针异常。

运行时异常不遵循Catch或者Specity requirement约束,它是Runtiem exception和它的子类。

Errorsruntime exception合称为非受查异常。

绕过Catch or Specify

有些编程人员认为Catch or Specify在异常体系中是一个严重的缺陷,应当用非受查异常替代受查异常。通常情况下,这使不建议的。

捕获并处理异常

本章节讨论如何使用三种异常处理组件—try,catch,finally块—来编写异常处理器。然后介绍java SE 1.7引入的try-with-resourcestry-with-resources尤其适合Closeable资源,例如Stream


// Note: This class will not compile yet.
import java.io.*;
import java.util.List;
import java.util.ArrayList;

public class ListOfNumbers {

    private List<Integer> list;
    private static final int SIZE = 10;

    public ListOfNumbers () {
        list = new ArrayList<Integer>(SIZE);
        for (int i = 0; i < SIZE; i++) {
            list.add(new Integer(i));
        }
    }

    public void writeList() {
    // The FileWriter constructor throws IOException, which must be caught.
        PrintWriter out = new PrintWriter(new FileWriter("OutFile.txt"));

        for (int i = 0; i < SIZE; i++) {
            // The get(int) method throws IndexOutOfBoundsException, which must be caught.
            out.println("Value at: " + i + " = " + list.get(i));
        }
        out.close();
    }
}

new FileWriter("OutFile.txt")是调用了构造函数。如果文件不存在,则会抛出一个IOExceptionlist.get(i)可能会抛出一个IndexOutOfBoundsException,如果其index值小于零或者大于SIZE时会抛出异常。

编译会出错因为IOException是受查异常,而IndexOutOfBoundsException是非受查异常。

try语句

构造异常处理器的第一个步骤是在可能抛出异常的代码块上用try语句包围住。如下所示:

try {
    code
}
catch and finally blocks . . .

code所包含的代码可能会抛出一个或者几个异常。

你可以为每一行可能抛出异常的代码用try语句包裹起来,也可以用一个try语句将所有可能抛出异常的语句全部包裹起来,如下所示:

private List<Integer> list;
private static final int SIZE = 10;

public void writeList() {
    PrintWriter out = null;
    try {
        System.out.println("Entered try statement");
        out = new PrintWriter(new FileWriter("OutFile.txt"));
        for (int i = 0; i < SIZE; i++) {
            out.println("Value at: " + i + " = " + list.get(i));
        }
    }
    catch and finally blocks  . . .
}

Catch语句

你可以直接在try块后面用一个或多个catch块来和异常处理器关联起来。在try块之后,catch块钱不能有任何代码:

try {

} catch (ExceptionType name) {

} catch (ExceptionType name) {

}

每一个catch块是一个异常处理器,它所处理的异常类型是由参数(ExceptionType)所决定。ExceptionType

必须是Throwable类或其子类的名称。

catch块包含的代码会在异常处理器调用时执行。当所抛出的异常类型ExceptionType第一次与调用栈内的相匹配,运行时系统便会执行该异常处理器。如果所抛出的异常对象可以正确赋值给异常处理器参数,则系统认为它们的异常类型相匹配。

以下是两个异常处理器(exception handler):

try {

} catch (IndexOutOfBoundsException e) {
    System.err.println("IndexOutOfBoundsException: " + e.getMessage());
} catch (IOException e) {
    System.err.println("Caught IOException: " + e.getMessage());
}

异常处理器不仅可以打印错误信息或者停止程序的执行,它还可以进行错误恢复,提示用户做出决定,或者使用异常链将错误传播到更高一层的处理器。

用一个异常处理器捕获一种或者多种类型的异常

java SE 7及其之后,一个catch块可以处理一种或者多种类型的异常。这种特性可以减少代码的冗余以及减少过度捕获更加宽泛的异常。

catch语句中,指定可以处理的异常类型,并用垂直短线来分割

catch (IOException|SQLException ex) {
    logger.log(ex);
    throw ex;
}

注意如果一个catch块处理多种异常类型,catch参数暗示着final。在这个例子中,exfinal的,是故不能赋予它新的值。

finally

finally块在try语句退出时总会执行。这保证了即使预料之外的异常发生了finally块仍可执行。但finally块的作用不仅局限于此,它防止因程序员的疏忽大意使用return,continue,break而绕过清理代码。即使没有异常发生,将清理代码放到finally块内总会是一种好的实践。

注意:当正在执行try或者catch块时,JVM退出finally块可能不会被执行。同理当线程被打断也是这样的。

writeList方法有3种方式可以跳出try块:

  1. new FileWriter语句执行失败并抛出IOException.
  2. list.get(i)执行失败,抛出IndexOutOfBoundsExceptions
  3. 整个代码执行成功,try块正常退出。

运行时系统总会执行finally内的语句而不管try语句内发生什么,所以finally块内是执行清理的最佳地方。

finally {
    if (out != null) { 
        System.out.println("Closing PrintWriter");
        out.close(); 
    } else { 
        System.out.println("PrintWriter not open");
    } 
} 

finally块是一个防止资源泄漏的一个重要的工具。当关闭一个文件或者回收资源,将代码放到finally块内保证资源总是被回收。

The try-with-resources Statement

try-with-resources语句是一个try语句,它声明了一个或者多个资源。资源是一个对象,它必须在程序执行完后关闭掉。try-with-resources语句保证每一个资源会在语句的最后被关闭掉。任何实现java.lang.AutoCloseable接口以及实现java.io.Closeable的对象都可以被当做一种资源。

以下的例子从文件中读取第一行文字。它用了BufferedReader的实例从文件中读取数据。BufferedReader是一种资源,因此程序结束后需要关掉。

static String readFirstLineFromFile(String path) throws IOException {
    try (BufferedReader br =
                   new BufferedReader(new FileReader(path))) {
        return br.readLine();
    }
}

在这个例子中,声明在try-with-resources语句内的是BufferedReader。声明的语句是在try之后小括号内的。类BufferedReaderjava SE 7及其之后实现了接口java.lang.AutoCloseable。因为BufferedReader是声明在try-with-resources语句内的,所以无论try语句发生什么(执行成功或者抛出异常),该资源都会被关闭掉。

java SE 7之前,你可以使用一个finally块来保证资源是关闭的,无论try是执行成功还是抛出异常。以下用是用finally块来关闭资源而不是使用try-with-resources语句。

static String readFirstLineFromFileWithFinallyBlock(String path)
                                                     throws IOException {
    BufferedReader br = new BufferedReader(new FileReader(path));
    try {
        return br.readLine();
    } finally {
        if (br != null) br.close();
    }
}

然而,在该例子中,如果方法readLineclose都抛出异常,则方法readFirstLineFromFileWithFinallyBlock只会抛出finally块内的异常,而try块内的异常被屏蔽掉了(supressed)。同理try块和try-with-resources同时抛出异常,则只会抛出try块内的异常。

你可能在try-with-resources语句内声明一种或者多种资源,如下所示:

    java.nio.charset.Charset charset =
         java.nio.charset.StandardCharsets.US_ASCII;
    java.nio.file.Path outputFilePath =
         java.nio.file.Paths.get(outputFileName);

    // Open zip file and create output file with 
    // try-with-resources statement

    try (
        java.util.zip.ZipFile zf =
             new java.util.zip.ZipFile(zipFileName);
        java.io.BufferedWriter writer = 
            java.nio.file.Files.newBufferedWriter(outputFilePath, charset)
    ) {
        // Enumerate each entry
        for (java.util.Enumeration entries =
                                zf.entries(); entries.hasMoreElements();) {
            // Get the entry name and write it to the output file
            String newLine = System.getProperty("line.separator");
            String zipEntryName =
                 ((java.util.zip.ZipEntry)entries.nextElement()).getName() +
                 newLine;
            writer.write(zipEntryName, 0, zipEntryName.length());
        }
    }
}

在本例子中,try-with-resources内声明的两个资源用分号(;)分隔开,无论程序执行成功与否,BufferedWriterZipFile对象的close方法会依次被调用,其方向是资源创建顺序的反顺序

下面的例子使用try-with-resources语句关闭一个java.sql.Statement对象

public static void viewTable(Connection con) throws SQLException {

    String query = "select COF_NAME, SUP_ID, PRICE, SALES, TOTAL from COFFEES";

    try (Statement stmt = con.createStatement()) {
        ResultSet rs = stmt.executeQuery(query);

        while (rs.next()) {
            String coffeeName = rs.getString("COF_NAME");
            int supplierID = rs.getInt("SUP_ID");
            float price = rs.getFloat("PRICE");
            int sales = rs.getInt("SALES");
            int total = rs.getInt("TOTAL");

            System.out.println(coffeeName + ", " + supplierID + ", " + 
                               price + ", " + sales + ", " + total);
        }
    } catch (SQLException e) {
        JDBCTutorialUtilities.printSQLException(e);
    }
}

注意:try-with-resources语句也可以有catchfinally块,但是它们是在资源关闭之后才调用的。

被压制的异常(Suppressed Exceptions)

异常可以被try-with-resources块内代码所抛出。try块和try-with-resources同时抛出异常,则只会抛出try块内的异常,而try-with-resources抛出的异常被忽略(Suppressed)了。

实现AutoCloseableCloseable的类

接口Closeable接口继承自AutoCloseableCloseable内的close方法抛出IOExceptionAutoCloseable内的close方法抛出Exception,一般来讲AutoCloseable的子类可以重写close方法来抛出具体的异常,如IOException或者干脆不抛出异常。

汇集一起(Put It All Together)

public void writeList() {
    PrintWriter out = null;

    try {
        System.out.println("Entering" + " try statement");

        out = new PrintWriter(new FileWriter("OutFile.txt"));
        for (int i = 0; i < SIZE; i++) {
            out.println("Value at: " + i + " = " + list.get(i));
        }
    } catch (IndexOutOfBoundsException e) {
        System.err.println("Caught IndexOutOfBoundsException: "
                           +  e.getMessage());

    } catch (IOException e) {
        System.err.println("Caught IOException: " +  e.getMessage());

    } finally {
        if (out != null) {
            System.out.println("Closing PrintWriter");
            out.close();
        } 
        else {
            System.out.println("PrintWriter not open");
        }
    }
}

//output
Entering try statement
Caught IOException: OutFile.txt
PrintWriter not open 

上面代码中,有三种方式可以退出try块,以下是其中之二:

  1. try块内代码抛出异常。即new FileWriter语句或者list.get(i)抛出异常。
  2. 代码执行正常。

第一种情况:抛出异常

new FileWriter抛出异常,比如程序不能创建指定文件。

当抛出IOException后,运行时系统停止执行try语句,方法调用不能执行完。运行系统开始在方法调用栈顶端依次往下找合适的异常处理器。在本例子中,寻找的过程为:

  • FileWriter构造器在方法调用栈的顶端,但是该构造器没有合适的异常处理器,于是找到下个方法。
  • writerList方法,有两个异常处理器IOExceptionIndexOutOfBoundsException
  • 运行时系统按顺序判断所抛出异常与异常处理器是否匹配,先找到的是IndexOutOfBoundsException,不匹配,往下继续找。
  • IOException正好匹配。运行时系统停止搜索,然后执行catch块。
  • 异常处理器执行完毕后,运行时系统将控制递交给finally块。然后执行finally块内代码,而不管上面Catch块的异常。因FileWriter没有被打开,所以并不会调用close方法。
  • finally块被执行完毕好后,程序继续执行finally块后的第一条语句。
    public void writeList() {
//        PrintWriter out = null;
//        try {
//            System.out.println("Entering" + " try statement");
//            out = new PrintWriter(new FileWriter("OutFile.txt"));
            for (int i = 0; i < SIZE; i++) {
                out.println("Value at: " + i + " = " + list.get(i));
            }
        } catch (IndexOutOfBoundsException e) {
            System.err.println("Caught IndexOutOfBoundsException: "
                    + e.getMessage());
//        } catch (IOException e) {
//            System.err.println("Caught IOException: " + e.getMessage());
//        } finally {
//            if (out != null) {
                System.out.println("Closing PrintWriter");
                out.close();
            }
//            else {
//                System.out.println("PrintWriter not open");
//            }
        }
    }

上面注释掉的代码是情况一所执行的代码情况。

情况二:try块正常退出

执行情况如下代码注释处


    public void writeList() {
//        PrintWriter out = null;
//        try {
//            System.out.println("Entering try statement");
//            out = new PrintWriter(new FileWriter("OutFile.txt"));
//            for (int i = 0; i < SIZE; i++)
//                out.println("Value at: " + i + " = " + list.get(i));

              }
//        catch (IndexOutOfBoundsException e) {
//            System.err.println("Caught IndexOutOfBoundsException: "
//                    + e.getMessage());
//
//        } catch (IOException e) {
//            System.err.println("Caught IOException: " + e.getMessage());

//        } finally {
//            if (out != null) {
//                System.out.println("Closing PrintWriter");
//                out.close();
//            }
            else {
               System.out.println("PrintWriter not open");
          }
//      }
}

指定方法所抛出的异常

上一节课阐述了如果编写异常处理器。有时,仅仅捕捉代码块内所发生的异常是合适的,但是更好的办法是让更上一层的调用栈来处理异常。例如,如果你把ListOfNumber类作为包的一部分给其他人用,那可能就无法满足一部分用户的需求(即每个用户处理异常的方式不一样,你在底层直接给解决掉了,那其它人只能重写方法了)。

可以如下例子所示把异常抛给上一层调用栈处理

public void writeList() throws IOException, IndexOutOfBoundsException {

由于list.get(i)所抛出的异常IndexOutOfBoundsException是非受查异常,所以可以简写如下

public void writeList() throws IOException{

如何抛出异常

在你捕获异常之前,某些代码必须要抛出一个异常。任何代码可以抛出异常,如你编写的代码,java平台提供的代码等。无论是什么抛出异常,总会用到throw语句来抛出。

正如你所注意到的那样,java平台提供了很多异常类,所有的类都是Throwable的子孙类,并允许程序对抛出的异常种类加以区分。

throw语句

所有的方法使用throw抛出异常。throw语句需要一个参数:一个throwable的对象。

throw someThrowableObject;

你可以抛出任意继承自throwable接口的异常类,如

public Object pop() {
    Object obj;

    if (size == 0) {
        throw new EmptyStackException();
    }

    obj = objectAt(size - 1);
    setObjectAt(size - 1, null);
    size--;
    return obj;
}

exceptions-throwable

Throwable类及其子类

如上图所示throwable有两直接的子孙类ErrorException

Error

发生Java虚拟机中的动态链接失败或其他硬故障时,虚拟机就会抛出Error

Exception

大多数程序抛出和所捕获的异常都是派生自Exception类。异常表明一个问题的产生,但是不是一个严重的系统问题。

RuntimeException表明错误地使用API

异常链Chained Exceptions

一个应用程序总会用抛出异常的方式来回应另一个异常。即第一个异常引发了第二个异常。知道何时一个异常引发另一个异常有时候非常有用处。异常链帮助软件开发者做这样的事情。

Throwable内的方法和构造器支持异常链:

Throwable getCause()
Throwable initCause(Throwable)
Throwable(String, Throwable)
Throwable(Throwable)

下面的例子展示了如何使用一个异常链

try {

} catch (IOException e) {
    throw new SampleException("Other IOException", e);
}

IOException被抛出后,利用原始的异常对象为参数创建出一个新的异常SampleException,因此异常链被跑到更高层次的异常处理器。

访问堆栈跟踪信息

假设现在更高层次的异常处理器想要以自己的格式导出堆栈跟踪信息。

定义:当异常发生时了,堆栈跟踪提供当前线程执行类和方法名称的列表信息,它是一种非常有用的调试工具。

catch (Exception cause) {
    StackTraceElement elements[] = cause.getStackTrace();
    for (int i = 0, n = elements.length; i < n; i++) {       
        System.err.println(elements[i].getFileName()
            + ":" + elements[i].getLineNumber() 
            + ">> "
            + elements[i].getMethodName() + "()");
    }
}
Logging API

把所要打印输出的信息放到文件中。

try {
    Handler handler = new FileHandler("OutFile.log");
    Logger.getLogger("").addHandler(handler);

} catch (IOException e) {
    Logger logger = Logger.getLogger("package.name"); 
    StackTraceElement elements[] = e.getStackTrace();
    for (int i = 0, n = elements.length; i < n; i++) {
        logger.log(Level.WARNING, elements[i].getMethodName());
    }
}

创建异常类

当面对选择抛出何种类型的异常时,你可以自己编写亦可以使用别人写好的—java平台提供了很多异常类可供选择。如果以下的任何一个问题的答案是yes的话,那么就编写自己的异常类,否则你应该使用已存在的异常类。

  • 你所需要的类型不存在与java平台内?
  • 如果用户能够将您的异常与其他供应商编写的区分开来,它会对用户有所帮助吗?
  • 你的代码抛出多于一个相关的异常么?
  • 如果你使用其他人提供的异常,那用户可以访问这些异常么?类似的问题是,你所编写的包是独立存在的还是自给自足的?
一个栗子

假设你编写一个链表类,该类包含以下方法:

  • objectAt(int n),返回列表中位置是n的对象,如果n<0或者大于列表元素的个数那会抛出一个异常。
  • firstObject(),返回列表中的第一个元素。如果列表不包含任何元素则抛出异常。
  • indexOf(Object o),返回指定对象在列表中的索引,假如入参不存在与列表中,则抛出异常。

该链表类可以抛出多种异常,如果只用一个异常处理器来捕获所有的异常会非常方便。

exceptions-hierarchy

选择一个父类

任意一个Exception的子类都能充当LinkedListException的父类。然而这些子类不是与LinkedListException无关就是太过具体,因此选择Exception作为其父类比较合适。

非受查异常的争议

因为java编程语言不要求方法捕获非受查异常(RuntimeException, Error, 以及它们的子类),因此编程者可能视图让编写的代码只抛出受查异常或者只抛出非受查异常。这两种捷径都允许程序员编写出编译通过或者不需要捕获异常的代码。这看似十分方便,但这种回避了Catch或者specify需求的捷径会给使用你编写类的人带来问题。

为何设计者决定强制方法指定所有未捕获的可以在其范围内抛出的异常?任何异常是方法的公共编程接口的一部分。调用方法时必须知道方法所能抛出的异常,这样使用者才能决定如何使用它们。

将所有的runtime异常加入到方法的声明当中会降低程序的间接性。因此编译器不需要你去捕获或者指定运行时异常。

一般而言,不要简单的抛出一个RuntimeException或者它的子类,因为你不想在为方法指定异常时受到打扰。

这是指南的底线:如果想让程序从异常中恢复,那么把该异常指定为受查异常;否则指定为非受查异常。

异常的优势

优势一:将错误处理代码和业务代码分离

假如要做以下的事情

readFile {
    open the file;
    determine its size;
    allocate that much memory;
    read the file into memory;
    close the file;
}

看起来似乎很简单,但是这些过程中可能会发生许多的以为,比如

  • 文件不能打开。
  • 文件的长度不能获取。
  • 内存开辟不成功。
  • 读文件失败。
  • 文件流不能关闭。

传统的方法如下:

errorCodeType readFile {
    initialize errorCode = 0;

    open the file;
    if (theFileIsOpen) {
        determine the length of the file;
        if (gotTheFileLength) {
            allocate that much memory;
            if (gotEnoughMemory) {
                read the file into memory;
                if (readFailed) {
                    errorCode = -1;
                }
            } else {
                errorCode = -2;
            }
        } else {
            errorCode = -3;
        }
        close the file;
        if (theFileDidntClose && errorCode == 0) {
            errorCode = -4;
        } else {
            errorCode = errorCode and -4;
        }
    } else {
        errorCode = -5;
    }
    return errorCode;
}

如果使用异常,则代码如下:

readFile {
    try {
        open the file;
        determine its size;
        allocate that much memory;
        read the file into memory;
        close the file;
    } catch (fileOpenFailed) {
       doSomething;
    } catch (sizeDeterminationFailed) {
        doSomething;
    } catch (memoryAllocationFailed) {
        doSomething;
    } catch (readFailed) {
        doSomething;
    } catch (fileCloseFailed) {
        doSomething;
    }
}
优势二:将异常传播到上一层调用栈

假如方法是嵌套调用的,如下所示

method1 {
    call method2;
}

method2 {
    call method3;
}

method3 {
    call readFile;
}

传统的方法是

method1 {
    errorCodeType error;
    error = call method2;
    if (error)
        doErrorProcessing;
    else
        proceed;
}

errorCodeType method2 {
    errorCodeType error;
    error = call method3;
    if (error)
        return error;
    else
        proceed;
}

errorCodeType method3 {
    errorCodeType error;
    error = call readFile;
    if (error)
        return error;
    else
        proceed;
}

而使用异常处理器则只需要在入口点method1处处理异常即可,其他方法可以不管

method1 {
    try {
        call method2;
    } catch (exception e) {
        doErrorProcessing;
    }
}

method2 throws exception {
    call method3;
}

method3 throws exception {
    call readFile;
}
优势三:可以对异常类型进行分组和区分
//没有子类的异常 可与其他异常加以区分
catch (FileNotFoundException e) {
    ...
}
//所有IO的异常,即异常分组
catch (IOException e) {
    // Output goes to System.err.
    e.printStackTrace();
    // Send trace to stdout.
    e.printStackTrace(System.out);
}

总结

程序可以使用异常来表明发生了错误。为了抛出一个异常,使用throw语句并为它提供一个异常对象—Throwable的子孙。

一个程序可以使用try catch finally的组合来捕获处理异常。

错误对象所属的class表明了所抛出异常对象的类别。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值