Java异常处理

简介

  异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的。例如你的代码少了一个分号,那么运行出来结果是提示错误java.lang.Error;如果你使用System.out.println(11/0),用0做了除数,会抛出java.lang.ArithmeticException的异常。
  异常发生的原因有很多,通常包含以下几大类:用户输入了非法数据;要打开的文件不存在;网络通信时连接中断;JVM内存溢出。这些异常有的是因为用户错误引起,有的是程序错误引起的,还有其它一些是因为物理错误引起的。
  要理解Java异常处理是如何工作的,你需要掌握以下三种类型的异常:

  • 检查性异常:最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。
  • 运行时异常:运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
  • 错误:错误不是异常,而是脱离程序员控制的问题。例如当栈溢出时,一个错误就发生了,它们在编译时检查不到。

Exception类的层次

  所有的异常类是从java.lang.Exception类继承的子类,Exception类是Throwable类的子类。除了Exception类外,Throwable还有一个子类ErrorError用来指示运行时环境发生的错误,例如JVM内存溢出。一般情况下,程序不会从错误中恢复。异常类有两个主要的子类:IOException类和RuntimeException类。

Java内置异常类

  Java定义了一些异常类在java.lang标准包中。标准运行时异常类的子类是最常见的异常类。由于java.lang包是默认加载到所有的Java程序的,所以大部分从运行时异常类继承而来的异常都可以直接使用。
  Java根据各个类库也定义了一些其他的异常,下面列出了Java的非检查性异常:

  • ArithmeticException:当出现异常的运算条件时,抛出此异常。例如一个整数除以零时,Java将会抛出此类异常。
  • ArrayIndexOutOfBoundsException:用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引。
  • ArrayStoreException:试图将错误类型的对象存储到一个对象数组时抛出的异常。
  • ClassCastException:试图将对象强制转换为不是实例的子类时抛出该异常。
  • IllegalArgumentException:抛出的异常表明向方法传递了一个不合法或不正确的参数。
  • IllegalMonitorStateException:抛出的异常表明某一线程已经试图等待对象的监视器,或者试图通知其他正在等待对象的监视器而本身没有指定监视器的线程。
  • IllegalStateException:在非法或不适当的时间调用方法时产生的信号。换句话说,即Java环境或Java应用程序没有处于请求操作所要求的适当状态下。
  • IllegalThreadStateException:线程没有处于请求操作所要求的适当状态时抛出的异常。
  • IndexOutOfBoundsException:指示某排序索引(例如对数组、字符串或向量的排序)超出范围时抛出。
  • NegativeArraySizeException:如果应用程序试图创建大小为负的数组,则抛出该异常。
  • NullPointerException:当应用程序试图在需要对象的地方使用null时,抛出该异常。
  • NumberFormatException:当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常。
  • SecurityException:由安全管理器抛出的异常,指示存在安全侵犯。
  • StringIndexOutOfBoundsException:此异常由String方法抛出,指示索引或者为负,或者超出字符串的大小。
  • UnsupportedOperationException:当出现不支持的操作时,抛出该异常。

下面的表中列出了Java定义在java.lang包中的检查性异常类:

  • ClassNotFoundException:应用程序加载类时,如果找不到相应的类,抛出该异常。
  • CloneNotSupportedException:当调用Object类中的clone方法克隆对象,但该对象的类无法实现Cloneable接口时,抛出该异常。
  • IllegalAccessException:拒绝访问一个类的时候,抛出该异常。
  • InstantiationException:当试图使用Class类中的newInstance方法创建一个类的实例,而指定的类对象因为是一个接口或是一个抽象类而无法实例化时,抛出该异常。
  • InterruptedException:一个线程被另一个线程中断,抛出该异常。
  • NoSuchFieldException:请求的变量不存在。
  • NoSuchMethodException:请求的方法不存在。

异常方法

  下面的列表是Throwable类的主要方法:

  • public String getMessage():返回关于发生的异常的详细信息。这个消息在Throwable类的构造函数中初始化了。
  • public Throwable getCause():返回一个Throwable对象代表异常原因。
  • public String toString():使用getMessage的结果返回类的串级名字。
  • public void printStackTrace():打印toString结果和栈层次到System.err,即错误输出流。
  • public StackTraceElement [] getStackTrace():返回一个包含堆栈层次的数组。下标为0的元素代表栈顶,最后一个元素代表方法调用堆栈的栈底。
  • public Throwable fillInStackTrace():用当前的调用栈层次填充Throwable对象栈层次,添加到栈层次任何先前信息中。

捕获异常

  使用trycatch关键字可以捕获异常,try/catch代码块放在异常可能发生的地方。try/catch代码块中的代码称为保护代码,使用try/catch的语法如下:

try {
    /* 程序代码 */
} catch(ExceptionName e1) {
    /* Catch块 */
}

Catch语句包含要捕获异常类型的声明。当保护代码块中发生一个异常时,try后面的catch块就会被检查。如果发生的异常包含在catch块中,异常会被传递到该catch块,这和传递一个参数到方法是一样。
  下面的例子声明有两个元素的一个数组,当代码试图访问数组的第三个元素的时候,就会抛出一个异常:

public class ExcepTest {
    public static void main(String args[]) {
        try {
            int[] a = new int[2];
            System.out.println("Access element three: " + a[3]);
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("Exception thrown: " + e);
        }

        System.out.println("Out of the block");
    }
}

执行结果:

Exception thrown: java.lang.ArrayIndexOutOfBoundsException: 3
Out of the block

多重捕获块

  一个try代码块后面跟随多个catch代码块的情况就叫多重捕获,可以在try语句后面添加任意数量的catch块:

try {
    /* ... */
} catch (异常类型1 异常的变量名1) {
    /* ... */
} catch (异常类型2 异常的变量名2) {
    /* ... */
} catch (异常类型2 异常的变量名2) {
    /* ... */
}

上面的代码段包含了3catch块,如果保护代码中发生异常,异常被抛给第一个catch块。如果抛出异常的数据类型与ExceptionType1匹配,它在这里就会被捕获;如果不匹配,它会被传递给第二个catch块。如此,直到异常被捕获或者通过所有的catch块:

try {
    file = new FileInputStream(fileName);
    x = (byte) file.read();
} catch (IOException i) {
    i.printStackTrace();
    return -1;
} catch (FileNotFoundException f) {
    f.printStackTrace();
    return -1;
}

throws/throw关键字

  throw一般用于程序出现某种逻辑时,程序员主动抛出某种特定类型的异常:

public class ExcepTest {
    public static void main(String[] args) {
        String s = "abc";

        if (s.equals("abc")) {
            throw new NumberFormatException();
        } else {
            System.out.println(s);
        }
    }
}

执行结果:

Exception in thread "main" java.lang.NumberFormatException
    at ExcepTest.main(ExcepTest.java:5)

  throws是方法可能抛出异常的声明(用在声明方法时,表示该方法可能要抛出异常):

[(修饰符)] (返回值类型) (方法名) ([参数列表]) [throws 异常类1 [, 异常类2]] {
    /* ... */
}

当某个方法可能会抛出某种异常时,用于throws声明可能抛出的异常,然后交给上层调用它的方法程序处理:

public class ExcepTest {
    public static void function() throws NumberFormatException {
        String s = "abc";
        System.out.println(Double.parseDouble(s));
    }

    public static void main(String[] args) {
        try {
            function();
        } catch (NumberFormatException e) {
            /* 输出“非数据类型不能转换” */
            System.err.println("非数据类型不能转换");
        }
    }
}

finally关键字

  finally关键字用来创建在try代码块后面执行的代码块。无论是否发生异常,finally代码块中的代码总会被执行。finally代码块出现在catch代码块的最后:

try {
    /* ... */
} catch (异常类型1 异常的变量名1) {
    /* ... */
} catch (异常类型2 异常的变量名2) {
    /* ... */
} finally {
    /* ... */
}

代码如下:

public class ExcepTest {
    public static void main(String args[]) {
        int a[] = new int[2];

        try {
            System.out.println("Access element three: " + a[3]);
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("Exception thrown: " + e);
        } finally {
            a[0] = 6;
            System.out.println("First element value: " + a[0]);
            System.out.println("The finally statement is executed");
        }
    }
}

执行结果:

Exception thrown: java.lang.ArrayIndexOutOfBoundsException: 3
First element value: 6
The finally statement is executed

  注意下面的事项:

  • catch不能独立于try存在。
  • try/catch后面添加finally块并非强制性要求的。
  • try代码后不能既没catch块也没finally块。
  • trycatchfinally块之间不能添加任何代码。

声明自定义异常

  在Java中,你可以自定义异常。编写自己的异常类时需要记住下面的几点:

  • 所有异常都必须是Throwable的子类。
  • 如果希望写一个检查性异常类,则需要继承Exception类。
  • 如果你想写一个运行时异常类,那么需要继承RuntimeException类。

可以像下面这样定义自己的异常类:

class MyException extends Exception {
}

只继承Exception类来创建的异常类是检查性异常类。一个异常类和其它任何类一样,包含有变量和方法。
  下面的InsufficientFundsException类是用户定义的异常类,它继承自Exception。以下实例是一个银行账户的模拟,通过银行卡的号码完成识别,可以进行存钱和取钱的操作:

public class InsufficientFundsException extends Exception {
    /* amount用来储存当出现异常(取出钱多于余额时)所缺乏的钱 */
    private double amount;

    public InsufficientFundsException(double amount) {
        this.amount = amount;
    }

    public double getAmount() {
        return amount;
    }
}

为了展示如何使用自定义的异常类,在下面的CheckingAccount类中包含一个withdraw方法,它可能会抛出一个InsufficientFundsException异常:

public class CheckingAccount { /* 此类模拟银行账户 */
    private double balance; /* 余额 */
    private int number; /* 卡号 */

    public CheckingAccount(int number) {
        this.number = number;
    }

    public void deposit(double amount) { /* 存钱 */
        balance += amount;
    }

    public void withdraw(double amount) throws InsufficientFundsException { /* 取钱 */
        if (amount <= balance) {
            balance -= amount;
        } else {
            double needs = amount - balance;
            throw new InsufficientFundsException(needs);
        }
    }

    public double getBalance() { /* 返回余额 */
        return balance;
    }

    public int getNumber() { /* 返回卡号 */
        return number;
    }
}

下面的BankDemo程序示范了如何调用CheckingAccount类的depositwithdraw方法:

public class BankDemo {
    public static void main(String[] args) {
        CheckingAccount c = new CheckingAccount(101);
        System.out.println("Depositing $500...");
        c.deposit(500.00);

        try {
            System.out.println("\nWithdrawing $100...");
            c.withdraw(100.00);
            System.out.println("\nWithdrawing $600...");
            c.withdraw(600.00);
        } catch (InsufficientFundsException e) {
            System.out.println("Sorry, but you are short $" + e.getAmount());
            e.printStackTrace();
        }
    }
}

执行结果:

Depositing $500...

Withdrawing $100...

Withdrawing $600...
InsufficientFundsException
    at CheckingAccount.withdraw(CheckingAccount.java:18)
    at BankDemo.main(BankDemo.java:10)
Sorry, but you are short $200.0

通用异常

  在Java中定义了两种类型的异常和错误:

  • JVM(Java虚拟机)异常:由JVM抛出的异常或错误,例如NullPointerException类、ArrayIndexOutOfBoundsException类、ClassCastException类。
  • 程序级异常:由程序或者API程序抛出的异常,例如IllegalArgumentException类、IllegalStateException类。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值