异常

异常体系

  1. 程序出现的不正常的情况.
  2. 异常的体系
    • Throwable
      • |–Error 严重问题, 我们不处理.
      • |–Exception
        • |–RuntimeException 运行期异常, 我们需要修正代码
        • |–非RuntimeException 编译期异常, 必须处理的, 否则程序编译不通过

内置异常

异常处理

  • JVM的默认处理
    • 把异常的名称,原因,位置等信息输出在控制台, 但是呢程序不能继续执行了.
  • 自己处理
    • a:try…catch…finally
      • 自己编写处理代码,后面的程序可以继续执行
    • b:throws
      • 把自己处理不了的, 在方法上声明, 告诉调用者, 这里有问题

捕获异常

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

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

catch 语句包含要捕获异常类型的声明. 当保护代码块中发生一个异常时, try 后面的 catch 块就会被检查.

如果发生的异常包含在 catch 块中, 异常会被传递到该 catch 块, 这和传递一个参数到方法是一样.

实例

下面的例子中声明有两个元素的一个数组, 当代码试图访问数组的第三个元素的时候就会抛出一个异常.

// 文件名 : ExcepTest.java
import java.io.*;
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");
   }
}

多重捕获块

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

try{
   // 程序代码
}catch(异常类型1 异常的变量名1){
  // 程序代码
}catch(异常类型2 异常的变量名2){
  // 程序代码
}catch(异常类型2 异常的变量名2){
  // 程序代码
}

上面的代码段包含了 3 个 catch块.

可以在 try 语句后面添加任意数量的 catch 块.

如果保护代码中发生异常, 异常被抛给第一个 catch 块.

如果抛出异常的数据类型与 ExceptionType1 匹配, 它在这里就会被捕获.

如果不匹配, 它会被传递给第二个 catch 块.

如此, 直到异常被捕获或者通过所有的 catch 块.

实例

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

捕获方式

try...catch...finally
try...catch...
try...catch...catch...
try...catch...catch...fianlly
try...finally

throws/throw

throws

如果一个方法有捕获一个检查性异常, 那么该方法必须使用 throws 关键字来声明. throws 关键字放在方法签名的尾部.

  • 在方法声明上,后面跟的是异常的类名,可以是多个
  • throws是声明方法有异常, 是一种可能性, 这个异常并不一定会产生
throw
  • 在方法体中, 后面跟异常对象名, 并且只能是一个
  • throw 触发异常, 无论它是新实例化的还是刚捕获到的.

下面方法的声明抛出一个 RemoteException 异常:

import java.io.*;
public class className
{
  public void deposit(double amount) throws RemoteException
  {
    // Method implementation
    throw new RemoteException();
  }
  //Remainder of class definition
}

一个方法可以声明抛出多个异常, 多个异常之间用逗号隔开.

例如, 下面的方法声明抛出 RemoteExceptionInsufficientFundsException

import java.io.*;
public class className
{
   public void withdraw(double amount) throws RemoteException,
                              InsufficientFundsException
   {
       // Method implementation
   }
   //Remainder of class definition
}

finally

finally 关键字用来创建在 try 代码块后面执行的代码块.

无论是否发生异常, finally 代码块中的代码总会被执行, 以运行清理类型等收尾善后性质的语句. 特殊情况: 在执行到finally之前JVM退出了

finally 代码块出现在 catch 代码块最后

try{
  // 程序代码
}catch(异常类型1 异常的变量名1){
  // 程序代码
}catch(异常类型2 异常的变量名2){
  // 程序代码
}finally{
  // 程序代码
}

注意下面事项

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

自定义异常

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

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

只需要提供无参构造和一个带参构造即可

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

class MyException extends Exception{
}

只继承Exception 类来创建的异常类是检查性异常类.

下面的 InsufficientFundsException 类是用户定义的异常类, 它继承自 Exception.

一个异常类和其它任何类一样, 包含有变量和方法.

编译器异常和运行期异常

  • 编译期异常 必须要处理的, 否则编译不通过
  • 运行期异常 可以不处理, 也可以处理

异常注意事项

  • 异常的注意实现
    • 父的方法有异常抛出,子的重写方法在抛出异常的时候必须要小于等于父的异常
    • 父的方法没有异常抛出,子的重写方法不能有异常抛出
    • 父的方法抛出多个异常,子的重写方法必须比父少或者小