`

[转] 异常处理

阅读更多

  在看一些项目源码中,突然发现了异常的处理也是一门比较大的学问,其实处处都有学问,只是你是否留意而已。。好了,少点啰嗦,关于异常的处理,je上有个很精彩的讨论,我花了快一个钟时间才看完了,确实值得一看,帖子的网址:http://www.iteye.com/topic/2038 

  当然我这里写的,只是一个纯属的建议,大都是看了网上的帖子后,总结的。都是些前辈的经验。。下面开始吧。

——————————————————————————————————————————————

Java异常有三类:错误,运行时异常,检查型异常。

 

官方的观点是

39 条:最好为异常条件使用异常。也就是说,最好不为控制流使用异常。

40 条:为可恢复的条件使用检查型异常,为编程错误使用运行时异常。

41 条:避免不必要的使用检查型异常。

43 条:抛出与抽象相适应的异常。(使处理异常更直观)

在异常的使用上,专家的观点是很不一样的

C#作者Anders根本就忽略检查型异常。

Bruce Eckel,声称在使用 Java 语言多年后,他已经得出这样的结论,认为检查型异常是一个错误 —— 一个应该被声明为失败的试验。

——————————————————————————————————————————————

缺点:

缺点1,代码中包含了过多的catch,使得代码不清晰

缺点2,有时候捕捉的异常没有什么实际意义

缺点3,不够清晰的错误指示。

缺点4,过深的异常层次。

缺点4,性能。

——————————————————————————————————————————————

Eckel 提倡将所有的异常都作为非检查型的,并且提供将检查型异常转变为非检查型异常的一个方法,同时保留当异常从栈向上扩散时捕获特定类型的异常的能力

 

Rod Johnson 他采取一个不太激进的方法。他列举了异常的多个类别,并且为每个类别确定一个策略。一些异常本质上是次要的返回代码(它通常指示违反业务规则),而一些异常则是发生某种可怕错误(例如数据库连接失败)的变种。Johnson 提倡对于第一种类别的异常(可选的返回代码)使用检查型异常,而对于后者使用运行时异常。在发生某种可怕错误的类别中,其动机是简单地认识到没有调用者能够有效地处理该异常,因此它也可能以各种方式沿着栈向上扩散而对于中间代码的影响保持最小(并且最小化异常淹没的可能性)。

——————————————————————————————————————————————

解决1:谨慎的抛出检查型异常。或者你认为,你可以处理它。否则,包装为运行时异常。

解决2:如果遵守12不是问题

解决3:异常不跨层,否则必须捕捉或者包装。

         比如持久层丢出的SalException,你或者丢弃/处理/包装(为运行时异常),或者重新包装为业务层异常。保持JEE层的独立和异常的清晰性。

         包装底层异常,保持异常链。

解决4:如果符合14也不是问题。再次强调,能捕捉就捕捉。

解决5:减少异常使用,减少层次。

—————————————————————————————————————————————— 

je里面讨论,在上面的网址可找到:

 

robin认为异常是流程控制的一部分——当然,考虑到性能问题,这个流程不应该是大概率流程——也就是异常流程

例如用户登录

 

Java代码 复制代码
  1. User login(String username, String password); throws UserNotFoundException, PasswordNotMatchException;    
  2. try {   
  3. UserManager.login(xx,xx);   
  4.  .... 用户登陆以后的主事件流代码    
  5. catch (UserNotFoundException e){    
  6. ... 用户名称没有的事件处理,例如产生一个提示用户注册的页面    
  7. catch (PasswordNotMatchException e){   
  8.  .... 密码不对的事件处理,例如forward到重新登陆的页面    
  9. }  
User login(String username, String password); throws UserNotFoundException, PasswordNotMatchException; 
try {
UserManager.login(xx,xx);
 .... 用户登陆以后的主事件流代码 
} catch (UserNotFoundException e){ 
... 用户名称没有的事件处理,例如产生一个提示用户注册的页面 
} catch (PasswordNotMatchException e){
 .... 密码不对的事件处理,例如forward到重新登陆的页面 
}

 

 

Potian则认为,没有用户是正常业务逻辑的一部分

If(!用户业务层.没有这个用户(用户名))错误提示界面;

If(用户业务层.检验密码(用户名,密码))登录成功;

else 登录失败;

Potian认为不应该在一个业务中包含了过多的责任。

 

muziq认为:

    从做应用的角度看,我希望利用异常来管理显示在用户面前的错误提示,这样我就会将异常划分为两类,一类是可以显示给用户看的,或者说用户可以看的懂的,一类是给维护人员看的,就是通常说的系统错误,可以想象,让用户看到“空指针异常”是极不友好的行为。我们的项目中有一个专门显示错误的页面,它要么显示有具体业务含义的错误,要么就显示“系统错误”(然后维护人员可以从log中查找错误)。而前一类错误里面,每种错误总有一个不同的异常类和它对应,或者说,业务逻辑方法向表示逻辑抛出的异常,表示逻辑不需要分别处理(不要显示错误的除外),一律交给错误页面(其实是ErrorPageUIAction,我们用Struts),错误页面看是什么异常就显示什么错误提示,这里错误页面需要一个XML,XML里面定义了异常和错误提示的对应关系,当然XML里面只能有用户异常,不在XML里面的异常错误页面就会显示“系统错误”。业务逻辑事先将用户异常登记到XML中,这样错误信息还可以单独、统一的维护。 
    上面讲的是将异常展示到错误页面的方法,这其实只涉及到最后一层(Action)捕捉异常和业务逻辑层抛出异常的问题,而如何中间层内的异常如何抛、如何捕捉的问题考虑的思路一定是不一样的,不过,做应用系统的,业务逻辑层内部交叉引用的不多,所以这一块异常设计的矛盾并不突出。而如果你正在开发高度重用的组件甚至框架,异常就一定要好好设计一下了。 
    至于组件、框架的异常如何设计,我想只要去学JDK、Hibernate的那一套东西就没错了。能想到的就是,异常一定是调用者可以理解的,和方法的语义密切相关的。

—————————————————————————————————————————————— 

一些网上人的评论:

    在servlet中,我喜欢仅仅简单的在action中调用最好一个业务层方法就可以完成此action的任务。这意味着我的servlet非常瘦,可以比较容易的被替换。如果采用了potian的办法,则意味着我要把业务层中的代码前移到servlet中来,这模糊了业务层的责任。解决的办法是回到老路子上来。

    我还认为,没有异常的业务方法表达能力太弱,异常给了他们更丰富的表达能力。这使得业务层可以更丰富的表达业务意义。避免将业务责任分散掉。

———————————————————————————————————————————————

我的个人偏向:

  我个人比较偏向je上muziq 的看法,项目的最终用户是网民,而大都不是计算机专业者,所以不管异常怎么处理,都要对用户有个友好的提示,尽力少,尽量简单。而对于程序设计者来讲又必须依据异常对程序进行修复处理。所以异常一般分为两类:一类是可以显示给用户看的,或者说用户可以看的懂的;一类是给维护人员看的。这是对于异常在程序表面上的处理。

  而对于程序设计的底层(即代码的设计),异常尽量往高层抛出,让高层进行统一的处理,当然这是对于检查型异常而言。

  如果一个异常是致命的,不可恢复的。或者调用者去捕获它没有任何益处,使用unChecked异常。

  如果一个异常是可以恢复的,可以被调用者正确处理的,使用checked异常

  在使用unChecked异常时,必须在在方法声明中详细的说明该方法可能会抛出的unChekced异常。由调用者自己去决定是否捕获

 

相关的讨论比较好的帖子:http://www.iteye.com/topic/72170

 

文笔比较差,加上实际经验比较少,很难描述,还是看看代码比较容易懂,该实例是结合了ssh2:

      

Dao层:不抛出检查型异常,都由Hibernate包装成了运行期异常了

 

 

Java代码 复制代码
  1. package dao; import dao.base.BaseDao; import model.User;    
  2. public interface UserDao extends BaseDao<User,Integer>{   
  3.    public void addUser(User user);    
  4.    public User getUser(String id);    
  5. }  
package dao; import dao.base.BaseDao; import model.User; 
public interface UserDao extends BaseDao<User,Integer>{
   public void addUser(User user); 
   public User getUser(String id); 
}

 

Dao 的实现层不写了,继承HibernateDaoSupport,由它实现,把异常都包装成运行期异常了。

 

service 层:抛出XXXService类对应的XXXException 异常

Java代码 复制代码
  1. package service; import java.util.List;    
  2. import exception.UserException;   
  3. import bean.User;    
  4. public interface UserManager {   
  5.  public void addUser(User user) throws UserException;   
  6.  public User getUser(String id) throws UserException;   
  7. }  
package service; import java.util.List; 
import exception.UserException;
import bean.User; 
public interface UserManager {
 public void addUser(User user) throws UserException;
 public User getUser(String id) throws UserException;
}

 

 Service 实现层:抛出XXXService类对应的XXXException 异常

Java代码 复制代码
  1. public class UserServiceImp implements UserService {   
  2.  static Logger log=Logger.getLogger( UserServiceImp.class.getName());   
  3.  private UserDao userDao;   
  4.  @Override public User addUser(User user) throws UserException      
  5.  {       
  6.    try{   
  7.    ........    
  8.   }catch(Exception e){   
  9.    //用日志记录错误信息 log.debug(e.getMessage());    
  10.   //重新抛出新的异常,因为用户没必要知道详细的底层异常信息    
  11.    throw new UserException("验证用户登陆出现错误,请重试");    
  12.  }   
  13.  。。。。setXXX()。。。。。   
  14.  }  
public class UserServiceImp implements UserService {
 static Logger log=Logger.getLogger( UserServiceImp.class.getName());
 private UserDao userDao;
 @Override public User addUser(User user) throws UserException   
 {    
   try{
   ........ 
  }catch(Exception e){
   //用日志记录错误信息 log.debug(e.getMessage()); 
  //重新抛出新的异常,因为用户没必要知道详细的底层异常信息 
   throw new UserException("验证用户登陆出现错误,请重试"); 
 }
 。。。。setXXX()。。。。。
 }

 

action 层:如果action 不处理UserException异常,则继续抛出,由拦截器拦截

 

Java代码 复制代码
  1. public class AddUserAction extends BaseAction {   
  2.  private String name;   
  3.  private String password;   
  4.  public String execute() throws Exception{   
  5.  //如果action 不处理UserException异常,则继续抛出,由拦截器拦截    
  6.  }    
  7. }  
public class AddUserAction extends BaseAction {
 private String name;
 private String password;
 public String execute() throws Exception{
 //如果action 不处理UserException异常,则继续抛出,由拦截器拦截 
 } 
}

 

Struts2 配置文件:利用配置文件,把相应的异常转向相应的页面。

 

Xml代码 复制代码
  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2. <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd">  
  3. <struts>  
  4.     <constant name="struts.i18n.encoding" value="GBK"></constant>  
  5.     <package name="struts" extends="struts-default">  
  6.         <global-results>  
  7.             <result name="XXXException">/error.jsp</result>  
  8.         </global-results>  
  9.         <global-results>  
  10.             <result name="userException">/error.jsp</result>  
  11.         </global-results>  
  12.         <global-exception-mappings>  
  13.             <exception-mapping exception="exception.XXXException"  
  14.                 result="XXXException" />  
  15.         </global-exception-mappings>  
  16.         <global-exception-mappings>  
  17.             <exception-mapping exception="exception.UserException"  
  18.                 result="userException" />  
  19.         </global-exception-mappings>  
  20.         <action name="addUser" class="action.AddUserAction">  
  21.             <result name="input">/WEB-INF/jsp/addUser.jsp</result>  
  22.             <result name="success">/WEB-INF/jsp/main.jsp</result>  
  23.             <result name="failure">/WEB-INF/jsp/addUser.jsp</result>  
  24.         </action>  
  25.     </package>  
  26. </struts>  
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
	<constant name="struts.i18n.encoding" value="GBK"></constant>
	<package name="struts" extends="struts-default">
		<global-results>
			<result name="XXXException">/error.jsp</result>
		</global-results>
		<global-results>
			<result name="userException">/error.jsp</result>
		</global-results>
		<global-exception-mappings>
			<exception-mapping exception="exception.XXXException"
				result="XXXException" />
		</global-exception-mappings>
		<global-exception-mappings>
			<exception-mapping exception="exception.UserException"
				result="userException" />
		</global-exception-mappings>
		<action name="addUser" class="action.AddUserAction">
			<result name="input">/WEB-INF/jsp/addUser.jsp</result>
			<result name="success">/WEB-INF/jsp/main.jsp</result>
			<result name="failure">/WEB-INF/jsp/addUser.jsp</result>
		</action>
	</package>
</struts>

 

分享到:
评论

相关推荐

    磁力勘探物探中磁异常的转换与处理

    磁异常的转换与处理是磁力勘探解译重要的一个组成部分

    springboot 全局异常处理

    springboot 全局异常处理.捕捉全局异常,转换格式,以友好方式提示客户正确的操作步骤. 避免系统错误出现在用户勉强,造成不良使用体验.

    异常处理-简单的除法运算器

    2. 两个加数分别输入2和3,...5. 两个加数分别输入123和0,调试并修改程序,尝试用finally处理该异常。 6. 定义一个自己的异常MyException,并在TxtOp1或者TxtOp2为空的时候,抛出该异常。利用catch语句将该异常捕捉。

    实验3-磁异常处理与转换 实验报告1

    《应用地磁学》实验报告 姓 名:10101213班 第四组 学 号:10101213 指导教师:李淑玲 实验地点:教五楼319 实验日期:2015年5月 小组成

    Java异常处理.md

    - 当代码执行过程中出现如除数为零的`ArithmeticException`等错误时,Java会立即转至相应的catch块进行异常处理,如输出错误信息并打印堆栈跟踪。 2. **finally块的使用**: - 不论try块中的代码是否抛出异常,...

    企业库 异常处理 给以用户友好的异常机制

    企业库 异常处理 企业库 异常处理 配置文件 如何 转换错误,给以用户友好的异常机制

    jisuanqi.zip_java 计算器 异常处理

    要求:有简单的需求分析,功能设计,详细设计(程序流程),异常处理,测试,源程序清单,总结,具体格式见附件。 知识点: 掌握 图形用户界面设计; 掌握基本类的设计与使用; 掌握事件处理机制; 掌握使用异常...

    android异常类型和处理

    ClassCastException 强制转换类失败异常 ...NumberFormatException 把字符串转成数字失败时出现的数字格式异常 AssertionError 断言错误 ExceptionInInitializerError 试图初始化静态变量或者静态初始化块时抛出

    扩展php内置的异常处理类.zip

    一个扩展php内置的异常处理类,在try代码块中,需要使用throw语句抛出一个异常对象,才能跳到转到catch代码块中执行,并在catch代码块中捕获并使用这个异常类的对象。虽然在PHP中提供的内置异常处理类Exception,...

    内存出现异常的几种故障及其处理方法

    内存出现异常的几种故障及其处理方法

    C++异常处理的编程方法

    C++异常处理的编程方法,网上很好的一个关于异常处理的资源,原来的是DOC的,看起来很难受,里面个格式很乱,doc里面看起来很难受,稍微整理了一下,转成了PDF,阅读起来舒服多了

    扩展php内置的异常处理类

    一个扩展php内置的异常处理类,在try代码块中,需要使用throw语句抛出一个异常对象,才能跳到转到catch代码块中执行,并在catch代码块中捕获并使用这个异常类的对象。虽然在PHP中提供的内置异常处理类Exception,...

    Struts处理类型转换错误,如时间转换

    Struts2处理类型转换错误,全局处理,如处理时间转换错误

    java 方法的流程控制与异常处理

    方法的流程控制与异常处理 (1) 编写Java应用程序,求1!+2!+...+20!的和并显示,要求用方法实现求阶乘。 (2) 编写Java应用程序,从键盘输入一个整数,将其转换为的二进制、十六进制并把相应的表示输出到屏幕上。 (3) ...

    Linux系统系统内核分析系列_2 Linux的中断和异常处理

    中断有很多类,可能是外部硬件触发,也可能是由软件触发,对于CPU来说,中断处理的过程都是一样的:中断现行程序,转到中断服务程序处执行,回到被中断的程序继续执行。CPU总共可以处理256种中断。CPU对于其它的模块...

    如何用java实现不同编码方式字符串的转换(包含异常处理、重复不终止输入、缓冲区bufferedreader的使用)

    如何用java实现不同编码方式字符串的转换(包含异常处理、重复不终止输入、缓冲区bufferedreader的使用)

    struts2处理项目全局异常

    项目中出现的异常通常要用一个友好的异常页面来显示,通过对struts2.xml的配置能拦截全局异常,只要出现异常就会转向异常页面。

    模拟请求页式存储管理中硬件的地址转换和缺页中断处理

    模拟请求页式存储管理中硬件的地址转换和缺页中断处理

    学习Java,你需要知道这些——Java异常

    文章目录异常处理的概念异常的基本概念Java异常处理机制的优点错误的分类异常的分类预定义的一些常见异常异常的处理抛出异常捕获异常的语法生成异常对象声明自己的异常类 异常处理的概念 在程序在运行的时候可能会...

Global site tag (gtag.js) - Google Analytics