Java 异常与捕获
几乎所有的代码里面都会出现异常,为了保证程序在出现异常之后可以正常执行完毕,就需要进行异常处理。
先来看一下异常的继承类结构:
所有的异常都是由Throwable继承而来,我们来看他下面的两个子类Error和Exception.
Error类描述了Java运行时内部错误和资源耗尽错误。应用程序不抛出此类异常,这种内部错误一旦出现,除了告知 用户并使程序安全终止之外,再无能无力。这种情况很少出现。
我们需要关心的是Exception以及其子类。在Exception之下又分为两个分支,RuntimeException和IOException。
由于程序错误导致的异常属于RuntimeException;而如果程序本身没有问题,但由于像I/O错误这类问题导致的异 常属于IOException。 Java语言规范将派生于Error类或RuntimeException类的所有异常称为非受查异常;所有的其他异常称为受查异常。
异常的影响:
异常是导致程序中断执行的一种指令流。程序之中如果出现异常并且没有合理处理的话就会导致程序终止执行。
观察正确程序流
public class Test { public static void main(String[] args) { System.out.println("1.数学计算开始前"); System.out.println("2.进行数学计算:"+10/2); System.out.println("3.数学计算结束后"); } }
此时没有任何异常产生,程序可以正常执行完毕
产生异常:
public class Test { public static void main(String[] args) { System.out.println("1.数学计算开始前"); System.out.println("2.进行数学计算:"+10/0); System.out.println("3.数学计算结束后"); } }
现在程序之中产生了异常,但是在异常语句产生之前的语句可以正常执行完毕,而异常产生之后程序直接进行了结 束。为了保证程序出现异常后还可以继续向下执行,就需要异常处理
3.2 异常处理格式
异常处理的语法格式如下:
try{ 有可能出现异常的语句 ; }[catch (异常类 对象) { } ... ] [finally { 异常的出口 }
对于以上三个关键字,可以出现的组合: try..catch 、 try..finally 、 try..catch..finally
范例:对异常进行处理:
public class Test { public static void main(String[] args) { System.out.println("1.数学计算开始前"); try { System.out.println("2.进行数学计算:"+10/0); } catch (ArithmeticException e) { System.out.println("异常已经被处理了"); } System.out.println("3.数学计算结束后"); }
运行结果:
出现了异常之后,由于存在异常处理机制,依然可以正常执行完毕。
以上代码虽然进行了异常处理,但是存在一个问题:你现在根本不知道程序产生了什么样的异常。所以为了明确的 取得异常信息,可以直接输出异常类对象,或者调用所有异常类中提供的printStackTrace()方法进行完整异常信息 的输出
public class Test { public static void main(String[] args) { System.out.println("1.数学计算开始前"); try { System.out.println("2.进行数学计算:"+10/0); } catch (ArithmeticException e) { e.printStackTrace(); } System.out.println("3.数学计算结束后"); } }
运行结果:
在进行异常处理的时候还可以使用 try..catch..finally 进行处理。
范例:使用 try..catch..finally 进行处理
public class Test { public static void main(String[] args) { System.out.println("[1].数学计算开始前"); try { System.out.println("[2].进行数学计算:"+10/0); } catch (ArithmeticException e) { e.printStackTrace(); } finally { System.out.println("[Finally]不管是否产生异常,都执行此语句"); } System.out.println("[3].数学计算结束后"); } }
运行结果:
不管此时是否产生异常,终都要执行finally程序代码,所以finally会作为程序统一出口。
以上程序是直接固定好了两个数字进行除法运算,现在通过初始化参数来进行除法运算
范例:初始化参数进行数学运算
public class Test { public static void main(String[] args) { System.out.println("[1].数学计算开始前"); try { int x = Integer.parseInt(args[0]); int y = Integer.parseInt(args[1]); System.out.println("[2].进行数学计算:" + x / y); } catch (ArithmeticException e) { e.printStackTrace(); } finally { System.out.println("[Finally]不管是否产生异常,都执行此语句"); } System.out.println("[3].数学计算结束后"); } }
关于parseInt的用法:
http://www.w3school.com.cn/js/jsref_parseInt.asp
运行结果:
此时会存在如下问题:
用户没有输入初始化参数:ArrayIndexOutOfBoundsException 用户输入的不是数字: NumberFormatException 被除数为0:ArithmeticExceptio
以上代码我们发现,通过catch捕获异常的时候如果没有捕获指定异常,程序依然无法进行处理,现在直白的解 决方法就使用多个catch
问题是真这么写,真这么测试,不使用异常处理也一样,因为完全可以使用if..else判断。如果要想更好的处理异 常,必须清楚异常的处理流程。
throws 关键字:
在进行方法定义的时候,如果要告诉调用者本方法可能产生哪些异常,就可以使用throws方法进行声明。即,如果 该方法出现问题后不希望进行处理,就使用throws抛出。
thorws用在方法上
范例:使用throws定义方法
public class Test { public static void main(String[] args) { try { System.out.println(calculate(10, 0)); } catch (Exception e) { e.printStackTrace(); } } public static int calculate(int x,int y) throws Exception { return x/y ; } }
如果现在调用了throws声明的方法,那么在调用时必须明确的使用try..catch..进行捕获,因为该方法有可能产生异 常,所以必须按照异常的方式来进行处理。
主方法本身也属于一个方法,所以主方法上也可以使用throws进行异常抛出,这个时候如果产生了异常就会交给 JVM处理。
范例:主方法抛出异常
public class Test { public static void main(String[] args) throws Exception{ System.out.println(calculate(10, 0)); } public static int calculate(int x,int y) throws Exception { return x/y ; } }
以后编写代码里面,一定要斟酌好可能产生的异常。面对未知的程序类,如果要进行异常的处理,就必须知道有多少种异常
throw关键字:
thorw是直接编写在语句之中,表示人为进行异常的抛出。如果现在异常类对象实例化不希望由JVM产生而由用户产 生,就可以使用throw来完成 throw用在方法中
范例:使用throw产生异常类对象。
class Test { public static void main(String[] args) { try { throw new Exception("抛个异常玩玩"); } catch (Exception e) { e.printStackTrace(); } } }
一般而言throw和break、continue、break都要和if结合使用。
请解释throw和throws的区别
1. throw用于方法内部,主要表示手工异常抛出。
2. throws主要在方法声明上使用,明确告诉用户本方法可能产生的异常,同时该方法可能不处理此异常。
异常处理标准格式
以上为止,异常中的所有核心概念都掌握了: try、catch、finally、throws、throw 。
现在要求编写一个方法进行除法操作,但是对于此方法有如下要求:
1. 在进行除法计算操作之前打印一行语句"**"。 2. 如果在除法计算过程中出现错误,则应该将异常返回给调用处。 3. 不管终是否有异常产生,都要求打印一行计算结果信息。
范例:以上程序实现
public class Test { public static void main(String[] args) { try { System.out.println(calculate(10, 0)); } catch (Exception e) { e.printStackTrace(); } } public static int calculate(int x, int y) throws Exception { int result = 0; System.out.println("1.[计算开始前]*****"); try { result = x / y; } finally { System.out.println("2.[计算结束]#####"); } return result; } }
try 和 finally之间省略了:
catch (Exception e) { throw e ; // 抛出去 }
对于此时的格式一定要吸收,后面会对此结构进一步优化。(使用代理模式)
RuntimeException类:
先来看一段贼简单的代码:
public class Test { public static void main(String[] args) { String str = "100"; int num = Integer.parseInt(str); System.out.println(num * 2); } }
我们来看parseInt的源码定义:
public static int parseInt(String s) throws NumberFormatException
这个方法上已经明确抛出异常,但是在进行调用的时候发现,即使没有进行异常处理也可以正常执行。这个就属于 RuntimeException的范畴。 很多的代码上都可能出现异常,例如"10/0"都可能产生异常,如果所有可能产生异常的地方都强制性异常处理,这 个代码就太复杂了。所以在异常设计的时候,考虑到一些异常可能是简单问题,所以将这类异常称为 RuntimeException,也就是使用RuntimeException定义的异常类可以不需要强制性进行异常处理。
请解释Exception与RuntimeException的区别,请列举几个常见的RuntimeException: 1. 使用Exception是RuntimeException的父类,使用Exception定义的异常都要求必须使用异常处理,而 使用RuntimeException定义的异常可以由用户选择性的来进行异常处理。 2. 常见的RuntimeException:ClassCastException、NullPointerException等。
自定义异常类
在Java里,针对于可能出现的公共的程序问题都会提供有相应的异常信息,但是很多时候这些异常信息往往不够我 们使用。例如,现在有需求:在进行加法运算时,如果发现两个数相加内容为50,那么就应当抛出一个 AddException异常。这种异常Java不会提供,所以就必须定义一个属于自己的异常类。
自定义异常类可以继承两种父类:Exception、RuntimeException。
范例:实现自定义异常类
class AddException extends Exception { public AddException(String msg) { super(msg) ; } } public class Test { public static void main(String[] args) throws Exception{ int num1 = 20 ; int num2 = 30 ; if (num1+num2==50) { throw new AddException("错误的相加操作") ; } } }
在项目的系统设计或者架构设计时,一定会涉及到与业务相关的异常问题,此时需要自定义异常类