您的位置:首页 > 职场人生

黑马程序员——Java 异常的捕获及处理

2013-12-08 17:32 567 查看
------- android培训java培训、期待与您交流!
----------

 

Java 异常的捕获及处理

 

1.异常的概念

 

异常是导致程序中断运行的一种指令流,如果不对异常进行正确处理,则可能导致程序的中断执行,造成不必要的损失,所以在程序的设计中必须要考虑各种异常的发生,并正确地做好相应的处理,这样才能保证程序正常地进行。在Java中一切的异常都面向对象的,所有的异常都以类和对象的形式存在,除了系统提供的各种异常类外,我们也可根据自己需要定义自己的异常类。

在异常处理机制诞生之前,传统的异常处理方式多数是为了采用返回值来标识程序出现异常的情况,这种方式都很熟悉,如同在调试过程即是有良好的调试工具,但是常用的手段就是System.out.println的方式,但是这样的方式隐含一定的缺点。

 

2.异常的处理

在开发过程中,当一个程序本身抛出了异常过后,程序会从程序导致异常的地方跳出来,在Java语言里面,使用trycatch块来实现,当JVM碰到这个语句块的时候,它会监听整个Java程序,一旦出现了任何异常情况,它会将整个程序的执行权交给catch块来执行。异常处理形式:

 

try{
可能出现异常的语句 ;
} catch(异常类型 异常对象){
处理异常 ;
} catch(异常类型 异常对象){
处理异常 ;
} ...
finally{
异常处理的统一出口 ;
}


 

在try语句中捕获可能出现的异常代码,如果在try中产生了异常,则程序会自动跳转到catch语句中找到匹配的异常类型进行相应的处理。最后不管程序是否产生异常,则肯定都会执行到finally语句,finally语句就作为异常的统一出口,finally块是可以省略的,如果省略了finally块,则在catch()块运行结束后,程序会跳到try-catch块之后继续执行。

首先来看下面代码示例,程序出现异常,程序将会中断退出。

public class Demo2 {
public static void main(String args[]){
int x = 3 ;
int y = 0 ;
System.out.println("计算开始 ") ;
System.out.println("计算结果是:" + (x/y)) ;
System.out.println("计算结束") ;
}
}


 

程序输出结果:

---------- java ----------
计算开始
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Demo2.main(Demo2.java:6)

 

可以看出出现抛出了一个(ArithmeticException: / by zero)异常信息,导致程序不能完整执行,而中断退出。这时我们可以在将要出现异常代码处做出异常处理。

public class Demo2 {
public static void main(String args[]){
int x = 3 ;
int y = 0 ;
System.out.println("计算开始 ") ;
try{
System.out.println("计算结果是:" + (x/y)) ;
}catch(Exception e){
System.out.println("出现异常了:"+e) ;
}
System.out.println("计算结束") ;
}
}

 

程序输出结果:

---------- java ----------
计算开始
出现异常了:java.lang.ArithmeticException: / by zero
计算结束

 

此时,程序就可以完整的执行,并且打印了相应的异常信息。这里我们可以加上finally作为异常处理的统一出口。

public class Demo2 {
public static void main(String args[]){
int x = 3 ;
int y = 0 ;
System.out.println("计算开始 ") ;
try{
System.out.println("计算结果是:" + (x/y)) ;
}catch(Exception e){
System.out.println("出现异常了:"+e) ;
}finally{
System.out.println("finally执行结束.") ;
}
System.out.println("计算结束") ;
}
}


程序运行结果:

---------- java ----------
计算开始
出现异常了:java.lang.ArithmeticException: / by zero
finally执行结束.
计算结束


当然在很多程序中不只存在一个异常,可能会同时出现多个异常,此时,就需要使用多个catch语句进行处理。如在命令行输入两个数字,并进行相除操作。

public class Demo2 {
public static void main(String args[]){
int x = 0 ;
int y = 0 ;
System.out.println("计算开始----> ") ;
try{
String str1 = args[0];
String str2 = args[1];
x  = Integer.parseInt(str1);
y  = Integer.parseInt(str2);
System.out.println("相除结果是-->:" + (x/y)) ;
}catch(ArithmeticException e){
System.out.println("出现异常了-->:"+e) ;
}finally{
System.out.println("-->finally执行结束.") ;
}
System.out.println("-->计算结束") ;
}
}

//输入的被除数是0,此错误已经被捕获了

C:\code>java Demo2 4 0
计算开始---->
出现异常了-->:java.lang.ArithmeticException: / by zero
-->finally执行结束.
-->计算结束


//运行时参数输入的不是数字

C:\code>java Demo2 4 a
计算开始---->
-->finally执行结束.
Exception in thread "main" java.lang.NumberFormatException: For input string: "a"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48)
at java.lang.Integer.parseInt(Integer.java:449)
at java.lang.Integer.parseInt(Integer.java:499)
at Demo2.main(Demo2.java:10)

//没有输入参数或输入的参数不够

C:\code>java Demo2 4
计算开始---->
-->finally执行结束.
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 1
at Demo2.main(Demo2.java:8)

此时如果想要保持程序的正确运行,就必须同时对3个异常进行处理,所以此时的catch语句应该有3个,分别处理不同异常。

public class Demo2 {
public static void main(String args[]){
int x = 0 ;
int y = 0 ;
System.out.println("计算开始----> ") ;
try{
String str1 = args[0];
String str2 = args[1];
x  = Integer.parseInt(str1);
y  = Integer.parseInt(str2);
System.out.println("相除结果是-->:" + (x/y)) ;
}catch(ArithmeticException e){
System.out.println("算术异常-->:"+e) ;
}catch(NumberFormatException e){
System.out.println("数字转换异常-->:"+e) ;
}catch(ArrayIndexOutOfBoundsException e){
System.out.println("数组越界异常-->:"+e) ;
}
finally{
System.out.println("-->finally执行结束.") ;
}
System.out.println("-->计算结束-->") ;
}
}


程序测试结果:

C:\code>java Demo2 4
计算开始---->
数组越界异常-->:java.lang.ArrayIndexOutOfBoundsException: 1
-->finally执行结束.
-->计算结束-->

C:\code>java Demo2 4 a
计算开始---->
数字转换异常-->:java.lang.NumberFormatException: For input string: "a"
-->finally执行结束.
-->计算结束-->

C:\code>java Demo2 4 0
计算开始---->
算术异常-->:java.lang.ArithmeticException: / by zero
-->finally执行结束.
-->计算结束-->

 

 

3.异常类的体系

 Java里面的异常分类按照下边的结构来分:Error和Exception



Throwable是所有异常的基类,程序中一般不会直接抛出Throwable对象,Throwable本身存在两个子类实例,一个是Error、一个是Exception;

1).Error:在Java里面Error表示程序在运行期间出现了十分严重的问题以及不可以恢复的错误,这种情况唯一的办法是中止程序运行,JVM一般不会检查Error是否被处理,而本身在程序中也不能捕获Error类型的异常,因为Error一旦产生,该程序基本会处于需要中止的状态。

2).Exception:在Java里面Exception指的就是在编程过程可能会遇到的异常的概念,也属于Java程序里面允许出现的例外的状况,而Exception本身分为以下两种:

a).运行时异常(RuntimeException/Unchecked Exception):Java中所有的运行时异常都直接或间接的继承自RuntimeException。该异常继承于Exception类,这种类型的异常可以这样理解,为不可估计的异常,一般称为运行时异常,从字面意思上讲就是在程序正式运行的时候会碰到的偶发性异常,这种异常因为是在运行时抛出一般情况下不需要进行捕获操作。

b).非运行时异常(CheckedException):该类异常不存在继承于Exception类的说法,因为Java里面没有CheckedException异常类,而之所以这样区分,因为CheckedException类的异常是在编程过程中经常会遇到的异常,可以翻译为“可检测异常”或者“捕获型异常”,该类异常往往属于编译期异常,一般开发过程会针对系统进行这类异常(CheckedException)的设计。

RuntimeException和非运行时异常(CheckedException)的区别,非运行时异常(CheckedException)必须处理,而RuntimeException可以不用处理。

throw、throws关键字:

throw关键字总是出现在方法体内部,用来抛出一个异常对象,程序会在throw语句后立即终止执行,也就是说位于throw语句之后的语句块不会执行,一旦它抛出了一个异常过后,JVM会在包含它的try块所对应的catch里面根据抛出的异常类型匹配操作,如果能匹配就直接被捕捉,一旦不能匹配就继续往外层抛出该异常。

throws关键字总是出现在方法头部,用来表明该方法有可能会抛出某种异常,有几点需要注意:

a).方法可以抛出多个不同类型的异常,直接使用throws,将每种不同异常分开;

b).如果方法体里面存在throw语句,而且方法体本身没有进行捕捉的话,那么必须使用throws在方法头部里面添加对应的异常抛出语句,否则无法通过编译;

c).如果编写代码的时候需要明确抛出一个RuntimeException,那么必须显示使用throws语句来声明它的类型;

d).以上的规则主要是针对CheckedException,针对Error和RuntimeException或者它们的子类,这些规则不起作用。

public class Demo {
public static void main(String args[]) {
try{
throw new Exception("抛着玩的!") ;
}catch(Exception e){
e.printStackTrace() ;
}
}
}


输出运行结果:
---------- java ----------
java.lang.Exception: 抛着玩的!
at Demo.main(Demo.java:4)


//throws处理异常

class MyMath {
public int div(int i,int j) throws Exception{
return i / j ;
}
}
public class Demo {
public static void main(String args[]){
try{
System.out.println(new MyMath().div(10,0)) ;
}catch(Exception e){
e.printStackTrace() ;
}
}
}


输出运行结果:

---------- java ----------
java.lang.ArithmeticException: / by zero
at MyMath.div(Demo.java:3)
at Demo.main(Demo.java:9)
也可以直接在main方法上继续抛出异常:

class MyMath {
public int div(int i,int j) throws Exception{
return i / j ;
}
}
public class Demo {
public static void main(String args[]) throws Exception {
System.out.println(new MyMath().div(10,0)) ;
}
}


输出运行结果:

---------- java ----------
Exception in thread "main" java.lang.ArithmeticException: / by zero
at MyMath.div(Demo.java:3)
at Demo.main(Demo.java:8)


此时往外抛出的异常就由JVM帮助我们自动处理。

4.异常处理的标准格式

异常的处理语句:try…catch…finally、throw、throws在开发中是要一起使用的,

class MyMath {
public int div(int i,int j) throws Exception{
System.out.print("计算开始--") ;
int temp = 0 ;
try{
temp = i / j ;
}catch(Exception e){
throw e ;	// 向方法上抛出
}finally{
System.out.println("-->计算结束") ;
}
return temp ;
}
}
public class Demo {
public static void main(String args[]){
try{
System.out.println(new MyMath().div(10,0)) ;
}catch(Exception e){
e.printStackTrace() ;//打印异常堆栈信息
}
}
}


输出运行结果:

---------- java ----------
计算开始---->计算结束
java.lang.ArithmeticException: / by zero
at MyMath.div(Demo.java:6)
at Demo.main(Demo.java:18)


5.自定义异常(Custom Exception)

自定义异常通常是定义一个继承自Exception类的子类。一般情况下我们都会直接继承自Exception类,而不会继承某个运行时的异常类。

public class MyException extends Exception{
public MyException(){
super();
}
public MyException(String msg){
super(msg);
}
}


使用自定义异常类,

public class ExcDemo {

public static void execute(String a) throws MyException {
System.out.println("execute...");
if("true".equals(a)){
throw new MyException("参数不能为 true");
}
}
}


捕获自定义异常,

public static void main(String[] args) throws MyException {
execute("true");
}


输出运行结果:

---------- java ----------
execute...
Exception in thread "main" MyException: 参数不能为 true
at Demo.execute(Demo.java:15)
at Demo.main(Demo.java:19)


如果要使用多个catch块来捕获异常,这时需要将父类型的catch块放到子类型的catch块之后,这样才能保证后续的catch块可能被执行,否则子类型的异常块永远无法达到,Java编译器就会报错;如果多个catch块的异常类型是独立的(MyException,MyException2),那么谁前谁后都可以。

public class MyException2 extends Exception
{
public MyException2()
{
super();
}

public MyException2(String message)
{
super(message);
}
}


//MyException、MyException2

public class Demo{
public void method(String str) throws Exception{
if(null == str)		{
throw new MyException("传入的字符串参数不能为null");
}else if("hello".equals(str)){
throw new MyException2("传入的字符串不能为hello");
}else{
System.out.println(str);
}
}
public static void main(String[] args){
try{
Demo test = new Demo();
test.method("hello");
}catch(MyException e){
System.out.println("进入到MyException catch块");
e.printStackTrace();
}catch(MyException2 e){
System.out.println("进入到MyException2 catch块");
e.printStackTrace();
}catch(Exception e){
System.out.println("进入到Exception catch块");
e.printStackTrace();
}finally{
System.out.println("异常处理完毕");
}
System.out.println("程序执行完毕");
}
}


输出运行结果:

---------- java ----------
进入到MyException2 catch块
MyException2: 传入的字符串不能为hello
at Demo.method(Demo.java:6)
at Demo.main(Demo.java:14)
异常处理完毕
程序执行完毕

注意:如果在try块中存在return语句,那么先将finally块中的代码执行完毕之后,方法才返回。如果在try块中存在System.exit(0)语句,那么就不会执行finally块中的代码,因为System.exit(0)会终止当前运行的JVM,程序会在JVM终止前结束执行。

考题:12、final, finally, finalize 的区别。

final用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,类不可继承。

finally是异常处理语句结构的一部分,表示总是执行。
finalize是Object 类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回收,例如关闭文件等。

总结:

1).异常的概念及处理。

2).异常的体系结构,Error、Exception,运行时异常(RuntimeException/Unchecked Exception)、非运行时异常(CheckedException)及捕获。

3).异常的标准处理格式。

4).自定义异常类。

 

 

 

 

------- android培训java培训、期待与您交流!
----------
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  异常 java