JAVA异常解读

一、异常的概念和Java 异常体系结构
Java 把异常当作对象来处理,并定义一个基类java.lang.Throwable 作为所有异常的超类。在Java API 中已经定义了许多异常类,这些异常类分为两大类,错误Error 和异常Exception
Thorwable
类所有异常和错误的超类,有两个子类ErrorException ,分别表示错误和异常。其中异常类Exception 又分为运行时 异常(RuntimeException) 和非运行时异常,也称之为不检查异常(Unchecked Exception )和检查异常(Checked Exception )。
Error
是程序无法处理的错误,比如OutOfMemoryErrorThreadDeath 等。这些异常发生时,Java 虚拟机(JVM )一般会选择线程终止。
Exception
是程序本身可以处理的异常,这种异常分两大类运行时异常和非运行时异常。程序中应当尽可能去处理这些异常。
运行时异常都是RuntimeException 类及其子类异常,如NullPointerException IndexOutOfBoundsException 等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引 起的,程序应该从逻辑角度尽可能避免这类异常的发生。
非运行时异常是RuntimeException 以外的异常,类型上都属于Exception 类及其子类。从程序语法角度讲是必须进行处理的异常,如果不 处理,程序就不能编译通过。如IOExceptionSQLException 等以及用户自定义的Exception 异常,一般情况下不自定义检查异 常。

二、异常的捕获和处理
Java 异常的捕获和处理是一个不容易把握的事情,如果处理不当,不但会让程序代码的可读性大大降低,而且导致系统性能低下,甚至引发一些难以发现的错误。
Java
异常处理涉及到五个关键字,分别是:trycatchfinallythrowthrows

try catchfinally 三个语句块应注意的问题
1>try catchfinally 三个语句块均不能单独使用,三者可以组成 try...catch...finallytry...catchtry...finally 三种结构,catch 语句可以有一个或多个,finally 语句最多一个。
2>try
catchfinally 三个代码块中变量的作用域为代码块内部,分别独立而不能相互访问。如果要在三个块中都可以访问,则需要将变量定义到这些块的外面。
3>
多个catch 块时候,只会匹配其中一个异常类并执行catch 块代码,而不会再执行别的catch 块,并且匹配catch 语句的顺序是由上到下。
throw
throws 关键字
throw 关键字是用于方法体内部,用来抛出一个Throwable 类型的异常。如果抛出了检查异常,则还应该在方法头部声明方法可能 抛出的异常类型。该方法的调用者也必须检查处理抛出的异常。如果所有方法都层层上抛获取的异常,最终JVM 会进行处理,处理也很简单,就是打印异常消息和 堆栈信息。如果抛出的是ErrorRuntimeException ,则该方法的调用者可选择处理该异常。
throws
关键字用于方法体外部的方法声明部分,用来声明方法可能会抛出某些异常。仅当抛出了检查异常,该方法的调用者才必须处理或者重新抛出该异常。 当方法的调用者无力处理该异常的时候,应该继续抛出,而不是囫囵吞枣一般在catch 块中打印一下堆栈信息做个勉强处理。

三、  异常处理的一般原则
1> 能处理就早处理,抛出不去还不能处理的就想法消化掉或者转换为RuntimeException 处理。因为对于一个应用系统来说,抛出大量异常是有问题的,应该从程序开发角度尽可能的控制异常发生的可能。
2>
对于检查异常,如果不能行之有效的处理,还不如转换为RuntimeException 抛出。这样也让上层的代码有选择的余地―― 可处理也可不处理。
3>
对于一个应用系统来说,应该有自己的一套异常处理框架,这样当异常发生时,也能得到统一的处理风格,将优雅的异常信息反馈给用户。

J2EE 项目异常处理               

       为什么要在J2EE 项目中谈异常处理呢?可能许多java 初学者都想说: 异常处理不就是 try….catch…finally 吗?这谁都会啊! 。笔者在初学java 时也是这样认为的。如何在一个多层的j2ee 项目中定义相应的异常类?在项 目中的每一层如何进行异常处理?异常何时被抛出?异常何时被记录?异常该怎么记录?何时需要把checked Exception 转化成unchecked Exception ,何时需要把unChecked Exception 转化成checked Exception? 异常是否应该呈现到前端页面?如何设计一个异常框架?本文将就这些问题进行探讨。

1  JAVA 异常处理

在面向过程式的编程语言中,我们可以通过返回值来确定方法是否正常执行。比如在一个c 语言编写的程序中,如果方法正确的执行则返回1. 错误则返回0 。在vbdelphi 开发的应用程序中,出现错误时,我们就弹出一个消息框给用户。

通过方法的返回值我们并不能获得错误的详细信息。可能因为方法由不同的程序员编写,当同一类错误在不同的方法出现时,返回的结果和错误信息并不一致。

所以java 语言采取了一个统一的异常处理机制。

什么是异常?运行时发生的可被捕获和处理的错误。

java 语言中,Exception 是所有异常的父类。任何异常都扩 展于Exception 类。Exception 就相当于一个错误类型。如果要定义一个新的错误类型就扩展一个新的Exception 子类。采用异常的好处 还在于可以精确的定位到导致程序出错的源代码位置, 并获得详细的错误信息。

Java 异常处理通过五个关键字来实 现,try,catch,throw ,throws, finally 。具体的异常处理结构由try….catch….finally 块来实现。try 块存放可能出现异常的java 语句,catch 用来捕获发 生的异常,并对异常进行处理。Finally 块用来清除程序中未释放的资源。不管理try 块的代码如何返回,finally 块都总是被执行。

一个典型的异常处理代码

java 代码

1.     

2.   public String getPassword(String userId)throws DataAccessException{   

3.   String sql = “select password from userinfo where userid=’”+userId +”’”;   

4.   String password = null;   

5.   Connection con = null;   

6.   Statement s = null;   

7.   ResultSet rs = null;   

8.   try{   

9.    con = getConnection();// 获得数据连接   

10.   s = con.createStatement();   

11.   rs = s.executeQuery(sql);   

12.   while(rs.next()){   

13.      password = rs.getString(1);   

14.   }   

15.   rs.close();   

16.   s.close();   

17.       

18.  }   

19.  Catch(SqlException ex){   

20.   throw new DataAccessException(ex);   

21.  }   

22.  finally{   

23.   try{   

24.        if(con != null){   

25.          con.close();   

26.        }   

27.   }   

28.     Catch(SQLException sqlEx){   

29.       throw new DataAccessException(“ 关闭连接失败!”,sqlEx);   

30.     }   

31.  }   

32.  return password;   

33.  }   

34.     

可以看出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 代码

1.          public String getPassword(String userId){   

2.          try{   

3.          ……   

4.                 Statement s = con.createStatement();   

5.                 ……   

6.          Catch(SQLException sqlEx){   

7.                 ……   

8.      }   

9.   ……   

10.  }   

或者

java 代码

1.   public String getPassword(String userId)throws SQLException{   

2.      Statement s = con.createStatement();   

3.   }  

( 当然,像Connection,Satement 这些资源是需要及时关闭的,这里仅是为了说明checked 异常必须强制调用者进行捕获或继续抛出)

 

unChecked 异常也称为运行时异常,通常 RuntimeException 都表示用户无法恢复的异常,如无法获得数据库连接,不能打开文件等。虽然用户也可以像处理checked 异常一样捕获 unChecked 异常。但是如果调用者并没有去捕获unChecked 异常时,编译器并不会强制你那么做。

 

比如一个把字符转换为整型数值的代码如下:

 

java 代码

1.   String str = “123”;   

2.   int value = Integer.parseInt(str);  

 

parseInt 的方法签名为:

java 代码

1.   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

   Bruce Eckel  http://www.mindView.net/Etc/Disscussions/CheckedExceptions

   java Tutorial http://java.sun.com/docs/books/tutorial/essential/exceptions/runtime.html

 

使用checked 异常会带来许多的问题。

       checked 异常导致了太多的try…catch 代码

              可能有很多checked 异常对开发人员来说是无法合理地进行处理的,比如 SQLException 。而开发人员却不得不去进行try…catch 。当开发人员对一个checked 异常无法正确的处理时,通常是简单的把异常打印 出来或者是干脆什么也不干。特别是对于新手来说,过多的checked 异常让他感到无所适从。

java 代码

1.          try{   

2.          ……   

3.                 Statement s = con.createStatement();   

4.                 ……   

5.          Catch(SQLException sqlEx){   

6.                 sqlEx.PrintStackTrace();   

7.      }   

8.       或者   

9.          try{   

10.         ……   

11.                Statement s = con.createStatement();   

12.                ……   

13.         Catch(SQLException sqlEx){   

14.            // 什么也不干   

15.  }   

 

checked 异常导致了许多难以理解的代码产生

        当开发人员必须去捕获一个自己无法正确处理的checked 异常,通常的是重新封装成一个新的异常后再抛出。这样做并没有为程序带来任何好处。反而使代码晚难以理解。

就像我们使用JDBC 代码那样,需要处理非常多的try…catch. ,真正有用的代码被包含在try…catch 之内。使得理解这个方法变理困难起来

checked 异常导致异常被不断的封装成另一个类异常后再抛出

java 代码

1.           public void methodA()throws ExceptionA{   

2.            …..          

3.             throw new ExceptionA();        

4.   }   

5.             

6.   public void methodB()throws ExceptionB{   

7.      try{   

8.         methodA();   

9.         ……   

10.     }catch(ExceptionA ex){   

11.     throw new ExceptionB(ex);   

12.     }   

13.  }   

14.             

15.          Public void methodC()throws ExceptinC{   

16.         try{   

17.           methodB();   

18.           …   

19.         }   

20.         catch(ExceptionB ex){   

21.            throw new ExceptionC(ex);   

22.          }   

23.      }   

我们看到异常就这样一层层无休止的被封装和重新抛出。

 

checked 异常导致破坏接口方法

   一个接口上的一个方法已被多个类使用,当为这个方法额外添加一个checked 异常时,那么所有调用此方法的代码都需要修改。

 

可见上面这些问题都是因为调用者无法正确的处理checked 异常时而被迫去捕获和处理,被迫封装后再重新抛出。这样十分不方便,并不能带来任何好处。在这种情况下通常使用unChecked 异常。

chekced 异常并不是无一是处,checked 异常比传统编程的错误返回值要好用得多。通过编译器来确保正确的处理异常比通过返回值判断要好得多。

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

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

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

 

倒底什么时候使用checked 异常,什么时候使用unChecked 异常?并没有一个绝对的标准。但是笔者可以给出一些建议

当所有调用者必须处理这个异常,可以让调用者进行重试操作;或者该异常相当于该方法的第二个返回值。使用checked 异常。

这个异常仅是少数比较高级的调用者才能处理,一般的调用者不能正确的处理。使用unchecked 异常。有能力处理的调用者可以进行高级处理,一般调用者干脆就不处理。

这个异常是一个非常严重的错误,如数据库连接错误,文件无法打开等。或者这些异常是与外部环境相关的。不是重试可以解决的。使用unchecked 异常。因为这种异常一旦出现,调用者根本无法处理。

如果不能确定时,使用unchecked 异常。并详细描述可能会抛出的异常, 以让调用者决定是否进行处理。

 

3   设计一个新的异常类

在设计一个新的异常类时,首先看看是否真正的需要这个异常类。一般情况下尽量不要去设计新的异常类,而是尽量使用java 中已经存在的异常类。

java 代码

1.   IllegalArgumentException, UnsupportedOperationException  

 

不管是新的异常是chekced 异常还是unChecked 异常。我们都必须考虑异常的嵌套问题。

java 代码

1.   public void methodA()throws ExceptionA{   

2.            …..          

3.             throw new ExceptionA();        

4.   }   

 

方法methodA 声明会抛出ExceptionA.

 

public void methodB()throws ExceptionB

methodB 声明会抛出ExceptionB, 当在methodB 方 法中调用methodA 时,ExceptionA 是无法处理的, 所以ExceptionA 应该继续往上抛出。一个办法是把methodB 声明会抛出 ExceptionA. 但这样已经改变了MethodB 的方法签名。一旦改变,则所有调用methodB 的方法都要进行改变。

另一个办法是把ExceptionA 封装成ExceptionB ,然后再抛出。如果我们不把ExceptionA 封装在ExceptionB 中,就丢失了根异常信息,使得无法跟踪异常的原始出处。

java 代码

1.   public void methodB()throws ExceptionB{   

2.      try{   

3.         methodA();   

4.         ……   

5.      }catch(ExceptionA ex){   

6.        throw new ExceptionB(ex);   

7.      }   

8.   }  

 

   如上面的代码中,ExceptionB 嵌套一个ExceptionA. 我们暂且把ExceptionA 称为 起因异常”, 因为ExceptionA 导致了ExceptionB 的产生。这样才不使异常信息丢失。

所以我们在定义一个新的异常类时,必须提供这样一个可以包含嵌套异常的构造函数。并有一个私有成员来保存这个 起因异常

java 代码

1.   public Class ExceptionB extends Exception{   

2.   private Throwable cause;   

3.       

4.   public ExceptionB(String msg, Throwable ex){   

5.    super(msg);   

6.    this.cause = ex;   

7.   }   

8.       

9.   public ExceptionB(String msg){   

10.   super(msg);   

11.  }   

12.      

13.  public ExceptionB(Throwable ex){   

14.   this.cause = ex;   

15.  }   

16.  }   

当然,我们在调用printStackTrace 方法时,需要把所有的 起因异常 的信息也同时打印出来。所以我们需要覆写printStackTrace 方法来显示全部的异常栈跟踪。包括嵌套异常的栈跟踪。

java 代码

1.   public void printStackTrace(PrintStrean ps){   

2.   if(cause == null){   

3.    super.printStackTrace(ps);   

4.   }else{   

5.    ps.println(this);   

6.    cause.printStackTrace(ps);   

7.   }   

8.   }   

 

一个完整的支持嵌套的checked 异常类源码如下。我们在这里暂且把它叫做NestedException

 

java 代码

1.   public NestedException extends Exception{   

2.   private Throwable cause;   

3.   public NestedException (String msg){   

4.    super(msg);   

5.   }   

6.       

7.   public NestedException(String msg, Throwable ex){   

8.    super(msg);   

9.    This.cause = ex;   

10.  }   

11.      

12.  public Throwable getCause(){   

13.   return (this.cause == null ? this :this.cause);   

14.  }   

15.      

16.  public getMessage(){   

17.   String message = super.getMessage();   

18.   Throwable cause = getCause();   

19.     if(cause != null){   

20.       message = message + “;nested Exception is ” + cause;   

21.     }   

22.   return message;   

23.  }   

24.  public void printStackTrace(PrintStream ps){   

25.   if(getCause == null){   

26.      super.printStackTrace(ps);   

27.         

28.   }else{   

29.   ps.println(this);   

30.   getCause().printStackTrace(ps);   

31.   }   

32.  }   

33.      

34.  public void printStackTrace(PrintWrite pw){   

35.   if(getCause() == null){   

36.      super.printStackTrace(pw);   

37.   }   

38.   else{   

39.      pw.println(this);   

40.      getCause().printStackTrace(pw);   

41.   }   

42.  }   

43.  public void printStackTrace(){   

44.   printStackTrace(System.error);   

45.  }   

46.  }   

47.      

同样要设计一个unChecked 异常类也与上面一样。只是需要继承RuntimeException

 

 

4   如何记录异常

作为一个大型的应用系统都需要用日志文件来记录系统的运行,以便于跟踪和记录系统的运行情况。系统发生的异常理所当然的需要记录在日志系统中。

java 代码

1.   public String getPassword(String userId)throws NoSuchUserException{   

2.   UserInfo user = userDao.queryUserById(userId);   

3.   If(user == null){   

4.    Logger.info(“ 找不到该用户信息,userId=”+userId);   

5.    throw new NoSuchUserException(“ 找不到该用户信息,userId=”+userId);   

6.   }   

7.   else{   

8.    return user.getPassword();   

9.   }   

10.  }   

11.      

12.  public void sendUserPassword(String userId)throws Exception {   

13.  UserInfo user = null;   

14.  try{   

15.    user = getPassword(userId);   

16.     //……..   

17.   sendMail();   

18.   //   

19.  }catch(NoSuchUserException ex)(   

20.     logger.error(“ 找不到该用户信息:”+userId+ex);   

21.     throw new Exception(ex);   

22.  }   

 

我们注意到,一个错误被记录了两次. 在错误的起源位置我们仅是以info 级别进行记录。而在sendUserPassword 方法中,我们还把整个异常信息都记录了。

笔者曾看到很多项目是这样记录异常的,不管三七二一,只有遇到异常就把整个异常全部记录下。如果一个异常被不断的封装抛出多次,那么就被记录了多次。那么异常倒底该在什么地方被记录?

异常应该在最初产生的位置记录

 

如果必须捕获一个无法正确处理的异常,仅仅是把它封装成另外一种异常往上抛出。不必再次把已经被记录过的异常再次记录。

 

如果捕获到一个异常,但是这个异常是可以处理的。则无需要记录异常

 

java 代码

1.   public Date getDate(String str){   

2.    Date applyDate = null;   

3.   SimpleDateFormat format = new SimpleDateFormat(“MM/dd/yyyy”);   

4.   try{   

5.    applyDate = format.parse(applyDateStr);   

6.   }   

7.   catch(ParseException ex){   

8.    // 乎略,当格式错误时,返回null   

9.   }   

10.  return applyDate;   

11.  }   

 

捕获到一个未记录过的异常或外部系统异常时,应该记录异常的详细信息

java 代码

1.   try{   

2.          ……   

3.           String sql=”select * from userinfo”;   

4.                 Statement s = con.createStatement();   

5.                 ……   

6.          Catch(SQLException sqlEx){   

7.             Logger.error(“sql 执行错误”+sql+sqlEx);   

8.   }   

      

   究竟在哪里记录异常信息,及怎么记录异常信息,可能是见仁见智的问题了。甚至有些系统让异常类一记录异常。当产生一个新异常对象时,异常信息就被自动记录。

java 代码

1.     public class BusinessException extends Exception {   

2.         private void logTrace() {   

3.             StringBuffer buffer=new StringBuffer();   

4.             buffer.append("Business Error in Class: ");   

5.             buffer.append(getClassName());   

6.             buffer.append(",method: ");   

7.             buffer.append(getMethodName());   

8.             buffer.append(",messsage: ");   

9.             buffer.append(this.getMessage());   

10.            logger.error(buffer.toString());   

11.               

12.  }   

13.  public BusinessException(String s) {   

14.           super(s);   

15.  race();   

16.  }   

这似乎看起来是十分美妙的,其实必然导致了异常被重复记录。同时违反了 类的职责分配原则 ,是一种不好的设计。记录异常不属于异常类的行为,记录异常应 该由专门的日志系统去做。并且异常的记录信息是不断变化的。我们在记录异常同应该给更丰富些的信息。以利于我们能够根据异常信息找到问题的根源,以解决问 题。

虽然我们对记录异常讨论了很多,过多的强调这些反而使开发人员更为疑惑,一种好的方式是为系统提供一个异常处理框架。由框架来决定是否记录异常和怎么记录异常。而不是由普通程序员去决定。但是了解些还是有益的。

5  J2EE 项目中的异常处理

目前,J2ee 项目一般都会从逻辑上分为多层。比较经典的分为三层:表示层,业务层,集成层( 包括数据库访问和外部系统的访问)

J2ee 项目有着其复杂性,J2ee 项目的异常处理需要特别注意几个问题。

在分布式应用时,我们会遇到许多checked 异常。所有RMI 调用 (包括EJB 远程接口调用)都会抛出java.rmi.RemoteException; 同时RemoteExceptionchecked 异常,当我 们在业务系统中进行远程调用时,我们都需要编写大量的代码来处理这些checked 异常。而一旦发生RemoteException 这些checked 异 常对系统是非常严重的,几乎没有任何进行重试的可能。也就是说,当出现RemoteException 这些可怕的checked 异常,我们没有任何重试的 必要性,却必须要编写大量的try…catch 代码去处理它。一般我们都是在最底层进行RMI 调用,只要有一个RMI 调用,所有上层的接口都会要求抛出 RemoteException 异常。因为我们处理RemoteException 的方式就是把它继续往上抛。这样一来就破坏了我们业务接口。 RemoteException 这些J2EE 系统级的异常严重的影响了我们的业务接口。我们对系统进行分层的目的就是减少系统之间的依赖,每一层的技术改 变不至于影响到其它层。

 

java 代码

1.   //   

2.   public class UserSoaImpl implements UserSoa{   

3.      public UserInfo getUserInfo(String userId)throws RemoteException{   

4.         //……   

5.   远程方法调用.   

6.         //……   

7.      }   

8.   }   

9.   public interface UserManager{   

10.     public UserInfo getUserInfo(Stirng userId)throws RemoteException;   

11.  }   

 

同样JDBC 访问都会抛出SQLExceptionchecked 异常。

 

为了避免系统级的checked 异常对业务系统的深度侵入,我们可以为 业务方法定义一个业务系统自己的异常。针对像SQLException,RemoteException 这些非常严重的异常,我们可以新定义一个 unChecked 的异常,然后把SQLExceptionRemoteException 封装成unChecked 异常后抛出。

如果这个系统级的异常是要交由上一级调用者处理的,可以新定义一个checked 的业务异常,然后把系统级的异常封存装成业务级的异常后再抛出。

一般地,我们需要定义一个unChecked 异常,让集成层接口的所有方法都声明抛出这unChecked 异常

java 代码

1.   public DataAccessException extends RuntimeException{   

2.    ……   

3.   }   

4.   public interface UserDao{   

5.    public String getPassword(String userId)throws DataAccessException;   

6.   }   

7.       

8.   public class UserDaoImpl implements UserDAO{   

9.   public String getPassword(String userId)throws DataAccessException{   

10.   String sql = “select password from userInfo where userId= ‘”+userId+”’”;   

11.  try{   

12.      …   

13.       //JDBC 调用   

14.       s.executeQuery(sql);   

15.      …   

16.     }catch(SQLException ex){   

17.        throw new DataAccessException(“ 数据库查询失败”+sql,ex);   

18.     }   

19.  }   

20.  }   

 

定义一个checked 的业务异常,让业务层的接口的所有方法都声明抛出Checked 异常 .

 

java 代码

1.   public class BusinessException extends Exception{   

2.    …..   

3.   }   

4.       

5.   public interface UserManager{   

6.      public Userinfo copyUserInfo(Userinfo user)throws BusinessException{   

7.         Userinfo newUser = null;   

8.         try{   

9.           newUser = (Userinfo)user.clone();   

10.  }catch(CloneNotSupportedException ex){   

11.   throw new BusinessException(“ 不支持clone 方法:”+Userinfo.class.getName(),ex);   

12.  }   

13.   }   

14.  }  

 

J2ee 表示层应该是一个很薄的层,主要的功能为:获得页面请求,把页 面的参数组装成POJO 对象,调用相应的业务方法,然后进行页面转发,把相应的业务数据呈现给页面。表示层需要注意一个问题,表示层需要对数据的合法性进 行校验,比如某些录入域不能为空,字符长度校验等。

J2ee 从页面所有传给后台的参数都是字符型的,如果要求输入数值或日期类型的参数时,必须把字符值转换为相应的数值或日期值。

如果表示层代码校验参数不合法时,应该返回到原始页面,让用户重新录入数据,并提示相关的错误信息。

 

通常把一个从页面传来的参数转换为数值,我们可以看到这样的代码

java 代码

1.   ModeAndView handleRequest(HttpServletRequest request,HttpServletResponse response)throws Exception{   

2.      String ageStr = request.getParameter(“age”);   

3.      int age = Integer.parse(ageStr);   

4.      …………   

5.       

6.    String birthDayStr = request.getParameter(“birthDay”);   

7.   SimpleDateFormat format = new SimpleDateFormat(“MM/dd/yyyy”);   

8.   Date birthDay = format.parse(birthDayStr);   

9.       

10.  }   

 

上面的代码应该经常见到,但是当用户从页面录入一个不能转换为整型的字符或一个错误的日期值。

Integer.parse() 方法被抛出一个 NumberFormatExceptionunChecked 异常。但是这个异常绝对不是一个致命的异常, 一般当用户在页面的录入域录入的值不合法 时,我们应该提示用户进行重新录入。但是一旦抛出unchecked 异常,就没有重试的机会了。像这样的代码造成大量的异常信息显示到页面。使我们的系统 看起来非常的脆弱。

同样,SimpleDateFormat.parse() 方法也会抛出ParseExceptionunChecked 异常。

这种情况我们都应该捕获这些unChecked 异常,并给提示用户重新录入。

java 代码

1.   ModeAndView handleRequest(HttpServletRequest request,HttpServletResponse response)throws Exception{   

2.      String ageStr = request.getParameter(“age”);   

3.   String birthDayStr = request.getParameter(“birthDay”);   

4.      int age = 0;   

5.    Date birthDay = null;   

6.   try{   

7.   age=Integer.parse(ageStr);   

8.      }catch(NumberFormatException ex){   

9.        error.reject(“age”,” 不是合法的整数值”);   

10.     }   

11.     …………   

12.      

13.   try{   

14.  SimpleDateFormat format = new SimpleDateFormat(“MM/dd/yyyy”);   

15.   birthDay = format.parse(birthDayStr);   

16.  }catch(ParseException ex){   

17.   error.reject(“birthDay”,” 不是合法的日期,请录入’MM/dd/yyy’ 格式的日期”);   

18.  }   

19.      

20.  }  

在表示层一定要弄清楚调用方法的是否会抛出unChecked 异常,什么情况下会抛出这些异常,并作出正确的处理。

在表示层调用系统的业务方法,一般情况下是无需要捕获异常的。如果调用的业务方法抛出的异常相当于第二个返回值时,在这种情况下是需要捕获

六种异常处理的陋习

你觉得自己是一个Java 专家吗?是否肯定自己已经全面掌握了Java 的异常处理机制?在下面这段代码中,你能够迅速找出异常处理的六个问题吗?

1 OutputStreamWriter out = ...
2 java.sql.Connection conn = ...
3 try { // ⑸
4
 Statement stat = conn.createStatement();
5
 ResultSet rs = stat.executeQuery(
6
  "select uid, name from user");
7
 while (rs.next())
8
 {
9
  out.println("ID" + rs.getString("uid") // ⑹
10
   " ,姓名:" + rs.getString("name"));
11
 }
12
 conn.close(); // ⑶
13
 out.close();
14 }
15 catch(Exception ex) // ⑵
16 {
17
 ex.printStackTrace(); //⑴
18 }


  作为一个Java 程序员,你至少应该能够找出两个问题。但是,如果你不能找出全部六个问题,请继续阅读本文。

  本文讨论的不是Java 异常处理的一般性原则,因为这些原则已经被大多数人熟知。我们要做的是分析各种可称为 反例anti-pattern )的违背优秀编码规范的常见坏习惯,帮助读者熟悉这些典型的反面例子,从而能够在实际工作中敏锐地察觉和避免这些问题。

  反例之一:丢弃异常

  代码:15-18 行。

  这段代码捕获了异常却不作任何处理,可以算得上Java 编程中的杀手。从问题出现的频繁程度和祸害程度来看,它也许可以和C/C++ 程序的一个恶名远 播的问题相提并论?? 不检查缓冲区是否已满。如果你看到了这种丢弃(而不是抛出)异常的情况,可以百分之九十九地肯定代码存在问题(在极少数情况下,这段 代码有存在的理由,但最好加上完整的注释,以免引起别人误解)。

  这段代码的错误在于,异常(几乎)总是意味着某些事情不对劲了,或者说至少发生了某些不寻常的事情,我们不应该对程序发出的求救信号保持沉默和无动于 衷。调用一下printStackTrace 算不上 处理异常 。不错,调用printStackTrace 对调试程序有帮助,但程序调试阶段结束之 后,printStackTrace 就不应再在异常处理模块中担负主要责任了。

  丢弃异常的情形非常普遍。打开JDKThreadDeath 类的文档,可以看到下面这段说明: 特别地,虽然出现ThreadDeath 是一种 正 常的情形 ,但ThreadDeath 类是Error 而不是Exception 的子类,因为许多应用会捕获所有的Exception 然后丢弃它不再理 睬。 这段话的意思是,虽然ThreadDeath 代表的是一种普通的问题,但鉴于许多应用会试图捕获所有异常然后不予以适当的处理,所以JDK ThreadDeath 定义成了Error 的子类,因为Error 类代表的是一般的应用不应该去捕获的严重问题。可见,丢弃异常这一坏习惯是如此常见,它 甚至已经影响到了Java 本身的设计。

  那么,应该怎样改正呢?主要有四个选择:

  1 、处理异常。针对该异常采取一些行动,例如修正问题、提醒某个人或进行其他一些处理,要根据具体的情形确定应该采取的动作。再次说明,调用printStackTrace 算不上已经 处理好了异常

  2 、重新抛出异常。处理异常的代码在分析异常之后,认为自己不能处理它,重新抛出异常也不失为一种选择。

  3 、把该异常转换成另一种异常。大多数情况下,这是指把一个低级的异常转换成应用级的异常(其含义更容易被用户了解的异常)。

  4 、不要捕获异常。

  结论一:既然捕获了异常,就要对它进行适当的处理。不要捕获异常之后又把它丢弃,不予理睬。

  反例之二:不指定具体的异常

  代码:15 行。

  许多时候人们会被这样一种 美妙的 想法吸引:用一个catch 语句捕获所有的异常。最常见的情形就是使用catch(Exception ex) 语句。但实际上,在绝大多数情况下,这种做法不值得提倡。为什么呢?

  要理解其原因,我们必须回顾一下catch 语句的用途。catch 语句表示我们预期会出现某种异常,而且希望能够处理该异常。异常类的作用就是告诉 Java 编译器我们想要处理的是哪一种异常。由于绝大多数异常都直接或间接从java.lang.Exception 派 生,catch(Exception ex) 就相当于说我们想要处理几乎所有的异常。

  再来看看前面的代码例子。我们真正想要捕获的异常是什么呢?最明显的一个是SQLException ,这是JDBC 操作中常见的异常。另一个可能的异 常是IOException ,因为它要操作OutputStreamWriter 。显然,在同一个catch 块中处理这两种截然不同的异常是不合适的。如 果用两个catch 块分别捕获SQLExceptionIOException 就要好多了。这就是说,catch 语句应当尽量指定具体的异常类型,而不 应该指定涵盖范围太广的Exception 类。

  另一方面,除了这两个特定的异常,还有其他许多异常也可能出现。例如,如果由于某种原因,executeQuery 返回了null ,该怎么办?答案是 让它们继续抛出,即不必捕获也不必处理。实际上,我们不能也不应该去捕获可能出现的所有异常,程序的其他地方还有捕获异常的机会?? 直至最后由JVM 处 理。

  结论二:在catch 语句中尽可能指定具体的异常类型,必要时使用多个catch 。不要试图处理所有可能出现的异常。

  反例之三:占用资源不释放

  代码:3-14 行。

  异常改变了程序正常的执行流程。这个道理虽然简单,却常常被人们忽视。如果程序用到了文件、SocketJDBC 连接之类的资源,即使遇到了异常,也要正确释放占用的资源。为此,Java 提供了一个简化这类操作的关键词finally

  finally 是样好东西:不管是否出现了异常,Finally 保证在try/catch/finally 块结束之前,执行清理任务的代码总是有机会执行。遗憾的是有些人却不习惯使用finally

  当然,编写finally 块应当多加小心,特别是要注意在finally 块之内抛出的异常?? 这是执行清理任务的最后机会,尽量不要再有难以处理的错误。

  结论三:保证所有资源都被正确释放。充分运用finally 关键词。

反例之四:不说明异常的详细信息

  代码:3-18 行。

  仔细观察这段代码:如果循环内部出现了异常,会发生什么事情?我们可以得到足够的信息判断循环内部出错的原因吗?不能。我们只能知道当前正在处理的类发生了某种错误,但却不能获得任何信息判断导致当前错误的原因。

  printStackTrace 的堆栈跟踪功能显示出程序运行到当前类的执行流程,但只提供了一些最基本的信息,未能说明实际导致错误的原因,同时也不易解读。

  因此,在出现异常时,最好能够提供一些文字信息,例如当前正在执行的类、方法和其他状态信息,包括以一种更适合阅读的方式整理和组织printStackTrace 提供的信息。

  结论四:在异常处理模块中提供适量的错误原因信息,组织错误信息使其易于理解和阅读。

  反例之五:过于庞大的try

  代码:3-14 行。

  经常可以看到有人把大量的代码放入单个try 块,实际上这不是好习惯。这种现象之所以常见,原因就在于有些人图省事,不愿花时间分析一大块代码中哪几 行代码会抛出异常、异常的具体类型是什么。把大量的语句装入单个巨大的try 块就象是出门旅游时把所有日常用品塞入一个大箱子,虽然东西是带上了,但要找 出来可不容易。

  一些新手常常把大量的代码放入单个try 块,然后再在catch 语句中声明Exception ,而不是分离各个可能出现异常的段落并分别捕获其异常。这种做法为分析程序抛出异常的原因带来了困难,因为一大段代码中有太多的地方可能抛出Exception

  结论五:尽量减小try 块的体积。

  反例之六:输出数据不完整

  代码:7-11 行。

  不完整的数据是Java 程序的隐形杀手。仔细观察这段代码,考虑一下如果循环的中间抛出了异常,会发生什么事情。循环的执行当然是要被打断的,其 次,catch 块会执行?? 就这些,再也没有其他动作了。已经输出的数据怎么办?使用这些数据的人或设备将收到一份不完整的(因而也是错误的)数据,却得 不到任何有关这份数据是否完整的提示。对于有些系统来说,数据不完整可能比系统停止运行带来更大的损失。

  较为理想的处置办法是向输出设备写一些信息,声明数据的不完整性;另一种可能有效的办法是,先缓冲要输出的数据,准备好全部数据之后再一次性输出。

  结论六:全面考虑可能出现的异常以及这些异常对执行流程的影响。

  改写后的代码

  根据上面的讨论,下面给出改写后的代码。也许有人会说它稍微有点? 嗦,但是它有了比较完备的异常处理机制。

OutputStreamWriter out = ...
java.sql.Connection conn = ...
try {
 Statement stat = conn.createStatement();
 ResultSet rs = stat.executeQuery(
  "select uid, name from user");
 while (rs.next())
 {
  out.println("ID" + rs.getString("uid") + " ,姓名: " + rs.getString("name"));
 }
}
catch(SQLException sqlex)
{
 out.println(" 警告:数据不完整");
 throw new ApplicationException(" 读取数据时出现SQL 错误", sqlex);
}
catch(IOException ioex)
{
 throw new ApplicationException(" 写入数据时出现IO 错误", ioex);
}
finally
{
 if (conn != null) {
  try {
   conn.close();
  }
  catch(SQLException sqlex2)
  {
   System.err(this.getClass().getName() + ".mymethod - 不能关闭数据库连接: " + sqlex2.toString());
  }
 }

 if (out != null) {
  try {
   out.close();
  }
  catch(IOException ioex2)
  {
   System.err(this.getClass().getName() + ".mymethod - 不能关闭输出文件" + ioex2.toString());
  }
 }
}


  本文的结论不是放之四海皆准的教条,有时常识和经验才是最好的老师。如果你对自己的做法没有百分之百的信心,务必加上详细、全面的注释。

  另一方面,不要笑话这些错误,不妨问问你自己是否真地彻底摆脱了这些坏习惯。即使最有经验的程序员偶尔也会误入歧途,原因很简单,因为它们确确实实带 来了 方便 。所有这些反例都可以看作Java 编程世界的恶魔,它们美丽动人,无孔不入,时刻诱惑着你。也许有人会认为这些都属于鸡皮蒜毛的小事,不足挂 齿,但请记住:勿以恶小而为之,勿以善小而不为。





------------------------------------------------------------------
下面是一些 java 异常集 -------------------------------------------------------------------------------------------

 

算术异常类:ArithmeticExecption

空指针异常类:NullPointerException

类型强制转换异常:ClassCastException

数组负下标异常:NegativeArrayException

数组下标越界异常:ArrayIndexOutOfBoundsException

违背安全原则异常:SecturityException

文件已结束异常:EOFException

文件未找到异常:FileNotFoundException

字符串转换为数字异常:NumberFormatException


操作数据库异常:SQLException


输入输出异常:IOException


方法未找到异常:NoSuchMethodException

java.lang.AbstractMethodError

抽象方法错误。当应用试图调用抽象方法时抛出。

java.lang.AssertionError

断言错。用来指示一个断言失败的情况。

java.lang.ClassCircularityError

类循环依赖错误。在初始化一个类时,若检测到类之间循环依赖则抛出该异常。

java.lang.ClassFormatError

类格式错误。当Java 虚拟机试图从一个文件中读取Java 类,而检测到该文件的内容不符合类的有效格式时抛出。

java.lang.Error

错误。是所有错误的基类,用于标识严重的程序运行问题。这些问题通常描述一些不应被应用程序捕获的反常情况。

java.lang.ExceptionInInitializerError

初始化程序错误。当执行一个类的静态初始化程序的过程中,发生了异常时抛出。静态初始化程序是指直接包含于类中的static 语句段。

java.lang.IllegalAccessError

违法访问错误。当一个应用试图访问、修改某个类的域(Field )或者调用其方法,但是又违反域或方法的可见性声明,则抛出该异常。

java.lang.IncompatibleClassChangeError

不兼容的类变化错误。当正在执行的方法所依赖的类定义发生了不兼容的改变时,抛出该异常。一般在修改了应用中的某些类的声明定义而没有对整个应用重新编译而直接运行的情况下,容易引发该错误。

java.lang.InstantiationError

实例化错误。当一个应用试图通过Javanew 操作符构造一个抽象类或者接口时抛出该异常.

java.lang.InternalError

内部错误。用于指示Java 虚拟机发生了内部错误。

java.lang.LinkageError

链接错误。该错误及其所有子类指示某个类依赖于另外一些类,在该类编译之后,被依赖的类改变了其类定义而没有重新编译所有的类,进而引发错误的情况。

java.lang.NoClassDefFoundError

未找到类定义错误。当Java 虚拟机或者类装载器试图实例化某个类,而找不到该类的定义时抛出该错误。

java.lang.NoSuchFieldError

域不存在错误。当应用试图访问或者修改某类的某个域,而该类的定义中没有该域的定义时抛出该错误。

java.lang.NoSuchMethodError

方法不存在错误。当应用试图调用某类的某个方法,而该类的定义中没有该方法的定义时抛出该错误。

java.lang.OutOfMemoryError

内存不足错误。当可用内存不足以让Java 虚拟机分配给一个对象时抛出该错误。

java.lang.StackOverflowError

堆栈溢出错误。当一个应用递归调用的层次太深而导致堆栈溢出时抛出该错误。

java.lang.ThreadDeath

线程结束。当调用Thread 类的stop 方法时抛出该错误,用于指示线程结束。

java.lang.UnknownError

未知错误。用于指示Java 虚拟机发生了未知严重错误的情况。

java.lang.UnsatisfiedLinkError

未满足的链接错误。当Java 虚拟机未找到某个类的声明为native 方法的本机语言定义时抛出。

java.lang.UnsupportedClassVersionError

不支持的类版本错误。当Java 虚拟机试图从读取某个类文件,但是发现该文件的主、次版本号不被当前Java 虚拟机支持的时候,抛出该错误。

java.lang.VerifyError

验证错误。当验证器检测到某个类文件中存在内部不兼容或者安全问题时抛出该错误。

java.lang.VirtualMachineError

虚拟机错误。用于指示虚拟机被破坏或者继续执行操作所需的资源不足的情况。


java.lang.ArithmeticException

算术条件异常。譬如:整数除零等。

java.lang.ArrayIndexOutOfBoundsException

数组索引越界异常。当对数组的索引值为负数或大于等于数组大小时抛出。

java.lang.ArrayStoreException

数组存储异常。当向数组中存放非数组声明类型对象时抛出。

java.lang.ClassCastException

类造型异常。假设有类ABA 不是B 的父类或子类),OA 的实例,那么当强制将O 构造为类B 的实例时抛出该异常。该异常经常被称为强制类型转换异常。

java.lang.ClassNotFoundException

找不到类异常。当应用试图根据字符串形式的类名构造类,而在遍历CLASSPAH 之后找不到对应名称的class 文件时,抛出该异常。

java.lang.CloneNotSupportedException

不支持克隆异常。当没有实现Cloneable 接口或者不支持克隆方法时, 调用其clone() 方法则抛出该异常。

java.lang.EnumConstantNotPresentException

枚举常量不存在异常。当应用试图通过名称和枚举类型访问一个枚举对象,但该枚举对象并不包含常量时,抛出该异常。

java.lang.Exception

根异常。用以描述应用程序希望捕获的情况。

java.lang.IllegalAccessException

违法的访问异常。当应用试图通过反射方式创建某个类的实例、访问该类属性、调用该类方法,而当时又无法访问类的、属性的、方法的或构造方法的定义时抛出该异常。

java.lang.IllegalMonitorStateException

违法的监控状态异常。当某个线程试图等待一个自己并不拥有的对象(O )的监控器或者通知其他线程等待该对象(O )的监控器时,抛出该异常。

java.lang.IllegalStateException

违法的状态异常。当在Java 环境和应用尚未处于某个方法的合法调用状态,而调用了该方法时,抛出该异常。

java.lang.IllegalThreadStateException

违法的线程状态异常。当县城尚未处于某个方法的合法调用状态,而调用了该方法时,抛出异常。

java.lang.IndexOutOfBoundsException

索引越界异常。当访问某个序列的索引值小于0 或大于等于序列大小时,抛出该异常。

java.lang.InstantiationException

实例化异常。当试图通过newInstance() 方法创建某个类的实例,而该类是一个抽象类或接口时,抛出该异常。

java.lang.InterruptedException

被中止异常。当某个线程处于长时间的等待、休眠或其他暂停状态,而此时其他的线程通过Threadinterrupt 方法终止该线程时抛出该异常。

java.lang.NegativeArraySizeException

数组大小为负值异常。当使用负数大小值创建数组时抛出该异常。

java.lang.NoSuchFieldException

属性不存在异常。当访问某个类的不存在的属性时抛出该异常。

java.lang.NoSuchMethodException

方法不存在异常。当访问某个类的不存在的方法时抛出该异常。

java.lang.NullPointerException

空指针异常。当应用试图在要求使用对象的地方使用了null 时,抛出该异常。譬如:调用null 对象的实例方法、访问null 对象的属性、计算null 对象的长度、使用throw 语句抛出null 等等。

java.lang.NumberFormatException

数字格式异常。当试图将一个String 转换为指定的数字类型,而该字符串确不满足数字类型要求的格式时,抛出该异常。

java.lang.RuntimeException

运行时异常。是所有Java 虚拟机正常操作期间可以被抛出的异常的父类。

java.lang.SecurityException

安全异常。由安全管理器抛出,用于指示违反安全情况的异常。

java.lang.StringIndexOutOfBoundsException

字符串索引越界异常。当使用索引值访问某个字符串中的字符,而该索引值小于0 或大于等于序列大小时,抛出该异常。

java.lang.TypeNotPresentException

类型不存在异常。当应用试图以某个类型名称的字符串表达方式访问该类型,但是根据给定的名称又找不到该类型是抛出该异常。该异常与 ClassNotFoundException 的区别在于该异常是unchecked (不被检查)异常,而ClassNotFoundException checked (被检查)异常。

java.lang.UnsupportedOperationException

不支持的方法异常。指明请求的方法不被支持情况的异常。

异常
javax.servlet.jsp.JspException: Cannot retrieve mapping for action /Login
/Login 是你的action 名字)  

可能原因
action
没有再struts-config.xml 中定义,或没有找到匹配的action ,例如在JSP 文件中使用 <html:form action="Login.do". 将表单提交给Login.do 处理,如果出现上述异常,请查看struts-config.xml 中的定义部分,有 时可能是打错了字符或者是某些不符合规则,可以使用strutsconsole 工具来检查。
-----------------------------------------------------------------------------------------------------------------
异常
org.apache.jasper.JasperException: Cannot retrieve definition for form bean null

可能原因     
      
这个异常是因为Struts 根据struts-config.xml 中的mapping 没有找到action 期望的form bean 。大部分的情况可能是因为在form-bean 中设置的name 属性和action 中设置的name 属性不匹配所致。换句话说,action form 都应该各自有一个name 属性,并且要精确匹配,包括大小写。这个错误当没有name 属性和action 关联时也会发生,如果没有在action 中指定name 属性,那么就没有name 属性和action 相关联。当然当action 制作某些控制时,譬如根据参数值跳转到相应的jsp 页面,而不是处 理表单数据,这是就不用name 属性,这也是action 的使用方法之一。
-----------------------------------------------------------------------------------------------------------------
异常
No action instance for path /xxxx could be created

可能原因
特别提示:因为有很多中情况会导致这个错误的发生,所以推荐大家调高你的web 服务器的日志/ 调试级别,这样可以从更多的信息中看到潜在的、在试图创建 action 类时发生的错误,这个action 类你已经在struts-config.xml 中设置了关联(即添加了<action> 标 签)。

struts-config.xml 中通过action 标签的class 属性指定的action 类不能被找到有很多种原因,例如:定位编译后 的.class 文件失败。Failure to place compiled .class file for the action in the classpath (web 开发中,class 的的位置在r WEB-INF/classes ,所以你的action class 必须要在这个目录下。例如你的action 类位于WEB-INF/classes/action/Login.class, 那么在 struts-config.xml 中设置action 的属性type 时就是action.Login).
拼写错误,这个也时有发生,并且不易找到,特别注意第一个字母的大小写和包的名称。
-----------------------------------------------------------------------------------------------------------------
异常
javax.servlet.jsp.JspException: No getter method for property username of bean org.apache.struts.taglib.html.BEAN

可能原因
没有位form bean 中的某个变量定义getter 方法

这个错误主要发生在表单提交的FormBean 中,用struts 标记<html:text property=”username”> 时,在FormBean 中必须有一个getUsername() 方法。注意字母“U”
-----------------------------------------------------------------------------------------------------------------
异常
java.lang.NoClassDefFoundError: org/apache/struts/action/ActionForm

可能原因
这个错误主要发生在在classpath 中找不到相应的Java .class 文件。如果这个错误发生在web 应用程序的运行时,主要是因为指定的class 文件不在web serverclasspath 中(/WEB-INF/classes /WEB-INF/lib )。在上面的错误中,原因是找不到ActionForm 类。
-----------------------------------------------------------------------------------------------------------------
异常
javax.servlet.jsp.JspException: Exception creating bean of class org.apache.struts.action.ActionForm: {1}

可能原因
Instantiating Struts-provided ActionForm class directly instead of instantiating a class derived off ActionForm. This mightoccur implicitly if you specify that a form-bean is this Struts ActionForm class rather than specifying a child of this classfor the form-bean.

Not associating an ActionForm-descended class with an action can also lead to this error.
-----------------------------------------------------------------------------------------------------------------
异常
javax.servlet.jsp.JspException: Cannot find ActionMappings or ActionFormBeans collection

可能原因
不是标识Struts actionServlet<servlet> 标记就是映射.do 扩展名的<sevlet-mapping> 标记或者两者都没有在web.xml 中声明。

struts-config.xml 中的打字或者拼写错误也可导致这个异常的发生。例如缺少一个标记的关闭符号/> 。最好使用struts console 工具检查一下。

另外,load-on-startup 必须在web.xml 中声明,这要么是一个空标记,要么指定一个数值,这个数值用来表servlet 运行的优先级,数值越大优先级越低。

还有一个和使用load-on-startup 有关的是使用Struts 预编译JSP 文件时也可能导致这个异常。
-----------------------------------------------------------------------------------------------------------------
异常
java.lang.NullPointerException at org.apache.struts.util.RequestUtils.forwardURL(RequestUtils.java:1223)

可能原因
struts-config.xml 中的forward 元素缺少path 属性。例如应该是如下形式:
<forward name="userhome" path="/user/userhome.jsp"/>
-----------------------------------------------------------------------------------------------------------------
异常
javax.servlet.jsp.JspException: Cannot find bean org.apache.struts.taglib.html.BEAN in any scope


 

Probable Causes
试图在Strutsform 标记外使用form 的子元素。这常常发生在你在</html:form> 后面使用Strutshtml 标记。 另外要注意可能你不经意使用的无主体的标记,如<html:form … /> ,这样web 服务器解析时就当作一个无主体的标记,随后使用的所有<html> 标记都被认为是在这个标记之外的,如又使用了<html:text property=”id”> 还有就是在使用taglib 引入HTML 标记库时,你使用的prefix 的值不是html
-----------------------------------------------------------------------------------------------------------------
异常
javax.servlet.jsp.JspException: Missing message for key xx.xx.xx

Probable Causes
这个key 的值对没有在资源文件ApplicationResources.properties 中定义。如果你使用eclipse 时经常碰到这样的情况,当项目重新编译时,eclipse 会自动将classes 目录下的资源文件删除。

资源文件ApplicationResources.properties 不在classpath 中应将资源文件放到 WEB-INF/classes 目录下,当然要在struts-config.xml 中定义)
-----------------------------------------------------------------------------------------------------------------
异常
Cannot find message resources under key org.apache.struts.action.MESSAGE

可能原因
很显然,这个错误是发生在使用资源文件时,而Struts 没有找到资源文件。

Implicitly trying to use message resources that are not available (such as using empty html:options tag instead of specifyingthe options in its body -- this assumes options are specified in ApplicationResources.properties file)

XML parser issues -- too many, too few, incorrect/incompatible versions
-----------------------------------------------------------------------------------------------------------------
异常
Strange and seemingly random characters in HTML and on screen, but not in original JSP or servlet.

可能原因
混和使用Strutshtml:form 标记和标准的HTML 标记不正确。

使用的编码样式在本页中不支持。
-----------------------------------------------------------------------------------------------------------------
异常
"Document contained no data" in Netscape

No data rendered (completely empty) page in Microsoft Internet Explorer

可能原因
使用一个Action 的派生类而没有实现perform() 方法或execute() 方法。在Struts1.0 中实现的是perform() 方法,在 Struts1.1 中实现的是execute() 方法,但Struts1.1 向后兼容perform() 方法。但你使用Struts1.1 创建一个 Action 的派生类,并且实现了execute() 方法,而你在Struts1.0 中运行的话,就会得到"Document contained nodata" error message in Netscape or a completely empty (no HTML whatsoever) page rendered in Microsoft Internet Explorer.” 的错误信息。

---------------------------------------------------------------------------------------------------------------------------
异常
ServletException: BeanUtils.populate
解决方案
在用Struts 上传文件时, 遇到了javax.servlet.ServletException: BeanUtils.populate 异常。
我的ActionServlet 并没有用到BeanUtils 这些工具类。后来仔细检查代码发现是在jsp 文件里的form 忘了加enctype=& amp;quot;multipart/form-data&quot; 了。所以写程序遇到错误或异常应该从多方面考虑问题存在的可能性,想到系统提示信息以外的东西。
----------------------------------------------------------------------------------------------------------------------------
1.
定义Action, 如果指定了name, 那么必须要定义一个与它同名的FormBean 才能进行form 映射.2. 如果定义Action, 提交页面时出现 "No input attribute for mapping path..." 错误, 则需要在其input 属性中定义转向的页面.3. 如果插入新的数据时出现 "Batch update row count wrong:..." 错误, 则说明XXX.hbm.xml 中指定的key 的类型为原始类型(int, long), 因为这种类型会自动分配值, 而这个值往往会让系统认为已经存在该记录, 正确的方法是使用java.lang.Integerjava.lang.Long 对象.4. 如果插入数据时出现 "argument type mismatch" 错误, 可能是你使用了Date 等特殊对象, 因为struts 不能自动从String 型转换成Date, 所以, 你需要在Action 中手动把String 型转换成Date.5. Hibernate, Queryiterator()list() 方法快很多.6. 如果出现 "equal symbol expected" 错误, 说明你的strtus 标签中包含另一个标签或者变量, 例如:
<html:select property="test" οnchange="<%=test%>"/>
或者
<html:hidden property="test" value="<bean:write name="t" property="p"/>"/>
这样的情况...
---------------------------------------------------------------------------------------------------------------------------
错误:Exception in thread "main" org.hibernate.exception.SQLGrammarException: Could not execute JDBC batch update 原因与解决:      因为Hibernate Tools (或者Eclipse 本身的Database Explorer )生成*.hbn.xml 工具中包含有catalog="***"* 表示数据库名称)这样的属性, 将该属性删除就可以了
---------------------------------------------------------------------------------------------------------------------------
错误:org.hibernate.ObjectDeletedException: deleted object would be re-saved by cascade (remove deleted object from associations)
原因与解决:
方法1 删除Set 方的cascade
方法2 解决关联关系后,再删除
方法3 many-to-one 方增加cascade 但值不能是none
最后一招:
检查一下hashCode equals 是否使用了id 作为唯一标示的选项了;我用uuid.hex 时是没有问题的;但是用了native ,就不行了,怎么办?删除啊!
----------------------------------------------------------------------------------------------------------------------------
问题:今天用Tomcat 5.5.12 ,发现原来很好用的系统不能用了,反复测试发现页面中不能包含 taglib ,否则会出现以下提示:HTTP Status 500 -type Exception reportMessage description The server encountered an internal error () that prevented it from fulfilling this request.exceptionorg.apache.jasper.JasperException: /index.jsp(1,1) Unable to read TLD "META-INF/tlds/struts-bean.tld" from JAR file"file:*****/WEB-INF/lib/struts.jar": 原因:更新了工程用的lib 文件夹下的jar ,发布时也发布了 servlet.jarjsp-api.jar 。解决:把jsp-api.jar 删除就解决这个问题了。 -----------------------------------------------------------------------------------------------------------------------------
错误: java.lang.NullPointerException
原因: 发现 dao 实例、 manage 实例等需要注入的东西没有被注入(俗称空指针异常)解决:这个时候,你应该查看日志文件;默认是应用服务器的 log 文件,比如 Tomcat 就是 [Tomcat 安装目录 ]/logs ;你会发现提示你:可能是:org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sf' defined in ServletContextresource [/WEB-INF/applicationContext.xml]: Initialization of bean failed; nested exception isorg.hibernate.HibernateException: could not configure from URL: file:src/hibernate.cfg.xmlorg.hibernate.HibernateException: could not configure from URL: file:src/hibernate.cfg.xml……………………….Caused by: java.io.FileNotFoundException: src/hibernate.cfg.xml 可能 是:org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory' defined inServletContext resource [/WEB-INF/applicationContext.xml]: Initialization of bean failed; nested exception isorg.hibernate.MappingException: Resource: com/mcc/coupon/model/UserRole.hbm.xml not foundorg.hibernate.MappingException: Resource: com/mcc/coupon/model/UserRole.hbm.xml not found 然后你就知道原因是因为配置文件的解析出了错误,这个通过 Web 页面是看不出来的。更多的是持久化影射文件出的错误;导致了没有被解析;当然你需要的功能就无法使用了。
----------------------------------------------------------------------------------------------------------------------------
错误:StandardWrapperValve[action]: Servlet.service() for servlet action threw exception
javax.servlet.jsp.JspException: Cannot retrieve mapping for action /settlementTypeManage
或者:      type Status report      message Servlet action is not available      description The requested resource (Servlet action is not available) is not available.
原因: 同 上
----------------------------------------------------------------------------------------------------------------------------
错误StandardWrapperValve[jsp]: Servlet.service() for servlet jsp threw exceptionjava.lang.ClassNotFoundException: org.apache.struts.taglib.bean.CookieTei 界面错误具体描述:
org.apache.jasper.JasperException: Failed to load or instantiate TagExtraInfo class: org.apache.struts.taglib.bean.CookieTei
     
原因与解决:    < 方案一> 你的“html:” 开头的标签没有放在一个<html:form>       < 方案二> 重新启动你的应用服务器,自动就没有这个问题

 

 

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值