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 }
运行结果如下图:
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 }
抛出处理
抛出处理是通过 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 }
何时使用抛出处理?何时捕获处理?原则是什么呢?
如果需要通知到调用者,代码出现了问题,此时就需要使用抛出处理。
如果代码时直接和用户打交道,遇到异常时就不要抛出,因为用户不会处理异常,此时就需要使用捕获处理了。
自定义异常类
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 }
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 }
综上所述try块的使用方式有以下三种方式:
第一种: 比较适用于有异常要处理,但是没有资源要释放的。
try{
可能发生异常的代码
}catch(捕获的异常类型 变量名){
处理异常的代码
}
第二种:比较适用于既有异常要处理又要释放资源的代码。
try{
可能发生异常的代码
}catch(捕获的异常类型 变量名){
处理异常的代码
}finally{
释放资源的代码;
}
第三种: 比较适用于内部抛出的是运行时异常,并且有资源要被释放。(运行时异常可以捕获也可以不捕获)
try{
可能发生异常的代码
}finally{
释放资源的代码;
}