- 浏览: 184236 次
- 性别:
- 来自: 深圳
最新评论
-
mengfei86:
你们讨论的时候我刚上大学,。。。。、、现在都过去好多年了,。 ...
J2EE项目异常处理 -
di1984HIT:
文章不错,学习了
Ibatis读写CLOB数据 -
wulixiaodao:
main{
metodA();
}
详解spring事务属性 -
wulixiaodao:
Main{
Connection con=null;
...
详解spring事务属性 -
tao_gun:
感谢,有点懂了
详解spring事务属性
J2EE项目异常处理
为什么要在J2EE项目中谈异常处理呢?可能许多java初学者都想说:“异常处理不就是try….catch…finally吗?这谁都会啊!”。笔者在初学java时也是这样认为的。如何在一个多层的j2ee项目中定义相应的异常类?在项目中的每一层如何进行异常处理?异常何时被抛出?异常何时被记录?异常该怎么记录?何时需要把checked Exception转化成unchecked Exception ,何时需要把unChecked Exception转化成checked Exception?异常是否应该呈现到前端页面?如何设计一个异常框架?本文将就这些问题进行探讨。
1. JAVA异常处理
在面向过程式的编程语言中,我们可以通过返回值来确定方法是否正常执行。比如在一个c语言编写的程序中,如果方法正确的执行则返回1.错误则返回0。在vb或delphi开发的应用程序中,出现错误时,我们就弹出一个消息框给用户。
通过方法的返回值我们并不能获得错误的详细信息。可能因为方法由不同的程序员编写,当同一类错误在不同的方法出现时,返回的结果和错误信息并不一致。
所以java语言采取了一个统一的异常处理机制。
什么是异常?运行时发生的可被捕获和处理的错误。
在java语言中,Exception是所有异常的父类。任何异常都扩展于Exception类。Exception就相当于一个错误类型。如果要定义一个新的错误类型就扩展一个新的Exception子类。采用异常的好处还在于可以精确的定位到导致程序出错的源代码位置,并获得详细的错误信息。
Java异常处理通过五个关键字来实现,try,catch,throw ,throws, finally。具体的异常处理结构由try….catch….finally块来实现。try块存放可能出现异常的java语句,catch用来捕获发生的异常,并对异常进行处理。Finally块用来清除程序中未释放的资源。不管理try块的代码如何返回,finally块都总是被执行。
一个典型的异常处理代码
java 代码
- public String getPassword(String userId)throws DataAccessException{
- String sql = “select password from userinfo where userid=’”+userId +”’”;
- String password = null;
- Connection con = null;
- Statement s = null;
- ResultSet rs = null;
- try{
- con = getConnection();//获得数据连接
- s = con.createStatement();
- rs = s.executeQuery(sql);
- while(rs.next()){
- password = rs.getString(1);
- }
- rs.close();
- s.close();
- }
- Catch(SqlException ex){
- throw new DataAccessException(ex);
- }
- finally{
- try{
- if(con != null){
- con.close();
- }
- }
- Catch(SQLException sqlEx){
- throw new DataAccessException(“关闭连接失败!”,sqlEx);
- }
- }
- return password;
- }
可以看出Java的异常处理机制具有的优势:
给错误进行了统一的分类,通过扩展Exception类或其子类来实现。从而避免了相同的错误可能在不同的方法中具有不同的错误信息。在不同的方法中出现相同的错误时,只需要throw 相同的异常对象即可。
获得更为详细的错误信息。通过异常类,可以给异常更为详细,对用户更为有用的错误信息。以便于用户进行跟踪和调试程序。
把正确的返回结果与错误信息分离。降低了程序的复杂度。调用者无需要对返回结果进行更多的了解。
强制调用者进行异常处理,提高程序的质量。当一个方法声明需要抛出一个异常时,那么调用者必须使用try….catch块对异常进行处理。当然调用者也可以让异常继续往上一层抛出。
2. Checked 异常 还是 unChecked 异常?
Java异常分为两大类:checked 异常和unChecked 异常。所有继承java.lang.Exception 的异常都属于checked异常。所有继承java.lang.RuntimeException的异常都属于unChecked异常。
当一个方法去调用一个可能抛出checked异常的方法,必须通过try…catch块对异常进行捕获进行处理或者重新抛出。
我们看看Connection接口的createStatement()方法的声明。
public Statement createStatement() throws SQLException;
SQLException是checked异常。当调用createStatement方法时,java强制调用者必须对SQLException进行捕获处理。
java 代码
- public String getPassword(String userId){
- try{
- ……
- Statement s = con.createStatement();
- ……
- Catch(SQLException sqlEx){
- ……
- }
- ……
- }
java 代码
- public String getPassword(String userId)throws SQLException{
- Statement s = con.createStatement();
- }
(当然,像Connection,Satement这些资源是需要及时关闭的,这里仅是为了说明checked 异常必须强制调用者进行捕获或继续抛出)
unChecked异常也称为运行时异常,通常RuntimeException都表示用户无法恢复的异常,如无法获得数据库连接,不能打开文件等。虽然用户也可以像处理checked异常一样捕获unChecked异常。但是如果调用者并没有去捕获unChecked异常时,编译器并不会强制你那么做。
比如一个把字符转换为整型数值的代码如下:
java 代码
- String str = “123”;
- int value = Integer.parseInt(str);
parseInt的方法签名为:
java 代码
- public static int parseInt(String s) throws NumberFormatException
当传入的参数不能转换成相应的整数时,将会抛出NumberFormatException。因为NumberFormatException扩展于RuntimeException,是unChecked异常。所以调用parseInt方法时无需要try…catch
因为java不强制调用者对unChecked异常进行捕获或往上抛出。所以程序员总是喜欢抛出unChecked异常。或者当需要一个新的异常类时,总是习惯的从RuntimeException扩展。当你去调用它些方法时,如果没有相应的catch块,编译器也总是让你通过,同时你也根本无需要去了解这个方法倒底会抛出什么异常。看起来这似乎倒是一个很好的办法,但是这样做却是远离了java异常处理的真实意图。并且对调用你这个类的程序员带来误导,因为调用者根本不知道需要在什么情况下处理异常。而checked异常可以明确的告诉调用者,调用这个类需要处理什么异常。如果调用者不去处理,编译器都会提示并且是无法编译通过的。当然怎么处理是由调用者自己去决定的。
所以Java推荐人们在应用代码中应该使用checked异常。就像我们在上节提到运用异常的好外在于可以强制调用者必须对将会产生的异常进行处理。包括在《java Tutorial》等java官方文档中都把checked异常作为标准用法。
使用checked异常,应意味着有许多的try…catch在你的代码中。当在编写和处理越来越多的try…catch块之后,许多人终于开始怀疑checked异常倒底是否应该作为标准用法了。
甚至连大名鼎鼎的《thinking in java》的作者Bruce Eckel也改变了他曾经的想法。Bruce Eckel甚至主张把unChecked异常作为标准用法。并发表文章,以试验checked异常是否应该从java中去掉。Bruce Eckel语:“当少量代码时,checked异常无疑是十分优雅的构思,并有助于避免了许多潜在的错误。但是经验表明,对大量代码来说结果正好相反”
关于checked异常和unChecked异常的详细讨论可以参考
Alan Griffiths http://www.octopull.demon.co.uk/java/ExceptionalJava.html
使用checked异常会带来许多的问题。
checked异常导致了太多的try…catch 代码
可能有很多checked异常对开发人员来说是无法合理地进行处理的,比如SQLException。而开发人员却不得不去进行try…catch。当开发人员对一个checked异常无法正确的处理时,通常是简单的把异常打印出来或者是干脆什么也不干。特别是对于新手来说,过多的checked异常让他感到无所适从。
java 代码
- try{
- ……
- Statement s = con.createStatement();
- ……
- Catch(SQLException sqlEx){
- sqlEx.PrintStackTrace();
- }
- 或者
- try{
- ……
- Statement s = con.createStatement();
- ……
- Catch(SQLException sqlEx){
- //什么也不干
- }
checked异常导致了许多难以理解的代码产生
当开发人员必须去捕获一个自己无法正确处理的checked异常,通常的是重新封装成一个新的异常后再抛出。这样做并没有为程序带来任何好处。反而使代码晚难以理解。
就像我们使用JDBC代码那样,需要处理非常多的try…catch.,真正有用的代码被包含在try…catch之内。使得理解这个方法变理困难起来
checked异常导致异常被不断的封装成另一个类异常后再抛出
java 代码
- public void methodA()throws ExceptionA{
- …..
- throw new ExceptionA();
- }
- public void methodB()throws ExceptionB{
- try{
- methodA();
- ……
- }catch(ExceptionA ex){
- throw new ExceptionB(ex);
- }
- }
- Public void methodC()throws ExceptinC{
- try{
- methodB();
- …
- }
- catch(ExceptionB ex){
- throw new ExceptionC(ex);
- }
- }
checked异常导致破坏接口方法
一个接口上的一个方法已被多个类使用,当为这个方法额外添加一个checked异常时,那么所有调用此方法的代码都需要修改。
可见上面这些问题都是因为调用者无法正确的处理checked异常时而被迫去捕获和处理,被迫封装后再重新抛出。这样十分不方便,并不能带来任何好处。在这种情况下通常使用unChecked异常。
chekced异常并不是无一是处,checked异常比传统编程的错误返回值要好用得多。通过编译器来确保正确的处理异常比通过返回值判断要好得多。
如果一个异常是致命的,不可恢复的。或者调用者去捕获它没有任何益处,使用unChecked异常。
如果一个异常是可以恢复的,可以被调用者正确处理的,使用checked异常。
在使用unChecked异常时,必须在在方法声明中详细的说明该方法可能会抛出的unChekced异常。由调用者自己去决定是否捕获unChecked异常
倒底什么时候使用checked异常,什么时候使用unChecked异常?并没有一个绝对的标准。但是笔者可以给出一些建议
当所有调用者必须处理这个异常,可以让调用者进行重试操作;或者该异常相当于该方法的第二个返回值。使用checked异常。
这个异常仅是少数比较高级的调用者才能处理,一般的调用者不能正确的处理。使用unchecked异常。有能力处理的调用者可以进行高级处理,一般调用者干脆就不处理。
这个异常是一个非常严重的错误,如数据库连接错误,文件无法打开等。或者这些异常是与外部环境相关的。不是重试可以解决的。使用unchecked异常。因为这种异常一旦出现,调用者根本无法处理。
如果不能确定时,使用unchecked异常。并详细描述可能会抛出的异常,以让调用者决定是否进行处理。
3. 设计一个新的异常类
在设计一个新的异常类时,首先看看是否真正的需要这个异常类。一般情况下尽量不要去设计新的异常类,而是尽量使用java中已经存在的异常类。
如java 代码
- IllegalArgumentException, UnsupportedOperationException
不管是新的异常是chekced异常还是unChecked异常。我们都必须考虑异常的嵌套问题。
java 代码
- public void methodA()throws ExceptionA{
- …..
- throw new ExceptionA();
- }
方法methodA声明会抛出ExceptionA.
public void methodB()throws ExceptionB
methodB声明会抛出ExceptionB,当在methodB方法中调用methodA时,ExceptionA是无法处理的,所以ExceptionA应该继续往上抛出。一个办法是把methodB声明会抛出ExceptionA.但这样已经改变了MethodB的方法签名。一旦改变,则所有调用methodB的方法都要进行改变。
另一个办法是把ExceptionA封装成ExceptionB,然后再抛出。如果我们不把ExceptionA封装在ExceptionB中,就丢失了根异常信息,使得无法跟踪异常的原始出处。
java 代码
- public void methodB()throws ExceptionB{
- try{
- methodA();
- ……
- }catch(ExceptionA ex){
- throw new ExceptionB(ex);
- }
- }
如上面的代码中,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;
- }
- }
java 代码
- public void printStackTrace(PrintStrean ps){
- if(cause == null){
- super.printStackTrace(ps);
- }else{
- ps.println(this);
- cause.printStackTrace(ps);
- }
- }
一个完整的支持嵌套的checked异常类源码如下。我们在这里暂且把它叫做NestedException
java 代码
- public 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);
- }
- }
4. 如何记录异常
作为一个大型的应用系统都需要用日志文件来记录系统的运行,以便于跟踪和记录系统的运行情况。系统发生的异常理所当然的需要记录在日志系统中。
java 代码
- public String getPassword(String userId)throws NoSuchUserException{
- UserInfo user = userDao.queryUserById(userId);
- If(user == null){
- Logger.info(“找不到该用户信息,userId=”+userId);
- throw new NoSuchUserException(“找不到该用户信息,userId=”+userId);
- }
- else{
- return user.getPassword();
- }
- }
- public void sendUserPassword(String userId)throws Exception {
- UserInfo user = null;
- try{
- user = getPassword(userId);
- //……..
- sendMail();
- //
- }catch(NoSuchUserException ex)(
- logger.error(“找不到该用户信息:”+userId+ex);
- throw new Exception(ex);
- }
我们注意到,一个错误被记录了两次.在错误的起源位置我们仅是以info级别进行记录。而在sendUserPassword方法中,我们还把整个异常信息都记录了。
笔者曾看到很多项目是这样记录异常的,不管三七二一,只有遇到异常就把整个异常全部记录下。如果一个异常被不断的封装抛出多次,那么就被记录了多次。那么异常倒底该在什么地方被记录?
异常应该在最初产生的位置记录!
如果必须捕获一个无法正确处理的异常,仅仅是把它封装成另外一种异常往上抛出。不必再次把已经被记录过的异常再次记录。
如果捕获到一个异常,但是这个异常是可以处理的。则无需要记录异常
java 代码
- public Date getDate(String str){
- Date applyDate = null;
- SimpleDateFormat format = new SimpleDateFormat(“MM/dd/yyyy”);
- try{
- applyDate = format.parse(applyDateStr);
- }
- catch(ParseException ex){
- //乎略,当格式错误时,返回null
- }
- return applyDate;
- }
捕获到一个未记录过的异常或外部系统异常时,应该记录异常的详细信息
java 代码
- try{
- ……
- String sql=”select * from userinfo”;
- Statement s = con.createStatement();
- ……
- Catch(SQLException sqlEx){
- Logger.error(“sql执行错误”+sql+sqlEx);
- }
究竟在哪里记录异常信息,及怎么记录异常信息,可能是见仁见智的问题了。甚至有些系统让异常类一记录异常。当产生一个新异常对象时,异常信息就被自动记录。
java 代码
- public class BusinessException extends Exception {
- private void logTrace() {
- StringBuffer buffer=new StringBuffer();
- buffer.append("Business Error in Class: ");
- buffer.append(getClassName());
- buffer.append(",method: ");
- buffer.append(getMethodName());
- buffer.append(",messsage: ");
- buffer.append(this.getMessage());
- logger.error(buffer.toString());
- }
- public BusinessException(String s) {
- super(s);
- race();
- }
虽然我们对记录异常讨论了很多,过多的强调这些反而使开发人员更为疑惑,一种好的方式是为系统提供一个异常处理框架。由框架来决定是否记录异常和怎么记录异常。而不是由普通程序员去决定。但是了解些还是有益的。
5. J2EE项目中的异常处理
目前,J2ee项目一般都会从逻辑上分为多层。比较经典的分为三层:表示层,业务层,集成层(包括数据库访问和外部系统的访问)。
J2ee项目有着其复杂性,J2ee项目的异常处理需要特别注意几个问题。
在分布式应用时,我们会遇到许多checked异常。所有RMI调用(包括EJB远程接口调用)都会抛出java.rmi.RemoteException;同时RemoteException是checked异常,当我们在业务系统中进行远程调用时,我们都需要编写大量的代码来处理这些checked异常。而一旦发生RemoteException这些checked异常对系统是非常严重的,几乎没有任何进行重试的可能。也就是说,当出现RemoteException这些可怕的checked异常,我们没有任何重试的必要性,却必须要编写大量的try…catch代码去处理它。一般我们都是在最底层进行RMI调用,只要有一个RMI调用,所有上层的接口都会要求抛出RemoteException异常。因为我们处理RemoteException的方式就是把它继续往上抛。这样一来就破坏了我们业务接口。RemoteException这些J2EE系统级的异常严重的影响了我们的业务接口。我们对系统进行分层的目的就是减少系统之间的依赖,每一层的技术改变不至于影响到其它层。
java 代码
- //
- public class UserSoaImpl implements UserSoa{
- public UserInfo getUserInfo(String userId)throws RemoteException{
- //……
- 远程方法调用.
- //……
- }
- }
- public interface UserManager{
- public UserInfo getUserInfo(Stirng userId)throws RemoteException;
- }
同样JDBC访问都会抛出SQLException的checked异常。
为了避免系统级的checked异常对业务系统的深度侵入,我们可以为业务方法定义一个业务系统自己的异常。针对像SQLException,RemoteException这些非常严重的异常,我们可以新定义一个unChecked的异常,然后把SQLException,RemoteException封装成unChecked异常后抛出。
如果这个系统级的异常是要交由上一级调用者处理的,可以新定义一个checked的业务异常,然后把系统级的异常封存装成业务级的异常后再抛出。
一般地,我们需要定义一个unChecked异常,让集成层接口的所有方法都声明抛出这unChecked异常。
java 代码
- public DataAccessException extends RuntimeException{
- ……
- }
- public interface UserDao{
- public String getPassword(String userId)throws DataAccessException;
- }
- public class UserDaoImpl implements UserDAO{
- public String getPassword(String userId)throws DataAccessException{
- String sql = “select password from userInfo where userId= ‘”+userId+”’”;
- try{
- …
- //JDBC调用
- s.executeQuery(sql);
- …
- }catch(SQLException ex){
- throw new DataAccessException(“数据库查询失败”+sql,ex);
- }
- }
- }
定义一个checked的业务异常,让业务层的接口的所有方法都声明抛出Checked异常.
java 代码
- public class BusinessException extends Exception{
- …..
- }
- public interface UserManager{
- public Userinfo copyUserInfo(Userinfo user)throws BusinessException{
- Userinfo newUser = null;
- try{
- newUser = (Userinfo)user.clone();
- }catch(CloneNotSupportedException ex){
- throw new BusinessException(“不支持clone方法:”+Userinfo.class.getName(),ex);
- }
- }
- }
J2ee表示层应该是一个很薄的层,主要的功能为:获得页面请求,把页面的参数组装成POJO对象,调用相应的业务方法,然后进行页面转发,把相应的业务数据呈现给页面。表示层需要注意一个问题,表示层需要对数据的合法性进行校验,比如某些录入域不能为空,字符长度校验等。
J2ee从页面所有传给后台的参数都是字符型的,如果要求输入数值或日期类型的参数时,必须把字符值转换为相应的数值或日期值。
如果表示层代码校验参数不合法时,应该返回到原始页面,让用户重新录入数据,并提示相关的错误信息。
通常把一个从页面传来的参数转换为数值,我们可以看到这样的代码
java 代码
- ModeAndView handleRequest(HttpServletRequest request,HttpServletResponse response)throws Exception{
- String ageStr = request.getParameter(“age”);
- int age = Integer.parse(ageStr);
- …………
- String birthDayStr = request.getParameter(“birthDay”);
- SimpleDateFormat format = new SimpleDateFormat(“MM/dd/yyyy”);
- Date birthDay = format.parse(birthDayStr);
- }
上面的代码应该经常见到,但是当用户从页面录入一个不能转换为整型的字符或一个错误的日期值。
Integer.parse()方法被抛出一个NumberFormatException的unChecked异常。但是这个异常绝对不是一个致命的异常,一般当用户在页面的录入域录入的值不合法时,我们应该提示用户进行重新录入。但是一旦抛出unchecked异常,就没有重试的机会了。像这样的代码造成大量的异常信息显示到页面。使我们的系统看起来非常的脆弱。
同样,SimpleDateFormat.parse()方法也会抛出ParseException的unChecked异常。
这种情况我们都应该捕获这些unChecked异常,并给提示用户重新录入。
java 代码
- ModeAndView handleRequest(HttpServletRequest request,HttpServletResponse response)throws Exception{
- String ageStr = request.getParameter(“age”);
- String birthDayStr = request.getParameter(“birthDay”);
- int age = 0;
- Date birthDay = null;
- try{
- age=Integer.parse(ageStr);
- }catch(NumberFormatException ex){
- error.reject(“age”,”不是合法的整数值”);
- }
- …………
- try{
- SimpleDateFormat format = new SimpleDateFormat(“MM/dd/yyyy”);
- birthDay = format.parse(birthDayStr);
- }catch(ParseException ex){
- error.reject(“birthDay”,”不是合法的日期,请录入’MM/dd/yyy’格式的日期”);
- }
- }
在表示层一定要弄清楚调用方法的是否会抛出unChecked异常,什么情况下会抛出这些异常,并作出正确的处理。
在表示层调用系统的业务方法,一般情况下是无需要捕获异常的。如果调用的业务方法抛出的异常相当于第二个返回值时,在这种情况下是需要捕获
评论
38 楼
klyuan
2007-06-06
引用
汗。。。返回值?你的意思可以理解但,语法有问题,并且要加上非常 用返回值。。。
返回值,这没有什么难以理解的啦!!
当方法出现几种逻辑时,有时用方法的返回值无法描述,这时可以用一个异常信息来代表返回结果!
唉,可能我还是没有说清楚!!我得去翻以前的程序,把例子弄出来
引用
第四第五也不错但想不到怎么用很少的语言表术出来。
由于很多好的文章由于太长不很被人了解,所以作了一个简化版 ,
用以吸引读者,扩大讨论。
这的确也是,很多人难以一次读完
37 楼
抛出异常的爱
2007-06-06
引用
使用checked异常,应意味着有许多的try…catch在你的代码中。当在编写和处理越来越多的try…catch块之后,许多人终于开始怀疑checked异常倒底是否应该作为标准用法了。
如果对异常有一定的规律性可以用spring来作异常处理,
减少恶心的try catch 块。。。。
(这样用checked的理由又多了一点点)
引用
如果一个异常是致命的,不可恢复的。或者调用者去捕获它没有任何益处,使用unChecked异常。
如果一个异常是可以恢复的,可以被调用者正确处理的,使用checked异常。
在使用unChecked异常时,必须在在方法声明中详细的说明该方法可能会抛出的unChekced异常。由调用者自己去决定是否捕获unChecked异常
如果一个异常是可以恢复的,可以被调用者正确处理的,使用checked异常。
在使用unChecked异常时,必须在在方法声明中详细的说明该方法可能会抛出的unChekced异常。由调用者自己去决定是否捕获unChecked异常
仔细看了后本贴非常不错。
引用
倒底什么时候使用checked异常,什么时候使用unChecked异常?并没有一个绝对的标准。但是笔者可以给出一些建议
当所有调用者必须处理这个异常,可以让调用者进行重试操作;或者该异常相当于该方法的第二个返回值。使用checked异常。
当所有调用者必须处理这个异常,可以让调用者进行重试操作;或者该异常相当于该方法的第二个返回值。使用checked异常。
汗。。。返回值?你的意思可以理解但,语法有问题,并且要加上非常 用返回值。。。
引用
3. 设计一个新的异常类
代码就不拷贝了,非常的精典用法。
第四第五也不错但想不到怎么用很少的语言表术出来。
由于很多好的文章由于太长不很被人了解,所以作了一个简化版 ,
用以吸引读者,扩大讨论。
36 楼
fuhao9611
2007-06-05
giscat 写道
异常处理
try{}catch{}finally{}就完事了
至于是什么类型的异常,其实不必关心
try{}catch{}finally{}就完事了
至于是什么类型的异常,其实不必关心
楼上的要是这么说就不对了,要知其然,知其所以然嘛!
35 楼
hiwzg
2007-06-05
我觉得非常好,正在为项目中的异常处理犯愁呢。<br/>
楼主的异常处理方式非常具有参考性。<br/>
<br/>
<br/>
<strong>klyuan 写道:</strong><br/>
<div class='quote_div'>
<div align='center'><span style='font-size: 16pt;'>J2EE</span><span style='font-size: 16pt;'>项目异常处理</span></div>
<div><span> </span></div>
<div><span> </span>为什么要在J2EE项目中谈异常处理呢?可能许多java初学者都想说:“异常处理不就是try….catch…finally吗?这谁都会啊!”。笔者在初学java时也是这样认为的。如何在一个多层的j2ee项目中定义相应的异常类?在项目中的每一层如何进行异常处理?异常何时被抛出?异常何时被记录?异常该怎么记录?何时需要把checked Exception转化成unchecked Exception ,何时需要把unChecked Exception转化成checked Exception?异常是否应该呈现到前端页面?如何设计一个异常框架?本文将就这些问题进行探讨。</div>
<div style='margin: 0cm 0cm 0pt 18pt; text-indent: -18pt;'>1. JAVA异常处理</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>在面向过程式的编程语言中,我们可以通过返回值来确定方法是否正常执行。比如在一个c语言编写的程序中,如果方法正确的执行则返回1.错误则返回0。在vb或delphi开发的应用程序中,出现错误时,我们就弹出一个消息框给用户。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>通过方法的返回值我们并不能获得错误的详细信息。可能因为方法由不同的程序员编写,当同一类错误在不同的方法出现时,返回的结果和错误信息并不一致。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>所以java语言采取了一个统一的异常处理机制。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>什么是异常?运行时发生的可被捕获和处理的错误。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>在java语言中,Exception是所有异常的父类。任何异常都扩展于Exception类。Exception就相当于一个错误类型。如果要定义一个新的错误类型就扩展一个新的Exception子类。采用异常的好处还在于可以精确的定位到导致程序出错的源代码位置,并获得详细的错误信息。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>Java异常处理通过五个关键字来实现,try,catch,throw ,throws, finally。具体的异常处理结构由try….catch….finally块来实现。try块存放可能出现异常的java语句,catch用来捕获发生的异常,并对异常进行处理。Finally块用来清除程序中未释放的资源。不管理try块的代码如何返回,finally块都总是被执行。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>一个典型的异常处理代码</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'> </div>
<ol class='dp-j'>
<li class='alt'><span><span> </span></span> </li>
<li class=''><span/><span class='keyword'>public</span><span> String getPassword(String userId)</span><span class='keyword'>throws</span><span> DataAccessException{ </span> </li>
<li class='alt'><span>String sql = “select password from userinfo where userid=’”+userId +”’”; </span> </li>
<li class=''><span>String password = </span><span class='keyword'>null</span><span>; </span> </li>
<li class='alt'><span>Connection con = </span><span class='keyword'>null</span><span>; </span> </li>
<li class=''><span>Statement s = </span><span class='keyword'>null</span><span>; </span> </li>
<li class='alt'><span>ResultSet rs = </span><span class='keyword'>null</span><span>; </span> </li>
<li class=''><span/><span class='keyword'>try</span><span>{ </span> </li>
<li class='alt'><span> con = getConnection();</span><span class='comment'>//获得数据连接 </span><span> </span> </li>
<li class=''><span> s = con.createStatement(); </span> </li>
<li class='alt'><span> rs = s.executeQuery(sql); </span> </li>
<li class=''><span> </span><span class='keyword'>while</span><span>(rs.next()){ </span> </li>
<li class='alt'><span> password = rs.getString(</span><span class='number'>1</span><span>); </span> </li>
<li class=''><span> } </span> </li>
<li class='alt'><span> rs.close(); </span> </li>
<li class=''><span> s.close(); </span> </li>
<li class='alt'><span> </span> </li>
<li class=''><span>} </span> </li>
<li class='alt'><span>Catch(SqlException ex){ </span> </li>
<li class=''><span> </span><span class='keyword'>throw</span><span> </span><span class='keyword'>new</span><span> DataAccessException(ex); </span> </li>
<li class='alt'><span>} </span> </li>
<li class=''><span/><span class='keyword'>finally</span><span>{ </span> </li>
<li class='alt'><span> </span><span class='keyword'>try</span><span>{ </span> </li>
<li class=''><span> </span><span class='keyword'>if</span><span>(con != </span><span class='keyword'>null</span><span>){ </span> </li>
<li class='alt'><span> con.close(); </span> </li>
<li class=''><span> } </span> </li>
<li class='alt'><span> } </span> </li>
<li class=''><span> Catch(SQLException sqlEx){ </span> </li>
<li class='alt'><span> </span><span class='keyword'>throw</span><span> </span><span class='keyword'>new</span><span> DataAccessException(“关闭连接失败!”,sqlEx); </span> </li>
<li class=''><span> } </span> </li>
<li class='alt'><span>} </span> </li>
<li class=''><span/><span class='keyword'>return</span><span> password; </span> </li>
<li class='alt'><span>} </span> </li>
<li class=''><span> </span> </li>
</ol>
</div>
</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>可以看出Java的异常处理机制具有的优势:</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>给错误进行了统一的分类,通过扩展Exception类或其子类来实现。从而避免了相同的错误可能在不同的方法中具有不同的错误信息。在不同的方法中出现相同的错误时,只需要throw 相同的异常对象即可。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>获得更为详细的错误信息。通过异常类,可以给异常更为详细,对用户更为有用的错误信息。以便于用户进行跟踪和调试程序。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>把正确的返回结果与错误信息分离。降低了程序的复杂度。调用者无需要对返回结果进行更多的了解。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>强制调用者进行异常处理,提高程序的质量。当一个方法声明需要抛出一个异常时,那么调用者必须使用try….catch块对异常进行处理。当然调用者也可以让异常继续往上一层抛出。</div>
<div> </div>
<div style='margin: 0cm 0cm 0pt 18pt; text-indent: -18pt;'>2. Checked 异常 还是 unChecked 异常?</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>Java异常分为两大类:checked 异常和unChecked 异常。所有继承java.lang.Exception 的异常都属于checked异常。所有继承java.lang.RuntimeException的异常都属于unChecked异常。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>当一个方法去调用一个可能抛出checked异常的方法,必须通过try…catch块对异常进行捕获进行处理或者重新抛出。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>我们看看Connection接口的createStatement()方法的声明。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>public Statement createStatement() throws SQLException;</div>
<div><span> </span></div>
<div><span> SQLException</span>是checked异常。当调用createStatement方法时,java强制调用者必须对SQLException进行捕获处理。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'> </div>
<ol class='dp-j'>
<li class='alt'><span><span> </span><span class='keyword'>public</span><span> String getPassword(String userId){ </span></span> </li>
<li class=''><span> </span><span class='keyword'>try</span><span>{ </span> </li>
<li class='alt'><span> …… </span> </li>
<li class=''><span> Statement s = con.createStatement(); </span> </li>
<li class='alt'><span> …… </span> </li>
<li class=''><span> Catch(SQLException sqlEx){ </span> </li>
<li class='alt'><span> …… </span> </li>
<li class=''><span> } </span> </li>
<li class='alt'><span>…… </span> </li>
<li class=''><span>} </span> </li>
</ol>
</div>
或者</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'> </div>
<ol class='dp-j'>
<li class='alt'><span><span class='keyword'>public</span><span> String getPassword(String userId)</span><span class='keyword'>throws</span><span> SQLException{ </span></span> </li>
<li class=''><span> Statement s = con.createStatement(); </span> </li>
<li class='alt'><span>} </span> </li>
</ol>
</div>
</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>(当然,像Connection,Satement这些资源是需要及时关闭的,这里仅是为了说明checked 异常必须强制调用者进行捕获或继续抛出)</div>
<div style='margin: 0cm 0cm 0pt 18pt;'> </div>
<div style='margin: 0cm 0cm 0pt 18pt;'>unChecked异常也称为运行时异常,通常RuntimeException都表示用户无法恢复的异常,如无法获得数据库连接,不能打开文件等。虽然用户也可以像处理checked异常一样捕获unChecked异常。但是如果调用者并没有去捕获unChecked异常时,编译器并不会强制你那么做。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'> </div>
<div style='margin: 0cm 0cm 0pt 18pt;'>比如一个把字符转换为整型数值的代码如下:</div>
<div style='margin: 0cm 0cm 0pt 18pt;'> </div>
<div style='margin: 0cm 0cm 0pt 18pt;'>
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'> </div>
<ol class='dp-j'>
<li class='alt'><span><span>String str = “</span><span class='number'>123</span><span>”; </span></span> </li>
<li class=''><span/><span class='keyword'>int</span><span> value = Integer.parseInt(str); </span> </li>
</ol>
</div>
</div>
<div style='margin: 0cm 0cm 0pt 18pt;'> </div>
<div style='margin: 0cm 0cm 0pt 18pt;'>parseInt的方法签名为:</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'> </div>
<ol class='dp-j'>
<li class='alt'><span><span class='keyword'>public</span><span> </span><span class='keyword'>static</span><span> </span><span class='keyword'>int</span><span> parseInt(String s) </span><span class='keyword'>throws</span><span> NumberFormatException </span></span> </li>
</ol>
</div>
</div>
<div style='margin: 0cm 0cm 0pt 18pt;'> </div>
<div style='margin: 0cm 0cm 0pt 18pt;'>当传入的参数不能转换成相应的整数时,将会抛出NumberFormatException。因为NumberFormatException扩展于RuntimeException,是unChecked异常。所以调用parseInt方法时无需要try…catch</div>
<div style='margin: 0cm 0cm 0pt 18pt;'> </div>
<div style='margin: 0cm 0cm 0pt 18pt;'>因为java不强制调用者对unChecked异常进行捕获或往上抛出。所以程序员总是喜欢抛出unChecked异常。或者当需要一个新的异常类时,总是习惯的从RuntimeException扩展。当你去调用它些方法时,如果没有相应的catch块,编译器也总是让你通过,同时你也根本无需要去了解这个方法倒底会抛出什么异常。看起来这似乎倒是一个很好的办法,但是这样做却是远离了java异常处理的真实意图。并且对调用你这个类的程序员带来误导,因为调用者根本不知道需要在什么情况下处理异常。而checked异常可以明确的告诉调用者,调用这个类需要处理什么异常。如果调用者不去处理,编译器都会提示并且是无法编译通过的。当然怎么处理是由调用者自己去决定的。</div>
<div> </div>
<div><span> </span>所以Java推荐人们在应用代码中应该使用checked异常。就像我们在上节提到运用异常的好外在于可以强制调用者必须对将会产生的异常进行处理。包括在《java Tutorial》等java官方文档中都把checked异常作为标准用法。</div>
<div><span> </span>使用checked异常,应意味着有许多的try…catch在你的代码中。当在编写和处理越来越多的try…catch块之后,许多人终于开始怀疑checked异常倒底是否应该作为标准用法了。</div>
<div>甚至连大名鼎鼎的《thinking in java》的作者Bruce Eckel也改变了他曾经的想法。Bruce Eckel甚至主张把unChecked异常作为标准用法。并发表文章,以试验checked异常是否应该从java中去掉。Bruce Eckel语:“当少量代码时,checked异常无疑是十分优雅的构思,并有助于避免了许多潜在的错误。但是经验表明,对大量代码来说结果正好相反”</div>
<div>关于checked异常和unChecked异常的详细讨论可以参考</div>
<div style='text-indent: 15.75pt;'>Alan Griffiths <a href='http://www.octopull.demon.co.uk/java/ExceptionalJava.html'>http://www.octopull.demon.co.uk/java/ExceptionalJava.html</a> </div>
<div><span> Bruce Eckel <a href='http://www.mindview.net/Etc/Disscussions/CheckedExceptions'>http://www.mindView.net/Etc/Disscussions/CheckedExceptions</a></span></div>
<div><span> </span>《java Tutorial》 <a href='http://java.sun.com/docs/books/tutorial/essential/exceptions/runtime.html'>http://java.sun.com/docs/books/tutorial/essential/exceptions/runtime.html</a></div>
<div> </div>
<div>使用checked异常会带来许多的问题。</div>
<div><span> checked</span>异常导致了太多的try…catch 代码</div>
<div><span> </span>可能有很多checked异常对开发人员来说是无法合理地进行处理的,比如SQLException。而开发人员却不得不去进行try…catch。当开发人员对一个checked异常无法正确的处理时,通常是简单的把异常打印出来或者是干脆什么也不干。特别是对于新手来说,过多的checked异常让他感到无所适从。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'> </div>
<ol class='dp-j'>
<li class='alt'><span><span> </span><span class='keyword'>try</span><span>{ </span></span> </li>
<li class=''><span> …… </span> </li>
<li class='alt'><span> Statement s = con.createStatement(); </span> </li>
<li class=''><span> …… </span> </li>
<li class='alt'><span> Catch(SQLException sqlEx){ </span> </li>
<li class=''><span> sqlEx.PrintStackTrace(); </span> </li>
<li class='alt'><span> } </span> </li>
<li class=''><span> 或者 </span> </li>
<li class='alt'><span> </span><span class='keyword'>try</span><span>{ </span> </li>
<li class=''><span> …… </span> </li>
<li class='alt'><span> Statement s = con.createStatement(); </span> </li>
<li class=''><span> …… </span> </li>
<li class='alt'><span> Catch(SQLException sqlEx){ </span> </li>
<li class=''><span> </span><span class='comment'>//什么也不干 </span><span> </span> </li>
<li class='alt'><span>} </span> </li>
</ol>
</div>
</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>checked异常导致了许多难以理解的代码产生</div>
<div style='margin: 0cm 0cm 0pt 18pt;'><span> </span>当开发人员必须去捕获一个自己无法正确处理的checked异常,通常的是重新封装成一个新的异常后再抛出。这样做并没有为程序带来任何好处。反而使代码晚难以理解。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>就像我们使用JDBC代码那样,需要处理非常多的try…catch.,真正有用的代码被包含在try…catch之内。使得理解这个方法变理困难起来</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>checked异常导致异常被不断的封装成另一个类异常后再抛出</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'> </div>
<ol class='dp-j'>
<li class='alt'><span><span> </span><span class='keyword'>public</span><span> </span><span class='keyword'>void</span><span> methodA()</span><span class='keyword'>throws</span><span> ExceptionA{ </span></span> </li>
<li class=''><span> ….. </span> </li>
<li class='alt'><span> </span><span class='keyword'>throw</span><span> </span><span class='keyword'>new</span><span> ExceptionA(); </span> </li>
<li class=''><span>} </span> </li>
<li class='alt'><span> </span> </li>
<li class=''><span/><span class='keyword'>public</span><span> </span><span class='keyword'>void</span><span> methodB()</span><span class='keyword'>throws</span><span> ExceptionB{ </span> </li>
<li class='alt'><span> </span><span class='keyword'>try</span><span>{ </span> </li>
<li class=''><span> methodA(); </span> </li>
<li class='alt'><span> …… </span> </li>
<li class=''><span> }</span><span class='keyword'>catch</span><span>(ExceptionA ex){ </span> </li>
<li class='alt'><span> </span><span class='keyword'>throw</span><span> </span><span class='keyword'>new</span><span> ExceptionB(ex); </span> </li>
<li class=''><span> } </span> </li>
<li class='alt'><span>} </span> </li>
<li class=''><span> </span> </li>
<li class='alt'><span> Public </span><span class='keyword'>void</span><span> methodC()</span><span class='keyword'>throws</span><span> ExceptinC{ </span> </li>
<li class=''><span> </span><span class='keyword'>try</span><span>{ </span> </li>
<li class='alt'><span> methodB(); </span> </li>
<li class=''><span> … </span> </li>
<li class='alt'><span> } </span> </li>
<li class=''><span> </span><span class='keyword'>catch</span><span>(ExceptionB ex){ </span> </li>
<li class='alt'><span> </span><span class='keyword'>throw</span><span> </span><span class='keyword'>new</span><span> ExceptionC(ex); </span> </li>
<li class=''><span> } </span> </li>
<li class='alt'><span> } </span> </li>
</ol>
</div>
我们看到异常就这样一层层无休止的被封装和重新抛出。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'> </div>
<div style='margin: 0cm 0cm 0pt 18pt;'>checked异常导致破坏接口方法</div>
<div style='margin: 0cm 0cm 0pt 18pt;'><span> </span>一个接口上的一个方法已被多个类使用,当为这个方法额外添加一个checked异常时,那么所有调用此方法的代码都需要修改。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'> </div>
<div style='margin: 0cm 0cm 0pt 18pt;'>可见上面这些问题都是因为调用者无法正确的处理checked异常时而被迫去捕获和处理,被迫封装后再重新抛出。这样十分不方便,并不能带来任何好处。在这种情况下通常使用unChecked异常。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>chekced异常并不是无一是处,checked异常比传统编程的错误返回值要好用得多。通过编译器来确保正确的处理异常比通过返回值判断要好得多。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'><strong>如果一个异常是致命的,不可恢复的。或者调用者去捕获它没有任何益处,使用unChecked</strong><strong>异常。</strong></div>
<div style='margin: 0cm 0cm 0pt 18pt;'><strong>如果一个异常是可以恢复的,可以被调用者正确处理的,使用checked</strong><strong>异常</strong>。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'><strong>在使用unChecked</strong><strong>异常时,必须在在方法声明中详细的说明该方法可能会抛出的unChekced</strong><strong>异常。由调用者自己去决定是否捕获unChecked</strong><strong>异常</strong></div>
<div style='margin: 0cm 0cm 0pt 18pt;'> </div>
<div style='margin: 0cm 0cm 0pt 18pt;'>倒底什么时候使用checked异常,什么时候使用unChecked异常?并没有一个绝对的标准。但是笔者可以给出一些建议</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>当所有调用者必须处理这个异常,可以让调用者进行重试操作;或者该异常相当于该方法的第二个返回值。使用checked异常。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>这个异常仅是少数比较高级的调用者才能处理,一般的调用者不能正确的处理。使用unchecked异常。有能力处理的调用者可以进行高级处理,一般调用者干脆就不处理。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>这个异常是一个非常严重的错误,如数据库连接错误,文件无法打开等。或者这些异常是与外部环境相关的。不是重试可以解决的。使用unchecked异常。因为这种异常一旦出现,调用者根本无法处理。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>如果不能确定时,使用unchecked异常。并详细描述可能会抛出的异常,以让调用者决定是否进行处理。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'> </div>
<div style='margin: 0cm 0cm 0pt 18pt; text-indent: -18pt;'>3.<span style='font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal;'><font size='3'> </font></span>设计一个新的异常类</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>在设计一个新的异常类时,首先看看是否真正的需要这个异常类。一般情况下尽量不要去设计新的异常类,而是尽量使用java中已经存在的异常类。</div>
<pre>如<div class='code_title'>java 代码</div><div class='dp-highlighter'><ol class='dp-j'><li class='alt'><span><span>IllegalArgumentException, UnsupportedOperationException </span></span></li></ol></div></pre>
<div style='margin: 0cm 0cm 0pt 18pt;'> </div>
<div style='margin: 0cm 0cm 0pt 18pt;'>不管是新的异常是chekced异常还是unChecked异常。我们都必须考虑异常的嵌套问题。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'> </div>
<ol class='dp-j'>
<li class='alt'><span><span class='keyword'>public</span><span> </span><span class='keyword'>void</span><span> methodA()</span><span class='keyword'>throws</span><span> ExceptionA{ </span></span> </li>
<li class=''><span> ….. </span> </li>
<li class='alt'><span> </span><span class='keyword'>throw</span><span> </span><span class='keyword'>new</span><span> ExceptionA(); </span> </li>
<li class=''><span>} </span> </li>
</ol>
</div>
</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>方法methodA声明会抛出ExceptionA.</div>
<div style='margin: 0cm 0cm 0pt 18pt;'> </div>
<div style='margin: 0cm 0cm 0pt 18pt;'>public void methodB()throws ExceptionB</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>methodB声明会抛出ExceptionB,当在methodB方法中调用methodA时,ExceptionA是无法处理的,所以ExceptionA应该继续往上抛出。一个办法是把methodB声明会抛出ExceptionA.但这样已经改变了MethodB的方法签名。一旦改变,则所有调用methodB的方法都要进行改变。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>另一个办法是把ExceptionA封装成ExceptionB,然后再抛出。如果我们不把ExceptionA封装在ExceptionB中,就丢失了根异常信息,使得无法跟踪异常的原始出处。</div>
<div style='text-indent: 15.75pt;'>
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'> </div>
<ol class='dp-j'>
<li class='alt'><span><span class='keyword'>public</span><span> </span><span class='keyword'>void</span><span> methodB()</span><span class='keyword'>throws</span><span> ExceptionB{ </span></span> </li>
<li class=''><span> </span><span class='keyword'>try</span><span>{ </span> </li>
<li class='alt'><span> methodA(); </span> </li>
<li class=''><span> …… </span> </li>
<li class='alt'><span> }</span><span class='keyword'>catch</span><span>(ExceptionA ex){ </span> </li>
<li class=''><span> </span><span class='keyword'>throw</span><span> </span><span class='keyword'>new</span><span> ExceptionB(ex); </span> </li>
<li class='alt'><span> } </span> </li>
<li class=''><span>} </span> </li>
</ol>
</div>
</div>
<div style='margin: 0cm 0cm 0pt 18pt;'> </div>
<div><span> </span>如上面的代码中,ExceptionB嵌套一个ExceptionA.我们暂且把ExceptionA称为“起因异常”,因为ExceptionA导致了ExceptionB的产生。这样才不使异常信息丢失。</div>
<div>所以我们在定义一个新的异常类时,必须提供这样一个可以包含嵌套异常的构造函数。并有一个私有成员来保存这个“起因异常”。</div>
<div>
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'> </div>
<ol class='dp-j'>
<li class='alt'><span><span class='keyword'>public</span><span> Class ExceptionB </span><span class='keyword'>extends</span><span> Exception{ </span></span> </li>
<li class=''><span/><span class='keyword'>private</span><span> Throwable cause; </span> </li>
<li class='alt'><span> </span> </li>
<li class=''><span/><span class='keyword'>public</span><span> ExceptionB(String msg, Throwable ex){ </span> </li>
<li class='alt'><span> </span><span class='keyword'>super</span><span>(msg); </span> </li>
<li class=''><span> </span><span class='keyword'>this</span><span>.cause = ex; </span> </li>
<li class='alt'><span>} </span> </li>
<li class=''><span> </span> </li>
<li class='alt'><span/><span class='keyword'>public</span><span> ExceptionB(String msg){ </span> </li>
<li class=''><span> </span><span class='keyword'>super</span><span>(msg); </span> </li>
<li class='alt'><span>} </span> </li>
<li class=''><span> </span> </li>
<li class='alt'><span/><span class='keyword'>public</span><span> ExceptionB(Throwable ex){ </span> </li>
<li class=''><span> </span><span class='keyword'>this</span><span>.cause = ex; </span> </li>
<li class='alt'><span>} </span> </li>
<li class=''><span>} </span> </li>
</ol>
</div>
当然,我们在调用printStackTrace方法时,需要把所有的“起因异常”的信息也同时打印出来。所以我们需要覆写printStackTrace方法来显示全部的异常栈跟踪。包括嵌套异常的栈跟踪。</div>
<div>
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'> </div>
<ol class='dp-j'>
<li class='alt'><span><span class='keyword'>public</span><span> </span><span class='keyword'>void</span><span> printStackTrace(PrintStrean ps){ </span></span></li>
<li class=''><span/><span class='keyword'>if</span><span>(cause == </span><span class='keyword'>null</span><span>){ </span></li>
<li class='alt'><span> </span><span class='keyword'>super</span><span>.printStackTrace(ps); </span></li>
<li class=''><span>}</span><span class='keyword'>else</span><span>{ </span></li>
<li class='alt'><span> ps.println(</span><span class='keyword'>this</span><span>); </span></li>
<li class=''><span> cause.printStackTrace(ps); </span></li>
<li class='alt'><span>} </span></li>
<li class=''><span>} </span></li>
</ol>
</div>
</div>
<div>一个完整的支持嵌套的checked异常类源码如下。我们在这里暂且把它叫做NestedException</div>
<div> </div>
<div>
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'> </div>
<ol class='dp-j'>
<li class='alt'><span><span class='keyword'>public</span><span> NestedException </span><span class='keyword'>extends</span><span> Exception{ </span></span></li>
<li class=''><span/><span class='keyword'>private</span><span> Throwable cause; </span></li>
<li class='alt'><span/><span class='keyword'>public</span><span> NestedException (String msg){ </span></li>
<li class=''><span> </span><span class='keyword'>super</span><span>(msg); </span></li>
<li class='alt'><span>} </span></li>
<li class=''><span> </span></li>
<li class='alt'><span/><span class='keyword'>public</span><span> NestedException(String msg, Throwable ex){ </span></li>
<li class=''><span> </span><span class='keyword'>super</span><span>(msg); </span></li>
<li class='alt'><span> This.cause = ex; </span></li>
<li class=''><span>} </span></li>
<li class='alt'><span> </span></li>
<li class=''><span/><span class='keyword'>public</span><span> Throwable getCause(){ </span></li>
<li class='alt'><span> </span><span class='keyword'>return</span><span> (</span><span class='keyword'>this</span><span>.cause == </span><span class='keyword'>null</span><span> ? </span><span class='keyword'>this</span><span> :</span><span class='keyword'>this</span><span>.cause); </span></li>
<li class=''><span>} </span></li>
<li class='alt'><span> </span></li>
<li class=''><span/><span class='keyword'>public</span><span> getMessage(){ </span></li>
<li class='alt'><span> String message = </span><span class='keyword'>super</span><span>.getMessage(); </span></li>
<li class=''><span> Throwable cause = getCause(); </span></li>
<li class='alt'><span> </span><span class='keyword'>if</span><span>(cause != </span><span class='keyword'>null</span><span>){ </span></li>
<li class=''><span> message = message + “;nested Exception is ” + cause; </span></li>
<li class='alt'><span> } </span></li>
<li class=''><span> </span><span class='keyword'>return</span><span> message; </span></li>
<li class='alt'><span>} </span></li>
<li class=''><span/><span class='keyword'>public</span><span> </span><span class='keyword'>void</span><span> printStackTrace(PrintStream ps){ </span></li>
<li class='alt'><span> </span><span class='keyword'>if</span><span>(getCause == </span><span class='keyword'>null</span><span>){ </span></li>
<li class=''><span> </span><span class='keyword'>super</span><span>.printStackTrace(ps); </span></li>
<li class='alt'><span> </span></li>
<li class=''><span> }</span><span class='keyword'>else</span><span>{ </span></li>
<li class='alt'><span> ps.println(</span><span class='keyword'>this</span><span>); </span></li>
<li class=''><span> getCause().printStackTrace(ps); </span></li>
<li class='alt'><span> } </span></li>
<li class=''><span>} </span></li>
<li class='alt'><span> </span></li>
<li class=''><span/><span class='keyword'>public</span><span> </span><span class='keyword'>void</span><span> printStackTrace(PrintWrite pw){ </span></li>
<li class='alt'><span> </span><span class='keyword'>if</span><span>(getCause() == </span><span class='keyword'>null</span><span>){ </span></li>
<li class=''><span> </span><span class='keyword'>super</span><span>.printStackTrace(pw); </span></li>
<li class='alt'><span> } </span></li>
<li class=''><span> </span><span class='keyword'>else</span><span>{ </span></li>
<li class='alt'><span> pw.println(</span><span class='keyword'>this</span><span>); </span></li>
<li class=''><span> getCause().printStackTrace(pw); </span></li>
<li class='alt'><span> } </span></li>
<li class=''><span>} </span></li>
<li class='alt'><span/><span class='keyword'>public</span><span> </span><span class='keyword'>void</span><span> printStackTrace(){ </span></li>
<li class=''><span> printStackTrace(System.error); </span></li>
<li class='alt'><span>} </span></li>
<li class=''><span>} </span></li>
<li class='alt'><span> </span></li>
</ol>
</div>
同样要设计一个unChecked异常类也与上面一样。只是需要继承RuntimeException。</div>
<div> </div>
<div style='margin: 0cm 0cm 0pt 18pt;'> </div>
<div style='margin: 0cm 0cm 0pt 18pt; text-indent: -18pt;'>4.<span style='font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal;'><font size='3'> </font></span>如何记录异常</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>作为一个大型的应用系统都需要用日志文件来记录系统的运行,以便于跟踪和记录系统的运行情况。系统发生的异常理所当然的需要记录在日志系统中。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'> </div>
<ol class='dp-j'>
<li class='alt'><span><span class='keyword'>public</span><span> String getPassword(String userId)</span><span class='keyword'>throws</span><span> NoSuchUserException{ </span></span></li>
<li class=''><span>UserInfo user = userDao.queryUserById(userId); </span></li>
<li class='alt'><span>If(user == </span><span class='keyword'>null</span><span>){ </span></li>
<li class=''><span> Logger.info(“找不到该用户信息,userId=”+userId); </span></li>
<li class='alt'><span> </span><span class='keyword'>throw</span><span> </span><span class='keyword'>new</span><span> NoSuchUserException(“找不到该用户信息,userId=”+userId); </span></li>
<li class=''><span>} </span></li>
<li class='alt'><span/><span class='keyword'>else</span><span>{ </span></li>
<li class=''><span> </span><span class='keyword'>return</span><span> user.getPassword(); </span></li>
<li class='alt'><span>} </span></li>
<li class=''><span>} </span></li>
<li class='alt'><span> </span></li>
<li class=''><span/><span class='keyword'>public</span><span> </span><span class='keyword'>void</span><span> sendUserPassword(String userId)</span><span class='keyword'>throws</span><span> Exception { </span></li>
<li class='alt'><span>UserInfo user = </span><span class='keyword'>null</span><span>; </span></li>
<li class=''><span/><span class='keyword'>try</span><span>{ </span></li>
<li class='alt'><span> user = getPassword(userId); </span></li>
<li class=''><span> </span><span class='comment'>//…….. </span><span> </span></li>
<li class='alt'><span> sendMail(); </span></li>
<li class=''><span> </span><span class='comment'>// </span><span> </span></li>
<li class='alt'><span>}</span><span class='keyword'>catch</span><span>(NoSuchUserException ex)( </span></li>
<li class=''><span> logger.error(“找不到该用户信息:”+userId+ex); </span></li>
<li class='alt'><span> </span><span class='keyword'>throw</span><span> </span><span class='keyword'>new</span><span> Exception(ex); </span></li>
<li class=''><span>} </span></li>
</ol>
</div>
</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>我们注意到,一个错误被记录了两次.在错误的起源位置我们仅是以info级别进行记录。而在sendUserPassword方法中,我们还把整个异常信息都记录了。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>笔者曾看到很多项目是这样记录异常的,不管三七二一,只有遇到异常就把整个异常全部记录下。如果一个异常被不断的封装抛出多次,那么就被记录了多次。那么异常倒底该在什么地方被记录?</div>
<div style='margin: 0cm 0cm 0pt 18pt;'><strong>异常应该在最初产生的位置记录</strong>!</div>
<div style='margin: 0cm 0cm 0pt 18pt;'> </div>
<div style='margin: 0cm 0cm 0pt 18pt;'>如果必须捕获一个无法正确处理的异常,仅仅是把它封装成另外一种异常往上抛出。不必再次把已经被记录过的异常再次记录。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'> </div>
<div style='margin: 0cm 0cm 0pt 18pt;'><strong>如果捕获到一个异常,但是这个异常是可以处理的。则无需要记录异常</strong></div>
<div style='margin: 0cm 0cm 0pt 18pt;'> </div>
<div style='margin: 0cm 0cm 0pt 18pt;'>
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'> </div>
<ol class='dp-j'>
<li class='alt'><span><span class='keyword'>public</span><span> Date getDate(String str){ </span></span></li>
<li class=''><span> Date applyDate = </span><span class='keyword'>null</span><span>; </span></li>
<li class='alt'><span>SimpleDateFormat format = </span><span class='keyword'>new</span><span> SimpleDateFormat(“MM/dd/yyyy”); </span></li>
<li class=''><span/><span class='keyword'>try</span><span>{ </span></li>
<li class='alt'><span> applyDate = format.parse(applyDateStr); </span></li>
<li class=''><span>} </span></li>
<li class='alt'><span/><span class='keyword'>catch</span><span>(ParseException ex){ </span></li>
<li class=''><span> </span><span class='comment'>//乎略,当格式错误时,返回null </span><span> </span></li>
<li class='alt'><span>} </span></li>
<li class=''><span/><span class='keyword'>return</span><span> applyDate; </span></li>
<li class='alt'><span>} </span></li>
</ol>
</div>
</div>
<div style='margin: 0cm 0cm 0pt 18pt;'><strong>捕获到一个未记录过的异常或外部系统异常时,应该记录异常的详细信息</strong></div>
<div><span>
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'> </div>
<ol class='dp-j'>
<li class='alt'><span><span class='keyword'>try</span><span>{ </span></span></li>
<li class=''><span> …… </span></li>
<li class='alt'><span> String sql=”select * from userinfo”; </span></li>
<li class=''><span> Statement s = con.createStatement(); </span></li>
<li class='alt'><span> …… </span></li>
<li class=''><span> Catch(SQLException sqlEx){ </span></li>
<li class='alt'><span> Logger.error(“sql执行错误”+sql+sqlEx); </span></li>
<li class=''><span>} </span></li>
</ol>
</div>
</span></div>
<div><span> </span>究竟在哪里记录异常信息,及怎么记录异常信息,可能是见仁见智的问题了。甚至有些系统让异常类一记录异常。当产生一个新异常对象时,异常信息就被自动记录。</div>
<div style='text-indent: 10.5pt;'>
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'> </div>
<ol class='dp-j'>
<li class='alt'><span><span> </span><span class='keyword'>public</span><span> </span><span class='keyword'>class</span><span> BusinessException </span><span class='keyword'>extends</span><span> Exception { </span></span></li>
<li class=''><span> </span><span class='keyword'>private</span><span> </span><span class='keyword'>void</span><span> logTrace() { </span></li>
<li class='alt'><span> StringBuffer buffer=</span><span class='keyword'>new</span><span> StringBuffer(); </span></li>
<li class=''><span> buffer.append(</span><span class='string'>"Business Error in Class: "</span><span>); </span></li>
<li class='alt'><span> buffer.append(getClassName()); </span></li>
<li class=''><span> buffer.append(</span><span class='string'>",method: "</span><span>); </span></li>
<li class='alt'><span> buffer.append(getMethodName()); </span></li>
<li class=''><span> buffer.append(</span><span class='string'>",messsage: "</span><span>); </span></li>
<li class='alt'><span> buffer.append(</span><span class='keyword'>this</span><span>.getMessage()); </span></li>
<li class=''><span> logger.error(buffer.toString()); </span></li>
<li class='alt'><span> </span></li>
<li class=''><span>} </span></li>
<li class='alt'><span/><span class='keyword'>public</span><span> BusinessException(String s) { </span></li>
<li class=''><span> </span><span class='keyword'>super</span><span>(s); </span></li>
<li class='alt'><span>race(); </span></li>
<li class=''><span>} </span></li>
</ol>
</div>
这似乎看起来是十分美妙的,其实必然导致了异常被重复记录。同时违反了“类的职责分配原则”,是一种不好的设计。记录异常不属于异常类的行为,记录异常应该由专门的日志系统去做。并且异常的记录信息是不断变化的。我们在记录异常同应该给更丰富些的信息。以利于我们能够根据异常信息找到问题的根源,以解决问题。</div>
<div style='text-indent: 10.5pt;'>虽然我们对记录异常讨论了很多,过多的强调这些反而使开发人员更为疑惑,一种好的方式是为系统提供一个异常处理框架。由框架来决定是否记录异常和怎么记录异常。而不是由普通程序员去决定。但是了解些还是有益的。</div>
<div style='margin: 0cm 0cm 0pt 18pt; text-indent: -18pt;'>5. J2EE项目中的异常处理</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>目前,J2ee项目一般都会从逻辑上分为多层。比较经典的分为三层:表示层,业务层,集成层(包括数据库访问和外部系统的访问)。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>J2ee项目有着其复杂性,J2ee项目的异常处理需要特别注意几个问题。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>在分布式应用时,我们会遇到许多checked异常。所有RMI调用(包括EJB远程接口调用)都会抛出java.rmi.RemoteException;同时RemoteException是checked异常,当我们在业务系统中进行远程调用时,我们都需要编写大量的代码来处理这些checked异常。而一旦发生RemoteException这些checked异常对系统是非常严重的,几乎没有任何进行重试的可能。也就是说,当出现RemoteException这些可怕的checked异常,我们没有任何重试的必要性,却必须要编写大量的try…catch代码去处理它。一般我们都是在最底层进行RMI调用,只要有一个RMI调用,所有上层的接口都会要求抛出RemoteException异常。因为我们处理RemoteException的方式就是把它继续往上抛。这样一来就破坏了我们业务接口。RemoteException这些J2EE系统级的异常严重的影响了我们的业务接口。我们对系统进行分层的目的就是减少系统之间的依赖,每一层的技术改变不至于影响到其它层。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'> </div>
<div style='margin: 0cm 0cm 0pt 18pt;'>
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'> </div>
<ol class='dp-j'>
<li class='alt'><span><span class='comment'>// </span><span> </span></span></li>
<li class=''><span/><span class='keyword'>public</span><span> </span><span class='keyword'>class</span><span> UserSoaImpl </span><span class='keyword'>implements</span><span> UserSoa{ </span></li>
<li class='alt'><span> </span><span class='keyword'>public</span><span> UserInfo getUserInfo(String userId)</span><span class='keyword'>throws</span><span> RemoteException{ </span></li>
<li class=''><span> </span><span class='comment'>//…… </span><span> </span></li>
<li class='alt'><span>远程方法调用. </span></li>
<li class=''><span> </span><span class='comment'>//…… </span><span> </span></li>
<li class='alt'><span> } </span></li>
<li class=''><span>} </span></li>
<li class='alt'><span/><span class='keyword'>public</span><span> </span><span class='keyword'>interface</span><span> UserManager{ </span></li>
<li class=''><span> </span><span class='keyword'>public</span><span> UserInfo getUserInfo(Stirng userId)</span><span class='keyword'>throws</span><span> RemoteException; </span></li>
<li class='alt'><span>} </span></li>
</ol>
</div>
</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>同样JDBC访问都会抛出SQLException的checked异常。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'> </div>
<div style='margin: 0cm 0cm 0pt 18pt;'>为了避免系统级的checked异常对业务系统的深度侵入,我们可以为业务方法定义一个业务系统自己的异常。针对像SQLException,RemoteException这些非常严重的异常,我们可以新定义一个unChecked的异常,然后把SQLException,RemoteException封装成unChecked异常后抛出。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>如果这个系统级的异常是要交由上一级调用者处理的,可以新定义一个checked的业务异常,然后把系统级的异常封存装成业务级的异常后再抛出。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'><strong>一般地,我们需要定义一个unChecked</strong><strong>异常,让集成层接口的所有方法都声明抛出这unChecked</strong><strong>异常</strong>。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'> </div>
<ol class='dp-j'>
<li class='alt'><span><span class='keyword'>public</span><span> DataAccessException </span><span class='keyword'>extends</span><span> RuntimeException{ </span></span></li>
<li class=''><span> …… </span></li>
<li class='alt'><span>} </span></li>
<li class=''><span/><span class='keyword'>public</span><span> </span><span class='keyword'>interface</span><span> UserDao{ </span></li>
<li class='alt'><span> </span><span class='keyword'>public</span><span> String getPassword(String userId)</span><span class='keyword'>throws</span><span> DataAccessException; </span></li>
<li class=''><span>} </span></li>
<li class='alt'><span> </span></li>
<li class=''><span/><span class='keyword'>public</span><span> </span><span class='keyword'>class</span><span> UserDaoImpl </span><span class='keyword'>implements</span><span> UserDAO{ </span></li>
<li class='alt'><span/><span class='keyword'>public</span><span> String getPassword(String userId)</span><span class='keyword'>throws</span><span> DataAccessException{ </span></li>
<li class=''><span> String sql = “select password from userInfo where userId= ‘”+userId+”’”; </span></li>
<li class='alt'><span/><span class='keyword'>try</span><span>{ </span></li>
<li class=''><span> … </span></li>
<li class='alt'><span> </span><span class='comment'>//JDBC调用 </span><span> </span></li>
<li class=''><span> s.executeQuery(sql); </span></li>
<li class='alt'><span> … </span></li>
<li class=''><span> }</span><span class='keyword'>catch</span><span>(SQLException ex){ </span></li>
<li class='alt'><span> </span><span class='keyword'>throw</span><span> </span><span class='keyword'>new</span><span> DataAccessException(“数据库查询失败”+sql,ex); </span></li>
<li class=''><span> } </span></li>
<li class='alt'><span>} </span></li>
<li class=''><span>} </span></li>
</ol>
</div>
</div>
<div style='margin: 0cm 0cm 0pt 18pt;'><strong>定义一个checked</strong><strong>的业务异常,让业务层的接口的所有方法都声明抛出unChecked</strong><strong>异常</strong>.</div>
<div style='margin: 0cm 0cm 0pt 18pt;'> </div>
<div style='margin: 0cm 0cm 0pt 18pt;'>
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'> </div>
<ol class='dp-j'>
<li class='alt'><span><span class='keyword'>public</span><span> </span><span class='keyword'>class</span><span> BusinessException </span><span class='keyword'>extends</span><span> Exception{ </span></span></li>
<li class=''><span> ….. </span></li>
<li class='alt'><span>} </span></li>
<li class=''><span> </span></li>
<li class='alt'><span/><span class='keyword'>public</span><span> </span><span class='keyword'>interface</span><span> UserManager{ </span></li>
<li class=''><span> </span><span class='keyword'>public</span><span> Userinfo copyUserInfo(Userinfo user)</span><span class='keyword'>throws</span><span> BusinessException{ </span></li>
<li class='alt'><span> Userinfo newUser = </span><span class='keyword'>null</span><span>; </span></li>
<li class=''><span> </span><span class='keyword'>try</span><span>{ </span></li>
<li class='alt'><span> newUser = (Userinfo)user.clone(); </span></li>
<li class=''><span>}</span><span class='keyword'>catch</span><span>(CloneNotSupportedException ex){ </span></li>
<li class='alt'><span> </span><span class='keyword'>throw</span><span> </span><span class='keyword'>new</span><span> BusinessException(“不支持clone方法:”+Userinfo.</span><span class='keyword'>class</span><span>.getName(),ex); </span></li>
<li class=''><span>} </span></li>
<li class='alt'><span> } </span></li>
<li class=''><span>} </span></li>
</ol>
</div>
</div>
<div style='margin: 0cm 0cm 0pt 18pt;'> </div>
<div style='margin: 0cm 0cm 0pt 18pt;'>J2ee表示层应该是一个很薄的层,主要的功能为:获得页面请求,把页面的参数组装成POJO对象,调用相应的业务方法,然后进行页面转发,把相应的业务数据呈现给页面。表示层需要注意一个问题,表示层需要对数据的合法性进行校验,比如某些录入域不能为空,字符长度校验等。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>J2ee从页面所有传给后台的参数都是字符型的,如果要求输入数值或日期类型的参数时,必须把字符值转换为相应的数值或日期值。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>如果表示层代码校验参数不合法时,应该返回到原始页面,让用户重新录入数据,并提示相关的错误信息。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'> </div>
<div style='margin: 0cm 0cm 0pt 18pt;'>通常把一个从页面传来的参数转换为数值,我们可以看到这样的代码</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'> </div>
<ol class='dp-j'>
<li class='alt'><span><span>ModeAndView handleRequest(HttpServletRequest request,HttpServletResponse response)</span><span class='keyword'>throws</span><span> Exception{ </span></span></li>
<li class=''><span> String ageStr = request.getParameter(“age”); </span></li>
<li class='alt'><span> </span><span class='keyword'>int</span><span> age = Integer.parse(ageStr); </span></li>
<li class=''><span> ………… </span></li>
<li class='alt'><span> </span></li>
<li class=''><span> String birthDayStr = request.getParameter(“birthDay”); </span></li>
<li class='alt'><span>SimpleDateFormat format = </span><span class='keyword'>new</span><span> SimpleDateFormat(“MM/dd/yyyy”); </span></li>
<li class=''><span>Date birthDay = format.parse(birthDayStr); </span></li>
<li class='alt'><span> </span></li>
<li class=''><span>} </span></li>
</ol>
</div>
</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>上面的代码应该经常见到,但是当用户从页面录入一个不能转换为整型的字符或一个错误的日期值。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>Integer.parse()方法被抛出一个NumberFormatException的unChecked异常。但是这个异常绝对不是一个致命的异常,一般当用户在页面的录入域录入的值不合法时,我们应该提示用户进行重新录入。但是一旦抛出unchecked异常,就没有重试的机会了。像这样的代码造成大量的异常信息显示到页面。使我们的系统看起来非常的脆弱。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>同样,SimpleDateFormat.parse()方法也会抛出ParseException的unChecked异常。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>这种情况我们都应该捕获这些unChecked异常,并给提示用户重新录入。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'> </div>
<ol class='dp-j'>
<li class='alt'><span><span>ModeAndView handleRequest(HttpServletRequest request,HttpServletResponse response)</span><span class='keyword'>throws</span><span> Exception{ </span></span></li>
<li class=''><span> String ageStr = request.getParameter(“age”); </span></li>
<li class='alt'><span>String birthDayStr = request.getParameter(“birthDay”); </span></li>
<li class=''><span> </span><span class='keyword'>int</span><span> age = </span><span class='number'>0</span><span>; </span></li>
<li class='alt'><span> Date birthDay = </span><span class='keyword'>null</span><span>; </span></li>
<li class=''><span/><span class='keyword'>try</span><span>{ </span></li>
<li class='alt'><span>age=Integer.parse(ageStr); </span></li>
<li class=''><span> }</span><span class='keyword'>catch</span><span>(NumberFormatException ex){ </span></li>
<li class='alt'><span> error.reject(“age”,”不是合法的整数值”); </span></li>
<li class=''><span> } </span></li>
<li class='alt'><span> ………… </span></li>
<li class=''><span> </span></li>
<li class='alt'><span> </span><span class='keyword'>try</span><span>{ </span></li>
<li class=''><span>SimpleDateFormat format = </span><span class='keyword'>new</span><span> SimpleDateFormat(“MM/dd/yyyy”); </span></li>
<li class='alt'><span> birthDay = format.parse(birthDayStr); </span></li>
<li class=''><span>}</span><span class='keyword'>catch</span><span>(ParseException ex){ </span></li>
<li class='alt'><span> error.reject(“birthDay”,”</span></li></ol></div></div></div>
楼主的异常处理方式非常具有参考性。<br/>
<br/>
<br/>
<strong>klyuan 写道:</strong><br/>
<div class='quote_div'>
<div align='center'><span style='font-size: 16pt;'>J2EE</span><span style='font-size: 16pt;'>项目异常处理</span></div>
<div><span> </span></div>
<div><span> </span>为什么要在J2EE项目中谈异常处理呢?可能许多java初学者都想说:“异常处理不就是try….catch…finally吗?这谁都会啊!”。笔者在初学java时也是这样认为的。如何在一个多层的j2ee项目中定义相应的异常类?在项目中的每一层如何进行异常处理?异常何时被抛出?异常何时被记录?异常该怎么记录?何时需要把checked Exception转化成unchecked Exception ,何时需要把unChecked Exception转化成checked Exception?异常是否应该呈现到前端页面?如何设计一个异常框架?本文将就这些问题进行探讨。</div>
<div style='margin: 0cm 0cm 0pt 18pt; text-indent: -18pt;'>1. JAVA异常处理</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>在面向过程式的编程语言中,我们可以通过返回值来确定方法是否正常执行。比如在一个c语言编写的程序中,如果方法正确的执行则返回1.错误则返回0。在vb或delphi开发的应用程序中,出现错误时,我们就弹出一个消息框给用户。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>通过方法的返回值我们并不能获得错误的详细信息。可能因为方法由不同的程序员编写,当同一类错误在不同的方法出现时,返回的结果和错误信息并不一致。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>所以java语言采取了一个统一的异常处理机制。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>什么是异常?运行时发生的可被捕获和处理的错误。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>在java语言中,Exception是所有异常的父类。任何异常都扩展于Exception类。Exception就相当于一个错误类型。如果要定义一个新的错误类型就扩展一个新的Exception子类。采用异常的好处还在于可以精确的定位到导致程序出错的源代码位置,并获得详细的错误信息。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>Java异常处理通过五个关键字来实现,try,catch,throw ,throws, finally。具体的异常处理结构由try….catch….finally块来实现。try块存放可能出现异常的java语句,catch用来捕获发生的异常,并对异常进行处理。Finally块用来清除程序中未释放的资源。不管理try块的代码如何返回,finally块都总是被执行。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>一个典型的异常处理代码</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'> </div>
<ol class='dp-j'>
<li class='alt'><span><span> </span></span> </li>
<li class=''><span/><span class='keyword'>public</span><span> String getPassword(String userId)</span><span class='keyword'>throws</span><span> DataAccessException{ </span> </li>
<li class='alt'><span>String sql = “select password from userinfo where userid=’”+userId +”’”; </span> </li>
<li class=''><span>String password = </span><span class='keyword'>null</span><span>; </span> </li>
<li class='alt'><span>Connection con = </span><span class='keyword'>null</span><span>; </span> </li>
<li class=''><span>Statement s = </span><span class='keyword'>null</span><span>; </span> </li>
<li class='alt'><span>ResultSet rs = </span><span class='keyword'>null</span><span>; </span> </li>
<li class=''><span/><span class='keyword'>try</span><span>{ </span> </li>
<li class='alt'><span> con = getConnection();</span><span class='comment'>//获得数据连接 </span><span> </span> </li>
<li class=''><span> s = con.createStatement(); </span> </li>
<li class='alt'><span> rs = s.executeQuery(sql); </span> </li>
<li class=''><span> </span><span class='keyword'>while</span><span>(rs.next()){ </span> </li>
<li class='alt'><span> password = rs.getString(</span><span class='number'>1</span><span>); </span> </li>
<li class=''><span> } </span> </li>
<li class='alt'><span> rs.close(); </span> </li>
<li class=''><span> s.close(); </span> </li>
<li class='alt'><span> </span> </li>
<li class=''><span>} </span> </li>
<li class='alt'><span>Catch(SqlException ex){ </span> </li>
<li class=''><span> </span><span class='keyword'>throw</span><span> </span><span class='keyword'>new</span><span> DataAccessException(ex); </span> </li>
<li class='alt'><span>} </span> </li>
<li class=''><span/><span class='keyword'>finally</span><span>{ </span> </li>
<li class='alt'><span> </span><span class='keyword'>try</span><span>{ </span> </li>
<li class=''><span> </span><span class='keyword'>if</span><span>(con != </span><span class='keyword'>null</span><span>){ </span> </li>
<li class='alt'><span> con.close(); </span> </li>
<li class=''><span> } </span> </li>
<li class='alt'><span> } </span> </li>
<li class=''><span> Catch(SQLException sqlEx){ </span> </li>
<li class='alt'><span> </span><span class='keyword'>throw</span><span> </span><span class='keyword'>new</span><span> DataAccessException(“关闭连接失败!”,sqlEx); </span> </li>
<li class=''><span> } </span> </li>
<li class='alt'><span>} </span> </li>
<li class=''><span/><span class='keyword'>return</span><span> password; </span> </li>
<li class='alt'><span>} </span> </li>
<li class=''><span> </span> </li>
</ol>
</div>
</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>可以看出Java的异常处理机制具有的优势:</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>给错误进行了统一的分类,通过扩展Exception类或其子类来实现。从而避免了相同的错误可能在不同的方法中具有不同的错误信息。在不同的方法中出现相同的错误时,只需要throw 相同的异常对象即可。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>获得更为详细的错误信息。通过异常类,可以给异常更为详细,对用户更为有用的错误信息。以便于用户进行跟踪和调试程序。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>把正确的返回结果与错误信息分离。降低了程序的复杂度。调用者无需要对返回结果进行更多的了解。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>强制调用者进行异常处理,提高程序的质量。当一个方法声明需要抛出一个异常时,那么调用者必须使用try….catch块对异常进行处理。当然调用者也可以让异常继续往上一层抛出。</div>
<div> </div>
<div style='margin: 0cm 0cm 0pt 18pt; text-indent: -18pt;'>2. Checked 异常 还是 unChecked 异常?</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>Java异常分为两大类:checked 异常和unChecked 异常。所有继承java.lang.Exception 的异常都属于checked异常。所有继承java.lang.RuntimeException的异常都属于unChecked异常。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>当一个方法去调用一个可能抛出checked异常的方法,必须通过try…catch块对异常进行捕获进行处理或者重新抛出。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>我们看看Connection接口的createStatement()方法的声明。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>public Statement createStatement() throws SQLException;</div>
<div><span> </span></div>
<div><span> SQLException</span>是checked异常。当调用createStatement方法时,java强制调用者必须对SQLException进行捕获处理。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'> </div>
<ol class='dp-j'>
<li class='alt'><span><span> </span><span class='keyword'>public</span><span> String getPassword(String userId){ </span></span> </li>
<li class=''><span> </span><span class='keyword'>try</span><span>{ </span> </li>
<li class='alt'><span> …… </span> </li>
<li class=''><span> Statement s = con.createStatement(); </span> </li>
<li class='alt'><span> …… </span> </li>
<li class=''><span> Catch(SQLException sqlEx){ </span> </li>
<li class='alt'><span> …… </span> </li>
<li class=''><span> } </span> </li>
<li class='alt'><span>…… </span> </li>
<li class=''><span>} </span> </li>
</ol>
</div>
或者</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'> </div>
<ol class='dp-j'>
<li class='alt'><span><span class='keyword'>public</span><span> String getPassword(String userId)</span><span class='keyword'>throws</span><span> SQLException{ </span></span> </li>
<li class=''><span> Statement s = con.createStatement(); </span> </li>
<li class='alt'><span>} </span> </li>
</ol>
</div>
</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>(当然,像Connection,Satement这些资源是需要及时关闭的,这里仅是为了说明checked 异常必须强制调用者进行捕获或继续抛出)</div>
<div style='margin: 0cm 0cm 0pt 18pt;'> </div>
<div style='margin: 0cm 0cm 0pt 18pt;'>unChecked异常也称为运行时异常,通常RuntimeException都表示用户无法恢复的异常,如无法获得数据库连接,不能打开文件等。虽然用户也可以像处理checked异常一样捕获unChecked异常。但是如果调用者并没有去捕获unChecked异常时,编译器并不会强制你那么做。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'> </div>
<div style='margin: 0cm 0cm 0pt 18pt;'>比如一个把字符转换为整型数值的代码如下:</div>
<div style='margin: 0cm 0cm 0pt 18pt;'> </div>
<div style='margin: 0cm 0cm 0pt 18pt;'>
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'> </div>
<ol class='dp-j'>
<li class='alt'><span><span>String str = “</span><span class='number'>123</span><span>”; </span></span> </li>
<li class=''><span/><span class='keyword'>int</span><span> value = Integer.parseInt(str); </span> </li>
</ol>
</div>
</div>
<div style='margin: 0cm 0cm 0pt 18pt;'> </div>
<div style='margin: 0cm 0cm 0pt 18pt;'>parseInt的方法签名为:</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'> </div>
<ol class='dp-j'>
<li class='alt'><span><span class='keyword'>public</span><span> </span><span class='keyword'>static</span><span> </span><span class='keyword'>int</span><span> parseInt(String s) </span><span class='keyword'>throws</span><span> NumberFormatException </span></span> </li>
</ol>
</div>
</div>
<div style='margin: 0cm 0cm 0pt 18pt;'> </div>
<div style='margin: 0cm 0cm 0pt 18pt;'>当传入的参数不能转换成相应的整数时,将会抛出NumberFormatException。因为NumberFormatException扩展于RuntimeException,是unChecked异常。所以调用parseInt方法时无需要try…catch</div>
<div style='margin: 0cm 0cm 0pt 18pt;'> </div>
<div style='margin: 0cm 0cm 0pt 18pt;'>因为java不强制调用者对unChecked异常进行捕获或往上抛出。所以程序员总是喜欢抛出unChecked异常。或者当需要一个新的异常类时,总是习惯的从RuntimeException扩展。当你去调用它些方法时,如果没有相应的catch块,编译器也总是让你通过,同时你也根本无需要去了解这个方法倒底会抛出什么异常。看起来这似乎倒是一个很好的办法,但是这样做却是远离了java异常处理的真实意图。并且对调用你这个类的程序员带来误导,因为调用者根本不知道需要在什么情况下处理异常。而checked异常可以明确的告诉调用者,调用这个类需要处理什么异常。如果调用者不去处理,编译器都会提示并且是无法编译通过的。当然怎么处理是由调用者自己去决定的。</div>
<div> </div>
<div><span> </span>所以Java推荐人们在应用代码中应该使用checked异常。就像我们在上节提到运用异常的好外在于可以强制调用者必须对将会产生的异常进行处理。包括在《java Tutorial》等java官方文档中都把checked异常作为标准用法。</div>
<div><span> </span>使用checked异常,应意味着有许多的try…catch在你的代码中。当在编写和处理越来越多的try…catch块之后,许多人终于开始怀疑checked异常倒底是否应该作为标准用法了。</div>
<div>甚至连大名鼎鼎的《thinking in java》的作者Bruce Eckel也改变了他曾经的想法。Bruce Eckel甚至主张把unChecked异常作为标准用法。并发表文章,以试验checked异常是否应该从java中去掉。Bruce Eckel语:“当少量代码时,checked异常无疑是十分优雅的构思,并有助于避免了许多潜在的错误。但是经验表明,对大量代码来说结果正好相反”</div>
<div>关于checked异常和unChecked异常的详细讨论可以参考</div>
<div style='text-indent: 15.75pt;'>Alan Griffiths <a href='http://www.octopull.demon.co.uk/java/ExceptionalJava.html'>http://www.octopull.demon.co.uk/java/ExceptionalJava.html</a> </div>
<div><span> Bruce Eckel <a href='http://www.mindview.net/Etc/Disscussions/CheckedExceptions'>http://www.mindView.net/Etc/Disscussions/CheckedExceptions</a></span></div>
<div><span> </span>《java Tutorial》 <a href='http://java.sun.com/docs/books/tutorial/essential/exceptions/runtime.html'>http://java.sun.com/docs/books/tutorial/essential/exceptions/runtime.html</a></div>
<div> </div>
<div>使用checked异常会带来许多的问题。</div>
<div><span> checked</span>异常导致了太多的try…catch 代码</div>
<div><span> </span>可能有很多checked异常对开发人员来说是无法合理地进行处理的,比如SQLException。而开发人员却不得不去进行try…catch。当开发人员对一个checked异常无法正确的处理时,通常是简单的把异常打印出来或者是干脆什么也不干。特别是对于新手来说,过多的checked异常让他感到无所适从。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'> </div>
<ol class='dp-j'>
<li class='alt'><span><span> </span><span class='keyword'>try</span><span>{ </span></span> </li>
<li class=''><span> …… </span> </li>
<li class='alt'><span> Statement s = con.createStatement(); </span> </li>
<li class=''><span> …… </span> </li>
<li class='alt'><span> Catch(SQLException sqlEx){ </span> </li>
<li class=''><span> sqlEx.PrintStackTrace(); </span> </li>
<li class='alt'><span> } </span> </li>
<li class=''><span> 或者 </span> </li>
<li class='alt'><span> </span><span class='keyword'>try</span><span>{ </span> </li>
<li class=''><span> …… </span> </li>
<li class='alt'><span> Statement s = con.createStatement(); </span> </li>
<li class=''><span> …… </span> </li>
<li class='alt'><span> Catch(SQLException sqlEx){ </span> </li>
<li class=''><span> </span><span class='comment'>//什么也不干 </span><span> </span> </li>
<li class='alt'><span>} </span> </li>
</ol>
</div>
</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>checked异常导致了许多难以理解的代码产生</div>
<div style='margin: 0cm 0cm 0pt 18pt;'><span> </span>当开发人员必须去捕获一个自己无法正确处理的checked异常,通常的是重新封装成一个新的异常后再抛出。这样做并没有为程序带来任何好处。反而使代码晚难以理解。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>就像我们使用JDBC代码那样,需要处理非常多的try…catch.,真正有用的代码被包含在try…catch之内。使得理解这个方法变理困难起来</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>checked异常导致异常被不断的封装成另一个类异常后再抛出</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'> </div>
<ol class='dp-j'>
<li class='alt'><span><span> </span><span class='keyword'>public</span><span> </span><span class='keyword'>void</span><span> methodA()</span><span class='keyword'>throws</span><span> ExceptionA{ </span></span> </li>
<li class=''><span> ….. </span> </li>
<li class='alt'><span> </span><span class='keyword'>throw</span><span> </span><span class='keyword'>new</span><span> ExceptionA(); </span> </li>
<li class=''><span>} </span> </li>
<li class='alt'><span> </span> </li>
<li class=''><span/><span class='keyword'>public</span><span> </span><span class='keyword'>void</span><span> methodB()</span><span class='keyword'>throws</span><span> ExceptionB{ </span> </li>
<li class='alt'><span> </span><span class='keyword'>try</span><span>{ </span> </li>
<li class=''><span> methodA(); </span> </li>
<li class='alt'><span> …… </span> </li>
<li class=''><span> }</span><span class='keyword'>catch</span><span>(ExceptionA ex){ </span> </li>
<li class='alt'><span> </span><span class='keyword'>throw</span><span> </span><span class='keyword'>new</span><span> ExceptionB(ex); </span> </li>
<li class=''><span> } </span> </li>
<li class='alt'><span>} </span> </li>
<li class=''><span> </span> </li>
<li class='alt'><span> Public </span><span class='keyword'>void</span><span> methodC()</span><span class='keyword'>throws</span><span> ExceptinC{ </span> </li>
<li class=''><span> </span><span class='keyword'>try</span><span>{ </span> </li>
<li class='alt'><span> methodB(); </span> </li>
<li class=''><span> … </span> </li>
<li class='alt'><span> } </span> </li>
<li class=''><span> </span><span class='keyword'>catch</span><span>(ExceptionB ex){ </span> </li>
<li class='alt'><span> </span><span class='keyword'>throw</span><span> </span><span class='keyword'>new</span><span> ExceptionC(ex); </span> </li>
<li class=''><span> } </span> </li>
<li class='alt'><span> } </span> </li>
</ol>
</div>
我们看到异常就这样一层层无休止的被封装和重新抛出。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'> </div>
<div style='margin: 0cm 0cm 0pt 18pt;'>checked异常导致破坏接口方法</div>
<div style='margin: 0cm 0cm 0pt 18pt;'><span> </span>一个接口上的一个方法已被多个类使用,当为这个方法额外添加一个checked异常时,那么所有调用此方法的代码都需要修改。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'> </div>
<div style='margin: 0cm 0cm 0pt 18pt;'>可见上面这些问题都是因为调用者无法正确的处理checked异常时而被迫去捕获和处理,被迫封装后再重新抛出。这样十分不方便,并不能带来任何好处。在这种情况下通常使用unChecked异常。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>chekced异常并不是无一是处,checked异常比传统编程的错误返回值要好用得多。通过编译器来确保正确的处理异常比通过返回值判断要好得多。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'><strong>如果一个异常是致命的,不可恢复的。或者调用者去捕获它没有任何益处,使用unChecked</strong><strong>异常。</strong></div>
<div style='margin: 0cm 0cm 0pt 18pt;'><strong>如果一个异常是可以恢复的,可以被调用者正确处理的,使用checked</strong><strong>异常</strong>。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'><strong>在使用unChecked</strong><strong>异常时,必须在在方法声明中详细的说明该方法可能会抛出的unChekced</strong><strong>异常。由调用者自己去决定是否捕获unChecked</strong><strong>异常</strong></div>
<div style='margin: 0cm 0cm 0pt 18pt;'> </div>
<div style='margin: 0cm 0cm 0pt 18pt;'>倒底什么时候使用checked异常,什么时候使用unChecked异常?并没有一个绝对的标准。但是笔者可以给出一些建议</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>当所有调用者必须处理这个异常,可以让调用者进行重试操作;或者该异常相当于该方法的第二个返回值。使用checked异常。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>这个异常仅是少数比较高级的调用者才能处理,一般的调用者不能正确的处理。使用unchecked异常。有能力处理的调用者可以进行高级处理,一般调用者干脆就不处理。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>这个异常是一个非常严重的错误,如数据库连接错误,文件无法打开等。或者这些异常是与外部环境相关的。不是重试可以解决的。使用unchecked异常。因为这种异常一旦出现,调用者根本无法处理。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>如果不能确定时,使用unchecked异常。并详细描述可能会抛出的异常,以让调用者决定是否进行处理。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'> </div>
<div style='margin: 0cm 0cm 0pt 18pt; text-indent: -18pt;'>3.<span style='font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal;'><font size='3'> </font></span>设计一个新的异常类</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>在设计一个新的异常类时,首先看看是否真正的需要这个异常类。一般情况下尽量不要去设计新的异常类,而是尽量使用java中已经存在的异常类。</div>
<pre>如<div class='code_title'>java 代码</div><div class='dp-highlighter'><ol class='dp-j'><li class='alt'><span><span>IllegalArgumentException, UnsupportedOperationException </span></span></li></ol></div></pre>
<div style='margin: 0cm 0cm 0pt 18pt;'> </div>
<div style='margin: 0cm 0cm 0pt 18pt;'>不管是新的异常是chekced异常还是unChecked异常。我们都必须考虑异常的嵌套问题。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'> </div>
<ol class='dp-j'>
<li class='alt'><span><span class='keyword'>public</span><span> </span><span class='keyword'>void</span><span> methodA()</span><span class='keyword'>throws</span><span> ExceptionA{ </span></span> </li>
<li class=''><span> ….. </span> </li>
<li class='alt'><span> </span><span class='keyword'>throw</span><span> </span><span class='keyword'>new</span><span> ExceptionA(); </span> </li>
<li class=''><span>} </span> </li>
</ol>
</div>
</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>方法methodA声明会抛出ExceptionA.</div>
<div style='margin: 0cm 0cm 0pt 18pt;'> </div>
<div style='margin: 0cm 0cm 0pt 18pt;'>public void methodB()throws ExceptionB</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>methodB声明会抛出ExceptionB,当在methodB方法中调用methodA时,ExceptionA是无法处理的,所以ExceptionA应该继续往上抛出。一个办法是把methodB声明会抛出ExceptionA.但这样已经改变了MethodB的方法签名。一旦改变,则所有调用methodB的方法都要进行改变。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>另一个办法是把ExceptionA封装成ExceptionB,然后再抛出。如果我们不把ExceptionA封装在ExceptionB中,就丢失了根异常信息,使得无法跟踪异常的原始出处。</div>
<div style='text-indent: 15.75pt;'>
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'> </div>
<ol class='dp-j'>
<li class='alt'><span><span class='keyword'>public</span><span> </span><span class='keyword'>void</span><span> methodB()</span><span class='keyword'>throws</span><span> ExceptionB{ </span></span> </li>
<li class=''><span> </span><span class='keyword'>try</span><span>{ </span> </li>
<li class='alt'><span> methodA(); </span> </li>
<li class=''><span> …… </span> </li>
<li class='alt'><span> }</span><span class='keyword'>catch</span><span>(ExceptionA ex){ </span> </li>
<li class=''><span> </span><span class='keyword'>throw</span><span> </span><span class='keyword'>new</span><span> ExceptionB(ex); </span> </li>
<li class='alt'><span> } </span> </li>
<li class=''><span>} </span> </li>
</ol>
</div>
</div>
<div style='margin: 0cm 0cm 0pt 18pt;'> </div>
<div><span> </span>如上面的代码中,ExceptionB嵌套一个ExceptionA.我们暂且把ExceptionA称为“起因异常”,因为ExceptionA导致了ExceptionB的产生。这样才不使异常信息丢失。</div>
<div>所以我们在定义一个新的异常类时,必须提供这样一个可以包含嵌套异常的构造函数。并有一个私有成员来保存这个“起因异常”。</div>
<div>
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'> </div>
<ol class='dp-j'>
<li class='alt'><span><span class='keyword'>public</span><span> Class ExceptionB </span><span class='keyword'>extends</span><span> Exception{ </span></span> </li>
<li class=''><span/><span class='keyword'>private</span><span> Throwable cause; </span> </li>
<li class='alt'><span> </span> </li>
<li class=''><span/><span class='keyword'>public</span><span> ExceptionB(String msg, Throwable ex){ </span> </li>
<li class='alt'><span> </span><span class='keyword'>super</span><span>(msg); </span> </li>
<li class=''><span> </span><span class='keyword'>this</span><span>.cause = ex; </span> </li>
<li class='alt'><span>} </span> </li>
<li class=''><span> </span> </li>
<li class='alt'><span/><span class='keyword'>public</span><span> ExceptionB(String msg){ </span> </li>
<li class=''><span> </span><span class='keyword'>super</span><span>(msg); </span> </li>
<li class='alt'><span>} </span> </li>
<li class=''><span> </span> </li>
<li class='alt'><span/><span class='keyword'>public</span><span> ExceptionB(Throwable ex){ </span> </li>
<li class=''><span> </span><span class='keyword'>this</span><span>.cause = ex; </span> </li>
<li class='alt'><span>} </span> </li>
<li class=''><span>} </span> </li>
</ol>
</div>
当然,我们在调用printStackTrace方法时,需要把所有的“起因异常”的信息也同时打印出来。所以我们需要覆写printStackTrace方法来显示全部的异常栈跟踪。包括嵌套异常的栈跟踪。</div>
<div>
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'> </div>
<ol class='dp-j'>
<li class='alt'><span><span class='keyword'>public</span><span> </span><span class='keyword'>void</span><span> printStackTrace(PrintStrean ps){ </span></span></li>
<li class=''><span/><span class='keyword'>if</span><span>(cause == </span><span class='keyword'>null</span><span>){ </span></li>
<li class='alt'><span> </span><span class='keyword'>super</span><span>.printStackTrace(ps); </span></li>
<li class=''><span>}</span><span class='keyword'>else</span><span>{ </span></li>
<li class='alt'><span> ps.println(</span><span class='keyword'>this</span><span>); </span></li>
<li class=''><span> cause.printStackTrace(ps); </span></li>
<li class='alt'><span>} </span></li>
<li class=''><span>} </span></li>
</ol>
</div>
</div>
<div>一个完整的支持嵌套的checked异常类源码如下。我们在这里暂且把它叫做NestedException</div>
<div> </div>
<div>
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'> </div>
<ol class='dp-j'>
<li class='alt'><span><span class='keyword'>public</span><span> NestedException </span><span class='keyword'>extends</span><span> Exception{ </span></span></li>
<li class=''><span/><span class='keyword'>private</span><span> Throwable cause; </span></li>
<li class='alt'><span/><span class='keyword'>public</span><span> NestedException (String msg){ </span></li>
<li class=''><span> </span><span class='keyword'>super</span><span>(msg); </span></li>
<li class='alt'><span>} </span></li>
<li class=''><span> </span></li>
<li class='alt'><span/><span class='keyword'>public</span><span> NestedException(String msg, Throwable ex){ </span></li>
<li class=''><span> </span><span class='keyword'>super</span><span>(msg); </span></li>
<li class='alt'><span> This.cause = ex; </span></li>
<li class=''><span>} </span></li>
<li class='alt'><span> </span></li>
<li class=''><span/><span class='keyword'>public</span><span> Throwable getCause(){ </span></li>
<li class='alt'><span> </span><span class='keyword'>return</span><span> (</span><span class='keyword'>this</span><span>.cause == </span><span class='keyword'>null</span><span> ? </span><span class='keyword'>this</span><span> :</span><span class='keyword'>this</span><span>.cause); </span></li>
<li class=''><span>} </span></li>
<li class='alt'><span> </span></li>
<li class=''><span/><span class='keyword'>public</span><span> getMessage(){ </span></li>
<li class='alt'><span> String message = </span><span class='keyword'>super</span><span>.getMessage(); </span></li>
<li class=''><span> Throwable cause = getCause(); </span></li>
<li class='alt'><span> </span><span class='keyword'>if</span><span>(cause != </span><span class='keyword'>null</span><span>){ </span></li>
<li class=''><span> message = message + “;nested Exception is ” + cause; </span></li>
<li class='alt'><span> } </span></li>
<li class=''><span> </span><span class='keyword'>return</span><span> message; </span></li>
<li class='alt'><span>} </span></li>
<li class=''><span/><span class='keyword'>public</span><span> </span><span class='keyword'>void</span><span> printStackTrace(PrintStream ps){ </span></li>
<li class='alt'><span> </span><span class='keyword'>if</span><span>(getCause == </span><span class='keyword'>null</span><span>){ </span></li>
<li class=''><span> </span><span class='keyword'>super</span><span>.printStackTrace(ps); </span></li>
<li class='alt'><span> </span></li>
<li class=''><span> }</span><span class='keyword'>else</span><span>{ </span></li>
<li class='alt'><span> ps.println(</span><span class='keyword'>this</span><span>); </span></li>
<li class=''><span> getCause().printStackTrace(ps); </span></li>
<li class='alt'><span> } </span></li>
<li class=''><span>} </span></li>
<li class='alt'><span> </span></li>
<li class=''><span/><span class='keyword'>public</span><span> </span><span class='keyword'>void</span><span> printStackTrace(PrintWrite pw){ </span></li>
<li class='alt'><span> </span><span class='keyword'>if</span><span>(getCause() == </span><span class='keyword'>null</span><span>){ </span></li>
<li class=''><span> </span><span class='keyword'>super</span><span>.printStackTrace(pw); </span></li>
<li class='alt'><span> } </span></li>
<li class=''><span> </span><span class='keyword'>else</span><span>{ </span></li>
<li class='alt'><span> pw.println(</span><span class='keyword'>this</span><span>); </span></li>
<li class=''><span> getCause().printStackTrace(pw); </span></li>
<li class='alt'><span> } </span></li>
<li class=''><span>} </span></li>
<li class='alt'><span/><span class='keyword'>public</span><span> </span><span class='keyword'>void</span><span> printStackTrace(){ </span></li>
<li class=''><span> printStackTrace(System.error); </span></li>
<li class='alt'><span>} </span></li>
<li class=''><span>} </span></li>
<li class='alt'><span> </span></li>
</ol>
</div>
同样要设计一个unChecked异常类也与上面一样。只是需要继承RuntimeException。</div>
<div> </div>
<div style='margin: 0cm 0cm 0pt 18pt;'> </div>
<div style='margin: 0cm 0cm 0pt 18pt; text-indent: -18pt;'>4.<span style='font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal;'><font size='3'> </font></span>如何记录异常</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>作为一个大型的应用系统都需要用日志文件来记录系统的运行,以便于跟踪和记录系统的运行情况。系统发生的异常理所当然的需要记录在日志系统中。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'> </div>
<ol class='dp-j'>
<li class='alt'><span><span class='keyword'>public</span><span> String getPassword(String userId)</span><span class='keyword'>throws</span><span> NoSuchUserException{ </span></span></li>
<li class=''><span>UserInfo user = userDao.queryUserById(userId); </span></li>
<li class='alt'><span>If(user == </span><span class='keyword'>null</span><span>){ </span></li>
<li class=''><span> Logger.info(“找不到该用户信息,userId=”+userId); </span></li>
<li class='alt'><span> </span><span class='keyword'>throw</span><span> </span><span class='keyword'>new</span><span> NoSuchUserException(“找不到该用户信息,userId=”+userId); </span></li>
<li class=''><span>} </span></li>
<li class='alt'><span/><span class='keyword'>else</span><span>{ </span></li>
<li class=''><span> </span><span class='keyword'>return</span><span> user.getPassword(); </span></li>
<li class='alt'><span>} </span></li>
<li class=''><span>} </span></li>
<li class='alt'><span> </span></li>
<li class=''><span/><span class='keyword'>public</span><span> </span><span class='keyword'>void</span><span> sendUserPassword(String userId)</span><span class='keyword'>throws</span><span> Exception { </span></li>
<li class='alt'><span>UserInfo user = </span><span class='keyword'>null</span><span>; </span></li>
<li class=''><span/><span class='keyword'>try</span><span>{ </span></li>
<li class='alt'><span> user = getPassword(userId); </span></li>
<li class=''><span> </span><span class='comment'>//…….. </span><span> </span></li>
<li class='alt'><span> sendMail(); </span></li>
<li class=''><span> </span><span class='comment'>// </span><span> </span></li>
<li class='alt'><span>}</span><span class='keyword'>catch</span><span>(NoSuchUserException ex)( </span></li>
<li class=''><span> logger.error(“找不到该用户信息:”+userId+ex); </span></li>
<li class='alt'><span> </span><span class='keyword'>throw</span><span> </span><span class='keyword'>new</span><span> Exception(ex); </span></li>
<li class=''><span>} </span></li>
</ol>
</div>
</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>我们注意到,一个错误被记录了两次.在错误的起源位置我们仅是以info级别进行记录。而在sendUserPassword方法中,我们还把整个异常信息都记录了。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>笔者曾看到很多项目是这样记录异常的,不管三七二一,只有遇到异常就把整个异常全部记录下。如果一个异常被不断的封装抛出多次,那么就被记录了多次。那么异常倒底该在什么地方被记录?</div>
<div style='margin: 0cm 0cm 0pt 18pt;'><strong>异常应该在最初产生的位置记录</strong>!</div>
<div style='margin: 0cm 0cm 0pt 18pt;'> </div>
<div style='margin: 0cm 0cm 0pt 18pt;'>如果必须捕获一个无法正确处理的异常,仅仅是把它封装成另外一种异常往上抛出。不必再次把已经被记录过的异常再次记录。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'> </div>
<div style='margin: 0cm 0cm 0pt 18pt;'><strong>如果捕获到一个异常,但是这个异常是可以处理的。则无需要记录异常</strong></div>
<div style='margin: 0cm 0cm 0pt 18pt;'> </div>
<div style='margin: 0cm 0cm 0pt 18pt;'>
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'> </div>
<ol class='dp-j'>
<li class='alt'><span><span class='keyword'>public</span><span> Date getDate(String str){ </span></span></li>
<li class=''><span> Date applyDate = </span><span class='keyword'>null</span><span>; </span></li>
<li class='alt'><span>SimpleDateFormat format = </span><span class='keyword'>new</span><span> SimpleDateFormat(“MM/dd/yyyy”); </span></li>
<li class=''><span/><span class='keyword'>try</span><span>{ </span></li>
<li class='alt'><span> applyDate = format.parse(applyDateStr); </span></li>
<li class=''><span>} </span></li>
<li class='alt'><span/><span class='keyword'>catch</span><span>(ParseException ex){ </span></li>
<li class=''><span> </span><span class='comment'>//乎略,当格式错误时,返回null </span><span> </span></li>
<li class='alt'><span>} </span></li>
<li class=''><span/><span class='keyword'>return</span><span> applyDate; </span></li>
<li class='alt'><span>} </span></li>
</ol>
</div>
</div>
<div style='margin: 0cm 0cm 0pt 18pt;'><strong>捕获到一个未记录过的异常或外部系统异常时,应该记录异常的详细信息</strong></div>
<div><span>
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'> </div>
<ol class='dp-j'>
<li class='alt'><span><span class='keyword'>try</span><span>{ </span></span></li>
<li class=''><span> …… </span></li>
<li class='alt'><span> String sql=”select * from userinfo”; </span></li>
<li class=''><span> Statement s = con.createStatement(); </span></li>
<li class='alt'><span> …… </span></li>
<li class=''><span> Catch(SQLException sqlEx){ </span></li>
<li class='alt'><span> Logger.error(“sql执行错误”+sql+sqlEx); </span></li>
<li class=''><span>} </span></li>
</ol>
</div>
</span></div>
<div><span> </span>究竟在哪里记录异常信息,及怎么记录异常信息,可能是见仁见智的问题了。甚至有些系统让异常类一记录异常。当产生一个新异常对象时,异常信息就被自动记录。</div>
<div style='text-indent: 10.5pt;'>
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'> </div>
<ol class='dp-j'>
<li class='alt'><span><span> </span><span class='keyword'>public</span><span> </span><span class='keyword'>class</span><span> BusinessException </span><span class='keyword'>extends</span><span> Exception { </span></span></li>
<li class=''><span> </span><span class='keyword'>private</span><span> </span><span class='keyword'>void</span><span> logTrace() { </span></li>
<li class='alt'><span> StringBuffer buffer=</span><span class='keyword'>new</span><span> StringBuffer(); </span></li>
<li class=''><span> buffer.append(</span><span class='string'>"Business Error in Class: "</span><span>); </span></li>
<li class='alt'><span> buffer.append(getClassName()); </span></li>
<li class=''><span> buffer.append(</span><span class='string'>",method: "</span><span>); </span></li>
<li class='alt'><span> buffer.append(getMethodName()); </span></li>
<li class=''><span> buffer.append(</span><span class='string'>",messsage: "</span><span>); </span></li>
<li class='alt'><span> buffer.append(</span><span class='keyword'>this</span><span>.getMessage()); </span></li>
<li class=''><span> logger.error(buffer.toString()); </span></li>
<li class='alt'><span> </span></li>
<li class=''><span>} </span></li>
<li class='alt'><span/><span class='keyword'>public</span><span> BusinessException(String s) { </span></li>
<li class=''><span> </span><span class='keyword'>super</span><span>(s); </span></li>
<li class='alt'><span>race(); </span></li>
<li class=''><span>} </span></li>
</ol>
</div>
这似乎看起来是十分美妙的,其实必然导致了异常被重复记录。同时违反了“类的职责分配原则”,是一种不好的设计。记录异常不属于异常类的行为,记录异常应该由专门的日志系统去做。并且异常的记录信息是不断变化的。我们在记录异常同应该给更丰富些的信息。以利于我们能够根据异常信息找到问题的根源,以解决问题。</div>
<div style='text-indent: 10.5pt;'>虽然我们对记录异常讨论了很多,过多的强调这些反而使开发人员更为疑惑,一种好的方式是为系统提供一个异常处理框架。由框架来决定是否记录异常和怎么记录异常。而不是由普通程序员去决定。但是了解些还是有益的。</div>
<div style='margin: 0cm 0cm 0pt 18pt; text-indent: -18pt;'>5. J2EE项目中的异常处理</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>目前,J2ee项目一般都会从逻辑上分为多层。比较经典的分为三层:表示层,业务层,集成层(包括数据库访问和外部系统的访问)。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>J2ee项目有着其复杂性,J2ee项目的异常处理需要特别注意几个问题。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>在分布式应用时,我们会遇到许多checked异常。所有RMI调用(包括EJB远程接口调用)都会抛出java.rmi.RemoteException;同时RemoteException是checked异常,当我们在业务系统中进行远程调用时,我们都需要编写大量的代码来处理这些checked异常。而一旦发生RemoteException这些checked异常对系统是非常严重的,几乎没有任何进行重试的可能。也就是说,当出现RemoteException这些可怕的checked异常,我们没有任何重试的必要性,却必须要编写大量的try…catch代码去处理它。一般我们都是在最底层进行RMI调用,只要有一个RMI调用,所有上层的接口都会要求抛出RemoteException异常。因为我们处理RemoteException的方式就是把它继续往上抛。这样一来就破坏了我们业务接口。RemoteException这些J2EE系统级的异常严重的影响了我们的业务接口。我们对系统进行分层的目的就是减少系统之间的依赖,每一层的技术改变不至于影响到其它层。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'> </div>
<div style='margin: 0cm 0cm 0pt 18pt;'>
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'> </div>
<ol class='dp-j'>
<li class='alt'><span><span class='comment'>// </span><span> </span></span></li>
<li class=''><span/><span class='keyword'>public</span><span> </span><span class='keyword'>class</span><span> UserSoaImpl </span><span class='keyword'>implements</span><span> UserSoa{ </span></li>
<li class='alt'><span> </span><span class='keyword'>public</span><span> UserInfo getUserInfo(String userId)</span><span class='keyword'>throws</span><span> RemoteException{ </span></li>
<li class=''><span> </span><span class='comment'>//…… </span><span> </span></li>
<li class='alt'><span>远程方法调用. </span></li>
<li class=''><span> </span><span class='comment'>//…… </span><span> </span></li>
<li class='alt'><span> } </span></li>
<li class=''><span>} </span></li>
<li class='alt'><span/><span class='keyword'>public</span><span> </span><span class='keyword'>interface</span><span> UserManager{ </span></li>
<li class=''><span> </span><span class='keyword'>public</span><span> UserInfo getUserInfo(Stirng userId)</span><span class='keyword'>throws</span><span> RemoteException; </span></li>
<li class='alt'><span>} </span></li>
</ol>
</div>
</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>同样JDBC访问都会抛出SQLException的checked异常。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'> </div>
<div style='margin: 0cm 0cm 0pt 18pt;'>为了避免系统级的checked异常对业务系统的深度侵入,我们可以为业务方法定义一个业务系统自己的异常。针对像SQLException,RemoteException这些非常严重的异常,我们可以新定义一个unChecked的异常,然后把SQLException,RemoteException封装成unChecked异常后抛出。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>如果这个系统级的异常是要交由上一级调用者处理的,可以新定义一个checked的业务异常,然后把系统级的异常封存装成业务级的异常后再抛出。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'><strong>一般地,我们需要定义一个unChecked</strong><strong>异常,让集成层接口的所有方法都声明抛出这unChecked</strong><strong>异常</strong>。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'> </div>
<ol class='dp-j'>
<li class='alt'><span><span class='keyword'>public</span><span> DataAccessException </span><span class='keyword'>extends</span><span> RuntimeException{ </span></span></li>
<li class=''><span> …… </span></li>
<li class='alt'><span>} </span></li>
<li class=''><span/><span class='keyword'>public</span><span> </span><span class='keyword'>interface</span><span> UserDao{ </span></li>
<li class='alt'><span> </span><span class='keyword'>public</span><span> String getPassword(String userId)</span><span class='keyword'>throws</span><span> DataAccessException; </span></li>
<li class=''><span>} </span></li>
<li class='alt'><span> </span></li>
<li class=''><span/><span class='keyword'>public</span><span> </span><span class='keyword'>class</span><span> UserDaoImpl </span><span class='keyword'>implements</span><span> UserDAO{ </span></li>
<li class='alt'><span/><span class='keyword'>public</span><span> String getPassword(String userId)</span><span class='keyword'>throws</span><span> DataAccessException{ </span></li>
<li class=''><span> String sql = “select password from userInfo where userId= ‘”+userId+”’”; </span></li>
<li class='alt'><span/><span class='keyword'>try</span><span>{ </span></li>
<li class=''><span> … </span></li>
<li class='alt'><span> </span><span class='comment'>//JDBC调用 </span><span> </span></li>
<li class=''><span> s.executeQuery(sql); </span></li>
<li class='alt'><span> … </span></li>
<li class=''><span> }</span><span class='keyword'>catch</span><span>(SQLException ex){ </span></li>
<li class='alt'><span> </span><span class='keyword'>throw</span><span> </span><span class='keyword'>new</span><span> DataAccessException(“数据库查询失败”+sql,ex); </span></li>
<li class=''><span> } </span></li>
<li class='alt'><span>} </span></li>
<li class=''><span>} </span></li>
</ol>
</div>
</div>
<div style='margin: 0cm 0cm 0pt 18pt;'><strong>定义一个checked</strong><strong>的业务异常,让业务层的接口的所有方法都声明抛出unChecked</strong><strong>异常</strong>.</div>
<div style='margin: 0cm 0cm 0pt 18pt;'> </div>
<div style='margin: 0cm 0cm 0pt 18pt;'>
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'> </div>
<ol class='dp-j'>
<li class='alt'><span><span class='keyword'>public</span><span> </span><span class='keyword'>class</span><span> BusinessException </span><span class='keyword'>extends</span><span> Exception{ </span></span></li>
<li class=''><span> ….. </span></li>
<li class='alt'><span>} </span></li>
<li class=''><span> </span></li>
<li class='alt'><span/><span class='keyword'>public</span><span> </span><span class='keyword'>interface</span><span> UserManager{ </span></li>
<li class=''><span> </span><span class='keyword'>public</span><span> Userinfo copyUserInfo(Userinfo user)</span><span class='keyword'>throws</span><span> BusinessException{ </span></li>
<li class='alt'><span> Userinfo newUser = </span><span class='keyword'>null</span><span>; </span></li>
<li class=''><span> </span><span class='keyword'>try</span><span>{ </span></li>
<li class='alt'><span> newUser = (Userinfo)user.clone(); </span></li>
<li class=''><span>}</span><span class='keyword'>catch</span><span>(CloneNotSupportedException ex){ </span></li>
<li class='alt'><span> </span><span class='keyword'>throw</span><span> </span><span class='keyword'>new</span><span> BusinessException(“不支持clone方法:”+Userinfo.</span><span class='keyword'>class</span><span>.getName(),ex); </span></li>
<li class=''><span>} </span></li>
<li class='alt'><span> } </span></li>
<li class=''><span>} </span></li>
</ol>
</div>
</div>
<div style='margin: 0cm 0cm 0pt 18pt;'> </div>
<div style='margin: 0cm 0cm 0pt 18pt;'>J2ee表示层应该是一个很薄的层,主要的功能为:获得页面请求,把页面的参数组装成POJO对象,调用相应的业务方法,然后进行页面转发,把相应的业务数据呈现给页面。表示层需要注意一个问题,表示层需要对数据的合法性进行校验,比如某些录入域不能为空,字符长度校验等。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>J2ee从页面所有传给后台的参数都是字符型的,如果要求输入数值或日期类型的参数时,必须把字符值转换为相应的数值或日期值。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>如果表示层代码校验参数不合法时,应该返回到原始页面,让用户重新录入数据,并提示相关的错误信息。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'> </div>
<div style='margin: 0cm 0cm 0pt 18pt;'>通常把一个从页面传来的参数转换为数值,我们可以看到这样的代码</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'> </div>
<ol class='dp-j'>
<li class='alt'><span><span>ModeAndView handleRequest(HttpServletRequest request,HttpServletResponse response)</span><span class='keyword'>throws</span><span> Exception{ </span></span></li>
<li class=''><span> String ageStr = request.getParameter(“age”); </span></li>
<li class='alt'><span> </span><span class='keyword'>int</span><span> age = Integer.parse(ageStr); </span></li>
<li class=''><span> ………… </span></li>
<li class='alt'><span> </span></li>
<li class=''><span> String birthDayStr = request.getParameter(“birthDay”); </span></li>
<li class='alt'><span>SimpleDateFormat format = </span><span class='keyword'>new</span><span> SimpleDateFormat(“MM/dd/yyyy”); </span></li>
<li class=''><span>Date birthDay = format.parse(birthDayStr); </span></li>
<li class='alt'><span> </span></li>
<li class=''><span>} </span></li>
</ol>
</div>
</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>上面的代码应该经常见到,但是当用户从页面录入一个不能转换为整型的字符或一个错误的日期值。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>Integer.parse()方法被抛出一个NumberFormatException的unChecked异常。但是这个异常绝对不是一个致命的异常,一般当用户在页面的录入域录入的值不合法时,我们应该提示用户进行重新录入。但是一旦抛出unchecked异常,就没有重试的机会了。像这样的代码造成大量的异常信息显示到页面。使我们的系统看起来非常的脆弱。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>同样,SimpleDateFormat.parse()方法也会抛出ParseException的unChecked异常。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>这种情况我们都应该捕获这些unChecked异常,并给提示用户重新录入。</div>
<div style='margin: 0cm 0cm 0pt 18pt;'>
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'> </div>
<ol class='dp-j'>
<li class='alt'><span><span>ModeAndView handleRequest(HttpServletRequest request,HttpServletResponse response)</span><span class='keyword'>throws</span><span> Exception{ </span></span></li>
<li class=''><span> String ageStr = request.getParameter(“age”); </span></li>
<li class='alt'><span>String birthDayStr = request.getParameter(“birthDay”); </span></li>
<li class=''><span> </span><span class='keyword'>int</span><span> age = </span><span class='number'>0</span><span>; </span></li>
<li class='alt'><span> Date birthDay = </span><span class='keyword'>null</span><span>; </span></li>
<li class=''><span/><span class='keyword'>try</span><span>{ </span></li>
<li class='alt'><span>age=Integer.parse(ageStr); </span></li>
<li class=''><span> }</span><span class='keyword'>catch</span><span>(NumberFormatException ex){ </span></li>
<li class='alt'><span> error.reject(“age”,”不是合法的整数值”); </span></li>
<li class=''><span> } </span></li>
<li class='alt'><span> ………… </span></li>
<li class=''><span> </span></li>
<li class='alt'><span> </span><span class='keyword'>try</span><span>{ </span></li>
<li class=''><span>SimpleDateFormat format = </span><span class='keyword'>new</span><span> SimpleDateFormat(“MM/dd/yyyy”); </span></li>
<li class='alt'><span> birthDay = format.parse(birthDayStr); </span></li>
<li class=''><span>}</span><span class='keyword'>catch</span><span>(ParseException ex){ </span></li>
<li class='alt'><span> error.reject(“birthDay”,”</span></li></ol></div></div></div>
34 楼
klyuan
2007-04-29
呵呵,初学者不是把异常想得太简单,就是想得太复杂
33 楼
skye
2007-04-27
klyuan 写道
jameswei 写道
讨厌NullException.
也讨厌try.catch.finally块
也讨厌try.catch.finally块
是的,NullPointException出错是很难查的,所以要小心!!!
我还记得我刚进公司我们老大就和我说:"你出啥错啥异常我都能容忍,但如果出现了空指针错误,抱歉,自己去QA小组做3天~~~".
32 楼
pufan
2007-04-27
是呀,需不需要处理是业务逻辑的事情,该处理就去处理摆,这和使用什么类型异常没关系。
说到方法嵌套这涉及到一个父类接口签名的问题,checked exception对此支持不是很好。
说到方法嵌套这涉及到一个父类接口签名的问题,checked exception对此支持不是很好。
31 楼
klyuan
2007-04-27
pufan 写道
klyuan 写道
pufan 写道
捕获unchecked再包装成checked,有着必要吗?
上层直接捕获unchecked处理不就得了。
上层直接捕获unchecked处理不就得了。
当然是有必要的!!
比如说,调用EJB,会抛出remote Exception!!
如果我们直接处理
methodA()
try{
//ejb调用
}catch(RemoteException ex){
//这里怎么处理比较合适呢?
}
一般的方式是转为业务系统的异常,相当于继续往上抛出。
methodA() throws BusinessException
try{
//ejb调用
}catch(RemoteException ex){
throw enw BusinessException(ex);
}
这里BusinessException是checked 异常。这相当于我自己处理不了,但是由上一层调用者去处理。
当然一般情况下,这样已经够了。可以避免了业务系统被深度的侵入。又不丢失异常信息。
但是像RemoteException这样的异常是非常严重的异常。一般是无法恢复的。所以转为unchecked异常后再处理会更好一点
methodA()throws RuntimeException
try{
//ejb调用
}catch(RemoteException ex){
throw new RuntimeException(ex);
}
这样,上层调用者就已经解脱了,而又没有丢失原本的异常的详细信息。
当然,我这里只是为了说明异常处理。并不是指EJB调用这样处理异常就够了。
审题要清楚哦,我说的是
引用
捕获unchecked再包装成checked
sorry!!!是我看错了吧!!!
如果你捕获unchecked异常就说明你能够去处理他。
如果你捕获unchecked异常,又没有能力去处理他。而是封装成一个checked异常抛出。得不偿失
30 楼
klyuan
2007-04-27
pufan 写道
因存在方法嵌套,通常JAVA DOC对unchecked Exception无法定义的非常详细。
反过来想一下,绝大多数状况我们会处理unchecked Exception吗?不会的,log以下而已,因此我的建议是当你需要对异常进行特殊处理时再去查JAVA DOC,这时候也来的及,其他情况交给异常框架处理吧。
反过来想一下,绝大多数状况我们会处理unchecked Exception吗?不会的,log以下而已,因此我的建议是当你需要对异常进行特殊处理时再去查JAVA DOC,这时候也来的及,其他情况交给异常框架处理吧。
java doc一般是会标出该方法会抛出什么异常。但是通常不会标出,在什么情况下会抛出该异常。
如果是一个基础性的代码,我个人的观点是需要标示在什么情况下会抛出该unchecked异常。
大多数情况下,我们是无需要处理unchecked 异常!!!!
但是有时我们必须处理unchecked异常!!!特别是越接近客户端的地方。这样才能给用户一个友好而坚固的系统。
比如用户从页面录入了一个日期,在controller或action中把日期字符转成Date类型时,
SimpleDateFormat.parse方法抛出的是unchecked异常!!!
但是用户录入错误是应该让用户重新录入的,并提示用户。
如果你不进行处理,可能就会直接报一个异常到页面!!!
这是用户最不期望的!!!!
29 楼
pufan
2007-04-27
klyuan 写道
pufan 写道
捕获unchecked再包装成checked,有着必要吗?
上层直接捕获unchecked处理不就得了。
上层直接捕获unchecked处理不就得了。
当然是有必要的!!
比如说,调用EJB,会抛出remote Exception!!
如果我们直接处理
methodA()
try{
//ejb调用
}catch(RemoteException ex){
//这里怎么处理比较合适呢?
}
一般的方式是转为业务系统的异常,相当于继续往上抛出。
methodA() throws BusinessException
try{
//ejb调用
}catch(RemoteException ex){
throw enw BusinessException(ex);
}
这里BusinessException是checked 异常。这相当于我自己处理不了,但是由上一层调用者去处理。
当然一般情况下,这样已经够了。可以避免了业务系统被深度的侵入。又不丢失异常信息。
但是像RemoteException这样的异常是非常严重的异常。一般是无法恢复的。所以转为unchecked异常后再处理会更好一点
methodA()throws RuntimeException
try{
//ejb调用
}catch(RemoteException ex){
throw new RuntimeException(ex);
}
这样,上层调用者就已经解脱了,而又没有丢失原本的异常的详细信息。
当然,我这里只是为了说明异常处理。并不是指EJB调用这样处理异常就够了。
审题要清楚哦,我说的是
引用
捕获unchecked再包装成checked
28 楼
klyuan
2007-04-27
抛出异常的爱 写道
pufan 写道
引用
如果一个异常是致命的,不可恢复的。或者调用者去捕获它没有任何益处,使用unChecked异常。
如果一个异常是可以恢复的,可以被调用者正确处理的,使用checked异常。
如果一个异常是可以恢复的,可以被调用者正确处理的,使用checked异常。
这句话有点问题,一个api会有不同的上下文调用环境,换句话说你无法预知未来,因而你无法确定一个异常是不是可恢复的,除非你的API只是包级私有,不过这适用环境也太小了点。像java推荐的做法大量使用checked Exception有点qj人视线的感觉,呵呵。
他有unchecked Exception但是我想捕获
也可以捕获。。。之后包装成checked再抛出就可以了。
我认为unchecked出现可能是由于程序问题才产生的。。。
而check的出现是由于业务逻辑上,
人为可能会进入系统的错误。
不必由程序员来解决的问题都用check来作处理
写代码时要注意一下这样的
方法名 (参数,参数){
如果参数错误用checked异常
try{
代码簖
}check(){
如果这里出了连接异常就出checked异常
}check(){
如果这里出了其它异常就出uncheck异常
}finalyy{}
但是也不竟然!!!
许多的RuntimeException是需要捕获的!!!!
我的文章已经说清楚了!!!
往往来说,unchecked 异常一般都会在系统的底层。
checked异常一般为业务异常!!!
27 楼
pufan
2007-04-27
因存在方法嵌套,通常JAVA DOC对unchecked Exception无法定义的非常详细。
反过来想一下,绝大多数状况我们会处理unchecked Exception吗?不会的,log以下而已,因此我的建议是当你需要对异常进行特殊处理时再去查JAVA DOC,这时候也来的及,其他情况交给异常框架处理吧。
反过来想一下,绝大多数状况我们会处理unchecked Exception吗?不会的,log以下而已,因此我的建议是当你需要对异常进行特殊处理时再去查JAVA DOC,这时候也来的及,其他情况交给异常框架处理吧。
26 楼
klyuan
2007-04-27
pufan 写道
捕获unchecked再包装成checked,有着必要吗?
上层直接捕获unchecked处理不就得了。
上层直接捕获unchecked处理不就得了。
当然是有必要的!!
比如说,调用EJB,会抛出remote Exception!!
如果我们直接处理
methodA()
try{
//ejb调用
}catch(RemoteException ex){
//这里怎么处理比较合适呢?
}
一般的方式是转为业务系统的异常,相当于继续往上抛出。
methodA() throws BusinessException
try{
//ejb调用
}catch(RemoteException ex){
throw enw BusinessException(ex);
}
这里BusinessException是checked 异常。这相当于我自己处理不了,但是由上一层调用者去处理。
当然一般情况下,这样已经够了。可以避免了业务系统被深度的侵入。又不丢失异常信息。
但是像RemoteException这样的异常是非常严重的异常。一般是无法恢复的。所以转为unchecked异常后再处理会更好一点
methodA()throws RuntimeException
try{
//ejb调用
}catch(RemoteException ex){
throw new RuntimeException(ex);
}
这样,上层调用者就已经解脱了,而又没有丢失原本的异常的详细信息。
当然,我这里只是为了说明异常处理。并不是指EJB调用这样处理异常就够了。
25 楼
klyuan
2007-04-27
pufan 写道
引用
如果一个异常是致命的,不可恢复的。或者调用者去捕获它没有任何益处,使用unChecked异常。
如果一个异常是可以恢复的,可以被调用者正确处理的,使用checked异常。
如果一个异常是可以恢复的,可以被调用者正确处理的,使用checked异常。
这句话有点问题,一个api会有不同的上下文调用环境,换句话说你无法预知未来,因而你无法确定一个异常是不是可恢复的,除非你的API只是包级私有,不过这适用环境也太小了点。像java推荐的做法大量使用checked Exception有点qj人视线的感觉,呵呵。
所以对于使用un checked的异常,它的API一定需要在JAVA DOC中说明清楚。这个异常在什么情况下会抛出。
反过来也说,在使用一个会抛出异常的方法时,需要去查看它的API文档。异常将会在什么情况下抛出。
24 楼
pufan
2007-04-27
捕获unchecked再包装成checked,有着必要吗?
上层直接捕获unchecked处理不就得了。
上层直接捕获unchecked处理不就得了。
23 楼
抛出异常的爱
2007-04-27
pufan 写道
引用
如果一个异常是致命的,不可恢复的。或者调用者去捕获它没有任何益处,使用unChecked异常。
如果一个异常是可以恢复的,可以被调用者正确处理的,使用checked异常。
如果一个异常是可以恢复的,可以被调用者正确处理的,使用checked异常。
这句话有点问题,一个api会有不同的上下文调用环境,换句话说你无法预知未来,因而你无法确定一个异常是不是可恢复的,除非你的API只是包级私有,不过这适用环境也太小了点。像java推荐的做法大量使用checked Exception有点qj人视线的感觉,呵呵。
他有unchecked Exception但是我想捕获
也可以捕获。。。之后包装成checked再抛出就可以了。
我认为unchecked出现可能是由于程序问题才产生的。。。
而check的出现是由于业务逻辑上,
人为可能会进入系统的错误。
不必由程序员来解决的问题都用check来作处理
写代码时要注意一下这样的
方法名 (参数,参数){
如果参数错误用checked异常
try{
代码簖
}check(){
如果这里出了连接异常就出checked异常
}check(){
如果这里出了其它异常就出uncheck异常
}finalyy{}
22 楼
pufan
2007-04-27
引用
如果一个异常是致命的,不可恢复的。或者调用者去捕获它没有任何益处,使用unChecked异常。
如果一个异常是可以恢复的,可以被调用者正确处理的,使用checked异常。
如果一个异常是可以恢复的,可以被调用者正确处理的,使用checked异常。
这句话有点问题,一个api会有不同的上下文调用环境,换句话说你无法预知未来,因而你无法确定一个异常是不是可恢复的,除非你的API只是包级私有,不过这适用环境也太小了点。像java推荐的做法大量使用checked Exception有点qj人视线的感觉,呵呵。
21 楼
klyuan
2007-04-27
一颗星星都还没有啊,郁闷
20 楼
klyuan
2007-04-27
抛出异常的爱 写道
你的ID与袁xx很不一样啊。。。(还以为是转的呢)
我评精华条件:
1.非常有意义的事(1=1是真的但没什么意义)
2.写的有条理,阅读起来很流畅
(用code包含代码那样代码就有高亮与格式了,
这里要说06z的排版非常的不爽如果不是说的东西很敢兴趣,就给评新手贴了)
3.要有讨论。。。如果没有维护一个帖子问了没人答,一会就沉了也没精华的可能。
我评精华条件:
1.非常有意义的事(1=1是真的但没什么意义)
2.写的有条理,阅读起来很流畅
(用code包含代码那样代码就有高亮与格式了,
这里要说06z的排版非常的不爽如果不是说的东西很敢兴趣,就给评新手贴了)
3.要有讨论。。。如果没有维护一个帖子问了没人答,一会就沉了也没精华的可能。
好的,我回去改一下,用code格式
文章肯定是原创的,只是发表在it168过的。
19 楼
dovecat
2007-04-27
绝对是原创.呵呵~
发表评论
-
一个特殊的异常处理
2008-12-13 23:59 1400一个特殊的异常处理 文:袁光东 一、业务需求说明 前段时间接 ... -
程序员为什么不写单元测试
2007-07-04 11:31 29212程序员为什么不写单 ... -
Spring JavaConfig开发指南(下)
2007-06-03 10:56 6556... -
Spring JavaConfig开发指南(上)
2007-06-03 10:25 7774Spring JavaConfig开发指南 作者:袁光东 1. ... -
ThreadLocal与synchronized
2007-05-22 17:49 27376ThreadLocal与synchronized Java良好 ... -
倒底该怎么写DAO的单元测试?
2007-05-17 16:17 14044public void testAddUserInfo() ... -
详解spring事务属性
2007-05-10 22:55 20431Spring声明式事务让我们从复杂的事务处理中得到解脱。使得我 ... -
Ibatis读写CLOB数据
2007-04-25 16:43 22856Ibatis是一个高效,方便,易于学习的数据访问组件,在性能上 ... -
细说框架风云 JSF能否拯救WEB江湖
2007-04-24 18:08 2057细说框架风云 JSF能否拯救WEB江湖 Java ... -
模板方法模式实现探讨
2007-04-23 18:30 4414模板方法(Template Method) ... -
Spring架构设计-增强MultiActionController
2007-04-20 12:04 4835Spring架构设计-增强MultiActionControl ... -
让Spring架构减化事务配置
2007-04-19 12:20 4687让Spring架构减化事务配置 注:原创文章,本文曾发表于it ...
相关推荐
### J2EE项目异常处理的几种方案:深入探讨与实践 在J2EE项目中,异常处理是一项至关重要的任务,它直接关系到系统的稳定性和用户体验。本文将详细解析几种常用的异常处理方案,尤其是针对Struts2框架下的异常管理...
在J2EE项目开发中,异常处理是一项至关重要的任务,它确保了系统的稳定性和用户体验。一个良好的异常处理机制能够提供详细的错误信息,帮助开发者快速定位问题,并且可以在生产环境中优雅地处理异常,防止用户看到...
1. **Java基础知识**:首先,理解J2EE项目需要扎实的Java语言基础,包括面向对象编程、异常处理、集合框架等。 2. **Servlet**:Servlet是Java中用于处理HTTP请求的核心组件,它们在服务器端运行,接收并响应来自...
《J2EE项目源码(企业内部源码)》提供了深入了解J2EE应用程序开发的宝贵机会,特别是对于那些想要深入研究Spring、Struts、Hibernate和JSP等核心技术的开发者而言。这个源码集合是一个实际项目的代码实现,可以作为...
7. **异常处理**:Struts框架提供了全局的异常处理机制,可以定义一个或多个错误页面,用于捕获并处理应用程序中抛出的异常。 8. **拦截器(Interceptor)**:拦截器是Struts2引入的重要特性,它们是实现了特定接口...
- 异常处理:确保代码包含适当的错误处理,以应对数据问题或文件操作失败。 - 版本兼容性:确保使用的Apache POI版本与目标Excel文件格式兼容。 综上所述,J2EE项目开发中的Excel导出涉及到Java编程、Apache POI...
在J2EE项目中,可以使用JUnit进行Java类的单元测试,它提供了断言、测试套件和异常处理等功能,便于开发者编写和运行测试用例。 2. **集成测试**:当单个组件通过单元测试后,需要验证它们之间的交互。JUnit和...
5. **异常处理**:在J2EE项目中,异常处理是关键部分。应定义统一的异常类,用于封装业务异常,并在可能抛出异常的地方进行捕获和处理,防止程序中断。 6. **事务管理**:在JPA或Hibernate等持久层框架中,事务管理...
### J2EE项目开发平台与环境搭建详解 #### 一、引言 J2EE(Java 2 Platform, Enterprise Edition)是Sun Microsystems公司提出的一种基于Java的大型企业级应用程序开发框架,它支持多层分布式应用,能够处理大量...
本书是学习J2EE编程的优秀参考书,主要包括以下内容:第一部分概述了有关J2EE编程的重要概念,如applet的创建、布局管理器和事件处理、异常处理和线程、存储数据和创建网络应用程序、RMI和CORBA;第二部分介绍J2EE的...
8. **异常处理**:良好的异常处理机制是任何项目的关键,本项目可能使用try-catch-finally结构和自定义异常来捕获和处理可能出现的问题。 9. **国际化与本地化**:大型项目通常需要支持多种语言,因此项目可能包含...
《J2EE项目代码编写规范详解》 代码编写规范是软件开发中不可或缺的一部分,它旨在提升代码质量,增强代码可读性,降低维护成本,并帮助开发者形成良好的编程习惯。对于J2EE项目而言,规范化的代码编写尤为重要,...
- **异常处理**:处理可能出现的文件读写异常,确保程序的健壮性。 - **配置和接口**:提供配置参数,如文件路径、列映射等,并定义对外的接口,使得其他模块可以方便地调用导入导出功能。 5. **说明文档**: ...
这通常涉及到良好的异常处理、资源管理以及负载均衡策略。通过分析这些源代码,我们可以学习如何在实际开发中实现这些目标。 界面的精美则体现了用户体验设计的重要性。在J2EE项目中,通常结合HTML、CSS和...
它们不仅展示了J2EE应用的基本架构,还可能涵盖了各种API的使用、异常处理、日志记录等方面,对于提升初学者的实战能力非常有帮助。在深入学习的过程中,不断实践和调试,将有助于初学者逐步成长为熟练的J2EE开发者...
这包括面向对象编程概念,如封装、继承和多态,以及异常处理、集合框架(如List、Set、Map)和IO流等核心内容。 2. **Servlet与JSP**:Servlet是J2EE服务器端编程的基础,用于接收和响应HTTP请求。JSP则是用于创建...
Java的学习资料涵盖了基础语法、类和对象、集合框架、异常处理、多线程、网络编程、I/O流等众多主题。对于初学者,理解基本的面向对象概念和JavaSE(Java Standard Edition)的核心API是入门的关键。随着经验的增长...
此外,项目可能还涉及权限管理、异常处理、国际化、缓存等高级特性。通过这个项目,开发者可以掌握如何在实际环境中运用J2EE技术栈,为后续的企业级项目开发打下坚实基础。对于初学者来说,这是一个很好的实践平台,...
回顾一下上一个J2EE工程,是否遇到过类似错误没有记 入日志或者被多次记录的情况?是否只是因为在某处代码吃 掉了异常导致你花费无数次时间...这篇文章为你提供了在J2EE项目中通过使用错误处理框架使用一些策略的基础。
8. **异常处理**:Struts框架提供了一种统一的异常处理机制,允许开发者定义全局的错误页面或者针对特定异常的处理策略。 9. ** strut2 框架**:随着技术的发展,Struts1逐渐被Struts2取代,Struts2在Struts1的基础...