银行卡掉了,手机也掉了,但有一笔微信转帐到别人银行卡正在退还中。现在补了新卡,换了卡号,钱还能到帐吗

java异常处理(初级) - 上善若水任方圆 - ITeye技术网站
博客分类:
从学习到现在从事java开发一年多了,个人觉得对java只了解皮毛,很多东西都是用到再去慢慢学习,编程真的是一项艺术,要完成一段好的代码,需要懂得很多。
最近项目经理让我负责一个组件开发,框架都由自己搭建,最让我头疼的是异常处理,我看了一些网上的源码,发现他们对异常的处理不是很重视,研究了很久都没有找到很好的解决方案。后来有幸看到一个200W美元的项目部分源码,通过他们对异常处理的解决方案,我终于弄通了java异常的一些基本机制,在这与大家分享一下。
(一)说到异常,首先从Throwable开始,Throwable是所有异常的基类,旗下有Error和Exception两个子类:Error属于程序无法控制的错误,通常不推荐捕获,捕获了你也处理不了;Exception就是我们熟知的可控制异常了。Exception旗下又分两种异常,运行时异常(RuntimeException)和非运行时异常:运行时异常是不需要强制捕获的,因为它随时随地可能发生,比如空指针异常(NullPointerException)和数组下标越界(IndexOutOfBoundsException);而非运行时异常在java里是需要你最终捕获的,不然编译都不通过,比如IO异常(IOException)和数据库错误(SqlException)。
下面来个例子:
①运行时异常数组下标越界(IndexOutOfBoundsException):
public class RuntimeDemo {
public static void main(String[] args) {
List&String& lst = new ArrayList&String&();
lst.add("1");
System.out.println(lst.get(10));
上面代码list里只有1条数据,但是我希望获取第11条数据,肯定会报异常的,但是我不需要捕获异常,因为结果是运行是异常,它会自动抛出异常:
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 10, Size: 1
at java.util.ArrayList.RangeCheck(ArrayList.java:547)
at java.util.ArrayList.get(ArrayList.java:322)
at demo.RuntimeDemo.main(RuntimeDemo.java:10)
②非运行时异常,IoException为例:
public class IoDemo {
public static void main(String[] args) {
File f = new File("");
f.createNewFile();
} catch (IOException e) {
e.printStackTrace();
这里要求我必须捕获或者抛出,看结果:
java.io.IOException: 系统找不到指定的路径。
at java.io.WinNTFileSystem.createFileExclusively(Native Method)
at java.io.File.createNewFile(File.java:883)
at demo.IoDemo.main(IoDemo.java:10)
(二)异常的两种处理方式:抛出和捕获。
抛出异常表示当前的错误不在本方法中处理,交由调用当前方法的地方处理;捕获异常表示当前错误在本方法中通过catch捕获进行一些特殊处理。
①首先来看try...catch:
public class ThrowDemo {
public void cathcMethod(){
System.out.println("running cathcMethod...");
File file = new File("");
file.createNewFile();
} catch (IOException e) {
//直接在本方法把异常消化掉
System.out.println("running cathcMethod's catch...");
public static void main(String[] args) {
ThrowDemo td = new ThrowDemo();
td.cathcMethod();
System.out.println("running???????");
这里main函数里会运行cathcMethod方法,而该方法绝对会出现IOException异常,我们要看的是当运行完cathcMethod后,下面一句 System.out.println("running???????")是否会执行,看结果:
running cathcMethod...
running cathcMethod's catch...
running???????
结果是,虽然td.cathcMethod();发生了异常,但是没有影响后面的代码的执行。这是因为当程序运行到cathcMethod方法时会发生IOException,但是cathcMethod方法通过catch直接捕获了异常,等于这个错误被该方法消化了,外面是不知道里面情况的。
这种捕获异常的方法通常是不可取的,因为你莫名其妙把这个重要的一个异常“吃”掉了,很可能会影响到一个系统的正常运作,通常我们会考虑抛出异常。
抛出异常需要说到两个关键字:throw和throws,这个东西口头不好说,直接代码说话:
public class ThrowDemo {
* Function
: 直接抛出异常
* @throws IOException
public void throwsMethod() throws IOException{
System.out.println("running throwsMethod...");
File file = new File("");
file.createNewFile();
public static void main(String[] args) throws IOException {
ThrowDemo td = new ThrowDemo();
td.throwsMethod();
System.out.println("running??????");
throws写在方法后面,表示当前方法可能会出现的异常,一但加上了throws xxxException则该异常在方法体内就不需要用try...catch刻意捕获了(当然你也可以写,后面再说)。这个throws表示什么意思呢?它表示当运行当前方法时,可能会出现一个异常,但是当前方法不做处理,让调用该方法的地方去处理。我们看到main方法也有throws IOException ,这样再往上抛的话就到java虚拟机了,java虚拟机捕获到错误时就会打印错误信息到控制台,我们看结果:
running throwsMethod...
Exception in thread "main" java.io.IOException: 系统找不到指定的路径。
at java.io.WinNTFileSystem.createFileExclusively(Native Method)
at java.io.File.createNewFile(File.java:883)
at demo.ThrowDemo.throwsMethod(ThrowDemo.java:16)
at demo.ThrowDemo.main(ThrowDemo.java:21)
我们看到上面运行了throwsMethod方法,但因为发生了异常终止了main方法里System.out.println("running??????");的执行,这是因为当运行到某个方法,该方法发生异常时候,如果该方法是throws了异常的,则该程序就只执行到发生异常的方法,然后把异常再往上处理。当然如果你在main函数里像厦门这样写,结果又不一样了:
public static void main(String[] args) {
ThrowDemo td = new ThrowDemo();
td.throwsMethod();
} catch (IOException e) {
System.out.println("running error...");
System.out.println("running??????");
(三)回头说说我正在开发的组件,后台三层Dao层(数据库操作)、Service层(业务操作)、Action层(实现页面交互),代码运行顺序依次是dao-&service-&action,action采用的是struts2框架,我的设计思想是dao和service对异常进行封装,到action层通过拦截器截获异常进行处理。为了明确发生了什么异常,个人建议使用自定义异常,通过自定义异常来处理各种错误。
首先看自定义异常代码:
public class SystemException extends RuntimeException{
private static final long serialVersionUID = 1L;
String errorCode = "default";
public SystemException(){
public SystemException(Throwable e){
public SystemException(String message){
super(message);
public SystemException(String message,Throwable e){
super(message,e);
public SystemException(String errorCode,String message){
super(message);
this.errorCode = errorC
* 最常用的构造方法
* @param errorCode 错误ID
* @param message
* @param e
public SystemException(String errorCode,String message,Throwable e){
super(message,e);
this.errorCode = errorC
public String getErrorCode() {
return errorC
public void setErrorCode(String errorCode) {
this.errorCode = errorC
首先介绍几点我这个自定义异常:
①为什么继承RuntimeException: 因为RuntimeException是运行时异常,是不需要被捕获或强行抛出的,如果自定义异常继承Exception,后面方法都得捕获或throws,很麻烦。
②errorCode的作用:错误编号,这个是关键,因为系统可能会有很多种异常,我们可以给每种异常定义一个错误编号,然后在action层通过拦截器捕获自定义异常的errorCode,就可以知道是什么错误了。
再说Dao层使用的是spring封装过的类,spring把所有非运行时异常全部转换成了运行时异常。为了保证用户能明白执行什么,我会对每个方法捕获RuntimeException然后转换成自定义异常:
public class BaseDaoImpl extends HibernateDaoSupport implements BaseDao {
public Object getObject(Class clazz, Serializable id)
throws RuntimeException {
Object o =
o = getHibernateTemplate().get(clazz, id);
} catch (RuntimeException e) {
throw new SystemException("query_error","Sql exceptioin : query data error!",e);;
其它层可能涉及到非运行时异常,比如action层ajax,后台可能会打印信息到页面,这时可能会有IOException:
out = response.getWriter();
} catch (IOException e) {
throw new SystemException("io_error","response.getWriter() error!",e);
未完待续...
白糖_ 写道linlin1987 写道
好厉害!你好,认真学习了你这篇文章,有一个疑惑请教一下,看你的回复,你相当于把errorCode处理成了一个映射表,对吧?就是前面是错误编号,后面对应的是错误信息,是这样么?然后,errorCode为什么不做成枚举类?能不能看一下你的errorCode代码?谢谢啦!!!!!errorCode完全可以做成枚举errorCode的作用是让开发人员立刻理解错误的大致方向,然后好迅速定位排errorCode 其实有一个问题,就是增加了开发人员的工作量。需要开发人员一边写程序,旁边还要放一个错误映射表,当他要抛出异常的时候,来查表确定异常对应的errorCode值,而且如果没有查到对应的errorCode值,还需要自行添加?errorCode用不用取决于开发人员自觉性,如果我throw new SystemException(e);就没有errorCode。但是如果每个开发人员都谨慎抛出异常,那么errorCode就能很好运作,这个的确是需要一定工作量,但是一旦你做好了就一劳永逸的事。至于你说的自行添加,肯定是多个开发人员一起往里添内容啦
linlin1987 写道
好厉害!你好,认真学习了你这篇文章,有一个疑惑请教一下,看你的回复,你相当于把errorCode处理成了一个映射表,对吧?就是前面是错误编号,后面对应的是错误信息,是这样么?然后,errorCode为什么不做成枚举类?能不能看一下你的errorCode代码?谢谢啦!!!!!errorCode完全可以做成枚举errorCode的作用是让开发人员立刻理解错误的大致方向,然后好迅速定位排errorCode 其实有一个问题,就是增加了开发人员的工作量。需要开发人员一边写程序,旁边还要放一个错误映射表,当他要抛出异常的时候,来查表确定异常对应的errorCode值,而且如果没有查到对应的errorCode值,还需要自行添加?
好厉害!你好,认真学习了你这篇文章,有一个疑惑请教一下,看你的回复,你相当于把errorCode处理成了一个映射表,对吧?就是前面是错误编号,后面对应的是错误信息,是这样么?然后,errorCode为什么不做成枚举类?能不能看一下你的errorCode代码?谢谢啦!!!!!errorCode完全可以做成枚举errorCode的作用是让开发人员立刻理解错误的大致方向,然后好迅速定位排
public class BaseDaoImpl extends HibernateDaoSupport implements BaseDao {
public Object getObject(Class clazz, Serializable id)
throws RuntimeException {
Object o =
o = getHibernateTemplate().get(clazz, id);
} catch (RuntimeException e) {
throw new SystemException("query_error","Sql exceptioin : query data error!",e);;
为什么要申明抛出RumtimeException?
其次为什么在捕获RumtimeException再重新抛出你自定义的RumtimeException后还要让代码继续执行下去?,如果我在你所谓的action层上这样调用你的代码:
Object obj = dao.getObject(xxx.class,id);
我是不是还要:
if(null!=obj)
//do something else
再者,为何要定义那些所谓的error_code,如果我维护你的代码,再得到异常后,我是不是要进去你的代码,看一下error_code的定义?异常的语义本身就很丰富,为什么还要定义什么error_code?
毋庸置疑,维护你所写的代码将会是艰辛的事。
①为什么要申明抛出RuntimeException?
因为spring在封装hibernate的时候已经将所有jdbc的非运行时异常(如SQLException)转换成自定义的运行时异常(RuntimeException),所以用RuntimeException捕获已经足够
②还要不要if(null!=obj)//do something else
答案是:不用,如果在上面发生了异常以后,异常会一直向上抛,直到某个地方在此try...catch捕获它,前面我已经说过:代码运行顺序依次是dao-&service-&action,action采用的是struts2框架,如果dao层抛出了RuntimeException,到action层通过拦截器截获异常进行处理即可
③为什么要定义error_code?
正是为了代码维护,这个error_code是为了给用户看的,我把所有的error_code按照编辑依次写到一个文件中,当发生异常的时候,拦截器获取error_code编号再去文件中找对应的错误信息,然后把文件中已经写好的错误信息返回给用户。比如在文件中定义一个编号为error_code001,错误消息为:该条数据已被删除。最后用户在页面上就能看到的信息是:该条数据已被删除。
异常的语义确实太丰富了,所以我们程序员要尽量去分析不同情况的异常,最后转换成用户能看懂的错误消息(总不能让用户看到:java.lang.NullPointerException
这样的信息吧)
浏览: 2087370 次
来自: 成都
博主 你好,请教个问题 公司产品之前有个日期表月视图,版本是1 ...
好像有道理!!
super.printJsMsg(response, sb.t ...
受教了!分享一个Dubbo分布式架构项目实战参考内容:http ...本帖子已过去太久远了,不再提供回复功能。1860人阅读
(2)core_java_basic(78)
【0】README
0.1) 本文描述+源代码均 转自 core java volume 1, 旨在理解 java异常——异常分类+声明已检查异常+如何抛出异常+自定义异常类 的相关知识;
0.2)异常处理的任务: 就是将控制权从错误产生的地方转移给能够处理这种case 的错误处理器;
0.3)为了能够在程序中处理异常情况, 必须研究程序中可能会出现的错误和问题,以及哪类问题需要关注:
0.3.1)用户输入错误:用户输入 URL , 而其语法却不正确;
0.3.2)设备错误:硬件不总是让它做什么,它就做什么, 比如打印机关掉了;
0.3.3)物理限制:磁盘满了,可用存储空间已被用完;
0.3.4)代码错误:程序方法有可能无法正确执行;
【1】异常分类
1.1)java中, 异常都是派生于 Throwable 类的一个实例: 且如果java内置的异常类不能够满足需求,用户可以自己创建异常类;(下图显示了 java异常层次结构的简化图)
对上图的分析(Analysis)
A1)Error类层次结构:描述了java运行时系统的内部错误和资源耗尽错误, 这种case 很少出现;
A2)Exception层次结构:划分成两个分支的规则是:
由程序错误导致的异常属于 RuntimeException, 而程序本身没有问题, 但由于像 IO错误这类问题导致的异常属于其他异常;
1.2)派生于RuntimeException 的异常包含下面几种 Cases(运行时异常的几种 cases), 这属于未检查类异常(unchecked):
C1)错误的类型转换;
C2)数组访问越界;
C3)访问空指针;
1.3)不是派生于 RuntimeException 的异常包括:
C1)试图在文件尾部后面读取数据;
C2)试图打开一个不存在的文件;
C3)试图根据给定的字符串查找class 对象, 而这个字符串表示的类并不存在;
Attention)
A1)如果出现 RuntimeException 异常, 那么就一定是你的问题了, 这是一条相当有道理的规则;
A2)应该通过检测数组下标是否越界来避免 ArrayIndexOutofBoundException 异常;应该通过在变量使用前检测是否为空来杜绝 NullPointerException 异常的发生;
1.4)异常分类:
1.4.1)未检查异常(不需要throw 抛出的异常,这是程序运行过程中出现的错误): java语言规范 派生于 Error类 或 RuntimeException类的所有异常称为 未检查异常(unchecked);
1.4.2)已检查异常(需要显式抛出的异常):所有其他的异常称为 已检查异常(checked);
【2】声明已检查异常
2.1)如果遇到了无法处理的case , java抛出一个异常, 这个道理很简单: 一个方法不仅需要告诉编译器将要返回什么值, 还要告诉编译器有可能发生什么错误;例如, 一段读取文件 的代码知道有可能读取的文件不存在, 或者内容为null , 因此, 试图处理文件信息的代码就需要通知编译器可能会抛出 IOException 类的异常;
2.2)方法应该在其首部声明所有可能抛出的 异常, 这样可以从首部反映出这个方法可能抛出哪类已检查异常:如,
public FileInputStream(String name) throws FileNotFoundException
, 如果上述代码真的抛出了异常, 那么运行时系统就会开始搜索异常处理器, 以便知道如何处理 FileNotFoundException 对象;
2.3)在自己编写方法时, 不必将所有 可能抛出的异常全部都进行声明。 至于什么时候需要在方法中用 throws 子句声明异常, 什么异常必须使用 throws 子句声明, 需要记住下面遇到的 4种 Cases 应该抛出异常:
C1)调用一个抛出已检查异常的方法, 例如, FileInputStream 构造器;
C2)程序运行过程中发现错误, 并且利用 throw 语句抛出一个已检查异常;
C3)程序出现错误, 例如, a[-1]=0 会抛出一个 ArrayIndexOutOfBoundsException
这样的未检查异常;
C4) java 虚拟机和运行时库出现的内部错误;
2.3.1)如果出现前两种cases 之一, 则必须告诉调用这个方法的程序员有可能抛出异常。因为任何一个抛出异常的方法都有可能是一个死亡陷阱。如果没有处理器捕获这个异常, 当前执行的线程就会结束;
2.3.2)对于那些可能被其他人使用的 java方法, 应该根据异常规范, 在方法的首部声明这个方法可能抛出的异常;
class MyClass
public Image loadImage(String s) throws IOException
2.3.3)如果一个方法有可能抛出多个已检查异常, 那么就必须在方法的首部列出所有的异常类。每个异常类之间用逗号隔开:
class MyClass
public Image loadImage(String s) throws FileNotFoundException, EOFException
2.3.4)但是,不需要 声明java 的内部错误, 即从Error 继承的错误。因为任何程序代码都具有抛出那些异常的潜能, 而我们对其没有任何控制能力;
2.3.5)同样, 也不应该声明从 RuntimeException 继承的那些未检查异常:(因为, 这些错误完全在我们的控制之下, 如果特别关注数组下标引发的错误, 就应该将更多的时间花在 修正程序的错误上, 而不是说明这些错误发生的可能性上)
//bad style 错误的异常抛出格式(RuntimeException属于未检查类异常,是不需要throw 抛出的异常,这是程序运行过程中出现的错误)
class MyClass
public Image loadImage(String s) throws ArrayIndexOutOfBoundsException //bad style 错误的异常抛出格式
Conclusion)
C1)总之, 一个方法必须 声明 所有可能抛出的已检查异常, 而未检查异常要么不可控制(Error), 要么就应该避免发生(RuntimeException)。 如果方法没有声明所有可能发生的已检查异常, 编译器就会给出一个错误消息;
C2)当然,除了声明异常之外, 还可以捕获异常, 这样会使得异常不被抛出到方法之外,也不需要 throws 规范;(稍后, 我们来讨论如何决定一个异常是被捕获, 还是被抛出让其他的处理器进行处理)
W1)如果在子类中覆盖了超类中的一个方法, 子类方法中声明的已检查异常不能比超类方法中声明的异常更通用(也就是说, 子类方法中可以抛出更特定的异常, 或者根本不抛出任何异常)
W2)特别需要说明的是: 如果超类方法没有抛出任何已检查异常, 子类也不能抛出任何已检查异常;
2.3.6)如果类中的一个方法声明将会抛出一个异常, 而这个异常是某个特定类的实例时, 则这个方法就有可能抛出一个这个类的异常, 或者这个类的任意一个子类的异常;看个荔枝:
例如: FileInputStream 构造器声明将有可能抛出一个 IOException异常,然而并不知道具体是哪种IOException异常, 它既可能是 IOException异常,也可能是其子类的异常,例如, FileNotFoundException;
【3】如何抛出异常
3.1)首先要决定抛出什么类型异常 。如, EOFException异常描述为“在输入过程中, 遇到了一个未预期的EOF后 的信号”;
3.2)下面抛出这个异常的语句:
throw new EOFException();
EOFException e = new EOFException();
3.2.1)EOFException类还有一个字符串参数的构造器。这个构造器更加详细的描述异常出现的情况:
String gripe = "content-length" + len + ", received: " +
throw new EOFException(gripe);
3.2.2)在以下cases 下, 一旦方法抛出异常, 这个方法就不可能返回到调用者了;
case1)找到一个合适的异常类;
case2)创建这个类的一个对象;
case3)将对象抛出;
【4】创建异常类(自定义异常类)
4.1)我们需要做的只是:定义一个派生于Exception的类, 或者派生于Exception子类的类;
4.2)定义的类应该包含两个构造器: 一个是默认的构造器, 另一个是带有详细描述信息的构造器(超类Throwable 的toString 方法将会打印出这些详细信息, 在调试中非常有用)
class FileFormatException extends IOException
public FileFormatException() {}
public FileFormatException(String gripe) //2、带有详细描述信息的构造器
super(gripe);
4.2.1)现在,就可以抛出自定义的异常类型了:
String readData(BufferedReader in) throws FileFormatException
throw new FileFormatException();
[API] java.lang.Throwable 1.0
Throwable() :构造一个新的 Throwable 对象, 这个对象没有详细的描述信息;
Throwable(String msg):构造一个新的 Throwable 对象, 这个对象包含详细的描述信息;习惯上, 所有派生的异常类都支持一个默认构造器和一个带有描述信息的构造器;
String getMessage(): 获得Throwable 对象 的详细描述信息;
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:420775次
积分:6905
积分:6905
排名:第3068名
原创:243篇
转载:222篇
评论:56条
(1)(3)(10)(8)(9)(10)(9)(9)(22)(37)(29)(24)(30)(41)(42)(53)(54)(34)(17)(11)(7)(8)本文讲的是Java有效处理异常的三个原则_java,
异常之所以是一种强大的调试手段,在于其回答了以下三个问题:
1、什么出了错?
2、在哪出的错?
3、为什么出错?
在有效使用异常的情况下,异常类型回答了“什么”被抛出,异常堆栈跟踪回答了“在哪“抛出,异常信息
异常之所以是一种强大的调试手段,在于其回答了以下三个问题:
1、什么出了错?
2、在哪出的错?
3、为什么出错?
在有效使用异常的情况下,异常类型回答了“什么”被抛出,异常堆栈跟踪回答了“在哪“抛出,异常信息回答了“为什么“会抛出,如果你的异常没有回答以上全部问题,那么可能你没有很好地使用它们。
有三个原则可以帮助你在调试过程中最大限度地使用好异常,这三个原则是:
1、具体明确
2、提早抛出
3、延迟捕获
为了阐述有效异常处理的这三个原则,本文通过杜撰个人财务管理器类JCheckbook进行讨论,JCheckbook用于记录及追踪诸如存取款,票据开具之类的银行账户活动。
Java定义了一个异常类的层次结构,其以Throwable开始,扩展出Error和Exception,而Exception又扩展出RuntimeException.如图1所示.
这四个类是泛化的,并不提供多少出错信息,虽然实例化这几个类是语法上合法的(如:new Throwable()),但是最好还是把它们当虚基类看,使用它们更加特化的子类。Java已经提供了大量异常子类,如需更加具体,你也可以定义自己的异常类。
例 如:java.io package包中定义了Exception类的子类IOException,更加特化确的是 FileNotFoundException,EOFException和ObjectStreamException这些IOException的子 类。每一种都描述了一类特定的I/O错误:分别是文件丢失,异常文件结尾和错误的序列化对象流.异常越具体,我们的程序就能更好地回答”什么出了错”这个 问题。
捕 获异常时尽量明确也很重要。例如:JCheckbook可以通过重新询问用户文件名来处理FileNotFoundException,对于 EOFException,它可以根据异常抛出前读取的信息继续运行。如果抛出的是ObjectStreamException,则程序应该提示用户文件 已损坏,应当使用备份文件或者其他文件。
Java让明确捕获异常变得容易,因为我们可以对同一try块定义多个catch块,从而对每种异常分别进行恰当的处理。
File prefsFile = new File(prefsFilename);
readPreferences(prefsFile);
catch (FileNotFoundException e){
// alert the user that the specified file
// does not exist
catch (EOFException e){
// alert the user that the end of the file
// was reached
catch (ObjectStreamException e){
// alert the user that the file is corrupted
catch (IOException e){
// alert the user that some other I/O
// error occurred
JCheckbook 通过使用多个catch块来给用户提供捕获到异常的明确信息。举例来说:如果捕获了FileNotFoundException,它可以提示用户指定另一 个文件,某些情况下多个catch块带来的额外编码工作量可能是非必要的负担,但在这个例子中,额外的代码的确帮助程序提供了对用户更友好的响应。
除前三个catch块处理的异常之外,最后一个catch块在IOException抛出时给用户提供了更泛化的错误信息.这样一来,程序就可以尽可能提供具体的信息,但也有能力处理未预料到的其他异常。
有 时开发人员会捕获范化异常,并显示异常类名称或者打印堆栈信息以求"具体"。千万别这么干!用户看到java.io.EOFException或者堆栈信息 只会头疼而不是获得帮助。应当捕获具体的异常并且用"人话"给用户提示确切的信息。不过,异常堆栈倒是可以在你的日志文件里打印。记住,异常和堆栈信息是用来帮助开发人 员而不是用户的。
最后,应该注意到JCheckbook并没有在readPreferences()中捕获异常,而是将捕获和处理异常留到用户界面层来做,这样就能用对话框或其他方式来通知用户。这被称为"延迟捕获",下文就会谈到。
异常堆栈信息提供了导致异常出现的方法调用链的精确顺序,包括每个方法调用的类名,方法名,代码文件名甚至行数,以此来精确定位异常出现的现场。
java.lang.NullPointerException
at java.io.FileInputStream.open(Native Method)
at java.io.FileInputStream.&init&(FileInputStream.java:103)
at jcheckbook.JCheckbook.readPreferences(JCheckbook.java:225)
at jcheckbook.JCheckbook.startup(JCheckbook.java:116)
at jcheckbook.JCheckbook.&init&(JCheckbook.java:27)
at jcheckbook.JCheckbook.main(JCheckbook.java:318)
以 上展示了FileInputStream类的open()方法抛出NullPointerException的情况。不过注意 FileInputStream.close()是标准Java类库的一部分,很可能导致这个异常的问题原因在于我们的代码本身而不是Java API。所以问题很可能出现在前面的其中一个方法,幸好它也在堆栈信息中打印出来了。
不幸的是,NullPointerException是Java中信息量最少的(却也是最常遭遇且让人崩溃的)异常。它压根不提我们最关心的事情:到底哪里是null。所以我们不得不回退几步去找哪里出了错。
通过逐步回退跟踪堆栈信息并检查代码,我们可以确定错误原因是向readPreferences()传入了一个空文件名参数。既然readPreferences()知道它不能处理空文件名,所以马上检查该条件:
public void readPreferences(String filename)
throws IllegalArgumentException{
if (filename == null){
throw new IllegalArgumentException("filename is null");
//...perform other operations...
InputStream in = new FileInputStream(filename);
//...read the preferences file...
通过提早抛出异常(又称"迅速失败"),异常得以清晰又准确。堆栈信息立即反映出什么出了错(提供了非法参数值),为什么出错(文件名不能为空值),以及哪里出的错(readPreferences()的前部分)。这样我们的堆栈信息就能如实提供:
java.lang.IllegalArgumentException: filename is null
at jcheckbook.JCheckbook.readPreferences(JCheckbook.java:207)
at jcheckbook.JCheckbook.startup(JCheckbook.java:116)
at jcheckbook.JCheckbook.&init&(JCheckbook.java:27)
at jcheckbook.JCheckbook.main(JCheckbook.java:318)
另外,其中包含的异常信息("文件名为空")通过明确回答什么为空这一问题使得异常提供的信息更加丰富,而这一答案是我们之前代码中抛出的NullPointerException所无法提供的。
通过在检测到错误时立刻抛出异常来实现迅速失败,可以有效避免不必要的对象构造或资源占用,比如文件或网络连接。同样,打开这些资源所带来的清理操作也可以省却。
菜鸟和高手都可能犯的一个错是,在程序有能力处理异常之前就捕获它。Java编译器通过要求检查出的异常必须被捕获或抛出而间接助长了这种行为。自然而然的做法就是立即将代码用try块包装起来,并使用catch捕获异常,以免编译器报错。
问 题在于,捕获之后该拿异常怎么办?最不该做的就是什么都不做。空的catch块等于把整个异常丢进黑洞,能够说明何时何处为何出错的所有信息都会永远丢失。把异常写到日志中还稍微好点,至少还有记录可查。但我们总不能指望用户去阅读或者理解日志文件和异常信息。让readPreferences()显示错误信息对话框也不合适,因为虽然JCheckbook目前是桌面应用程序,但我们还计划将它变成基于HTML的Web应用。那样的话,显示错误对话框显然不是个选择。同时,不管HTML还是C/S版本,配置信息都是在服务器上读取的,而错误信息需要显示给Web浏览器或者客户端程序。 readPreferences()应当在设计时将这些未来需求也考虑在内。适当分离用户界面代码和程序逻辑可以提高我们代码的可重用性。
在有条件处理异常之前过早捕获它,通常会导致更严重的错误和其他异常。例如,如果上文的readPreferences()方法在调用FileInputStream构造方法时立即捕获和记录可能抛出的FileNotFoundException,代码会变成下面这样:
public void readPreferences(String filename){
InputStream in =
// DO NOT DO THIS!!!
in = new FileInputStream(filename);
catch (FileNotFoundException e){
logger.log(e);
in.read(...);
上面的代码在完全没有能力从FileNotFoundException中恢复过来的情况下就捕获了它。如果文件无法找到,下面的方法显然无法读取它。如果 readPreferences()被要求读取不存在的文件时会发生什么情况?当然,FileNotFoundException会被记录下来,如果我们 当时去看日志文件的话,就会知道。然而当程序尝试从文件中读取数据时会发生什么?既然文件不存在,变量in就是空的,一个 NullPointerException就会被抛出。
调试程序时,本能告诉我们要看日志最后面的信息。那将会是NullPointerException,非常让人讨厌的是这个异常非常不具体。错误信息不仅误导我们什么出了错(真正的错误是FileNotFoundException而不是NullPointerException),还误导了错误的出处。真正 的问题出在抛出NullPointerException处的数行之外,这之间有可能存在好几次方法的调用和类的销毁。我们的注意力被这条小鱼从真正的错误处吸引了过来,一直到我们往回看日志才能发现问题的源头。
既然readPreferences() 真正应该做的事情不是捕获这些异常,那应该是什么?看起来有点有悖常理,通常最合适的做法其实是什么都不做,不要马上捕获异常。把责任交给 readPreferences()的调用者,让它来研究处理配置文件缺失的恰当方法,它有可能会提示用户指定其他文件,或者使用默认值,实在不行的话也 许警告用户并退出程序。
把异常处理的责任往调用链的上游传递的办法,就是在方法的throws子句声明异常。在声明可能抛出的异常时,注意越具体越好。这用于标识出调用你方法的程序需要知晓并且准备处理的异常类型。例如,“延迟捕获”版本的readPreferences()可能是这样的:
public void readPreferences(String filename)
throws IllegalArgumentException,
FileNotFoundException, IOException{
if (filename == null){
throw new IllegalArgumentException("filename is null");
InputStream in = new FileInputStream(filename);
技 术上来说,我们唯一需要声明的异常是IOException,但我们明确声明了方法可能抛出FileNotFoundException。 IllegalArgumentException不是必须声明的,因为它是非检查性异常(即RuntimeException的子类)。然而声明它是为 了化我们的代码(这些异常也应该在方法的JavaDocs中标注出来)。
当 然,最终你的程序需要捕获异常,否则会意外终止。但这里的技巧是在合适的层面捕获异常,以便你的程序要么可以从异常中有意义地恢复并继续下去,而不导致更 深入的错误;要么能够为用户提供明确的信息,包括引导他们从错误中恢复过来。如果你的方法无法胜任,那么就不要处理异常,把它留到后面捕获和在恰当的层面处理。
经验丰富的开发人员都知道,调试程序的最大难点不在于修复缺陷,而在于从海量的代码中找出缺陷的藏身之处。只要遵循本文的三个原则,就能让你的异常协助你跟踪和消灭缺陷,使你的程序更加健壮,对用户更加友好。以上就是这篇的全部内容,希望能对大家的学习或者工作带来一定的帮助。
以上是云栖社区小编为您精心准备的的内容,在云栖社区的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索java
java异常处理原则
java异常处理机制
java异常处理原则、java异常基本处理原则、异常处理原则、3sigma原则处理异常值、生产异常处理的3原则,以便于您获取更多的相关知识。
为您提供简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本...
RDS是一种稳定可靠、可弹性伸缩的在线数据库服务。支持MySQL、SQL Server、PostgreSQL、高...
云栖社区()为您免费提供相关信息,包括
的信息,还有java异常处理原则、java异常基本处理原则、异常处理原则、3sigma原则处理异常值、生产异常处理的3原则等
,所有相关内容均不代表云栖社区的意见!

我要回帖

更多关于 银行卡最多转帐多少 的文章

 

随机推荐