论坛首页 Java企业应用论坛

为什么 Java 中要使用 Checked Exceptions

浏览 193220 次
该帖已经被评为精华帖
作者 正文
   发表时间:2005-04-05  
	private final static String GET_ALL_USER_QUERY = "form User as user";
	private final static String CHECK_USER_NAME_QUERY = "from User as user where user.userName=:userName";
	private final static String LOGIN_QUERY = "from User as user where user.userName=:userName and user.passWord=:passWord";
	/**
	 * 检查此用户名是否可注册使用,如果已经存在则为false,如果不存在则为true
	 * @param userName
	 * @return
	 */
	public boolean checkUserName(final String userName); throws ExampleException{
		List list = this.getHibernateTemplate();.executeFind(new HibernateCallback(); {
			public Object doInHibernate(Session session); throws HibernateException, SQLException {
				Query query = session.createQuery(CHECK_USER_NAME_QUERY);;
				query.setString("userName",userName);;
				return query.list();;
			}
		});;
		if(list.size();==0);{
			return true;
		}else if(list.size();>1);{
			throw new ExampleException("数据库user表中有个两个"+userName+"帐号");;
		}else{
			return false;
		}	
		
	}
	/**
	 * 用户登录
	 * @param userName 用户名
	 * @param passWord 密码
	 * @return 二维对象数组,object[0]表示登陆状态,0为登陆成功,1为无此用户,2为密码错误,3为用户已经被禁用,若登陆成功oject[1]为User实体对象,若未成功则为null
	 * @throws ExampleException 数据库数据错误
	 */
	public Object[] login(final String userName,final String passWord); throws ExampleException{
		//登陆成功
		final Integer login_success = new Integer(0);;
		//无此用户名
		final Integer have_not_this_username = new Integer(1);;
		//密码错误
		final Integer wrong_password = new Integer(2);;
		//此用户已经被禁用
		final Integer deny_username = new Integer(3);;
		boolean usable = checkUserName(userName);;
		if(usable);{
			return new Object[]{have_not_this_username,null};
		}else{
			List list = this.getHibernateTemplate();.executeFind(new HibernateCallback(); {
						public Object doInHibernate(Session session); throws HibernateException, SQLException {
							Query query = session.createQuery(LOGIN_QUERY);;
							query.setString("userName",userName);;
							query.setString("passWord",passWord);;
							return query.list();;
						}
					});;
			if(list.size();==0);{
				return new Object[]{wrong_password,null};
			}else{
				User user = (User);list.get(0);;
				if(false);{//如果用户被禁用
					return new Object[]{deny_username,null};
				}else{
					return new Object[]{login_success,user};
				}
			}
		}
	}
0 请登录后投票
   发表时间:2005-04-05  
/*
 * 创建日期 2005-3-21
 *
 */
package com.bupticet.example.struts;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionMessage;
import org.apache.struts.action.ActionMessages;
import org.apache.struts.action.DynaActionForm;
import org.apache.struts.actions.DispatchAction;
import org.apache.struts.util.ModuleException;

import com.bupticet.example.Constants;
import com.bupticet.example.dao.UserDao;
import com.bupticet.example.entity.User;
import com.bupticet.example.exception.ExampleException;
import com.bupticet.example.transform.UserTransform;
import com.bupticet.util.MD5;

/**
 * <p>Title: LoginAction</p>
 *
 * <p>Description: 登陆AcitionServlet</p>
 *
 * <p>Copyright: Copyright (c);北京邮电大学网络教育技术研究所[www.buticet.com] 2005</p>
 *
 * <p>Company: 北京邮电大学网络教育技术研究所[www.buticet.com] </p>
 * 
 * @author LJ-silver
 * @version 1.0
 */
public class LoginTAction extends DispatchAction {
	
	private final static String LOGIN_SUCCESS = "login-success";
	private final static String LOGIN_FAILURE = "login-failure";
	
	static org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(LoginTAction.class);;
	
	private UserDao userDao;
	
	public ActionForward login(ActionMapping mapping,
							   ActionForm actionForm,
							   HttpServletRequest request,
							   HttpServletResponse response); throws Exception {
		DynaActionForm form = (DynaActionForm);actionForm;
		ActionMessages errors = new ActionMessages();;
		if (!isTokenValid(request,true);); {
			errors.add(ActionMessages.GLOBAL_MESSAGE,new ActionMessage("errors.token"););;
			saveErrors(request, errors);;
			saveToken(request);;
			return mapping.findForward(LOGIN_FAILURE);;
		}
		resetToken(request);;
		
		String userName = (String);form.get("userName");;
		String passWord = (String);form.get("passWord");;
		try{
			Object[] object = userDao.login(userName,MD5.toMD5(passWord););;
			switch (((Integer);object[0]);.intValue(););{
				case 1:
					errors.add(ActionMessages.GLOBAL_MESSAGE,new ActionMessage("error.login.havenotthisaccount"););;
					break;
				case 2:
					errors.add(ActionMessages.GLOBAL_MESSAGE ,new ActionMessage("error.login.wrongpassword"););;
					break;
				case 3:
					errors.add(ActionMessages.GLOBAL_MESSAGE ,new ActionMessage("error.login.denyusername"););;
					break;
				case 0:
					User user = (User);object[1];
					HttpSession  session = request.getSession(true);;
					session.setAttribute(Constants.USER_ATTRIBUTE,UserTransform.pOToVO(user););;
					break;
				default:
					errors.add(ActionMessages.GLOBAL_MESSAGE,new ActionMessage("error.login.returnunknowncode"););;
					break;
			}
			
		}catch(ExampleException e);{
			logger.error("In method login, Exception Occured ! Info :数据库member表中存在着多个"+userName+"帐户");;
			throw new ModuleException("error.databaseerror");;
		}
		if(!errors.isEmpty(););{
			saveErrors(request, errors);;
			saveToken(request);;
			return mapping.findForward(LOGIN_FAILURE);;
		}else{
			return mapping.findForward(LOGIN_SUCCESS);;
		}
	}
	
	/**
	 * @return
	 */
	public UserDao getUserDao(); {
		return userDao;
	}

	/**
	 * @param dao
	 */
	public void setUserDao(UserDao dao); {
		userDao = dao;
	}

}
0 请登录后投票
   发表时间:2005-04-05  
这是前几天为作技术交流写的一个小例子。
各位大牛说的很有道理,我觉得checked exception和unchecked exception结合着用就可以了。
0 请登录后投票
   发表时间:2005-04-15  
一、我对客户非常友好(指有非常详细的错误提示给他),这时的代码:
WebLayer {
try {
       User user = login(name, passwd);
} catch(LoginException e) {
}
}

BusinessLayer {
    User login(name, passwd) throws LoginException {
      throw LoginException("用户不存在");
      throw LoginException("密码错误");
      ...
   }
}

我们自己创建异常的原则是:
1、最好不要创建新异常,使用已有的异常,没办法就2
2、创建最少最通用的异常。这个可以避免aders 说的接口签名问题。

假如:
User login(name, passwd) throws UserExistsException,PasswdInvalidException {
}

要是以后要添加检查name 是否规范的逻辑,这是怎么办呢??抛出一个NameInvalidException 异常??可是我的这个login 已经在n 多个地方被使用了??

二、我对客户比较友好(有出错提示,但不详细):
这时的代码可以用第一种情况的代码实现。也可以是下面的代码:
boolean login(name, passwd) {
}

if (login(name, passwd))
   ....
else
   throw new XxxException("登录失败");

从上面两种实现看来应该是第一段代码实现的较好,能应付以后出现的许多情况。第二段就不行了。

非常反对用异常控制程序流程的想法。也许有人认为第一种情况中代码也属于异常控制流程。那我想我脑中想的程序流程和你的不一样。
0 请登录后投票
   发表时间:2005-04-17  
无数人说异常不应该控制程序流程。
谁仔细想过为什么吗?只怕都是人云亦云罢了。

知道,异常效率低嘛。可是程序流程不见得就是都要高效率的呀。
要真是效率决定一切,java程序员都去要饭得了,连c++扇子们都知道什么2/8原则,你们这里拿效率这么个鸡毛还当令箭耍什么劲?

没人什么地方都用异常。但是有些场合用异常就是方便,错误种类可扩展,错误信息详细,错误处理结构化,可以集中处理错误,不复杂化函数接口等等等等,这些都是用异常表达部分程序逻辑的好处。

你说不应该控制程序流程,好办。给个替代品啊。

UserInfo login(String username, String pwd);
throws AuthencationException;

我这里如果login失败,用异常清晰简单。您给个其它的好用的解决方案先?

也不知道哪个弱人脑子都不过就给出这么个教条来。scheme还有continuation呢,那可是超级异常,不是一样用来处理程序流程?
具体情况具体分析,哪个方案最好用就用哪个,哪来那么多教条?
0 请登录后投票
   发表时间:2005-04-17  
关键词:不正常正常

又把前面的帖子看了一遍,感觉是大家对程序控制流程有着不同的看法。在《Effective Java》(中文版)上说“异常只应该被用于不正常的条件,它们永远不应该被永远正常的控制流”。
我原先以为那个不是一个程序控制流程,所以我用
try {
    User login(...);
} catch (e) {}
来替代
   if (login(...)) {}
是正确的。
但实际上那是一个控制流程,只不过用boolean 来实现时变成了不正常的控制流程--不能返回更详细的出错信息。异常不应该被用来控制正常的控制流,但可以用来替换不正常的控制流。
0 请登录后投票
   发表时间:2005-05-13  
既然有争议说明双方的方案都有问题,也各有道理,各不服气。
窃以为checked exception和unchecked exception的区别在于显式的和隐式,分散和集中的处理exception。单纯从语言的角度看Anders Hejlsberg的观点不是瞎扯的,毕竟人家是语言大家,不能因为人家端微软的饭就鄙视人家。ah确实是点到了java的软肋上,何必否认呢。说人家不熟悉企业开发,真是扯淡,人家是站在什么层次上看问题,难道比企业级的鹅站的低吗? 谁是谁非,没有完美的解决方案的问题就没必要争论不息了,毕竟偶们不能左右语言的发展,讨论怎么用好这些机制可能更有意义。
0 请登录后投票
   发表时间:2005-05-13  
neooen 写道
毕竟偶们不能左右语言的发展,讨论怎么用好这些机制可能更有意义。


今天中午去食堂吃饭时也想到了这个问题。我们应该讨论怎么用好java的checked exception。
0 请登录后投票
   发表时间:2005-05-29  
我认为 在合适的地方使用异常会更加体现出OO的思想 以及合理的架构
0 请登录后投票
   发表时间:2005-05-30  
对于这个问题,我很难完全同意robbin的看法(如果我没有误解他的意思的话)。

首先明确几个问题:

1.exception机制是语言级的工具,用于递异常并进行处理(将遇到的异常情况交给知道如何去处理它的人来处理)。使用它有两个问题要注意:a.不是业务级别的分支处理工具;b.不应当处理相对常见的异常情况──这些在《effective java》已经讲得很清楚了。

2.如果我没有理解错的话,大家说的快乐日场景指的是“happy scenario”,这是use case的一条主线,它描述了所谓的基本路径(main path),同时,由于系统的复杂性,还存在可选路径,这些可选路径通常表现为用户所需的附加功能和──业务级的异常处理。

下面讨论user login这个例子,如果仅仅是true/false,那么也没有必要过度设计,但是如果存在不能登录(例如用户名、密码不匹配)/用户权受限(例如用户被锁定)/系统故障(例如数据库出错)这样一些需要考虑的问题的话,那么就不能用返回整数值的方式,这是很明显的bad smell。
但是我也不同意都使用exception机制,这样做与使用返回值相比,仅仅是多了一些信息,在程序结构上仍然是分支处理,就象老庄说的,不过是if变成了try,而且这样还有严重的性能问题(即便不考虑有人恶意DOS,在一般情况下,登录信息输入的错误也是很常见的现象)。

我的作法是──使用多态:对于login代码段,返回值是一个User对象,如果登录失败,则返回一个缓存的anonymous 的User对象,如果用户被锁定,就返回一个被锁定的User对象,调用方只要使用这个User对象的getMainPage()即可(这是一个简化的作法,具体还要进一步修改)。
至于系统故障,这是属于极为罕见的情况,况且这种情况的出现一般就意味着什么也做不了了,那么针对它抛出一个异常也是很正常的(就像我带了一把锤子一样正常Smile),这个异常可以一直抛到最外层去截获,然后给出一个统一的错误页面即可,所以应该使用runtime异常(客户不希望看到任何异常显示出来,因此即便是runtime也要在用户界面上保护起来)。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics