Java SE之异常机制

Java 异常机制

一.Java 异常机制

—— Java的异常机制是将异常处理代码和正常业务代码分开,保证程序代码的可读性及增强程序的健壮性(鲁棒性)。

二.Java 的异常处理流程

    基本异常处理流程如下:

try  // 执行业务处理,若完全执行,则一切正常

{

//正常业务处理代码

}

catch (异常1 ex1) 

{

// 进行异常1处理

catch (异常2 ex2) 

{

// 进行异常2处理

……

catch (异常n exn) 

{

// 进行异常n处理

    

说明:

① 当执行try 代码块时出现异常,系统会自动生成一个异常对象 ex 

② 依次用异常对象匹配catch 块异常类(用 instanceof 进行匹配);

③ 若果 instanceof 运算返回true ,系统会自动将异常对象传给 catch 块后面的形参,并执行 catch 块中的代码。

    

★ 不同的 catch 块负责不同的异常处理,对于一个异常,最多只有一个 catch 块能捕捉到该异常;

★ 多个 catch 块捕捉异常,应该先捕捉子类异常(小异常),再捕捉父类异常(大异常);

★ JDK 1.7 提供了新功能:多异常捕捉

   catch (类1 |2 | … ex 

★ 访问异常对象:

   — 当catch 块捕捉到异常时,异常对象会作为 catch 块的参数被传入,因此 catch 块可通过该参数来访问实际的异常对象。

三.Java 异常类的继承体系

  Java 提供了丰富的异常类,这些类有着严格的继承体系,如下图 :


  从图可以看出,Java 把所有非正常情况分为两种:异常(Exception)和错误(Error),二者都继承了 Throwable 父类(Error 错误,一般是指虚拟机JVM的相关问题,这种错误无法恢复或不可能捕获,将导致应用程序中断)。

四.异常处理的完整语法格式

try 

{

//正常业务处理代码

}

catch (异常 ex)    // 0~ N 个

{

//处理异常代码

}

Finally             // 0~ 1 个 

{

}

★ try 代码块不能独立存在——catch 和 finally 代码块至少要有一个

★ finally ——通常用于回收资源

JVM会保证 finally 代码块总是得到执行,无论程序是否抛出异常,即使是return 语句(return语句可以结束方法执行,并返回)也不能阻止;除非遇到退出JVM的代码,finally 代码块才不会执行。

示例程序1

public class FinallyTest  

{

public static void main(String[] args) 

{

System.out.println(test());

}

public static int test()

    {

int a = 10;

try

{

System.out.println("执行 try 代码块");

return ++a;

}

/* try 代码块里面的 return 语句会执行,原本执行 return 语句后,

   方法会返回,但是 finally 代码块总会获得执行,所以处理流程是:

   执行 try 代码块里面的 return后(此时 a= 11)方法在将要返回、而

   有没有返回时,系统会去执行 finally 代码块,最后遇到 finally代码

   块里面的 return 是返回(a=12*/

finally

{

System.out.println("执行 finally 代码块");

return ++a;

}

}

}

运行结果 :

执行 try 代码块

执行 finally 代码块

12

示例程序2:退出 JVM finally块不执行

public class FinallyTest  

{

public static void main(String[] args) 

{

try

{

System.out.println("执行 try 代码块");

System.exit(1);  //退出JVMfinally块不执行,也可以用下面一行代码

// Runtime.getRuntime().exit(1);

}

catch (Exception ex)

{

ex.printStackTrace(); //输出异常跟踪栈(堆栈)信息

}

finally

{

System.out.println("finally 块的执行");

}

}

}

运行结果:

         执行 try 代码块

五.异常处理的嵌套

       在 try块、catch 块、finally 块中包含完整的异常处理流程的情形被称为异常处理的嵌套。

       异常处理代码可以放在任何能放可执行代码的地方, 因此完整的异常处理流程代码可以放在try块、catch 块、finally 块中,不过一般很少放在 try块中。异常处理的嵌套程度没有明确的限制,但通常没有必要使用超过两层的嵌套,嵌套层次过多,会降低程序的可读性。

六.try语句自动关闭资源

— JDK1.7 提供了一个自动关闭资源的try语句

    try ( // 只能声明并创建可自动关闭的资源)

{

//业务处理

}

★ 对于自动关闭资源的 try 语句,可以没有 catchfinally 块而独立存在,自动关闭资源的 try 语句有两个注意点:

   — 只有放在 try 后面的 () 里面的资源才会被关闭;

   — try 后面的 () 里面 只能声明并创建可自动关闭的资源,创建的资源引用变量属于try代码块。

   — 能被自动关闭的资源必须实现 Closeable 或者 AutoCloseable 接口。

示例程序1

//本程序演示用finally 块手动关闭资源

import java.io.*;

import java.net.*;

public class FinallyCloseTest

{

public static void main(String[] args)

throws Exception

{

FileInputStream fis = null; //声明资源(资源变量 fis

try

{

//资源为“Test.java,即本程序文件

fis = new FileInputStream("Test.java"); 

//fis.read()读入加一个字符

System.out.println((char)fis.read());

System.out.println((char)fis.read());

System.out.println((char)fis.read());

System.out.println((char)fis.read());

System.out.println((char)fis.read());

System.out.println((char)fis.read());

    //以上业务代码读入“Test.java”文件(即本程序)

//的前六个字符并打印,及“i,m,p,o,r,t

}

finally 

{

try

{

fis.close();  //关闭文件

}

catch (Exception ex)

{

ex.printStackTrace();

}

}

}

}

   

运行结果:

i

m

o

示例程序2

//本程序演示 try 块自动关闭资源

import java.io.*;

import java.net.*;

public class AutoCloseTest

{

public static void main(String[] args)

throws Exception

{

try(FileInputStream fis = 

new FileInputStream("Test.java");)

//声明资源(资源变量 fis

    //资源为“Test.java,即本程序文件

{

    //fis.read()读入加一个字符

System.out.println((char)fis.read());

System.out.println((char)fis.read());

System.out.println((char)fis.read());

System.out.println((char)fis.read());

System.out.println((char)fis.read());

System.out.println((char)fis.read());

    //以上业务代码读入“Test.java”文件(即本程序)

//的前六个字符并打印,及“i,m,p,o,r,t

}

//自动关闭资源的 try块,相当于最后有一个隐藏的 finally 

}

   

运行结果:

i

m

o

七.异常分类:checked异常和 runtime 异常

— checked 异常:普通的、不是RuntimeException子类的异常,都是 checked异常;

   checked 异常,其设计初衷:没有完善的错误处理代码,不会得到执行的机会。对于与checked异常,编译器会强制检查,因而可以更好地保证“程序员一定会对异常进行处理”,但是给编程带来麻烦,因为编译器要求程序员:要么用 throws声明抛出(说明该方法已经注意到了异常,但目前该方法无法或者不想处理该异常),要么显示同catch捕捉。

     在方法重写是,要求子类方法用 throws声明抛出的异常必须比父类方法更小或相等,应该注意的是,子类方法不声明抛出也是可以的(不声明抛出异常,可理解为异常最小)。   

— runtime 异常:直接或者间接继承了 RuntimeException的子类都是 runtime 异常,否则就是checked异常。 

   对于runtime 异常,编译器不会强制进行检查,对于异常的的捕捉与处理与否取决于程序员。

八.异常转译和自定义异常

    —— 异常是主观的,只要程序出现业务不希望的情况,就可以认为是异常。

①  throws 和 throw 的区别:

   (1) throws 只能在方法签名中使用,后面可以跟多个异常类的类名,多个类名之间用逗号隔开;

   (2)  throw 就是一条普通语句,throw 后面紧跟一个异常实例。

示例程序: throw 抛出异常 

public class Test

{

public static void main(String[] args)

{

//将用户传入的第1个参数转换为double 

double d1 = Double.parseDouble(args[0]);

//将用户传入的第2个参数转换为double

double d2 = Double.parseDouble(args[1]);

        //主观认为第2个参数不能为0

//如果第2个参数 d2 的值为0,业务上认为是异常

if(d2 == 0)

{

throw new IllegalArgumentException("参数不能为0");

}

}

}

运行结果:


②  抛出异常的两种可能情况:

   (1)  程序不能成功地执行完成,由系统自动生成异常对象,并抛出;

   (2)  程序出现了与业务规则不相符的情况,由程序员来创建异常对象,并通过 throw关键字抛出。

   —— 即使是程序员通过throw 抛出的异常,如果是 checked 异常,JVM同样会强制检查,要求程序员要么显示捕捉,要么用throws 声明抛出。

③  有名的Runtime异常:

    — NullPointerException                空指针异常

    — ArrayIndexOutOfBoundsException     数组索引(下标)越界异常

    — ClassCastException                  类型转换异常

    — ArithmeticException                 算数异常

    — NumberFormatException              字符转数字格式(类型)异常

    — IllegalArgumentException             非法参数异常

④  异常转译:catchthrow的结合使用

    在某些情况下,A方法(例如main方法)调用了B方法(例如普通方法),当B方法出现了异常,而且B方法自己捕捉了异常,此时A方法就无法判断被调用的B方法是否发生过异常。

    在这种情况下,若是B方法虽然自身捕获了异常,但是A方法有需要知道被调用的B方法发生过的异常,可以采用的做法是:先用 catch 捕获原有的异常,再用 throw 抛出一个自定义异常,即 异常转译。

⑤  自定义异常:

    — 自定义 runtime 异常,继承 RuntimeException

    — 自定义 checked 异常,继承 Exception

示例程序:自定义异常

File1

//自定义一个runtime 异常类 MyException

public class MyException extends RuntimeException 

{

    public MyException(){} //无参构造器

public MyException(String msg)

{

// 显示调用父类 RuntimeException中带一个String参数的构造器

super(msg);

}

}

File2

public class Test

{

public static void main(String[] args)

{

try

{

//将用户传入的第1个参数转换为int  

 Integer tg1 =  Integer.parseInt(args[0]);

//将用户传入的第2个参数转换为int 

 Integer tg2 =  Integer.parseInt(args[1]);

//打印 tg1/tg2 的结果

         System.out.println(tg1 / tg2);

}

//捕捉数组索引越界异常

catch (ArrayIndexOutOfBoundsException ex)

{

throw new MyException("数组索引越界");

}

//捕捉字符转数字格式(类型)异常

catch (NumberFormatException ex)

{

throw new MyException("必须输入数字");

}

//捕捉算数异常

catch (ArithmeticException ex)

{

throw new MyException("除数不能为0");

}

//捕捉 Exception 应该放在后面

catch (Exception ex)

{

throw new MyException("捕捉 Exception");

}

}

}  

运行结果:


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值