`
MauerSu
  • 浏览: 513384 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

浅谈java异常[Exception]

 
阅读更多
源:http://www.cnblogs.com/focusj/archive/2011/12/26/2301524.html
评:
一. 异常的定义

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

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


public class Calculator {
    public int devide(int num1, int num2) {
        //判断除数是否为0
        if(num2 == 0) {
            throw new IllegalArgumentException("除数不能为零");
        }
        
        return num1/num2;
    }
}



看一下这个类中关于除运算的方法,如果你是新手你可能会直接返回计算结果,根本不去考虑什么参数是否正确,是否合法(当然可以原谅,谁都是这样过来的)。但是我们应尽可能的考虑周全,把可能导致程序失败的"苗头"扼杀在摇篮中,所以进行参数的合法性检查就很有必要了。其中执行参数检查抛出来的那个参数非法异常,这就属于这个方法的不正常情况。正常情况下我们会正确的使用计算器,但是不排除粗心大意把除数赋值为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. 这个例子主要通过两个方法对比来演示一下有了异常以后代码的执行流程。


public static void testException1() {
        int[] ints = new int[] { 1, 2, 3, 4 };
        System.out.println("异常出现前");
        try {
            System.out.println(ints[4]);
            System.out.println("我还有幸执行到吗");// 发生异常以后,后面的代码不能被执行
        } catch (IndexOutOfBoundsException e) {
            System.out.println("数组越界错误");
        }
        System.out.println("异常出现后");
    }
    /*output:
    异常出现前
    数组越界错误
    4
    异常出现后
    */



  
public static void testException2() {
        int[] ints = new int[] { 1, 2, 3, 4 };
        System.out.println("异常出现前");
        System.out.println(ints[4]);
        System.out.println("我还有幸执行到吗");// 发生异常以后,他后面的代码不能被执行
    }

  

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

例2. 重新抛出异常


public class Rethrow {
    public static void readFile(String file) throws FileNotFoundException {
        try {
            BufferedInputStream in = new BufferedInputStream(new FileInputStream(file));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            System.err.println("不知道如何处理该异常或者根本不想处理它,但是不做处理又不合适,这是重新抛出异常交给上一级处理");
            //重新抛出异常
            throw e;
        }
    }
    
    public static void printFile(String file) {
        try {
            readFile(file);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
    
    public static void main(String[] args) {
        printFile("D:/file");
    }
}



  

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



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

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


public class ExceptionA extends Exception {
    public ExceptionA(String str) {
        super();
    }
}

public class ExceptionB extends ExceptionA {

    public ExceptionB(String str) {
        super(str);
    }
}

public class ExceptionC extends ExceptionA {
    public ExceptionC(String str) {
        super(str);
    }
}

异常丢失的情况:


public class NeverCaught {
    static void f() throws ExceptionB{
        throw new ExceptionB("exception b");
    }

    static void g() throws ExceptionC {
        try {
            f();
        } catch (ExceptionB e) {
            ExceptionC c = new ExceptionC("exception a");
            throw c;
        }
    }

    public static void main(String[] args) {
            try {
                g();
            } catch (ExceptionC e) {
                e.printStackTrace();
            }
    }

}
/*
exception.ExceptionC
at exception.NeverCaught.g(NeverCaught.java:12)
at exception.NeverCaught.main(NeverCaught.java:19)
*/

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

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


public class NeverCaught {
    static void f() throws ExceptionB{
        throw new ExceptionB("exception b");
    }

    static void g() throws ExceptionC {
        try {
            f();
        } catch (ExceptionB e) {
            ExceptionC c = new ExceptionC("exception a");
            //异常连
            c.initCause(e);
            throw c;
        }
    }

    public static void main(String[] args) {
            try {
                g();
            } catch (ExceptionC e) {
                e.printStackTrace();
            }
    }

}
/*
exception.ExceptionC
at exception.NeverCaught.g(NeverCaught.java:12)
at exception.NeverCaught.main(NeverCaught.java:21)
Caused by: exception.ExceptionB
at exception.NeverCaught.f(NeverCaught.java:5)
at exception.NeverCaught.g(NeverCaught.java:10)
... 1 more
*/

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

例4. 清理工作

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


public void readFile(String file) {
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new InputStreamReader(
                    new FileInputStream(file)));
            // do some other work
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
            try {
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

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

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


public void readFile() {
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new InputStreamReader(
                    new FileInputStream("file")));
            // do some other work
        
            //close reader
            reader.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }



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


public void readFile() {
        BufferedReader reader = null;
        try {
            try {
                reader = new BufferedReader(new InputStreamReader(
                        new FileInputStream("file")));
                // do some other work

                // close reader
            } finally {
                reader.close();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

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

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


public class ResourceInConstructor {
    BufferedReader reader = null;
    public ResourceInConstructor() {
        try {
            reader = new BufferedReader(new InputStreamReader(new FileInputStream("")));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
    
    public void readFile() {
        try {
            while(reader.readLine()!=null) {
                //do some work
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    public void dispose() {
        try {
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

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



四. 异常的误用

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

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


public void readFile(String file) {
        BufferedReader reader = null;
        Connection conn = null;
        try {
            reader = new BufferedReader(new InputStreamReader(
                    new FileInputStream(file)));
            // do some other work
            
            conn = DriverManager.getConnection("");
            //...
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                reader.close();
                conn.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }



  

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

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

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

五.总结

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

相关推荐

    浅谈Java异常.docx

    本文将深入探讨Java异常处理的基本概念、分类以及最佳实践。 异常的好处在于它们将错误处理代码与正常逻辑分离,使主程序更加清晰和简洁。通过使用`try-catch`块,我们可以将可能会抛出异常的代码包裹起来,而将...

    浅谈Java异常.pdf

    Java异常处理机制是Java编程语言的一个重要组成部分,它提供了处理运行时错误的标准方法。在Java中,异常是指程序在执行过程中发生的一些不寻常的事件,它会打断正常的程序流程。Java提供了丰富的异常类层次结构,以...

    论文研究-基于Java异常处理机制的研究 .pdf

    Java异常处理机制研究的知识点涵盖了异常处理的基本概念、分类、原则以及实际应用等方面。 1. 异常处理概念 异常处理是Java语言中用于处理程序运行时遇到的错误和异常情况的一种机制。它通过异常类的层次结构来实现...

    浅谈Java自定义异常在教学中的教与学

    自定义异常是Java异常处理机制中的一个重要组成部分,其在教学和实际软件开发中的重要性不容忽视。 首先,了解自定义异常出现的背景是必要的。Java的标准异常库为开发者提供了丰富的异常类,例如空指针异常、数组...

    浅谈java异常处理(父子异常的处理)

    Java异常处理是编程过程中必不可少的部分,用于处理程序运行时可能出现的问题。Java的异常处理机制基于五个关键字:try、catch、finally、throw和throws。本文将深入探讨Java中关于父子异常处理的一些关键概念。 1....

    浅谈基于Java的异常处理机制.pdf

    Java异常处理机制是Java语言中一个非常重要的特性,它能够有效地提高程序的健壮性和安全性。异常处理机制允许程序在运行时发生错误或者意外情况时,能够给出明确的错误信息,并根据异常的类型进行相应的处理,而不是...

    浅谈Java异常的Exception e中的egetMessage()和toString()方法的区别

    在Java编程语言中,异常处理是程序健壮性的重要组成部分。当程序运行过程中遇到错误或非正常情况时,Java会抛出异常。处理这些异常时,我们常常会使用`Exception`类及其子类来捕获并打印异常信息。在异常处理中,有...

    浅谈java异常链与异常丢失

    Java异常处理是编程中至关重要的一个环节,它帮助开发者在程序出错时捕获和处理错误,保持程序的稳定性和可维护性。本文主要探讨两个关键概念:异常链和异常丢失,以及它们在实际编程中的应用。 异常链是Java提供的...

    浅谈java异常处理之空指针异常

    Java异常处理是编程中至关重要的一个环节,特别是对于空指针异常(NullPointerException),它是Java开发者在实际编程中经常遇到的问题。本文将深入探讨Java异常处理机制,并重点解析空指针异常的原因、解决方法以及...

    浅谈为什么java命令运行class文件出现异常共7页.p

    这里我们将深入探讨可能导致“java命令运行class文件出现异常”的几个关键因素。 首先,我们关注的异常与"MyDictProcess.class"有关,这表明问题可能出现在这个特定的类文件上。在Java中,类文件是编译后Java源代码...

    浅谈Java内部类的四个应用场景

    } catch (Exception e) { // 处理异常 return false; } } // 实现其他接口方法... private class PoolConn { // 内部类定义 private Connection conn; // 数据库连接 private boolean isUse; // 是否正在...

    浅谈java中异常抛出后代码是否会继续执行

    在Java编程语言中,异常处理是一个关键的概念,用于处理程序执行期间可能出现的错误或异常情况。异常(Exception)是程序运行时发生的不正常情况,它中断了正常的代码流程。那么,当Java中抛出异常后,代码是否会...

    浅谈MQTT监听设备上线与离线方法

    public void messageArrived(String topic, MqttMessage message) throws Exception { if ("device/status/on".equals(topic)) { System.out.println("设备上线: " + new String(message.getPayload())); } } ...

    浅谈十个常见的Java异常出现原因

    Java异常处理是编程过程中不可或缺的一部分,它帮助我们识别和修复程序中的错误。下面将详细讨论十个常见的Java异常及其出现原因: 1. **NullPointerException**: 当尝试访问或操作一个null对象的属性或方法时,会...

    浅谈java中BigDecimal类的简单用法

    此外,`divide` 方法默认可能抛出 `ArithmeticException`,因为在除不尽的情况下没有指定舍入模式。通常,我们需要指定一个舍入模式,如 `RoundingMode.HALF_UP` (四舍五入)以避免异常。 总的来说,`BigDecimal` ...

    浅谈java运用注解实现对类中的方法检测的工具

    Java 注解实现类方法检测工具 Java 注解是 Java 语言的一种特殊的注释方式,它可以在编译期或运行期对代码进行检查和处理。在本文中,我们将介绍如何使用 Java 注解来实现对类中的方法检测工具。 首先,我们需要...

    浅谈java web中常用对象对应的实例化接口

    8. **Exception对象**:Exception对象是`java.lang.Throwable`的实例,通常在处理JSP错误页面时出现。当JSP页面抛出异常时,这个对象包含了具体的异常信息,可以通过它来追踪和处理错误。 9. **Page对象**:Page...

    浅谈异常结构图、编译期异常和运行期异常的区别

    在Java中,如果一个方法声明抛出一个运行时期异常(如`RuntimeException`),调用该方法的代码可以选择不处理这个异常,因为运行时异常通常表示程序逻辑错误,如除以零。相反,如果方法声明抛出编译时期异常(非`...

    浅谈RxJava处理业务异常的几种方式

    本文介绍了RxJava处理业务异常的几种...除了RuntimeException及其子类以外,其他的Exception类及其子类都属于检查性异常。检查性异常必须被显式地捕获或者传递。当程序中可能出现检查性异常时,要么使用try-catch语句

    浅谈python抛出异常、自定义异常, 传递异常

    Python中的raise 关键字用于引发一个异常,基本上和C#和Java中的throw关键字相同,如下所示: import traceback def throw_error(): raise Exception("抛出一个异常")#异常被抛出,print函数无法执行 print("飞天...

Global site tag (gtag.js) - Google Analytics