在路上

 找回密码
 立即注册
在路上 站点首页 学习 查看内容

简单谈谈java的异常处理(Try Catch Finally)

2016-8-29 13:22| 发布者: zhangjf| 查看: 560| 评论: 0

摘要: 异常的英文单词是exception,字面翻译就是“意外、例外”的意思,也就是非正常情况。事实上,异常本质上是程序上的错误,包括程序逻辑错误和系统错误。 一 前言 java异常处理大家都不陌生,总的来说有下面两点: 1. ...

异常的英文单词是exception,字面翻译就是“意外、例外”的意思,也就是非正常情况。事实上,异常本质上是程序上的错误,包括程序逻辑错误和系统错误。

一 前言

java异常处理大家都不陌生,总的来说有下面两点:

1.抛出异常:throw exception

  1. class SimpleException{
  2. public void a() throws Exception{
  3. throw new Exception();
  4. };
  5. }
复制代码

2.捕获异常:

  1. public class MyException {
  2. public static void main(String[] args){
  3. MyException e = new MyException();
  4. SimpleException se = new SimpleException();
  5. try {
  6. se.a();
  7. } catch (Exception e1) {
  8. e1.printStackTrace();
  9. }
  10. }
  11. }
  12. class SimpleException{
  13. public void a() throws Exception{
  14. throw new Exception();
  15. };
复制代码

本文将在此基础上,更加深入的谈一些细节问题。

二 自定义异常类

java语言为我们提供了很多异常类,但是有时候我们为了写代码的方便还是要自定义的去创造异常类:

class SimpleException extends Exception {};
创建好之后我们可以使用try catch捕获它:

  1. public class MyException {
  2. public static void main(String[] args){
  3. MyException e = new MyException();
  4. try {
  5. e.a();
  6. } catch (SimpleException e1) {
  7. e1.printStackTrace();
  8. }
  9. }
  10. public void a() throws SimpleException{
  11. throw new SimpleException();
  12. }
  13. }
  14. class SimpleException extends Exception {};
复制代码

我们在MyException中定义了一个方法a(),让它抛出SimpleException异常,然后我们在main()中调用这个方法,并使用try catch捕获了这个异常:

  1. SimpleException
  2. at MyException.a(MyException.java:15)
  3. at MyException.main(MyException.java:8)
  4. at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  5. at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
  6. at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  7. at java.lang.reflect.Method.invoke(Method.java:606)
  8. at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
  9. Process finished with exit code 0
复制代码

编译执行后的结果,主要看前三行就行了。这里着重说明几点:
1.抛出异常类型的指定:(exception specification)
当我们需要在一个方法中抛出一个异常时,我们使用throw后加某异常类的实例,程序会在此向客户端程序(调用这段代码的程序)抛出对应异常并在此退出(相当于return)。另外需要注意的是,我们必须在定义该方法的时候指明异常类型,比如下面这段代码会抛出SimpleException异常

public void a() throws SimpleException

2.抛出多个异常:

  1. public void a() throws SimpleException,AException,BException{
  2. throw new SimpleException();
  3. }
复制代码

不同的异常类之间用逗号隔开即可,在这种情况下我们不必须throw每个异常类的实例(),但是客户端代码必须要catch到每个异常类:

  1. public class MyException {
  2. public static void main(String[] args){
  3. MyException e = new MyException();
  4. try {
  5. e.a();
  6. } catch (SimpleException e1) {
  7. e1.printStackTrace();
  8. } catch (BException e1) {
  9. e1.printStackTrace();
  10. } catch (AException e1) {
  11. e1.printStackTrace();
  12. }
  13. }
  14. public void a() throws SimpleException,AException,BException{
  15. throw new SimpleException();
  16. }
  17. }
  18. class SimpleException extends Exception {};
  19. class AException extends Exception{}
  20. class BException extends Exception{}
复制代码

三 stack trace

无论是抛出异常,或者是捕获处理异常,我们的目的是为了写出更健壮的程序,这很大程度上依赖于java异常机制给我们提供的异常信息,而它的载体就是stack trace。
前面的代码中我们直接使用printStackTrace()打印出异常信息,其实我们还可以使用getStackTrace()方法来获取StackTraceElement型的集合,如果你手头有IDEA的话,你可以先搜索出StackTraceElement类,可以发现它实现了接口Serializable ,再看看它的类描述:

  1. /**
  2. * An element in a stack trace, as returned by {@link
  3. * Throwable#getStackTrace()}. Each element represents a single stack frame.
  4. * All stack frames except for the one at the top of the stack represent
  5. * a method invocation. The frame at the top of the stack represents the
  6. * execution point at which the stack trace was generated. Typically,
  7. * this is the point at which the throwable corresponding to the stack trace
  8. * was created.
  9. *
  10. * @since 1.4
  11. * @author Josh Bloch
  12. */
复制代码

讲的很清楚,这个类的每个实例都是stack trace的一个元素,代表着一个stack frame,stack trace是由getStackTrace()方法返回的。后边的我试着翻译了几遍,都觉得不好,还是直接上代码才能说清楚:

  1. public class MyException {
  2. public static void main(String[] args){
  3. MyException e = new MyException();
  4. e.a();
  5. public void a(){
  6. try {
  7. throw new Exception();
  8. } catch (Exception e) {
  9. StackTraceElement[] ste = e.getStackTrace();
  10. System.out.println(ste.length);
  11. }
  12. }
  13. }
复制代码

我们定义了方法a,让它抛出Exception异常的同时捕获它,然后我们通过getStackTrace()方法得到一个StackTraceElement型的数组,并打印出数组的长度:

7

Process finished with exit code 0
我们把代码稍微改一下,不在a中捕获异常了,我们重新定义一个方法b,让它在调用a的同时将异常捕获:

  1. public class MyException {
  2. public static void main(String[] args){
  3. MyException e = new MyException();
  4. e.b();
  5. }
  6. public void b(){
  7. try {
  8. a();
  9. } catch (Exception e) {
  10. StackTraceElement[] ste = e.getStackTrace();
  11. System.out.println(ste.length);
  12. }
  13. }
  14. public void a() throws Exception{
  15. throw new Exception();
  16. }
  17. }
复制代码

结果如下:

8

Process finished with exit code 0
别急,我们再来看点有趣的:

  1. public class MyException {
  2. public static void main(String[] args){
  3. MyException exception = new MyException();
  4. try {
  5. exception.c();
  6. } catch (Exception e) {
  7. StackTraceElement[] ste = e.getStackTrace();
  8. System.out.println(ste.length);
  9. System.out.println("---------------------------------------------------------------");
  10. for (StackTraceElement s : e.getStackTrace()){
  11. System.out.println(s.getClassName()+":method "+s.getMethodName()+" at line"+s.getLineNumber());
  12. }
  13. System.out.println("---------------------------------------------------------------");
  14. }
  15. }
  16. public void c() throws Exception{
  17. try {
  18. a();
  19. }catch (Exception e){
  20. throw e;
  21. }
  22. }
  23. public void a() throws Exception{
  24. throw new Exception();
  25. }
  26. }
复制代码

下面是结果:

  1. 8
  2. ---------------------------------------------------------------
  3. MyException:method a at line43
  4. MyException:method c at line39
  5. MyException:method main at line9
  6. sun.reflect.NativeMethodAccessorImpl:method invoke0 at line-2
  7. sun.reflect.NativeMethodAccessorImpl:method invoke at line57
  8. sun.reflect.DelegatingMethodAccessorImpl:method invoke at line43
  9. java.lang.reflect.Method:method invoke at line606
  10. com.intellij.rt.execution.application.AppMain:method main at line144
  11. ---------------------------------------------------------------
  12. Process finished with exit code 0
复制代码

也就是说,getStackTrace()返回一个栈,它包含从调用者(main())到初始抛出异常者(a())的一些基本信息 ,在上面的代码中,我们在c方法中调用a方法时捕获异常并通过throws将其再次抛出(rethrow),调用c方法的方法可以捕获并处理异常,也可以选择继续抛出让更高层次的调用者(靠近栈底)处理。rethrow虽然很方便,但存在着一些问题,我们看下面这段代码:

  1. public class MyException {
  2. public static void main(String[] args){
  3. MyException exception = new MyException();
  4. try {
  5. exception.c();
  6. } catch (Exception e) {
  7. e.printStackTrace(System.out);
  8. }
  9. }
  10. public void c() throws Exception{
  11. try {
  12. a();
  13. }catch (Exception e){
  14. throw e;
  15. }
  16. }
  17. public void a() throws Exception{
  18. throw new Exception("Exception from a()");
  19. }
  20. }
  21. java.lang.Exception: Exception from a()
  22. at MyException.a(MyException.java:40)
  23. at MyException.c(MyException.java:30)
  24. at MyException.main(MyException.java:21)
复制代码

我们在c中重新抛出e,在main中使用 e.printStackTrace()打印出来,可以看到打印出来stack trace还是属于a的,如果我们想把stack trace变成c的可以这么写:

  1. public class MyException {
  2. public static void main(String[] args){
  3. MyException exception = new MyException();
  4. try {
  5. exception.c();
  6. } catch (Exception e) {
  7. e.printStackTrace(System.out);
  8. }
  9. }
  10. public void c() throws Exception{
  11. try {
  12. a();
  13. }catch (Exception e){
  14. // throw e;
  15. throw (Exception)e.fillInStackTrace();
  16. }
  17. }
  18. public void a() throws Exception{
  19. throw new Exception("Exception from a()");
  20. }
  21. }
  22. java.lang.Exception: Exception from a()
  23. at MyException.c(MyException.java:22)
  24. at MyException.main(MyException.java:10)
复制代码

四 异常链 Exception chaining

先来看一个场景:

  1. public class TestException {
  2. public static void main(String[] args){
  3. TestException testException = new TestException();
  4. try {
  5. testException.c();
  6. } catch (CException e) {
  7. e.printStackTrace();
  8. }
  9. }
  10. public void a() throws AException{
  11. AException aException = new AException("this is a exception");
  12. throw aException;
  13. }
  14. public void b() throws BException{
  15. try {
  16. a();
  17. } catch (AException e) {
  18. throw new BException("this is b exception");
  19. }
  20. }
  21. public void c() throws CException{
  22. try {
  23. b();
  24. } catch (BException e) {
  25. throw new CException("this is c exception");
  26. }
  27. }
  28. }
  29. class AException extends Exception{
  30. public AException(String msg){
  31. super(msg);
  32. }
  33. }
  34. class BException extends Exception{
  35. public BException(String msg){
  36. super(msg);
  37. }
  38. }
  39. class CException extends Exception{
  40. public CException(String msg){
  41. super(msg);
  42. }
  43. }
复制代码

创建了三个异常类AException、BException、CException,然后在a()中抛出AException,在b()中捕获AException并抛出BException,最后在c()中捕获BException并抛出CException,结果打印如下:

  1. CException: this is c exception
  2. at TestException.c(TestException.java:31)
  3. at TestException.main(TestException.java:8)
复制代码

好,我们只看到了CException的信息,AException,BException的异常信息已丢失,这时候异常链的作用就出来了,看代码:

  1. public class TestException {
  2. public static void main(String[] args){
  3. TestException testException = new TestException();
  4. try {
  5. testException.c();
  6. } catch (CException e) {
  7. e.printStackTrace();
  8. }
  9. }
  10. public void a() throws AException{
  11. AException aException = new AException("this is a exception");
  12. throw aException;
  13. }
  14. public void b() throws BException{
  15. try {
  16. a();
  17. } catch (AException e) {
  18. // throw new BException("this is b exception");
  19. BException bException = new BException("this is b exception");
  20. bException.initCause(e);
  21. throw bException;
  22. }
  23. }
  24. public void c() throws CException{
  25. try {
  26. b();
  27. } catch (BException e) {
  28. // throw new CException("this is c exception");
  29. CException cException = new CException("this is c exception");
  30. cException.initCause(e);
  31. throw cException;
  32. }
  33. }
  34. }
  35. class AException extends Exception{
  36. public AException(String msg){
  37. super(msg);
  38. }
  39. }
  40. class BException extends Exception{
  41. public BException(String msg){
  42. super(msg);
  43. }
  44. }
  45. class CException extends Exception{
  46. public CException(String msg){
  47. super(msg);
  48. }
  49. }
复制代码

我们用initCause()方法将异常信息给串联了起来,结果如下:

  1. CException: this is c exception
  2. at TestException.c(TestException.java:35)
  3. at TestException.main(TestException.java:8)
  4. at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  5. at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
  6. at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  7. at java.lang.reflect.Method.invoke(Method.java:606)
  8. at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
  9. Caused by: BException: this is b exception
  10. at TestException.b(TestException.java:24)
  11. at TestException.c(TestException.java:32)
  12. ... 6 more
  13. Caused by: AException: this is a exception
  14. at TestException.a(TestException.java:15)
  15. at TestException.b(TestException.java:21)
  16. ... 7 more
  17. Process finished with exit code 0
复制代码

五 后记

其实关于java异常处理还有很多需要探讨的地方,但是由于我经验有限,还不能体会的太深刻,最常用的也就是

  1. try {
  2. ...
  3. }catch (Exception e){
  4. ...
  5. }finally {
  6. //不管异常会不会被捕捉或者处理都会执行的代码,如关闭IO操作
  7. }
复制代码

但是无论如何我们还是要感谢java给我们提供的异常机制,它好似一个长者,时不时给我们指引道路,也让我们在编码的时候没有那么无聊:)

最新评论

小黑屋|在路上 ( 蜀ICP备15035742号-1 

;

GMT+8, 2025-7-7 00:41

Copyright 2015-2025 djqfx

返回顶部