在可能会出现exception的地方,要使用try-catch或者throws或者两者都要。判断依据是:
如果对可能出现的exception不想被外部(方法的调用者)知道,就在方法内部try-catch掉这个exception;
如果希望外部知道,则在catch到之后把exception直接抛出或者抛出自定义的exception。
一、异常的种类
java异常可以分成两大类:
1、Exception和RuntimeException(虽然RuntimeException是从Exception继承的)。exception异常代表“无法避免的异常” 如io异常,往往这类异常是由于外部原因造成的,程序本身无法保证他们不发生,所以这类异常必须捕获。如果在函数内部无法处理这个异常必须再次抛出(在函数后面用throws语句),如果什么都不做就出现编译错误。
2、runtimexception是指“可以避免的异常”,如 null引用异常,这类异常都是由程序内部原因造成的,是可以避免的。对于这类异常可以忽略他们,但一旦发生程序就会异常终止。这类异常对debug非常有帮助,当然,如果需要也可以catch。
另外,有些地方即使不会有exception,但是从商业逻辑上是错误的、非预期的,也可以抛出user exception。例如,用户输入非法,bank account非法透支等等。
二、主要原则
处理意外的一个重要原则,就是要么处理,要么接着抛,决不能吃掉(You either handle it, or throw it. You don’t eat it.)这就是说,当你捕获一个异常之后,必须决定是否立即处理这个异常,或者继续抛出这个异常(或者另一个自定义异常),以便由调用的客户端捕获之。当客户端捕获到以后,又会继续进行类似的判断。
一般来说,GUI端是要处理异常的,比如JSP捕获到异常之后,需要先是给用户一个友好的出错信息,而不要给出系统的出错信息。系统的出错信息一方面不太友好,另一方面提供了太多的系统信息,容易被恶意用户用来攻击系统。换句话说,所有的异常最终必须有一个终极的处理者,这就是GUI。至于中间的环节,比如在服务器端运行的JavaBean是否要处理捕获到的异常,还是继续抛出所捕获的异常,需要视具体情况处理。
除非你想把异常处理的责任交给调用者,一般不用throws。 比如你要读入一些文件,如果你想通知调用者,让调用者决定如何处理这个异常,你就把这个异常throws给调用者;如果你知道应该如何处理这个异常,或者你想把异常马上解决,你可以就地catch她。
这完全取决于你想把异常自己立即处理还是想把处理责任返回给调用者。取决于你的程序的结构和要求。
需要注意的有:
1、如果无法处理某个异常,那就不要捕获它。
2、如果捕获了一个异常,请不要胡乱处理它。
3、尽量在靠近异常被抛出的地方捕获异常。
4、在捕获异常的地方将它记录到日志中,除非您打算将它重新抛出。
5、按照您的异常处理必须多精细来构造您的方法。
6、需要用几种类型的异常就用几种,尤其是对于应用程序异常。
三、异常嵌套和捕获适当的异常
按照Java语言的定义,所谓异常(Exception)指的就是向调用方法(calling method)表示发生非正常情况的习惯方式。下面讨论两种在处理异常时可兹利用的技术:异常嵌套和捕获适当的异常。
异常嵌套
你在试图捕获异常并打算扔出异常时该采取什么措施呢?同时,你希望原始的异常信息可用吗? 不管是新的异常是chekced异常还是unChecked异常。我们都必须考虑异常的嵌套问题。
public void methodA() throws ExceptionA{
…
throw new ExceptionA();
}
方法methodA声明会抛出ExceptionA.
public void methodB() throws ExceptionB
methodB声明会抛出ExceptionB,当在methodB方法中调用methodA时,如何将ExceptionA继续往上抛出。一个办法是把methodB声明会抛出ExceptionA.但这样已经改变了MethodB的方法签名。一旦改变,则所有调用methodB的方法都要进行改变。
另一个办法是把ExceptionA封装成ExceptionB,然后再抛出。如果我们不把ExceptionA封装在ExceptionB中,就丢失了根异常信息,使得无法跟踪异常的原始出处。
public void methodB()throws ExceptionB{
try{
methodA();
……
}catch(ExceptionA ex){
throw new ExceptionB(ex); //把ExceptionA封装成ExceptionB,然后再抛出
}
}
如上面的代码中,ExceptionB嵌套一个ExceptionA.我们暂且把ExceptionA称为“起因异常”,因为ExceptionA导致了ExceptionB的产生。这样才不使异常信息丢失。所以我们在定义一个新的异常类时,必须提供这样一个可以包含嵌套异常的构造函数。并有一个私有成员来保存这个“起因异常”。
java 代码
public Class ExceptionB extends Exception{
private Throwable cause;
public ExceptionB(String msg, Throwable ex){
super(msg);
this.cause = ex;
}
public ExceptionB(String msg){
super(msg);
}
public ExceptionB(Throwable ex){
this.cause = ex;
}
}
当然,我们在调用printStackTrace方法时,需要把所有的“起因异常”的信息也同时打印出来。
所以我们需要覆写printStackTrace方法来显示全部的异常栈跟踪。包括嵌套异常的栈跟踪。
public void printStackTrace(PrintStrean ps){
if(cause == null){
super.printStackTrace(ps);
}else{
ps.println(this);
cause.printStackTrace(ps);
}
}
一个完整的支持嵌套的checked异常类源码如下。我们在这里暂且把它叫做NestedExceptionpublic。
NestedException extends Exception{
private Throwable cause;
public NestedException (String msg){
super(msg);
}
public NestedException(String msg, Throwable ex){
super(msg);
This.cause = ex;
}
public Throwable getCause(){
return (this.cause == null ? this :this.cause);
}
public getMessage(){
String message = super.getMessage();
Throwable cause = getCause();
if(cause != null){
message = message + “;nested Exception is ” + cause;
}
return message;
}
public void printStackTrace(PrintStream ps){
if(getCause == null){
super.printStackTrace(ps);
}else{
ps.println(this);
getCause().printStackTrace(ps);
}
}
public void printStackTrace(PrintWrite pw){
if(getCause() == null){
super.printStackTrace(pw);
}
else{
pw.println(this);
getCause().printStackTrace(pw);
}
}
public void printStackTrace(){
printStackTrace(System.error);
}
}
捕获适当的异常
正确地处理异常并不是一项轻松的任务,这是因为异常的处理有时会导致程序出现其他不明行为。不过,以下三条规则可以帮助你避免错误处理异常所可能遭遇的风险。
规则 #1: 总是捕获扔出异常的类型而不要理睬异常的超类。
规则 # 2: 决不让catch块留空。在很多情况下虽然确实编写了try/catch块但在代码的catch部分却什么都没有做。或者,如果采用了日志API(Logging API),那么请编写代码把异常写到日志中。
规则 # 3: 决不扔出Exception基类的实例。开发人员应当总是扔出自己创建的异常类。
以上提到的两种技术在处理异常时还可能用得更好、更适当。嵌套技术令异常扔到另一异常的内部,而捕获适当的异常令程序调试大大简化。
分享到:
相关推荐
《SQL语言(必知必会)第四版》是一本针对SQL初学者和进阶者的重要教程,涵盖了SQL语言的核心概念和实用技巧。SQL,全称Structured Query Language,即结构化查询语言,是用于管理和处理关系数据库的标准编程语言。...
《C++必知必会》是一本广受欢迎的C++编程教材,英文版更是许多程序员学习C++的首选。这本书深入浅出地介绍了C++语言的基础知识和核心概念,旨在帮助读者快速掌握C++编程技能。以下是根据标题、描述和标签提炼的一些...
《C++必知必会PDF简体中文版》是一本专注于C++编程语言核心知识和技术要点的图书。本书由Stephen C. Dewhurst所著,并由荣汐翻译成简体中文。作为图灵程序设计丛书之一,本书在出版之初便获得了业界的广泛认可,被...
"C++必知必会(入门经典)"这本书正是针对初学者,旨在帮助他们快速掌握C++的基础知识和核心概念。 1. **C++概述** - C++起源于C语言,由Bjarne Stroustrup在1983年发展而成。 - 它引入了类、模板、异常处理、命名...
C 必知必会 PDF 电子教程(译),本书涵盖了C 程序员必备知识、C 新手登堂入室的阶梯、C 编程界20年结晶;全书阐述C 编程和设计中必须掌握但通常被误解的主题,包括指针操作、模板、泛型编程、异常处理、内存分配、设计...
异常处理是处理程序错误和异常情况的方式。在可能出现错误的地方,程序员可以抛出异常,然后在程序的其他地方捕获并处理这些异常,以保证程序的健壮性。 最后,C++11、C++14、C++17及以后的版本引入了许多新的特性...
本书描述了C++编程和设计中必须掌握但通常被误解的主题,这些主题涉及的范围较广,包括指针操作、模板、泛型编程、异常处理、内存分配、设计模式等。作者根据本人以及其他有经验的管理人员和培训老师的经验总结,对...
总的来说,《C++必知必会》这本书涵盖了C++编程中的关键概念,包括类型转换、字符串处理、VARIANT、内存管理和异常处理,以及指针和引用的基础知识。通过深入学习这些内容,开发者能够更好地理解C++的特性和用法,...
- **异常处理**:理解Checked和Unchecked异常的区别,以及如何合理使用try-catch-finally语句。 2. **Android相关**: - **Activity生命周期**:掌握各状态转换,理解onCreate()、onStart()、onResume()、onPause...
在C++中,无论何时在处理程序内捕获一个异常,关于该异常的信息都是不为人知的。异常的具体可以提供许多更好地处理该异常的重要信息,或者提供一些可以附加到错误日志的信息,以便以后进行分析。 为了解决这一...
本篇将重点探讨"Java基础学习/高级类/异常处理/线程"这四个关键知识点。 一、Java基础学习 Java的基础学习涵盖了许多方面,包括语法、数据类型、控制结构、类和对象等。初学者应首先掌握Java的基本语法,例如变量...
下面,我们将深入探讨“运维必知必会的监控知识体系”。 监控知识体系主要涵盖以下几个核心领域: 1. **监控指标**:监控系统的基础是各种指标,包括但不限于CPU使用率、内存占用、磁盘I/O、网络流量、系统负载等...
本书描述了C++编程和设计中必须掌握但通常被误解的主题,包括指针操作、模版、泛型编程、异常处理、内存分配、设计模式等。作者称这些知识为C++程序员必备的"常识",其实并非意味着简单或平庸,而是"必不可少"。 ...
《C++必知必会》是一本非常受欢迎的C++编程教材,专为那些希望深入理解和掌握C++语言核心概念的读者设计。这本书由Frank M. Loy所著,旨在帮助初学者快速上手,同时也适合有一定经验的程序员作为参考。在中文版中,...
8. **异常处理**:合理使用try-catch,避免App因未捕获异常而崩溃,利用 Crashlytics 或 Firebase Analytics 追踪应用错误。 9. **单元测试与集成测试**:编写JUnit和Espresso测试用例,确保代码质量,使用Mockito...
异常处理是C++中处理程序错误的方式,书中有专门的章节讲述如何正确地捕获和处理异常,确保程序的健壮性。 最后,书中还会涉及预处理器、命名空间和模板等高级主题,帮助读者进一步提升C++编程的技能和灵活性。 总...
《C++必知必会》是一本专门为C++初学者设计的教程,旨在帮助学习者掌握C++编程的基础知识和核心技能。C++是一种强大的、通用的编程语言,以其高效、灵活和面向对象的特性而受到广泛使用。下面将详细阐述C++的一些...
这部分未在内容中具体列出,但通常包括Java语法、面向对象编程、异常处理、集合框架、多线程、I/O流、网络编程、反射机制、垃圾回收等核心知识。 四、软件测试 软件测试是验证和确认软件质量的过程,包括单元测试...