浅谈java异常[Exception]

本文转自:focusJ

一. 异常的定义

在《java编程思想》中这样定义 异常:阻止当前方法或作用域继续执行的问题。虽然java中有异常处理机制,但是要明确一点,决不应该用"正常"的态度来看待异常。绝对一点说异常就是某种意义上的错误,就是问题,它可能会导致程序失败。之所以java要提出异常处理机制,就是要告诉开发人员,你的程序出现了不正常的情况,请注意。

记得当初学习java的时候,异常总是搞不太清楚,不知道这个异常是什么意思,为什么会有这个机制?但是随着知识的积累逐渐也对异常有一点感觉了。举一个例子来说明一下异常的用途。

 1 public class Calculator {
 2     public int devide(int num1, int num2) {
 3         //判断除数是否为0
 4         if(num2 == 0) {
 5             throw new IllegalArgumentException("除数不能为零");
 6         }
 7
 8         return num1/num2;
 9     }
10 }

看一下这个类中关于除运算的方法,如果你是新手你可能会直接返回计算结果,根本不去考虑什么参数是否正确,是否合法(当然可以原谅,谁都是这样过来的)。但是我们应尽可能的考虑周全,把可能导致程序失败的"苗头"扼杀在摇篮中,所以进行参数的合法性检查就很有必要了。其中执行参数检查抛出来的那个参数非法异常,这就属于这个方法的不正常情况。正常情况下我们会正确的使用计算器,但是不排除粗心大意把除数赋值为0。如果你之前没有考虑到这种情况,并且恰巧用户数学基础不好,那么你完了。但是如果你之前考虑到了这种情况,那么很显然错误已在你的掌控之中。

二. 异常扫盲行动

今天和别人聊天时看到一个笑话:世界上最真情的相依,是你在try我在catch。无论你发神马脾气,我都默默承受,静静处理。 大多数新手对java异常的感觉就是:try...catch...。没错,这是用的最多的,也是最实用的。我的感觉就是:java异常是从"try...catch..."走来。

首先来熟悉一下java的异常体系:

Throwable 类是 Java 语言中所有错误或异常的超类(这就是一切皆可抛的东西)。它有两个子类:Error和Exception。

Error:用于指示合理的应用程序不应该试图捕获的严重问题。这种情况是很大的问题,大到你不能处理了,所以听之任之就行了,你不用管它。比如说VirtualMachineError:当 Java 虚拟机崩溃或用尽了它继续操作所需的资源时,抛出该错误。好吧,就算这个异常的存在了,那么应该何时,如何处理它呢??交给JVM吧,没有比它更专业的了。

Exception:它指出了合理的应用程序想要捕获的条件。Exception又分为两类:一种是CheckedException,一种是UncheckedException。这两种Exception的区别主要是CheckedException需要用try...catch...显示的捕获,而UncheckedException不需要捕获。通常UncheckedException又叫做RuntimeException。《effective java》指出:对于可恢复的条件使用被检查的异常(CheckedException),对于程序错误(言外之意不可恢复,大错已经酿成)使用运行时异常(RuntimeException)。

我们常见的RuntimeExcepiton有IllegalArgumentException、IllegalStateException、NullPointerException、IndexOutOfBoundsException等等。对于那些CheckedException就不胜枚举了,我们在编写程序过程中try...catch...捕捉的异常都是CheckedException。io包中的IOException及其子类,这些都是CheckedException。

三. 异常的使用

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

例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     4
16     异常出现后
17     */
1 public static void testException2() {
2         int[] ints = new int[] { 1, 2, 3, 4 };
3         System.out.println("异常出现前");
4         System.out.println(ints[4]);
5         System.out.println("我还有幸执行到吗");// 发生异常以后,他后面的代码不能被执行
6     }

首先指出例子中的不足之处,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     }
12
13     public static void printFile(String file) {
14         try {
15             readFile(file);
16         } catch (FileNotFoundException e) {
17             e.printStackTrace();
18         }
19     }
20
21     public static void main(String[] args) {
22         printFile("D:/file");
23     }
24 }

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

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

定义三个异常类:ExceptionA,ExceptionB,ExceptionC

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

异常丢失的情况:

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

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

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

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

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

例4. 清理工作

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

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

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

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

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

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

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

及早的关闭资源是一种良好的行为,因为时间越长你忘记关闭的可能性越大。这样在配合上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
11     public void readFile() {
12         try {
13             while(reader.readLine()!=null) {
14                 //do some work
15             }
16         } catch (IOException e) {
17             e.printStackTrace();
18         }
19     }
20
21     public void dispose() {
22         try {
23             reader.close();
24         } catch (IOException e) {
25             e.printStackTrace();
26         }
27     }
28 }

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

四. 异常的误用

对于异常的误用着实很常见,上一部分中已经列举了几个,大家仔细的看一下。下面再说两个其他的。

例1. 用一个Exception来捕捉所有的异常,颇有"一夫当关万夫莫开"的气魄。不过这也是最傻的行为。

 1 public void readFile(String file) {
 2         BufferedReader reader = null;
 3         Connection conn = null;
 4         try {
 5             reader = new BufferedReader(new InputStreamReader(
 6                     new FileInputStream(file)));
 7             // do some other work
 8
 9             conn = DriverManager.getConnection("");
10             //...
11         } catch (Exception e) {
12             e.printStackTrace();
13         } finally {
14             try {
15                 reader.close();
16                 conn.close();
17             } catch (Exception e) {
18                 e.printStackTrace();
19             }
20         }
21     }

  

从异常角度来说这样严格的程序确实是万无一失,所有的异常都能捕获。但是站在编程人员的角度,万一这个程序出错了我们该如何分辨是到底是那引起的呢,IO还是JDBC...所以,这种写法很值得当做一个反例。大家不要以为这种做法很幼稚,傻子才会做。我在公司实习时确实看见了类似的情况:只不过是人家没有用Exception而是用了Throwable。

例2. 这里就不举例子了,上面的程序都是反例。异常是程序处理意外情况的机制,当程序发生意外时,我们需要尽可能多的得到意外的信息,包括发生的位置,描述,原因等等。这些都是我们解决问题的线索。但是上面的例子都只是简单的printStackTrace()。如果我们自己写代码,就要尽可能多的对这个异常进行描述。比如说为什么会出现这个异常,什么情况下会发生这个异常。如果传入方法的参数不正确,告知什么样的参数是合法的参数,或者给出一个sample。

例3. 将try block写的简短,不要所有的东西都扔在这里,我们尽可能的分析出到底哪几行程序可能出现异常,只是对可能出现异常的代码进行try。尽量为每一个异常写一个try...catch,避免异常丢失。在IO操作中,一个IOException也具有"一夫当关万夫莫开"的气魄。

五.总结

总结非常简单,不要为了使用异常而使用异常。异常是程序设计的一部分,对它的设计也要考究点。

浅谈java异常[Exception],布布扣,bubuko.com

时间: 2024-10-05 20:36:13

浅谈java异常[Exception]的相关文章

浅谈Java异常

关于异常,本质上是某种意义程度上的程序错误,包括程序逻辑错误和系统错误. 简单看下异常类的层次结构: 简单的异常分类: RuntimeException: CheckedException: 对于CheckedException就不胜枚举了,我们在编写程序过程中try...catch...捕捉的异常都是CheckedException.io包中的IOException及其子类,这些都是CheckedException. Throw和Throws Throws出现在方法的声明中,表示该方法可能会抛

浅谈Java throw, throws, try catch异常处理

1.throws关键字通常被应用在声明方法时,用来指定可能抛出的异常.多个异常可以使用逗号隔开.当在主函数中调用该方法时,如果发生异常,就会将异常抛给指定异常对象.如下面例子所示: public class Shoot {   创建类 static void pop() throws NegativeArraySizeException { //定义方法并抛出NegativeArraySizeException异常 int [] arr = new int[-3];//创建数组 } public

浅谈Java语言中try{}catch{}和finally{}的执行顺序问题

浅谈Java语言中try{}catch{}和finally{}的执行顺序问题 2019-04-06  PM  13:41:46  1. 不管有没有出现异常,finally块中代码都会执行: 2. 当try和catch中有return时,finally仍然会执行: 3. finally是在return后面的表达式运算后执行的(此时并没有返回运算后的值,而是先把要返回的值保存起来,管finally中的代码怎么样,返回的值都不会改变,任然是之前保存的值),所以函数返回值是在finally执行前确定的:

浅谈Java中的深拷贝和浅拷贝

浅谈Java中的深拷贝和浅拷贝(转载) 原文链接: http://blog.csdn.net/tounaobun/article/details/8491392 假如说你想复制一个简单变量.很简单: [java] view plaincopyprint? int apples = 5; int pears = apples; int apples = 5; int pears = apples; 不仅仅是int类型,其它七种原始数据类型(boolean,char,byte,short,float

!! 浅谈Java学习方法和后期面试技巧

浅谈Java学习方法和后期面试技巧 昨天查看3303回复33 部落用户大酋长 下面简单列举一下大家学习java的一个系统知识点的一些介绍 一.java基础部分:java基础的时候,有些知识点是非常重要的,比如循环系列.For,while,do-while.这方面只要大家用心点基本没什么难点. 二.面向对象:oop面向对象的时候,偏重理论,相信这方面的文章也很多,大家可以多看看,在这就不说了.重点掌握面向对象的三大特征和基本原理. 三.java核心一:这方面主要偏重API,所以在学习了这章的时候,

浅谈Java锁

每当遇到Java面试,"锁"是个必然会被提到的东西.那么,在面试中,谈"锁"都会谈论些什么呢,诸位看官又是否对"锁"有足够的了解? 本文旨在剖析锁的底层原理,以及锁的应用场景. 一.Synchronized 1.一道面试题 同一个对象在A.B两个线程中分别访问该对象的两个同步方法writer和reader,是否会产生互斥? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

浅谈 Java Printing

浅谈 Java  Printing 其实怎么说呢?在写这篇博文之前,我对java printing 可以说是一无所知的.以至于我在敲文字时, 基本上是看着api文档翻译过来的.这虽然看起来非常的吃力,但是我相信,有道大哥不会辜负我的.嘻 嘻! Java Printing 技术,也就是我们平时所接触的打印,只不过是说可以用Java实现而已. 一.Java Printing 打印简介 Java Printing API能够使java应用程序实现相关的打印功能,如: 1.打印所有 Java 2D 和

【转】浅谈Java中的equals和==

浅谈Java中的equals和== 在初学Java时,可能会经常碰到下面的代码: 1 String str1 = new String("hello"); 2 String str2 = new String("hello"); 3 4 System.out.println(str1==str2); 5 System.out.println(str1.equals(str2)); 为什么第4行和第5行的输出结果不一样?==和equals方法之间的区别是什么?如果在初

浅谈Java虚拟机

最近发现MDT推出去的系统的有不同问题,其问题就不说了,主要是策略权限被域继承了.比如我们手动安装的很多东东都是未配置壮态,推的就默认为安全壮态了,今天细找了一下,原来把这个关了就可以了. 浅谈Java虚拟机,布布扣,bubuko.com