2023年11月29日发(作者:)
Java⾥的异常(Exception)详解
作为⼀位初学者, 本屌也没有能⼒对异常谈得很深⼊. 只不过⾥关于Exception的东西实在是很多. 所以这篇⽂章很长就是了..
⼀, 什么是java⾥的异常
由于java是cc++ 发展⽽来的, ⾸先我们先看看c语⾔⾥的错误.
1.1 c语⾔⾥的错误
我们实现1个程序的过程包括, 代码编写, 编译代码成为程序, 执⾏程序.
.
其中⼤部分常见的语法错误都会被编译代码这样部过滤掉. 但是即使通过了编译. 执⾏程序这⼀步可能还是会有错误.
原因很多, 例如常见的除数为0, 内存溢出(数组的index超出界限), 或者内存被其他程序修改等.
最简单的例⼦:
[java]
1. #include
2.
3. int f(int a, int b){
4. return a/b;
5. }
6.
7. int main(){
8. int i = f(8,0);
9. printf("i is %dn",i);
10. return 0;
11. }
上⾯的例⼦编译时是⽆错的, 但是⼀旦执⾏就会提⽰吐核错误了.
c语⾔⾥对这种执⾏时出现的错误是⽆能为⼒的, ⼀旦出错就会整个程序崩溃, 就不会在继续执⾏下⾯的代码.
⽽且很多时候出错信息很少, 让你⽆法判断出错的原因和地⽅, 只能⼀步步⼩⼼
所以很多⽤c写的程序有时会出现⾮法关闭的现象.
解决⽅法只能是在代码⾥对可能出错的地⽅添加if 判断.
例如f()函数⾥可以对b进⾏判断, 如果是0就不执⾏.
1.2 java⾥运⾏时出现的错误
java⾥编译器对代码的规范性⽐c严格得多. 但是即使如此, 通过编译的java程序有时也很难避免执⾏时出错.
例如, 将上⾯的c程序改编成java程序:
[java]
1. package Exception_kng;
2.
3. class Exp1{
4. public int f(int a, int b){
5. return a/b;
6. }
7. }
8.
9. public class Expt_1{
10. public static void g(){
11. Exp1 e = new Exp1();
12. int i = e.f(8,0);
13. ("i is %dn", i);
14. }
15. }
运⾏时⼀样会出错, 下⾯是出错信息:
[java]
1. [java] Caused by: eticException: / by zero
2. [java] at Exception_1.f(Expt_:5)
3. [java] at Exception__1.g(Expt_:12)
4. [java] at Enter_(Enter_:31)
但是可以见到, java告诉你出错的类型: 运算错误(ArithmeticExcetion), 出错信息和出错的类与⽂件⾏数输出, ⽅便你调试. jvm虚拟机是
会对错误作出⼀定的处理的.
所以可以简单地将java⾥的异常理解成java运⾏时出现的错误, 异常机制就是对这种错误进⾏处理的机制.
1.3 java异常的定义
实际上, 当java程序执⾏时出现错误时, jvm会把执⾏时出错的信息(例如出错原因, 类型, 位置) 收集,然后打包成为1个对象(object), 程序员
可以对这种对象进⾏处理. 这种对象就是所谓的异常.
可能出现的异常的代码并不是肯定会出现异常, 取决于执⾏环境和数据.!
⼆, java⾥的异常的分类.
见下图:
Throwable
/
Error Exception
/ /
xxxxxx xxxxxx RuntimeException
/
xxxxxx ArithmeticException
上图的所有对象都是类.
Throwable 代表是可抛出的.
Error 代表的是严重错误, 这种错误程序员⽆法进⾏处理, 例如操作系统崩溃, jvm出错, 动态链接库失败等. Error并不是异常, 不是本⽂
的重点.
Exception 代表的就是异常了. 它下⾯很多派⽣类, 其中它的派⽣类也分两种, ⼀种是RuntimeException(运⾏时异常), 其他的都是⾮运⾏时
异常
RuntimeException 包括除数为0, 数组下标超界等. 运⾏时异常的派⽣类有很多, 其产⽣频率较⾼. 它的派⽣类可以由程序处理或者抛给
(throw) 给jvm处理. 例如上⾯的例⼦就是抛给了jvm处理, jvm把程序中断执⾏, 并把错误信息输出到终端上.
⾮RuntimeExcption 这种异常属于Excepion的派⽣类(上⾯红⾊的xxx), 但是不是RuntimeException的派⽣类, 这种异常必须由程序员⼿动处
理,否则不通过编译.
ArithmeticExcpetion 算术异常, 它是RuntimeException的派⽣类, 所以程序员不⼿动处理也通过编译, 只不过出错时会被jvm处理.
三, java⾥对异常的处理
java⾥对异常的处理有三种.
3.1 程序猿对有可能出现的异常使⽤try catch处理.
例如我们将上⾯的例⼦改动⼀下:
[java]
1. package Exception_kng;
2.
3. class Exp2{
4. public int f(int a, int b){
5. int i = 0;
6. try{
7. i = a/b;
8. }
9. catch(Exception e){
10. ("Exception occurs!!n");
11. n(sage()); //print the root cause
12. ("===========================n");
13. tackTrace(); //print the info of function stuck.
14. }
15.
16. return i;
17. }
18. }
19.
20. public class Expt_2{
21. public static void g(){
22. Exp2 ex = new Exp2();
23. int i = ex.f(8,0); //call f()
24. ("i is %dn", i); //successfully executed
25. }
26. }
在f()函数中对可能出现的异常的代码进⾏try catch处理后, 程序会执⾏catch⾥的代码. ⽽且不会中断整个程序, 继续执⾏try catch后⾯的代码.
程序执⾏输出:
[java]
1. [java] Exception occurs!!
2. [java] / by zero
3. [java] ===========================
也就是说try catch处理后并不会终⽌程序, 令程序即使出现了错误, 也可以对错误进⾏⼀定的处理后继续执⾏. 这就是java异常机制⽐c语⾔安
全的地⽅.
下⾯会详细讲解 try catch.
注:
getMessage() ⽅法: Exception类的⽅法之⼀, 返回异常的原因, 上⾯的 / by zero 就是这个⽅法输出的.
printStackTrace(): Exception类的⽅法之⼀, 在屏幕输出函数栈信息, 也就是异常出现的地⽅.
3.2 函数⾥并不处理异常, 使⽤throw or throws 关键字 把可能出现的异常抛给调⽤该函数的上级函数处理.
例如我在f()函数中不想处理可能出现的异常, 想把它抛出上级函数处理:
下⾯是个例⼦:
[java]
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.
14. public class Expt_3{
15. public static void g() throws ArithmeticException{
16. Exp3 ex = new Exp3();
17. int i = 22;
18. i = ex.f(8,0); //throw excetpion
19. ("i is %dn", i); //failed executed
20. ("g() is done!!n"); //failed executed
21. }
22.
23. public static void h(){
24. try{
25. g();
26. }catch(ArithmeticException e){
27. ("Exception occurs!!n");
28. n(sage()); //print the root cause
29. ("===========================n");
30. tackTrace(); //print the info of function stuck.
31. }
32.
33. ("h() is done!!n"); //successfully executed
34. }
35. }
可以见到f() 加了个条件判断, 如果参数b = 0, 使⽤throw 直接⼿动抛出1个异常. 让调⽤它的函数处理.
g()调⽤f()函数, 预见到f()可能有异常, 但是也不想处理, 使⽤throws 关键字告诉调⽤它的函数本函数有可能抛出这种异常. // 注, 这⾥的throws
对程序并没有实质的影响.
h()调⽤g(), 简单g()定义的throws, ⽤try catch在本函数进⾏处理.
输出:
[java]
1. [java] Exception occurs!!
2. [java] Shit !!! / by zero!
3. [java] ===========================
4. [java] eticException: Shit !!! / by zero!
5. [java] at Exception_3.f(Expt_:6)
6. [java] at Exception__3.g(Expt_:18)
7. [java] at Exception__3.h(Expt_:25)
8. [java] at Enter_(Enter_:31)
9. [java] h() is done!!
注意这个程序没有执⾏g() 最后的代码.
throw 和 throws 后⾯也会详细讲解.
3.3 交给jvm虚拟机处理
假如上⾯的例⼦h() 也不处理怎么办? 就如1.2 的例⼦, 会抛给jvm处理.
但是这种情况只适⽤于RuntimeExecption及其派⽣类.
jvm怎么处理呢, 就是中断整个程序, 并把异常信息输出到屏幕上.
实际上, 当java程序的1个函数抛出异常时,
⾸先会检查当前函数有没有try catch处理, 如果⽆检查上⼀级函数有⽆atch处理....
这样在函数栈⾥⼀级⼀级向上检查, 如果直⾄main函数都⽆atch, 则抛给jvm..
项⽬中强烈建议尽量⼿动处理, 不要把异常交给jvm.
四,Try catch finally 的处理机制.
这⾥开始详解try catch finally了.
语法是这样的.
try{
可能出异常的若⼲⾏代码;
}
catch(ExceptionName1 e){
产⽣ExceptionName 1的处理代码;
}
catch(ExceptionName2 e){
产⽣ExceptionName 2的处理代码;
}
...
finally{
⽆论如何, 最终肯定会执⾏的代码
}
4.1 try catch finally的执⾏路线.
下⾯⽤个例⼦来说明:
[java]
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();
4.1.1 当try⾥⾯的f()抛出了IOException
当f()抛出了异常, 那么ff()就不会执⾏了. 程序会尝试捕捉异常.
⾸先捕捉ArithmeticException, 捕捉失败.
接下来捕捉IOException, 捕捉成功, 执⾏gg();
⼀旦捕捉到⼀个异常, 不会再尝试捕捉其他异常, 直接执⾏finally⾥的h();
执⾏后⾯的函数k().
也就是说路线是:
f() -> gg() -> h() -> k()
有2点要注意的.
1. f()函数极有可能未完整执⾏, 因为它抛出了异常, 抛出异常的语句执⾏失败, 之后的语句放弃执⾏.
2. try{} ⾥⾯, f()之后的语句, 例如ff()放弃执⾏.
4.1.2 没有任何异常抛出
这种情况很简单, 就是try{}⾥⾯的代码被完整执⾏, 因为没有抛出任何异常, 就不会尝试执⾏catch⾥的部分, 直接到finally部分了.
路线是:
f() -> ff() -> h() -> k()
4.2 如何确定要捕捉的异常名字.
也许有⼈会问, 我们怎么知道到底会抛出什么异常?
下⾯有3个解决⽅案.
1.看代码凭经验, 例如看到1段除法的代码, 则有可能抛出算术异常.
2.在catch的括号⾥写上Exception e, 毕竟Exception 是所有其他异常的超类, 这⾥涉及多态的知识, ⾄于什么是多态可以看看本⼈的另⼀篇⽂
章.
3. 观察被调⽤函数的函数定义, 如果有throws后缀, 则可以尝试捕捉throws 后缀抛出的异常
4.3 为什么需要finally
包括我在内很多⼈会觉得finally语句简直多勾余, 既然是否捕捉到异常都会执⾏, 上⾯那个例⼦⾥的h()为什么不跟下⾯的k() 写在⼀起呢.
上⾯的例⼦的确看不出区别.
但下⾯两种情况下就体现了finally独特的重要性.
4.3.1 抛出了1个异常, 但是没有被任何catch⼦句捕捉成功.
例如try⾥⾯抛出了1个A异常, 但是只有后⾯只有捕捉B异常, 和C异常的⼦句.
这种情况下, 程序直接执⾏finally{}⾥的⼦句, 然后中断当前函数, 把异常抛给上⼀级函数, 所以当前函数finally后⾯的语句不会被执⾏.
例⼦:
[java]
1. package Exception_kng;
2.
3. import .*;
4. import .*;
5.
6. class Exp4{
7. public int f(int a, int b) throws IOException, BindException{
8. return a/b;
9. }
10. }
11.
12. public class Expt_4{
13. public static void g(){
14. Exp4 ex = new Exp4();
15. int i = 22;
16. try{
17. ("g() : try!!n"); //failed
18. i = ex.f(8,0); //call f()
19. }
20. catch(BindException e){
21. ("g() : BindException!!n"); //failed
22. }
23. catch(IOException e){
24. ("g() : IOException!!n"); //failed
25. }
26. finally{
27. ("g() : finaly!!n"); //successfully executed
28. }
29. ("g() is done!!n"); //failed
30. }
31.
32. public static void h(){
33. try{
34. g();
35. }catch(ArithmeticException e){
36. ("Exception occurs!!n");
37. n(sage()); //print the root cause
38. ("===========================n");
39. tackTrace(); //print the info of function stuck.
40. }
41.
42. ("h() is done!!n"); //successfully executed
43. }
44. }
我所说的情况, 就在上⾯例⼦⾥的g()函数, g()函数⾥尝试捕捉两个异常, 但是抛出了第3个异常(ArithmeticException 算术异常).
所以这个异常会中断g()的执⾏, 因为没有被捕捉到, 然后抛给调⽤g()的 h()函数处理, ⽽在h()捕捉到了, 所以h()函数是能完整执⾏的.
也就是说g()⾥的
[java]
1. ("g() is done!!n"); //failed
执⾏失败
⽽h()⾥的
[java]
1. ("h() is done!!n"); //successfully executed
执⾏成功
但是⽆论如何, g()⾥的finally{}部分还是被执⾏了
执⾏结果如下:
[java]
1. [java] g() : try!!
2. [java] g() : finaly!!
3. [java] Exception occurs!!
4. [java] / by zero
5. [java] ===========================
6. [java] eticException: / by zero
7. [java] at Exception_4.f(Expt_:8)
8. [java] at Exception__4.g(Expt_:18)
9. [java] at Exception__4.h(Expt_:34)
10. [java] at Enter_(Enter_:31)
11. [java] h() is done!!
这种情况是1中编程的低级错误, 在项⽬中是不允许出现.
避免⽅法也⼗分简单, 在catch⼦句集的最后增加1个catch(Exception e)就ok, 因为Exception是所有异常的超类, 只要有异常抛出, 则肯定会捕
捉到.
4.3.2 在catch⼦句内有return⼦句.
下⾯例⼦:
[java]
1. try{
2. f();
3. ff();
4. }
5. catch(ArithException e){
6. g();
7. return j();
8. }
9. catch(IOException e){
10. gg();
11. return j();
12. }
13. catch(AuthorizedException e){
14. ggg();
15. return j();
16. }
17. finally{
18. h();
19. }
20.
21. k();
假如在f()函数抛出了IOExcepion 异常被捕捉到.
那么执⾏路线就是
f() -> gg() -> j() -> h() -> 上⼀级function
也就说, 这种情况下finally⾥的⼦句会在return回上⼀级function前执⾏. ⽽后⾯的k()就被放弃了.
4.3.3 finally作⽤⼩结.
可以看出, finally⾥的语句, ⽆论如何都会被执⾏.
⾄有两种情况除外, ⼀是断电, ⼆是exit函数.
在项⽬中, 我们⼀般在finally编写⼀些释放资源的动作, 例如初始化公共变量. 关闭connections, 关闭⽂件等.
4.4 try catch finally⾥⼀些要注意的问题.
4.4.1 ⽆论如何最多只有1个catch被执⾏
这个上⾯提到过了, ⼀旦捕捉到1个异常, 就不会尝试捕捉其他异常.
如果try⾥⾯的⼀段代码可能抛出3种异常A B C,
⾸先看它先抛出哪个异常, 如果先抛出A, 如果捕捉到A, 那么就执⾏catch(A)⾥的代码. 然后finally.. B和C就没有机会再抛出了.
如果捕捉不到A, 就执⾏finally{}⾥的语句后中断当前函数, 抛给上⼀级函数...(应该避免)
4.4.2 有可能所有catch都没有被执⾏
两种情况, 1就是没有异常抛出, 另⼀种就是抛出了异常但是没有捕捉不到(应该避免)
4.4.3 先捕捉⼦类异常, 再捕捉⽗类异常, 否则编译失败
加⼊try ⾥⾯尝试捕捉两个异常, 1个是A, 1个是B, 但是A是B的⽗类.
这种情况下, 应该把catch(B)写在catch(A)前⾯.
原因也很简单, 加⼊把catch(A)写在前⾯, 因为多态的存在, 即使抛出了B异常, 也会被catch(A)捕捉, 后⾯的catch(B)就没有意义了.
也就是说如果捕捉Exception这个异常基类, 应该放在最后的catch⾥, 项⽬中也强烈建议这么做, 可以避免上述4.3.1的情况出现.
4.4.4 catch与catch之间不能有任何代码.
这个没什么好说的. 语法规则
4.4.5 finally⾥不能访问catch⾥捕捉的异常对象e
每1个异常对象只能由catch它的catch⼦句⾥访问.
4.4.6 try⾥⾯的定义变量不能在try外⾯使⽤.
跟if类似, 不多说了.
4.4.7 try catch finally可以嵌套使⽤.
这个也不难理解..
五, throw 和throws的机制和⽤法.
下⾯开始详讲异常另⼀种处理⽅法throw 和 throws了.
注意的是, 这两种⽤法都没有真正的处理异常, 真正处理的异常⽅法只有try catch, 这两种⽅法只是交给上⼀级⽅法处理.
就如⼀个组织⾥ , 有1个⼤佬, 1个党主, 1个⼩弟.
⼤佬叫党主⼲活, 堂主叫⼩弟⼲活, 然后⼩弟碰上⿇烦了, 但是⼩弟不会处理这个⿇烦, 只能中断⼯作抛给党主处理, 然后堂主发现这个⿇烦只
有⼤佬能处理, 然后抛给⼤佬处理..
道理是相通的..
5.1 throw 的语法与作⽤
throws的语法很简单.
语法:
throw new XException();
其中xException必须是Exception的派⽣类.
这⾥注意throw 出的是1个异常对象, 所以new不能省略
作⽤就是⼿动令程序抛出1个异常对象.
5.2 throw 1个 RuntimeException及其派⽣类
我们看回上⾯3.2 的例⼦:
[java]
1. public int f(int a, int b){
2. if (0 == b){
3. throw new ArithmeticException("Shit !!! / by zero!");
4.
5. }
6.
7. return a/b;
8. }
5.2.1 throw会中断当前函数, 当前函数执⾏失败(不完整)
当这个函数的if 判断了b=0时, 就利⽤throws⼿动抛出了1个异常. 这个异常会中断这个函数. 也就是说f()执⾏不完整, 是没有返回值的.
5.2.2, 接下来哪个调⽤这个函数就会在调⽤这个函数的语句上收到异常.
[java]
1. public void g(){
2. int i;
3. h();
4. i = f(); //recevie excepton
5. k();
6. }
例如上没的g()函数, 在调⽤f() 会收到1个异常.
这时g()函数有三种选择.
1. 不做任何处理
这时, g()收到f()⾥抛出的异常就会打断g()执⾏, 也就是说g()⾥⾯的k(); 被放弃了, 然后程序会继续把这个函数抛给调⽤g()函数.
然后⼀级⼀级寻求处理, 如果都不处理, 则抛给jvm处理. jvm会中断程序, 输出异常信息. 这个上没提到过了.
2. 使⽤try catch处理
如果catch成功, 则g()函数能完整执⾏, ⽽且这个异常不会继续向上抛.
如果catch失败(尽量避免), 则跟情况1相同.
5.3 throw 1个 ⾮RuntimeException派⽣类的异常
将上⾯的例⼦改⼀下:
[java]
1. public int f(int a, int b){
2. if (0 == b){
3. throw new IOException("Shit !!! / by zero!");
4. }
5.
6. return a/b;
7. }
例如, 我不想抛出ArithmeticException, 我想抛出IOExcetpion.
注意 这⾥, IOException虽然逻辑上是错误的(完全不是IO的问题嘛), 但是在程序中完全可⾏, 因为程序猿可以根据需要控制程序指定抛出任何
1个异常.
但是这段代码编译失败, 因为IOException 不是 RuntimeException的派⽣类.
java规定:
5.3.1 如果⼀个⽅法⾥利⽤throw⼿动抛出1个⾮RuntimeException异常, 必须在函数定义声明⾥加上throws 后缀
改成这样就正确了:
[java]
1. public int f(int a, int b) throws IOException{
2. if (0 == b){
3. throw new IOException("Shit !!! / by zero!");
4. }
5.
6. return a/b;
7. }
注意在⽅法定义⾥加上了throws⼦句. 告诉调⽤它的函数我可能抛出这个异常.
5.3.2 调⽤该⽅法的⽅法则必须处理这个异常
例如抄回上⾯的例⼦, g()调⽤f()函数.
[java]
1. public void g(){
2. int i;
3. h();
4. i = f(); //recevie excepton
5. k()
6. }
但是编译失败.
因为f()利⽤throws 声明了会抛出1个⾮runtimeExcetpion. 这时g()必须做出处理.
处理⽅法有两种:
1. try catch⾃⼰处理:
[java]
1. public void g(){
2. int i = 0;
3. h();
4. try{
5. i = f(); //recevie excepton
6. }
7. catch(IOException e){
8.
9. }
10. k();
11. }
需要注意的是, catch⾥⾯要么写上throws对应的异常(这⾥是 IOException), 要么写上这个异常的超类, 否则还是编译失败.
2.g()利⽤throws 往上⼀级⽅法抛
.
[java]
1. public void g() throws IOException{
2. int i = 0;
3. h();
4. i = f(); //recevie excepton
5. k();
6. }
这是调⽤g()的函数也要考虑上⾯的这两种处理⽅法了...
但是最终上级的⽅法(main ⽅法)还是不处理的话, 就编译失败, 上⾯说过了, ⾮runtimeException⽆法抛给jvm处理.
虽然这两种处理⽅法都能通过编译, 但是运⾏效果是完全不同的.
第⼀种, g()能完整执⾏.
第⼆种, g()被中断, 也就是g()⾥⾯的k(); 执⾏失败.
5.4 throws 的语法.
throws稍微⽐throw难理解点:
语法是:
public void f() throws Exception1, {
}
也就是讲, thorws可以加上多个异常, 注意这⾥抛出的不是对象, 不能加上new.
⽽且不是告诉别⼈这个函数有可能抛出这么多个异常. ⽽是告诉别⼈, 有可能抛出这些异常的其中⼀种.
5.5 throws 的作⽤.
如果为f()函数加上throws后续, 则告诉调⽤f()的⽅法, f()函数有可能抛出这些异常的⼀种.
如果f()throws 了1个或若⼲个⾮RuntimeException, 则调⽤f()的函数必须处理这些⾮RuntimeException, 如上⾯的g()函数⼀样.
如果f() throws的都是RuntimeException, 则调⽤f()的函数可以不处理, 也能通过编译, 但是实际上还是强烈建议处理它们.
实际上, 如果1个⽅法f() throws A,B
那么它有可能不抛出任何异常.(程序运⾏状态良好)
也有能抛出C异常(应该避免, 最好在throws上加上C)
5.6 什么时候应该⽤throws
5.6.1 ⼀个函数体⾥⾯⼿动throw了1个RumtimeException, 则这个函数的定义必须加上throws⼦句
这个是强制, 告诉别⼈这个函数内有炸弹.
5.6.2 ⼀个函数内有可能由系统抛出异常.
这个是⾮强制的, 但是如果你知道⼀个函数内的代码有可能抛出异常, 最好还是写上throws 后缀
⽆论这个异常是否runtimeExcepion.
5.7 ⼀般情况下,调⽤1个带有throws⽅法时怎么办
个⼈建议, 如果你调⽤1个函数throws A, B, C
那么你就在当前函数写上
try
catch(A)
catch(B)
catch(C)
catch(Exception)
这样能处理能保证你的函数能完整执⾏, 不会被收到的异常中断.
当然如果你允许你的函数可以被中断, 那么就可以在当前函数定义加上throws A, B 继续抛给上⼀级的函数.
5.8 重写⽅法时, throws的范围不能⼤于超类的对应⽅法.
例如你在⼀个派⽣类重写⼀个⽅法f(), 在超类⾥的f() throws A, B 你重写⽅法时就不throws出 A,,B,C 或者throws A和B的超类.
原因也是由于多态的存在.
因为1个超类的引⽤可以指向1个派⽣类的对象并调⽤不同的⽅法. 如果派⽣类throws的范围加⼤
那么利⽤多态写的代码的try catch就不再适⽤.
六, throw和throws⼀些主要区别.
⾯试问得多,单独拉出来写了:
6.1 throw 写在函数体内, throws写在函数定义语句中.
应付⾯试官.
6.2 throw 是抛出1个异常对象, throws是有能抛出异常的种类
所以throw后⾯的⼀般加上new 和exception名字().
⽽throws后⾯不能加上new的
6.3 ⼀个⽅法最多只能throw1个异常, 但是可以throws多个种类异常
因为⼀旦⼀个函数throw出1个异常, 这个函数就会被中断执⾏, 后⾯的代码被放弃, 如果你尝试在函数内写两个throw, 编译失败.
⽽throws 是告诉别⼈这个函数有可能抛出这⼏种异常的⼀种. 但是最多只会抛出⼀种.
6.4 如果在⼀个函数体内throw 1个⾮runtimeException, 那么必须在函数定义上加上throws后缀. 但反过来
就不是必须的.
原因上⾯讲过了.
七, ⾃定义异常.
我们可以⾃定义异常, 只需要编写1个类, 继承1个异常类就ok
例⼦:
[java]
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. tackTrace();
10. ("hey man, i am an user_defined excetpionn");
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
35. }
36. }
上⾯的类User_Exception1 就是1个⾃定义异常, 并重写了printStackTrace()⽅法.
⼋,java异常的优缺点.
8.1 c语⾔是如何处理程序错误的.
我们要理解异常的优缺点, ⾸先看看没有异常的是如何处理错误的.
下⾯是个例⼦:
[cpp]
1. //openfile
2. if (fileOpen() > 0){
3. //check the length of the file
4. if (gotLengthOfTheFile() > 0){
5. //check the memory
6. if (gotEnoughMemory() > 0){
7. //load file to memory
8. if (loadFileToMem() > 0){
2. 需要把各种可能出现的错误全部考虑到, 才能保证程序的稳定性.
3. 程序可读性差, 错误处理代码混杂在其他代码中.
4. 出错返回信息少, ⼀旦出错难以调试.
5. ⼀旦出现了未考虑到的错误, 资源释放代码⽆法执⾏.
8.2 java异常机制下是如何编写上述代码的.
[java]
1. try{
2. fileOpen();
3. gotLengthOfTheFile();
4. gotEnoughMemory();
5. loadFileToMem();
6. readFile();
7. }
8. catch(fileOpenFail) { handle1()}
9. catch(gotLengthOfTheFileFail) { handle2()}
10. catch(gotEnoughMemoryFail) { handle3()}
11. catch(loadFileToMemFail) { handle4()}
12. catch(readFileFail) { handle4()}
13. catch(Exception e) { handle5()} //catch unexpected error
14. finally{
3. 异常并不能解决所有问题


发布评论