理解Java异常处理机制

举报
芝麻粒儿 发表于 2021/08/04 23:44:40 2021/08/04
【摘要】 推荐资源站:https://zhimalier.com/ 面试常见问题 /*try块中的退出语句笔试面试题解析public class ExceptionTest5{ public void method() { try { System.out.println("进入到try块"); //return; //会先执行finally块再返回 //虚拟机退出 //...

推荐资源站:https://zhimalier.com/

面试常见问题


  
  1. /*try块中的退出语句
  2. 笔试面试题解析
  3. public class ExceptionTest5
  4. {
  5. public void method()
  6. {
  7. try
  8. {
  9. System.out.println("进入到try块");
  10. //return;
  11. //会先执行finally块再返回
  12. //虚拟机退出
  13. //System.exit(0);
  14. //不会执行finally块中的语句,直接退出
  15. }
  16. catch (Exception e)
  17. {
  18. System.out.println("异常发生了!");
  19. }
  20. finally
  21. {
  22. System.out.println("进入到finally块");
  23. }
  24. System.out.println("后续代码");
  25. }
  26. public static void main(String[] args)
  27. {
  28. ExceptionTest5 test = new ExceptionTest5();
  29. test.method();
  30. }
  31. }
  32. 在加上return语句前,程序输出:
  33. 进入到try块
  34. 进入到finally块
  35. 后续代码
  36. 如果在try块中加入return语句:
  37. 程序执行输出:
  38. 进入到try块
  39. 进入到finally块
  40. 说明try块中有return语句时,仍然会首先执行finally块中的语句,然后方
  41. 法再返回。
  42.   如果try块中存在System.exit(0);语句,那么就不会执行finally块中
  43. 的代码,因为System.exit(0)会终止当前运行的Java虚拟机,程序会在虚拟
  44. 机终止前结束执行。*/

try..catch..finally相信大家再熟悉不过了,但是它没想象中那么简单!

Java异常的概念和体系结构
Java把异常当作对象来处理,并定义一个基类java.lang.Throwable作为所有异常的超类。 
         在Java API中已经定义了许多异常类,这些异常类分为两大类,错误Error和异常Exception。 
         Java异常体系结构呈树状  如图:

 

 

Thorwable类所有异常和错误的超类,有两个子类Error和Exception,分别表示错误和异常。 
 其中异常类Exception又分为运行时异常(RuntimeException)和非运行时异常

1、Error与Exception 

      Error(错误):是程序无法处理的错误,表示运行应用程序中较严重问题。大多数错误与代码编写者执行的操作无关,而表示代码运行时 JVM(Java 虚拟机)出现的问题。

例如,Java虚拟机运行错误(Virtual MachineError),当 JVM 不再有继续执行操作所需的内存资源时,将出现 OutOfMemoryError。这些异常发生时,Java虚拟机(JVM)

一般会选择线程终止 

      Exception是程序本身可以处理的异常,这种异常分两大类运行时异常和非运行时异常。 程序中应当尽可能去处理这些异常。
      注意:异常和错误的区别:异常能被程序本身可以处理,错误是无法处理。   

Java中exception的异常分为两大类:

        1.Checked Exception(非Runtime Exception)
  2.Unchecked Exception(Runtime Exception)
运行时异常
  这些异常是不检查异常 RuntimeException类是Exception类的子类,它叫做运行时异常,Java中的所有运行时异常都会直接或者间接地继承自RuntimeException类。

这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。

非运行时异常

Java中凡是继承自Exception,而不继承自RuntimeException类的异常都是非运行时异常。必须处理!

Java异常处理涉及到五个关键字,分别是:try、catch、finally、throw、throws。

1、try语句块,表示要尝试运行代码,try语句块中代码受异常监控,其中代码发生异常时,会抛出异常对象。    

catch语句块会捕获try代码块中发生的异常并在其代码块中做异常处理,catch语句带一个Throwable类型的参数,  表示可捕获异常类型。当try中出现异常时,catch会捕获到发生的异常,并和自己的异常类型匹配,  若匹配,则执行catch块中代码,并将catch块参数指向所抛的异常对象。catch语句可以有多个, 用来匹配多个中的一个异常,一旦匹配上后,就不再尝试匹配别的catch块了。 通过异常对象可以获取异常发生时完整的JVM堆栈信息,以及异常信息和异常发生的原因等。 

 finally语句块是紧跟catch语句后的语句块,这个语句块总是会在方法返回前执行,  而不管是否try语句块是否发生异常。并且这个语句块总是在方法返回前执行。  目的是给程序一个补救的机会。这样做也体现了Java语言的健壮性。 

2、 try、catch、finally三个语句块应注意的问题 
    第一、try、catch、finally三个语句块均不能单独使用,三者可以组成 try...catch...finally、try...catch、   try...finally三种结构,catch语句可以有一个或多个,finally语句最多一个。 
    第二、try、catch、finally三个代码块中变量的作用域为代码块内部,分别独立而不能相互访问。 如果要在三个块中都可以访问,则需要将变量定义到这些块的外面。 
    第三、多个catch块时候,只会匹配其中一个异常类并执行catch块代码,而不会再执行别的catch块, 并且匹配catch语句的顺序是由上到下。 

3、throw、throws关键字 
    throw关键字是用于方法体内部,用来抛出一个Throwable类型的异常。如果抛出了检查异常,  则还应该在方法头部声明方法可能抛出的异常类型。该方法的调用者也
必须检查处理抛出的异常。 如果所有方法都层层上抛获取的异常,最终JVM会进行处理,处理也很简单,就是打印异常消息和堆栈信息。  如果抛出的是Error或RuntimeException,则该方法的调用者可选择处理该异常。有关异常的转译会在下面说明。  throws关键字用于方法体外部的方法声明部分,用来声明方法可能会抛
出某些异常。仅当抛出了检查异常,  该方法的调用者才必须处理或者重新抛出该异常。当方法的调用者无力处理该异常的时候,应该继续抛出,  而不是囫囵吞枣一般在catch块中打印一下堆栈信息做个勉强处理。

异常处理的机制为:抛出异常,捕捉异常。

抛出异常:当一个方法出现错误引发异常时,方法创建异常对象并交付运行时系统,异常对象中包含了异常类型和异常出现时的程序状态等异常信息。运行时系统负责寻找处置异常的代码并执行。  

捕获异常:在方法抛出异常之后,运行时系统将转为寻找合适的异常处理器(exception handler)。潜在的异常处理器是异常发生时依次存留在调用栈中的方法的集合。当异常处理器所能处理的异常类型与方法抛出的异常类型相符时,即为合适 的异常处理器。运行时系统从发生异常的方法开始,依次回查调用栈中的方法,直至找到含有合适异常处理器的方法并执行。当运行时系统遍历调用栈而未找到合适 的异常处理器,则运行时系统终止。同时,意味着Java程序的终止。

异常处理的一般结构


  
  1. try
  2. {
  3. // 可能发生异常的代码
  4. // 如果发生了异常,那么异常之后的代码都不会被执行
  5. }
  6. catch (Exception e)
  7. {
  8. // 异常处理代码
  9. }
  10. finally
  11. {
  12. // 不管有没有发生异常,finally语句块都会被执行
  13. }

多个catch结构  一个try后面可以跟多个catch,但不管多少个,最多只会有一个catch块被执行。

异常处理方法

对于非运行时异常(checked exception),必须要对其进行处理,否则无法通过编译。
处理方式有两种:
1.使用try..catch..finally进行捕获;
2.在产生异常的方法声明后面写上throws 某一个Exception类型,如throws Exception,将异常抛出到外面一层去。对非运行时异常的处理详见代码例子:
处理方式1:将异常捕获


  
  1. public class ExceptionTest2
  2. {
  3. public void method() throws Exception // 将异常抛出,由调用这
  4. 个方法的方法去处理这个异常,如果main方法也将异常抛出,则交给Java虚拟
  5. 机来处理
  6. {
  7. System.out.println("Hello World");
  8. // 抛出异常
  9. throw new Exception();
  10. }
  11. public static void main(String[] args)
  12. {
  13. ExceptionTest2 test = new ExceptionTest2();
  14. try
  15. {
  16. test.method();
  17. }
  18. catch (Exception e)
  19. {
  20. e.printStackTrace();
  21. }
  22. finally
  23. {
  24. System.out.println("Welcome");
  25. }
  26. }
  27. }

处理方式2:将异常继续向外抛出


  
  1. public class ExceptionTest2
  2. {
  3. public void method() throws Exception // 将异常抛出,由调用这
  4. 个方法的方法去处理这个异常,如果main方法也将异常抛出,则交给Java虚拟
  5. 机来处理
  6. {
  7. System.out.println("Hello World");
  8. // 抛出异常
  9. throw new Exception();
  10. }
  11. public static void main(String[] args) throws Exception //
  12. main方法选择将异常继续抛出
  13. {
  14. ExceptionTest2 test = new ExceptionTest2();
  15. test.method(); // main方法需要对异常进行处理
  16. // 执行结果:
  17. // Hello World
  18. // Exception in thread "main" java.lang.Exception
  19. // at com.learnjava.exception.ExceptionTest2.method
  20. (ExceptionTest2.java:10)
  21. // at com.learnjava.exception.ExceptionTest2.main
  22. (ExceptionTest2.java:17)
  23. }
  24. }

对于运行时异常(runtime exception),可以对其进行处理,也可以不处理。推荐不对运行时异常进行处理。

自定义异常

所谓自定义异常,通常就是定义一个类,去继承Exception类或者它的子类。因为异常必须直接或者间接地继承自Exception类。通常情况下,会直接继承自Exception类,一般不会继承某个运行时的异常类。
      自定义异常可以用于处理用户登录错误,用户输入错误提示等。
自定义异常的例子:
自定义一个异常类型:


  
  1. public class MyException extends Exception
  2. {
  3. public MyException()
  4. {
  5. super();
  6. }
  7. public MyException(String message)
  8. {
  9. super(message);
  10. }
  11. }

异常处理方式一:


  
  1. public class ExceptionTest4
  2. {
  3. public void method(String str) throws MyException
  4. {
  5. if(null == str)
  6. {
  7. throw new MyException("传入的字符串参数不能为null!");
  8. }
  9. else
  10. {
  11. System.out.println(str);
  12. }
  13. }
  14. public static void main(String[] args) throws MyException //异常处理方式1,不断向外抛出
  15. {
  16. ExceptionTest4 test = new ExceptionTest4();
  17. test.method(null);
  18. }
  19. }

异常处理方式二


  
  1. public class ExceptionTest4
  2. {
  3. public void method(String str) throws MyException
  4. {
  5. if (null == str)
  6. {
  7. throw new MyException("传入的字符串参数不能为null!");
  8. }
  9. else
  10. {
  11. System.out.println(str);
  12. }
  13. }
  14. public static void main(String[] args)
  15. {
  16. //异常处理方式2,采用try...catch语句
  17. try
  18. {
  19. ExceptionTest4 test = new ExceptionTest4();
  20. test.method(null);
  21. }
  22. catch (MyException e)
  23. {
  24. e.printStackTrace();
  25. }
  26. finally
  27. {
  28. System.out.println("程序处理完毕");
  29. }
  30. }
  31. }

刚才说过,可以有多个catch块,去捕获不同的异常,真正执行的时候最多只进入一个catch块。
例:多种异常

 


  
  1. public class MyException extends Exception
  2. {
  3. public MyException()
  4. { super();
  5. }
  6. public MyException(String message)
  7. {
  8. super(message);
  9. }
  10. }
  11. public class MyException2 extends Exception
  12. {
  13. public MyException2()
  14. {
  15. super();
  16. }
  17. public MyException2(String message)
  18. {
  19. super(message);
  20. }
  21. }
  22. public class ExceptionTest4
  23. {
  24. public void method(String str) throws MyException, MyException2
  25. {
  26. if (null == str)
  27. {
  28. throw new MyException("传入的字符串参数不能为null!");
  29. }
  30. else if ("hello".equals(str))
  31. {
  32. throw new MyException2("传入的字符串不能为hello");
  33. }
  34. else
  35. {
  36. System.out.println(str);
  37. }
  38. }
  39. public static void main(String[] args)
  40. {
  41. // 异常处理方式2,采用try...catch语句
  42. try
  43. {
  44. ExceptionTest4 test = new ExceptionTest4();
  45. test.method(null);
  46. }
  47. catch (MyException e)
  48. {
  49. System.out.println("进入到MyException catch块");
  50. e.printStackTrace();
  51. }
  52. catch (MyException2 e)
  53. {
  54. System.out.println("进入到MyException2 catch块");
  55. e.printStackTrace();
  56. }
  57. finally
  58. {
  59. System.out.println("程序处理完毕");
  60. }
  61. }
  62. }

我们可以使用多个catch块来捕获异常,这时需要将父类型的catch块放到子类型的catch块之后,这样才能保证后续的catch块可能被执行,否则子类型
的catch块将永远无法到达,Java编译器会报错。如果异常类型是独立的,那么它们的前后顺序没有要求。
例子:
多个catch语句块的顺序

 


  
  1. public class ExceptionTest4
  2. {
  3. public void method(String str) throws Exception // 也可以声明Exception,只要声明的可以涵盖所有抛出的异常即可
  4. {
  5. if (null == str)
  6. {
  7. throw new MyException("传入的字符串参数不能为null!");
  8. }
  9. else if ("hello".equals(str))
  10. {
  11. throw new MyException2("传入的字符串不能为hello");
  12. }
  13. else
  14. {
  15. System.out.println(str);
  16. }
  17. }
  18. public static void main(String[] args)
  19. {
  20. // 异常处理方式2,采用try...catch语句
  21. try
  22. {
  23. ExceptionTest4 test = new ExceptionTest4();
  24. test.method(null);
  25. }
  26. catch (MyException e)
  27. {
  28. System.out.println("进入到MyException catch块");
  29. e.printStackTrace();
  30. }
  31. catch (MyException2 e)
  32. {
  33. System.out.println("进入到MyException2 catch块");
  34. e.printStackTrace();
  35. }
  36. catch (Exception e)
  37. {
  38. //虽然需要加上,但是这块代码不会被执行,只是为了编译成功
  39. System.out.println("进入到MyException catch块");
  40. e.printStackTrace();
  41. //如果去掉前面两个catch块或其中之一,则发生该异常时就会进入此catch块
  42. //catch块的匹配是按照从上到下的顺序,所以这个块如果放在最前面就会捕获所有的异常,后面的块永远不会执行,这时候会提示编译错误
  43. }
  44. finally
  45. {
  46. System.out.println("程序处理完毕");
  47. }
  48. }
  49. }

练习


  
  1. public class TestException {
  2. public TestException() {
  3. }
  4. boolean testEx() throws Exception {
  5. boolean ret = true;
  6. try {
  7. ret = testEx1();
  8. } catch (Exception e) {
  9. System.out.println("testEx, catch exception");
  10. ret = false;
  11. throw e;
  12. } finally {
  13. System.out.println("testEx, finally; return value=" + ret);
  14. return ret;
  15. }
  16. }
  17. boolean testEx1() throws Exception {
  18. boolean ret = true;
  19. try {
  20. ret = testEx2();
  21. if (!ret) {
  22. return false;
  23. }
  24. System.out.println("testEx1, at the end of try");
  25. return ret;
  26. } catch (Exception e) {
  27. System.out.println("testEx1, catch exception");
  28. ret = false;
  29. throw e;
  30. } finally {
  31. System.out.println("testEx1, finally; return value=" + ret);
  32. return ret;
  33. }
  34. }
  35. boolean testEx2() throws Exception {
  36. boolean ret = true;
  37. try {
  38. int b = 12;
  39. int c;
  40. for (int i = 2; i >= -2; i--) {
  41. c = b / i;
  42. System.out.println("i=" + i);
  43. }
  44. return true;
  45. } catch (Exception e) {
  46. System.out.println("testEx2, catch exception");
  47. ret = false;
  48. throw e;
  49. } finally {
  50. System.out.println("testEx2, finally; return value=" + ret);
  51. return ret;
  52. }
  53. }
  54. public static void main(String[] args) {
  55. TestException testException1 = new TestException();
  56. try {
  57. testException1.testEx();
  58. } catch (Exception e) {
  59. e.printStackTrace();
  60. }
  61. }
  62. }
  63. /*答案:
  64. i=2
  65. i=1
  66. testEx2, catch exception
  67. testEx2, finally; return value=false
  68. testEx1, finally; return value=false
  69. testEx, finally; return value=false
  70. 注意说明:
  71. finally语句块不应该出现 应该出现return。上面的return ret最好是其他
  72. 语句来处理相关逻辑。*/

 

 

 

 

 

文章来源: zhima.blog.csdn.net,作者:芝麻粒儿,版权归原作者所有,如需转载,请联系作者。

原文链接:zhima.blog.csdn.net/article/details/51973534

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。