3.16 Java异常

在程序执行过程中,可能有各种出错的情况,有的是不可控的内部原因,比如内存不够了、磁盘满了,有的是不可控的外部原因,比如网络连接有问题,更多的可能是程序的编写错误,比如引用变量未初始化就直接调用实例方法。这些非正常情况在Java中都被认为是异常。

异常简介

异常是相对于 return 的一种退出机制,可以由系统触发,也可以由程序通过 throw 语句触发,异常可以通过 try/catch 语句进行捕获并处理,如果没有捕获,则会导致程序退出并输出异常栈信息。

当发生异常时,Java 会启用异常处理机制,首先创建一个异常对象,然后异常处理机制会从当前方法开始查找看谁 “捕获” 了这个异常,当前方法没有就查看上一层,直到 main 方法,如果也没有,就使用默认机制,输出异常栈信息并退出。

异常栈信息包括从异常发生点到最上层调用者的轨迹,还包括行号,是分析异常最为重要的信息。

对于异常栈信息,普通用户无法理解,也不知道该怎么办,我们需要给用户一个更为友好的信息,又或者我们不希望程序在出现异常后就退出程序,而是抛出异常然后继续执行,为此需要使用 try/catch 捕获异常。

try 后面的花括号 {} 内包含可能抛出异常的代码,catch 语句包含能捕获的异常和处理代码,捕获异常后,程序就不会异常退出了,但 try 语句内异常点之后的代码不会执行,执行完 catch 内的语句后,程序会继续执行 try/catch 之后的代码。

Throwable

java.lang.Throwable 是所有异常的基类,它有两个子类:Error 和 Exception。

Error 用来指示运行时环境发生的错误,一般发生在严重故障时,Java 程序通常不捕获错误,比如 内存溢出错误(OutOfMemory-Error)和栈溢出错误(StackOverflowError)。

Exception 表示应用程序错误,它有很多子类,应用程序也可以通过继承 Exception 或其子类创建自定义异常。

RuntimeException 是未受检异常(unchecked exception),IOException、SQLException 和 Exception 自身则是受检异常(checked exception)。对于受检异常,Java 会强制要求程序员进行处理,否则会有编译错误,而对于未受检异常则没有这个要求。

大部分类在继承父类后只是定义了几个构造方法,这些构造方法也只是调用了父类的构造方法,并没有额外的操作。定义这么多不同的异常类主要是为了名字不同,异常类的名字本身就代表了异常的关键信息。

自定义异常

一般是继承 Exception 或者它的某个子类。如果父类是 RuntimeException 或它的某个子类,则自定义异常也是未受检异常;如果是 Exception 或 RuntimeException 以外的其他子类,则自定义异常是受检异常。

// 自定义异常类,继承Exception类
public class InsufficientFundsException extends Exception {
  // 此处的amount用来储存当出现异常(取出钱多于余额时)所缺乏的钱
  private double amount;
  
  public InsufficientFundsException(double amount) {
    this.amount = amount;
  } 
  
  public double getAmount() {
    return amount;
  }
}

异常处理

try catch

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

// 自定义异常类,继承Exception类
public class InsufficientFundsException extends Exception {
  // 此处的amount用来储存当出现异常(取出钱多于余额时)所缺乏的钱
  private double amount;
  public InsufficientFundsException(double amount) {
    this.amount = amount;
  } 
  public double getAmount() {
    return amount;
  }
}

一个 try 代码块后面跟随多个 catch 代码块的情况就叫多重捕获。

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

throw

在 catch 块内处理完后,可以重新抛出异常,异常可以是原来的,也可以是新建的。

如果一个方法抛出检查性异常,那么该方法必须使用 throws 关键字来声明抛出的异常,否则不能抛出。

throws 跟在方法的括号后面,可以声明多个异常,以逗号分隔。

声明的含义是,这个方法内可能抛出这些异常,且没有没有处理完这些异常,调用者必须进行处理。

public void test() throws Exception {
  try{
    // 可能触发异常的代码
  }catch(NumberFormatException e){
    System.out.println("not valid number");
    throw new AppException("输入格式不正确", e);
  }catch(Exception e){
    e.printStackTrace();
    throw e;
  }
}

finally

finally 内的代码不管有无异常发生,都会执行。一般用于释放资源,如数据库连接、文件流等。

  • 如果没有异常发生,在 try 内的代码执行结束后执行
  • 如果有异常发生且被 catch 捕获,在 catch 内的代码执行结束后执行
  • 如果有异常发生但没被 catch 捕获,则在异常 throw 给上层之前执行
  • 如果在 try 或者 catch 语句内有 return 语句,则 return 语句在 finally 语句执行结束后才执行,对于基本数据类型,finally 中并不能改变返回值(可以理解为返回值存储在返回值存储器中,finally 的操作不会改变返回值存储器中的值),对于对象类型,可以改变其中的值(返回值存储器存储的是对象地址)。
  • 如果 finally 中有 return,不仅会覆盖 try 和 catch 内的返回值,还会掩盖 try 和 catch 内的异常,就像异常没有发生一样,应该避免在 finally 中使用 return 语句或者抛出异常
try {
  // 可能抛出异常
} catch (Exception e){
  // 捕获异常
} finally {
  // 不管有无异常都执行
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值