一、Java异常结构:Throwable可以用来表示任何可以被作为异常抛出的类。Throwable对象派生出两种类型:Error和Exception,前者 代表的是严重错误, 这种错误程序员无法进行处理, 例如操作系统崩溃, jvm出错, 动态链接库失败等.;后者是可以被抛出的基本类型,需要程序员关注。RuntimeException是Exception的派生类。
1、Exception异常的分类:Java的异常(Exception)按照编译器检查方式又可以分为检查型异常(CheckedException)和非检查型异常(UncheckedException)
1.1检查型异常(CheckedException):在Java中所有不是RuntimeException派生的Exception都是检查型异常。当函数中存在抛出检查型异常的操作时该函数的函数声明中必须包含throws语句。调用改函数的函数也必须对该异常进行处理,如不进行处理则必须在调用函数上声明throws语句。
检查型异常是JAVA首创的,在编译期对异常的处理有强制性的要求。在JDK代码中大量的异常属于检查型异常,包括IOException,SQLException等等。
1.2非检查型异常(UncheckedException)
在Java中所有RuntimeException的派生类都是非检查型异常,与检查型异常相对抛出非检查型异常可以不在函数声明中添加throws语句,调用函数上也不需要强制处理。常见的NullPointException,ClassCastException是常见的非检查型异常。非检查型异常可以不使用try...catch进行处理,但是如果有异常产生,则异常将由JVM进行处理。对于RuntimeException的子类最好也使用异常处理机制。虽然RuntimeException的异常可以不使用try...catch进行处理,但是如果一旦发生异常,则肯定会导致程序中断执行,所以,为了保证程序再出错后依然可以执行,在开发代码时最好使用try...catch的异常处理机制进行处理。
二、java里对异常的处理
2.1程序猿对有可能出现的异常使用try catch处理.
例如:
package Exception_kng;
·
· class Exp2{
· public int f(int a, int b){
· int i = 0;
· try{
· i = a/b;
· }
· catch(Exception e){
· System.out.printf('Exception occurs!!\n');
· System.out.println(e.getMessage()); //print the root cause
· System.out.printf('===========================\n');
· e.printStackTrace(); //print the info of function stuck.
· }
·
· return i;
· }
· }
·
· public class Expt_2{
· public static void g(){
· Exp2 ex = new Exp2();
· int i = ex.f(8,0); //call f()
· System.out.printf('i is %d\n', i); //successfully executed
· }
在f()函数中对可能出现的异常的代码进行try catch处理后, 程序会执行catch里的代码. 而且不会中断整个程序, 继续执行try catch后面的代码.
2.2 函数里并不处理异常, 使用throw or throws 关键字把可能出现的异常抛给调用该函数的上级函数处理.
例如我在f()函数中不想处理可能出现的异常, 想把它抛出上级函数处理:
1. package Exception_kng;
2.
3. class Exp3{
4. public int f(int a, int b){
5. if (0 == b){
6. throw new ArithmeticException('Shit !!! / by zero!');
7.
8. }
9.
10. return a/b;
11. }
12. }
13. public class Expt_3{
14. public static void g() throws ArithmeticException{
15. Exp3 ex = new Exp3();
16. int i = 22;
17. i = ex.f(8,0); //throw excetpion
18. System.out.printf('i is %d\n', i); //failed executed
19. System.out.printf('g() is done!!\n'); //failed executed
20. }
21.
22. public static void h(){
23. try{
24. g();
25. }catch(ArithmeticException e){
26. System.out.printf('Exception occurs!!\n');
27. System.out.println(e.getMessage()); //print the root cause
28. System.out.printf('===========================\n');
29. e.printStackTrace(); //print the info of function stuck.
30. }
31.
32. System.out.printf('h() is done!!\n'); //successfully executed
33. }
34. }
可以见到f() 加了个条件判断, 如果参数b = 0, 使用throw 直接手动抛出1个异常. 让调用它的函数处理.
g()调用f()函数, 预见到f()可能有异常, 但是也不想处理, 使用throws 关键字告诉调用它的函数本函数有可能抛出这种异常. // 注, 这里的throws对程序并没有实质的影响.
h()调用g(), 简单g()定义的throws, 用try catch在本函数进行处理.
2.3 交给jvm虚拟机处理
jvm怎么处理呢, 就是中断整个程序, 并把异常信息输出到屏幕上.
实际上, 当java程序的1个函数抛出异常时,
首先会检查当前函数有没有try catch处理, 如果无检查上一级函数有无try..catch处理....
这样在函数栈里一级一级向上检查, 如果直至main函数都无try..catch, 则抛给jvm..
项目中强烈建议尽量手动处理, 不要把异常交给jvm.
三、Try catch finally 的处理机制.
语法是这样的.
try{
可能出异常的若干行代码;
}
catch(ExceptionName1 e){
产生ExceptionName 1的处理代码;
}
catch(ExceptionName2 e){
产生ExceptionName 2的处理代码;
}
...
finally{
无论如何, 最终肯定会执行的代码
}
3.1 try catchfinally的执行路线.
下面用个例子来说明:
1. try{
2. f();
3. ff();
4. }
5. catch(ArithmeticException e){
6. g();
7. }
8. catch(IOException e){
9. gg();
10. }
11. catch(AuthorizedException e){
12. ggg();
13. }
14. finally{
15. h();
16. }
17.
18. k();
当f()抛出了异常, 那么ff()就不会执行了. 程序会尝试捕捉异常.
首先捕捉ArithmeticException, 捕捉失败.
接下来捕捉IOException, 捕捉成功, 执行gg();
一旦捕捉到一个异常, 不会再尝试捕捉其他异常, 直接执行finally里的h();
执行后面的函数k().
也就是说路线是:
f() -> gg() -> h() -> k()
有2点要注意的.
1. f()函数极有可能未完整执行, 因为它抛出了异常, 抛出异常的语句执行失败, 之后的语句放弃执行.
2. try{} 里面, f()之后的语句, 例如ff()放弃执行.
路线是:
f() -> ff() -> h() -> k()
3.2 throw 和throws的机制和用法.
下面开始详讲异常另一种处理方法throw 和 throws了.
注意的是, 这两种用法都没有真正的处理异常, 真正处理的异常方法只有try catch, 这两种方法只是交给上一级方法处理.
3.2.1 throw 的语法与作用
throw的语法很简单.
语法:
throw new XException();
其中xException必须是Exception的派生类.
这里注意throw 出的是1个异常对象, 所以new不能省略
作用就是手动令程序抛出1个异常对象.
3.2.2throws 的语法.
throws稍微比throw难理解点:
语法是:
public void f() throwsException1, Exception2...{
}
也就是讲, thorws可以加上多个异常, 注意这里抛出的不是对象, 不能加上new.
而且不是告诉别人这个函数有可能抛出这么多个异常. 而是告诉别人, 有可能抛出这些异常的其中一种.
3.2.3 throws 的作用.
如果为f()函数加上throws后续, 则告诉调用f()的方法, f()函数有可能抛出这些异常的一种.
如果f()throws 了1个或若干个非RuntimeException, 则调用f()的函数必须处理这些非RuntimeException, 如上面的g()函数一样.
如果f() throws的都是RuntimeException,则调用f()的函数可以不处理, 也能通过编译, 但是实际上还是强烈建议处理它们.
实际上, 如果1个方法f() throwsA,B
那么它有可能不抛出任何异常.(程序运行状态良好)
也有能抛出C异常(应该避免, 最好在throws上加上C)
3.3 throw和throws一些主要区别.
#1 throw 写在函数体内, throws写在函数定义语句中.
因为一旦一个函数throw出1个异常, 这个函数就会被中断执行, 后面的代码被放弃, 如果你尝试在函数内写两个throw, 编译失败.
而throws 是告诉别人这个函数有可能抛出这几种异常的一种. 但是最多只会抛出一种.
#3一个方法最多只能throw1个异常, 但是可以throws多个种类异常
#4 如果在一个函数体内throw 1个非runtimeException,那么必须在函数定义上加上throws后缀. 但反过来就不是必须的.
四, 自定义异常.
我们可以自定义异常, 只需要编写1个类, 继承1个异常类就ok
例子:
1. package Exception_kng;
2.
3. class User_Exception1 extends ArithmeticException{
4. public User_Exception1(String Exception_name){
5. super(Exception_name);
6. }
7.
8. public void printStackTrace(){ //overwrite
9. super.printStackTrace();
10. System.out.printf('hey man, i am an user_defined excetpion\n');
11. }
12. }
13.
14. class Exp6{
15. public int f(int a, int b){
16. if (0 == b){
17. throw new User_Exception1('Shit !!! / by zero!'); //use User_defined exception
18. }
19.
20. return a/b;
21. }
22. }
23.
24. public class Expt_6{
25. public static void g() {
26. Exp6 ex = new Exp6();
27. int i = 22;
28. try{
29. i = ex.f(8,0); //throw excetpion
30. }catch(User_Exception1 e){
31. e.printStackTrace();
32. }
33. System.out.printf('i is %d\n', i);
34. System.out.printf('g() is done!!\n');
35. }
36. }
上面的类User_Exception1 就是1个自定义异常, 并重写了printStackTrace()方法。
联系客服