首页 > 代码库 > 异常处理

异常处理

java中异常体系

异常:java程序中存在某些不正常的情况。

异常体系图:

技术分享

通过上图基本可以了解java中异常体系的分类和继承关系:

异常体系:
-------| Throwable 异常和错误的超类
-----------| Errow 错误:一般是由于jvm或者硬件引发的问题,一般不会通过代码来处理。
-----------| Exception 异常:一般通过代码来处理。

--------------| 运行时异常:RuntimeException以及RuntimeException子类 都是属于运行时异常。如果一个方法内部抛出了一个运行时异常,那么方法上 可以声明也可以不 声明,调用者可以以处理也可以不处理。

--------------|编译时异常: 除了运行时异常就是编译异常。如果一个方法内部抛出了一个编译时异常对象,那么方法上就必须要声明,而且调用者也必须要处理。

 

 为什么java编译器会如此严格要求编译时异常,对运行时异常如此宽松?

 运行时异常都是可以通过程序员良好的编程习惯去避免,所以java编译器就没有严格要求处理运行时异常。

Throwable类

Throwable是异常和错误的超类。

常用方法:

  Throwabel常用的方法:
   toString() 返回当前异常对象的完整类名+病态信息。
   getMessage() 返回创建Throwable对象是传入的字符串信息。
   pintStackTrace() 打印异常的栈信息。

代码示例如下:

 

技术分享
 1 class Demo 
 2 {
 3     public static void main(String[] args) 
 4     {
 5         //Throwable类测试
 6         Throwable t = new Throwable("Throwable异常测试");
 7         String info = t.toString();
 8         System.out.println("toString函数返回值:"+info);
 9         String message = t.getMessage();
10         System.out.println("message函数返回值:"+message);
11         t.printStackTrace();
12     }
13 }
View Code

运行结果如下图:

技术分享

 Throwable下分为异常和错误,那么如何根据异常信息来判断是错误还是异常呢?

  如果程序出现不正常的信息,信息类名以Error结尾就是错误;以Exception结尾的就是异常。

Error:

  一般是由于jvm或者硬件引发的问题,一般不会通过代码来处理。如下面代码:

    //java虚拟机在默认的情况下只能管理64m内存。
      byte[] buf = new byte[1024*1024];

  

异常

  一般通过代码来处理。如:除数为0的算数异常。代码如下

  int a = 0;

  int b = 10/a;

  此时jvm虚拟机就会抛出ArithmeticException异常。

Jvm当遇到异常代码时时如何工作的呢?

  jvm运行到a/b这个语句的时候,发现b为0,除数为0在我们现实生活中是属于不正常的情况,jvm一旦发现了这种不正常的情况时候,那么jvm就会马上创建一个对应的异常对象,并且会调用这个异常对象 的printStackTrace的方法来处理。

 

在开发中不可能所有异常都由jvm来抛出和处理。程序人员必须在代码中进行相应的处理。

异常的处理方式:

  1、捕获处理。

  2、抛出处理。

下面来看一下捕获处理:

格式:

  try{
        可能发生异常的代码;

     }catch(捕获的异常类型 变量名){
        处理异常的代码....
     }

 注意的细节:

  1. 如果一个try块中出现了异常的代码,经过处理之后,那么try-catch块外面的代码可以正常执行。
    2. 如果一个try块中出现了异常的代码,那么在try块中出现异常的代码后面 的语句无法执行。
    3. 一个try块后面可以跟多个catch块,也就是一个try块可以捕获多种异常的类型,但是捕获的异常类型必须从小到大进行捕获。

测试代码如下:

技术分享
 1 class Demo 
 2 {
 3     public static void main(String[] args) 
 4     {
 5         int a = 10;
 6         int b = 2;
 7         int[] arr = null;
 8         div(a,b,arr);
 9         
10     }
11 
12     //除数为0的异常代码测试
13     public static void div(int a, int b,int[] arr){
14         int c = 0;
15         try{
16             c = a/b; //此处发生异常后,try块中的以后语句不执行
17             System.out.println("数组的长度:"+ arr.length);
18         }catch(ArithmeticException e){
19             System.out.println("除数不能为0");
20         }catch(NullPointerException e){
21             System.out.println("出现了空指针异常....");
22         }catch(Exception e){  
23             System.out.println("捕获所有类型的异常");
24         }
25         System.out.println("捕获异常后,可以执行");
26     }
27 }
View Code

抛出处理

抛出处理是通过 throw 和 throws两个关键字来实现。

throw 与throws两个关键字:
 1. throw关键字是用于方法内部的,throws是用于方法声声明上的。
 2. throw关键字是用于方法内部抛出一个异常对象的,throws关键字是用于在方法声明上声明抛出异常类型的。
 3. throw关键字后面只能有一个异常对象,throws后面一次可以声明抛出多种类型的 异常。

抛出处理要注意的细节:
   1. 如果一个方法内部抛出了一个编译时异常对象,那么该方法必须要声明抛出。
   2. 如果调用了一个声明抛出编译时异常的方法,那么调用者必须要处理。
   3. 如果一个方法抛出了一个异常对象,那么该方法也会马上停止(一个方法遇到了throw关键字,那么该方法就会马上停止)
   4. 在一种情况下只能抛出一种异常对象。

测试代码如下:

技术分享
 1 class Demo 
 2 {
 3     public static void main(String[] args) 
 4     {
 5         int a = 10;
 6         int b = 0;
 7         int[] arr = {1,2};
 8         try{
 9             div(a,b,arr);
10         }catch(Exception e){
11             System.out.println("发生了异常:"+e.getMessage());
12         }
13         
14     }
15 
16     //除数为0的异常代码测试
17     public static void div(int a, int b,int[] arr) throws ArithmeticException,NullPointerException{
18         if(b==0){
19             throw new ArithmeticException("除数不能为0");
20         }else if(arr==null){
21             throw new NullPointerException("数组指针为空");
22         }
23         int c = a/b;
24         System.out.println("数组的长度是:"+arr.length);
25         System.out.println("捕获异常后,可以执行");
26     }
27 }
View Code

何时使用抛出处理?何时捕获处理?原则是什么呢?

  如果需要通知到调用者,代码出现了问题,此时就需要使用抛出处理。

  如果代码时直接和用户打交道,遇到异常时就不要抛出,因为用户不会处理异常,此时就需要使用捕获处理了。

自定义异常类

sun提供了很多的异常类给我们用于描述程序中各种的不正常情况,但是sun 给我提供异常类还不足以描述我们现实生活中所有不正常情况,那么这时候我们就需要自定义异常类。

自定义异常类的格式:

  自定义一个类继承Exception。

下面通过一个例子来理解,需求如下:模拟QQ登录时的情况,当有网络是正常登录,没有网络的时候抛出没有网络的异常信息。

代码如下:

技术分享
 1 //自定义没有网络的异常类
 2 class NoNetWorkException extends Exception{
 3     public NoNetWorkException(String message){
 4         super(message);
 5     }
 6 }
 7 
 8 class Demo 
 9 {
10     public static void main(String[] args) 
11     {
12         String ip = "192.168.1.254";
13         ip = null;
14         try{
15             loginQQ(ip);
16         }catch(NoNetWorkException e){
17             System.out.println("异常信息:"+e.getMessage());
18         }
19     }
20 
21     public static void loginQQ(String ip)throws NoNetWorkException{
22         if (ip==null)
23         {
24             throw new NoNetWorkException("网络连接失败");
25         }
26         System.out.println("登录成功");
27     }
28 
29 }
View Code

finally 块

finally块的使用前提是必须要存在try块才能使用。

finally块的代码在任何情况下都会执行的(比如return和throw关键字后),除了jvm退出的情况。

finally非常适合做资源释放的工作,这样子可以保证资源文件在任何情况下都 会被释放。

使用代码示例如下:

 

技术分享
 1 //读取文件操作
 2 import java.io.*;
 3 class Demo 
 4 {
 5     public static void main(String[] args) 
 6     {
 7         FileReader fileReader = null;
 8         try{
 9             //获取目标文件
10             File file = new File("F:\\test.txt");
11             //建立数据通道
12             fileReader = new FileReader(file);
13             //读取文件
14             char[] buffer = new char[1024];
15             int lenght = 0;
16             lenght = fileReader.read(buffer);
17             System.out.println("读取到的内容:"+new String(buffer,0,lenght));
18         }catch(IOException e){
19             System.out.println("读取文件失败");
20         }finally{
21             try{
22                 fileReader.close();
23                 System.out.println("释放资源文件成功");
24             }catch(Exception e){
25                 System.out.println("释放资源文件失败");
26             }
27         }
28     }
29 }
View Code

 

综上所述try块的使用方式有以下三种方式:

第一种: 比较适用于有异常要处理,但是没有资源要释放的。
   try{

     可能发生异常的代码
 
   }catch(捕获的异常类型 变量名){
      处理异常的代码
   }

第二种:比较适用于既有异常要处理又要释放资源的代码。
  
  try{

     可能发生异常的代码
 
   }catch(捕获的异常类型 变量名){
      处理异常的代码
   }finally{ 
      释放资源的代码;
   }

第三种: 比较适用于内部抛出的是运行时异常,并且有资源要被释放。(运行时异常可以捕获也可以不捕获)
     try{

     可能发生异常的代码
 
   }finally{ 
      释放资源的代码;
   }

异常处理