深入探讨Java中的异常与错误处理

Java中的异常处理机制已经比较成熟,我们的Java程序到处充满了异常的可能,如果对这些异常不做预先的处理,那么将来程序崩溃就无从调试,很难找到异常所在的位置。本文将探讨一下Java中异常与错误的处理方法,一起来看看。

异常与错误:

异常:

在Java中程序的错误主要是语法错误和语义错误,一个程序在编译和运行时出现的错误我们统一称之为异常,它是VM(虚拟机)通知你的一种方式,通过这种方式,VM让你知道,你(开发人员)已经犯了个错误,现在有一个机会来修改它。Java中使用异常类来表示异常,不同的异常类代表了不同的异常。但是在Java中所有的异常都有一个基类,叫做Exception。

错误:

它指的是一个合理的应用程序不能截获的严重的问题。大多数都是反常的情况。错误是VM的一个故障(虽然它可以是任何系统级的服务)。所以,错误是很难处理的,一般的开发人员(当然不是你)是无法处理这些错误的,比如内存溢出。
和异常一样,在Java中用错误类来表示错误,不同的错误类代表了不同的错误。 但是在Java中所有的错误都有一个基类,叫做Error。

综上,我们可以知道异常和错误最本质的区别就是异常能被开发人员处理而错误时系统本来自带的,一般无法处理也不需要我们程序员来处理。

1.一个异常是在一个程序执行过程中出现的一个事件,它中断了正常指令的运行

2.错误,偏离了可接受的代码行为的一个动作或实例

异常的结构分类:

1、运行时异常(未检查异常)

2、编译时异常(已检查异常)

运行异常即是RuntimeException;其余的全部为编译异常

在Java中异常Exception和错误Error有个共同的父类Throwable。

Error Exception

runtimeException几个子类

1、 java.lang.ArrayIndexOutOfBoundsException

数组索引越界异常。当对数组的索引值为负数或大于等于数组大小时抛出。

2、java.lang.ArithmeticException

算术条件异常。譬如:整数除零等。

3、java.lang.NullPointerException

空指针异常。当应用试图在要求使用对象的地方使用了null时,抛出该异常。譬如:调用null对象的实例方法、访问null对象的

属性、计算null对象的长度、使用throw语句抛出null等等

4、java.lang.ClassNotFoundException

找不到类异常。当应用试图根据字符串形式的类名构造类,而在遍历CLASSPAH之后找不到对应名称的class文件时,抛出

该异常。

对异常的处理:

try{}catch{}

try{}catch{}finally{}无论有无异常finally代码块都会被执行

try{}finally{}也是可以组合使用的但是catch{}finally{}不可以

注意:在继承关系中,子类覆盖父类的方法,抛出异常的范围不能比父类更宽泛

异常的使用

在异常的使用这一部分主要是演示代码,都是我们平常写代码的过程中会遇到的(当然只是一小部分),抛砖引玉吗!

例1. 这个例子主要通过两个方法对比来演示一下有了异常以后代码的执行流程。


  1. public static void testException1() { 
  2.      int[] ints = new int[] { 1, 2, 3, 4 }; 
  3.      System.out.println("异常出现前"); 
  4.      try { 
  5.           System.out.println(ints[4]); 
  6.           System.out.println("我还有幸执行到吗");// 发生异常以后,后面的代码不能被执行 
  7.      } catch (IndexOutOfBoundsException e) { 
  8.           System.out.println("数组越界错误"); 
  9.      } 
  10.      System.out.println("异常出现后"); 
  11.  
  12. /*output: 
  13. 异常出现前 
  14. 数组越界错误 
  15. 常出现后 
  16. */ 
  17.  
  18. public static void testException2() { 
  19.      int[] ints = new int[] { 1, 2, 3, 4 }; 
  20.      System.out.println("异常出现前"); 
  21.      System.out.println(ints[4]); 
  22.      System.out.println("我还有幸执行到吗");// 发生异常以后,他后面的代码不能被执行 
  23. }  

首先指出例子中的不足之处,IndexOutofBoundsException是一个非受检异常,所以不用try…catch…显示捕捉,但是我的目的是对同一个异常用不同的处理方式,看它会有什么不同的而结果(这里也就只能用它将就一下了)。异常出现时第一个方法只是跳出了try块,但是它后面的代码会照样执行的。但是第二种就不一样了直接跳出了方法,比较强硬。从第一个方法中我们看到,try…catch…是一种”事务性”的保障,它的目的是保证程序在异常的情况下运行完毕,同时它还会告知程序员程序中出错的详细信息(这种详细信息有时要依赖于程序员设计)。

例2. 重新抛出异常


  1. public class Rethrow { 
  2.      public static void readFile(String file) throws FileNotFoundException { 
  3.      try { 
  4.           BufferedInputStream in = new BufferedInputStream(new FileInputStream(file)); 
  5.      } catch (FileNotFoundException e) { 
  6.           e.printStackTrace(); 
  7.           System.err.println("不知道如何处理该异常或者根本不想处理它,但是不做处理又不合适,这是重新抛出异常交给上一级处理"); 
  8.           //重新抛出异常 
  9.           throw e; 
  10.      } 
  11. public static void printFile(String file) { 
  12.      try { 
  13.           readFile(file); 
  14.      } catch (FileNotFoundException e) { 
  15.           e.printStackTrace(); 
  16.      } 
  17.      public static void main(String[] args) { 
  18.           printFile("D:/file"); 
  19.      } 
  20. }  

异常的本意是好的,让我们试图修复程序,但是现实中我们修复的几率很小,我们很多时候就是用它来记录出错的信息。如果你厌倦了不停的处理异常,重新抛出异常对你来说可能是一个很好的解脱。原封不动的把这个异常抛给上一级,抛给调用这个方法的人,让他来费脑筋吧。这样看来,java异常(当然指的是受检异常)又给我们平添很多麻烦,尽管它的出发点是好的。

例3. 异常链的使用及异常丢失


  1. ExceptionA,ExceptionB,ExceptionC 
  2. public class ExceptionA extends Exception { 
  3.      public ExceptionA(String str) { 
  4.           super(); 
  5.      } 
  6. public class ExceptionB extends ExceptionA { 
  7.      public ExceptionB(String str) { 
  8.           super(str); 
  9.      } 
  10. public class ExceptionC extends ExceptionA { 
  11.      public ExceptionC(String str) { 
  12.           super(str); 
  13.      } 
  14. }  

异常丢失的情况:


  1. public class NeverCaught { 
  2.      static void f() throws ExceptionB{ 
  3.           throw new ExceptionB("exception b"); 
  4.      } 
  5.      static void g() throws ExceptionC { 
  6.           try { 
  7.                f(); 
  8.           } catch (ExceptionB e) { 
  9.                ExceptionC c = new ExceptionC("exception a"); 
  10.                throw c; 
  11.           } 
  12.      } 
  13.      public static void main(String[] args) { 
  14.           try { 
  15.                g(); 
  16.           } catch (ExceptionC e) { 
  17.                e.printStackTrace(); 
  18.           } 
  19.      } 
  20. /* 
  21. exception.ExceptionC 
  22. at exception.NeverCaught.g(NeverCaught.java:12) 
  23. at exception.NeverCaught.main(NeverCaught.java:19) 
  24. */  

为什么只是打印出来了ExceptionC而没有打印出ExceptionB呢?这个还是自己分析一下吧!

上面的情况相当于少了一种异常,这在我们排错的过程中非常的不利。那我们遇到上面的情况应该怎么办呢?这就是异常链的用武之地:保存异常信息,在抛出另外一个异常的同时不丢失原来的异常。


  1. public class NeverCaught { 
  2.      static void f() throws ExceptionB{ 
  3.            throw new ExceptionB("exception b"); 
  4.      } 
  5.      static void g() throws ExceptionC { 
  6.           try { 
  7.                 f(); 
  8.           } catch (ExceptionB e) { 
  9.                 ExceptionC c = new ExceptionC("exception a"); 
  10.                 //异常连 
  11.                 c.initCause(e); 
  12.                 throw c; 
  13.           } 
  14.      } 
  15.      public static void main(String[] args) { 
  16.           try { 
  17.                 g(); 
  18.           } catch (ExceptionC e) { 
  19.                 e.printStackTrace(); 
  20.           } 
  21.      } 
  22. /* 
  23. exception.ExceptionC 
  24. at exception.NeverCaught.g(NeverCaught.java:12) 
  25. at exception.NeverCaught.main(NeverCaught.java:21) 
  26. Caused by: exception.ExceptionB 
  27. at exception.NeverCaught.f(NeverCaught.java:5) 
  28. at exception.NeverCaught.g(NeverCaught.java:10) 
  29. ... 1 more 
  30. */  

这个异常链的特性是所有异常均具备的,因为这个initCause()方法是从Throwable继承的。

例4. 清理工作

清理工作对于我们来说是必不可少的,因为如果一些消耗资源的操作,比如IO,JDBC。如果我们用完以后没有及时正确的关闭,那后果会很严重,这意味着内存泄露。异常的出现要求我们必须设计一种机制不论什么情况下,资源都能及时正确的清理。这就是finally。


  1. public void readFile(String file) { 
  2.      BufferedReader reader = null; 
  3.      try { 
  4.            reader = new BufferedReader(new InputStreamReader(new FileInputStream(file))); 
  5.            // do some other work 
  6.      } catch (FileNotFoundException e) { 
  7.            e.printStackTrace(); 
  8.      } finally { 
  9.            try { 
  10.                  reader.close(); 
  11.            } catch (IOException e) { 
  12.                  e.printStackTrace(); 
  13.            } 
  14.      } 
  15. }  

例子非常的简单,是一个读取文件的例子。这样的例子在JDBC操作中也非常的常见。(所以,我觉得对于资源的及时正确清理是一个程序员的基本素质之一。)

Try…finally结构也是保证资源正确关闭的一个手段。如果你不清楚代码执行过程中会发生什么异常情况会导致资源不能得到清理,那么你就用try对这段”可疑”代码进行包装,然后在finally中进行资源的清理。举一个例子:


  1. public void readFile() { 
  2.      BufferedReader reader = null; 
  3.      try { 
  4.            reader = new BufferedReader(new InputStreamReader(new FileInputStream("file"))); 
  5.            // do some other work 
  6.            //close reader 
  7.            reader.close(); 
  8.      } catch (FileNotFoundException e) { 
  9.            e.printStackTrace(); 
  10.      } catch (IOException e) { 
  11.            e.printStackTrace(); 
  12.      } 
  13. }  

我们注意一下这个方法和上一个方法的区别,下一个人可能习惯更好一点,及早的关闭reader。但是往往事与愿违,因为在reader.close()以前异常随时可能发生,这样的代码结构不能预防任何异常的出现。因为程序会在异常出现的地方跳出,后面的代码不能执行(这在上面应经用实例证明过)。这时我们就可以用try…finally来改造:


  1. public void readFile() { 
  2.      BufferedReader reader = null; 
  3.      try { 
  4.            try { 
  5.                  reader = new BufferedReader(new InputStreamReader(new FileInputStream("file"))); 
  6.                  // do some other work 
  7.                  // close reader 
  8.            } finally { 
  9.                  reader.close(); 
  10.            } 
  11.       } catch (FileNotFoundException e) { 
  12.            e.printStackTrace(); 
  13.       } catch (IOException e) { 
  14.            e.printStackTrace(); 
  15.       } 
  16. }  

及早的关闭资源是一种良好的行为,因为时间越长你忘记关闭的可能性越大。这样在配合上try…finally就保证万无一失了(不要嫌麻烦,java就是这么中规中矩)。

再说一种情况,假如我想在构造方法中打开一个文件或者创建一个JDBC连接,因为我们要在其他的方法中使用这个资源,所以不能在构造方法中及早的将这个资源关闭。那我们是不是就没辙了呢?答案是否定的。看一下下面的例子:


  1. public class ResourceInConstructor { 
  2.      BufferedReader reader = null; 
  3.      public ResourceInConstructor() { 
  4.           try { 
  5.                 reader = new BufferedReader(new InputStreamReader(new FileInputStream(""))); 
  6.           } catch (FileNotFoundException e) { 
  7.                 e.printStackTrace(); 
  8.           } 
  9.      } 
  10.      public void readFile() { 
  11.           try { 
  12.                  while(reader.readLine()!=null) { 
  13.                       //do some work 
  14.                  } 
  15.           } catch (IOException e) { 
  16.                  e.printStackTrace(); 
  17.           } 
  18.       } 
  19.       public void dispose() { 
  20.            try { 
  21.                 reader.close(); 
  22.            } catch (IOException e) { 
  23.                 e.printStackTrace(); 
  24.            } 
  25.       } 
  26. }  

这一部分讲的多了一点,但是异常确实是看起来容易用起来难的东西呀,java中还是有好多的东西需要深挖的。

作者:佚名

来源:51CTO

时间: 2024-09-15 16:40:02

深入探讨Java中的异常与错误处理的相关文章

深入探讨JAVA中的异常与错误处理_java

异常与错误: 异常: 在Java中程序的错误主要是语法错误和语义错误,一个程序在编译和运行时出现的错误我们统一称之为异常,它是VM(虚拟机)通知你的一种方式,通过这种方式,VM让你知道,你(开发人员)已经犯了个错误,现在有一个机会来修改它.Java中使用异常类来表示异常,不同的异常类代表了不同的异常.但是在Java中所有的异常都有一个基类,叫做Exception. 错误: 它指的是一个合理的应用程序不能截获的严重的问题.大多数都是反常的情况.错误是VM的一个故障(虽然它可以是任何系统级的服务).

JAVA中的异常与错误处理详解

  异常与错误: 异常: 在Java中程序的错误主要是语法错误和语义错误,一个程序在编译和运行时出现的错误我们统一称之为异常,它是VM(虚拟机)通知你的一种方式,通过这种方式,VM让你知道,你(开发人员)已经犯了个错误,现在有一个机会来修改它.Java中使用异常类来表示异常,不同的异常类代表了不同的异常.但是在Java中所有的异常都有一个基类,叫做Exception. 错误: 它指的是一个合理的应用程序不能截获的严重的问题.大多数都是反常的情况.错误是VM的一个故障(虽然它可以是任何系统级的服务

浅谈Java中的异常

  作为一个面向对象编程的程序员对于 下面的一句一定非常熟悉: 复制代码 try { // 代码块 } catch(Exception e) { // 异常处理 } finally { // 清理工作 } 复制代码 就是面向对象中最最常见的异常处理程序,而且甚至我们会莫名其妙的被编译器要求加上这个模块,甚至我们自己也不知道捕捉到异常该怎么处理... 为什么要有异常 其实这个问题不用多说,程序员都知道,简单总结为一句话:就是为了增强程序健壮性呗,比如下面的代码: Class Denominator

Java中的异常和处理,你知道多少?

程序运行时,发生的不被期望的事件,它阻止了程序按照程序员的预期正常执行,这就是异常.异常发生时,是任程序自生自灭,立刻退出终止,还是输出错误给用户?或者用C语言风格:用函数返回值作为执行状态. Java提供了更加优秀的解决办法:异常处理机制. 异常处理机制能让程序在异常发生时,按照代码的预先设定的异常处理逻辑,针对性地处理异常,让程序尽最大可能恢复正常并继续执行,且保持代码的清晰. Java中的异常可以是函数中的语句执行时引发的,也可以是程序员通过throw 语句手动抛出的,只要在Java程序中

全面了解Java中对于异常的捕捉方法_java

1. try-catch语句 在Java中,异常通过try-catch语句捕获.其一般语法形式为: try { // 可能会发生异常的程序代码 } catch (Type1 id1){ // 捕获并处置try抛出的异常类型Type1 } catch (Type2 id2){ //捕获并处置try抛出的异常类型Type2 } 关键词try后的一对大括号将一块可能发生异常的代码包起来,称为监控区域.Java方法在运行过程中出现异常,则创建异常对象.将异常抛出监控区域之 外,由Java运行时系统试图寻

Java千百问_04异常处理(001)_什么是java中的异常

1.java异常是什么 java在执行期间产生了某些问题,导致执行中断,这一问题就称为异常. 不同的原因都可能产生异常,包括以下内容:==用户输入无效数据.==需要打开的文件不存在.==网络连接已丢失.==JVM已经耗尽内存.==将null当作某种对象进行操作. 2.异常都分为哪些 要了解在Java中如何异常处理工作,需要了解三类异常:检查异常(checked exception) 检查异常通常是用户错误,程序员并不可预见的问题.例如,如果一个文件被打开,但该文件无法找到,则会出现异常.这些异常

Java中测试异常的多种方式

使用JUnit来测试Java代码中的异常有很多种方式,你知道几种? 给定这样一个class. Person.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class Person { private String name; private int age; public String getName() { return name; } public void setName(Stri

如何更合理的利用Java中的异常抛出

以前,我觉得编程语言中最让人不解的部分就是它能够创建错误.当时我对Java语言中的throw关键字的第一反应就是"啊,这也太傻了,为什么我们想要引发一个错误(error)?"我觉得错误是我的敌人,应当避免的,所以创建错误是毫无用处甚至是危险的.我认为在JavaScript中加入这样的关键字是多此一举.但随着我编程经验的丰富,我逐渐变成了throw我的error粉丝.合理的使用它们会让对代码的调试和维护大大简化. 在编程的时候,Error通常出现在不期望的事情发生时.可能是传入函数的参数

Java中的异常

Java异常是我们编程中时时刻刻都要使用的特性,但是我们却很少去关注它细节的一些信 息. Exception架构 所有的异常都是派生至Throwable,最主要的2个继承类是Exception和Error. 我们常说的异常都是指Exception,Exception分为Check Exception和Unchecked Exception. JDK文档中对Error中解释是:用于指示合理的应用程序不应该试图捕获的严重问题,大多 数这样的错误都是异常条件.其中一个比较特殊的就是ThreadDeat