2010-02-18 7 views
2

J'ai une méthode qui ressemble à ceci:Impossible de déterminer ce qui est de lancer une exception NullPointer

try { 
    doStuff(); 
} catch (Exception ex) { 
    logger.error(ex); 
} 

(je ne l'utilise pas vraiment les noms des méthodes comme doStuff - ceci est juste pour rendre les choses faciles)

en doStuff je fais toutes sortes de choses, parmi eux est d'appeler une méthode d'accès aux données (ainsi, une autre méthode dans doStuff) qui se termine par ce qui suit:

} catch (SQLException ex) { 
    logger.error(ex); 
} finally { 
    try { 
    connection.close(); 
    proc.close(); 
    results.close(); 
    } catch (Exception e) { 
    logger.error(e); 
    } //<--Exception thrown here. HUH? 
} 
return stuff; 

Quand marcher à travers ce code, je reçois la accolade de la seconde à la dernière (marquée par un commentaire), puis saute à la capture dans le premier bloc de code avec une exception NullPointer. Le results.close() est ce qui est exécuté juste avant (les résultats ne sont pas Nuls). Mon IDE (NetBeans) ne fournit pas de trace de pile (il montre que la trace de la pile est null) ou toute autre information que le nom de l'exception (d'après ce que je peux dire).

Ce code fonctionnait correctement précédemment. En fait, pendant qu'il était en cours d'exécution, j'ai changé la procédure stockée que la méthode d'accès aux données (où j'obtiens cette exception) appelait, puis cette erreur a commencé à se produire (sans que l'application ait été arrêtée du tout). J'ai depuis essayé de reconstruire et de redémarrer mais en vain. Je pourrais changer le sproc, mais je veux vraiment savoir de quoi cette erreur provient, car cela n'a aucun sens que le sproc fasse même partie de cette situation en considérant où se trouve l'exception dans le code.

+1

Que montre ex.printStackTrace()? –

+3

Question idiote, mais 'logger' est-il nul? –

+0

Je vais essayer, mais le navigateur de variables montre que la pileTrace est nulle. Aussi @Matt-logger est en effet initialisé dans les deux méthodes référencées ci-dessus. Dans la seconde, il n'essaie même pas d'entrer dans ce bloc d'Exceptions (normalement, quand il passe à travers, il entre dans la capture, puis entre - cela va des résultats à l'accolade à la capture de la première méthode). –

Répondre

3

Votre méthode doStuff() lance autre chose qu'une exception SQLException et n'est pas interceptée. ajouter un bloc catch (Exception e) et consigner cette exception et voir ce qui se passe.

cet exemple de code présente le même comportement que vous décrivez:

public class TryCatchTest { 
    public static void main(String[] args) { 
     try { 
      System.out.println("foo"); 
      throw new NullPointerException(); 
     } finally { 
      try { 
       System.out.println("bar"); 
      } catch (Exception e) { 
       e.printStackTrace(); 
      } 
     } // exception thrown here 
    } 
} 
+0

C'était exactement ça. Merci. –

+2

attraper (Exception e) est une mauvaise pratique, attraper les sous-classes attendues individuels au lieu –

2

Il se pourrait bien que l'enregistreur soit nul.

Des exceptions difficiles à localiser sont souvent levées dans le gestionnaire d'exceptions lui-même.

+0

+1: C'était aussi ma première pensée. – Powerlord

+0

C'était aussi ma première pensée, cependant, ce n'est pas le cas. En fait, l'enregistreur est réellement utilisé dans la même méthode avant le code dans le second bloc. –

3

Fermer les ressources dans l'ordre inverse dans lequel ils ont été obtenus:

try 
{ 
    results.close(); 
    proc.close(); 
    connection.close(); 
} 
catch (Exception e) 
{ 
    logger.error(e); 
} //<--Exception thrown here. HUH? 

Je vous recommande aussi des méthodes comme celles-ci:

public class DatabaseUtils 
{ 
    // similar for ResultSet and Statement 
    public static void close(Connection c) 
    { 
     try 
     { 
      if (c != null) 
      { 
       c.close(); 
      } 
     } 
     catch (SQLException e) 
     { 
      // log or print. 
     } 
    } 
} 
+0

Merci pour le conseil, je vais le faire. –

+0

vous ne devriez jamais attraper (Exception e) et toujours attraper une sous-classe. –

+0

Je ne vois pas pourquoi un vote négatif pour cela. Qu'est-ce qui pourrait éventuellement être faux? – duffymo

2

NullPointerException ne peut pas être jeté dans une ligne sans déclaration. Vérifiez que le fichier de classe que vous exécutez est de la même version que la source que vous consultez (j'ai rencontré des problèmes similaires lorsqu'un classpath incorrectement configuré contenait une classe deux fois et l'ancienne version trouvée en premier dans le classpath, ou un fichiers de classe recompilés pour ne pas être correctement copiés dans le conteneur Web que j'ai utilisé pour les tests). Edit: Comme le fait remarquer emh, il pourrait aussi y avoir une exception avant d'entrer dans le bloc finally.

+2

pas entièrement vrai. dans son cas si l'exception était lancée dans doStuff() et qu'il n'y avait pas de bloc catch pour l'attraper alors le chemin d'exécution devrait encore exécuter le bloc finally avant que l'exception puisse être lancée vers l'appelant. C'est pourquoi l'exception semble être lancée à partir de l'accolade fermante - c'est la dernière ligne du bloc finally. – emh

+0

Vous avez raison, va éditer. – meriton

0

Je suis 99% sûr que ce qui se passe dans le pilote JDBC. Pour commencer, vos déclarations proches sont à rebours. Vous devez fermer le jeu de résultats, l'instruction et la connexion, dans cet ordre.

Si vous exécutez dans un serveur d'applications qui gère les transactions, la tentative de validation de la transaction peut déclencher l'exception dans le pilote JDBC.

Il peut également s'agir de la manière dont les ensembles de résultats sont générés dans la procédure stockée, par exemple en accédant à un ensemble, puis en accédant à un autre, puis en revenant au premier.

-1

vous devez NE JAMAIS utiliser catch (Exception e) et toujours attraper une sous-classe Exception particulière, de cette façon vous savez ce qui se passe où.

+1

Je ne suis pas d'accord, mais seulement partiellement. Le locataire est de ne pas attraper des exceptions que vous n'allez pas traiter, à moins que vous ne fassiez quelque chose d'autre. Donc, si vous ne vous souciez pas de l'exception, juste une exception est survenue, attraper Exception. Juste ne faites pas l'erreur d'attraper Exception et en supposant que c'est un type particulier (dans ce cas SQLException). Dans ce cas particulier, je n'attraperais pas l'exception sauf si j'allais faire autre chose que log ou printStackTrace(). –

+1

J'utilise catch (Exception ex) quand je n'ai aucune idée de ce qui pourrait être jeté ou je veux juste empêcher l'application de plantage (ou au moins de se connecter quelque chose avant qu'il ne le fasse). Dans ce cas, il n'attrapait pas une exception spécifique qui a effectivement causé le problème. –

+0

En outre, il est parfois nécessaire d'attraper java.lang.Throwable pour éviter complètement l'écrasement. Mais bien sûr, utilisez cela avec prudence. –

0

Comme je l'ai dit dans un commentaire, ne jamais attraper une exception que vous ne voulez pas traiter. Dans votre code, en supposant qu'il soit complet, vous ne faites rien d'intéressant avec l'exception, et cela vous cause une confusion sur où et pourquoi l'exception se produit. Si vous voulez vraiment faire plus que log ou printStackTrace(), comme l'enveloppant avec une exception spécifique au domaine (comme votre.package.DataAccessException ou quelque chose), alors super.

Faites quelque chose plus comme ceci:


ResultSet results = null; 
CallableStatement proc = null; 
Connection connection = null; 
try { 
    connection = > 
    proc = connection.createCallableStatement(..); 
    results = proc.execute(..); 
    > 
} finally { 
    try { 
    if (results != null) { 
     results.close(); 
    } 
    } catch (Exception e) { 
    logger.error(e); 
    } 
    try { 
    if (proc != null) { 
     proc.close(); 
    } 
    } catch (Exception e) { 
    logger.error(e); 
    } 
    try { 
    if (connection != null) { 
     connection.close(); 
    } 
    } catch (Exception e) { 
    logger.error(e); 
    } 
} 

Et puis dans l'appelant:


    try { 
     doStuff(); 
    } catch (SQLException e) { 
     throw new your.package.DataAccessException(e); 
     // or just let the SQLException propagate upward 
    } catch (Exception e) { 
     throw new your.package.AppException("omg, crazy pills!", e); 
     // or, once again, just let the exception 
     // propagate and don't catch anything 
    } 

Alors, à emporter:

  1. ne note pas exception où ils se produisent , juste les transmettre, imbriqués dans une autre exception. Vous ne voulez pas que votre processus ne sache pas si l'action SQL a réussi ou non. Vous préférez vous arrêter et faire autre chose.
  2. Nest exceptions jusqu'à la fin de la ligne, de cette façon, vous avez toujours la trace complète à l'endroit que vous vouliez faire face à l'exception, et non pas dans cinq endroits dispersés dans votre journal du serveur. Nest exceptions (oui, je l'ai dit deux fois!) De sorte que vous ne vous souciez pas où la JVM renvoie réellement l'exception, vous avez la prochaine exception à suivre, vous disant qu'il était en fait une déclaration appelable, ou une fermeture incorrecte Ne pas imbriquer et ne pas lancer d'exceptions à partir d'erreurs interceptées dans votre code finally, qui interféreront avec l'exception d'origine et qui seront plus intéressantes que l'échec de fermeture et l'instruction qui n'a pas été ouverte en premier lieu.
  3. Définissez les variables sur null avant de les utiliser, puis recherchez null avant close().
  4. Catch chaque problème dans le bloc finally individuellement, vous pourriez ne pas être en mesure de fermer votre ResultSet (car une erreur d'exécution a causé de ne pas ouvrir en premier lieu), mais vous devez toujours essayer de fermer la CallableStatement et Connection, car il n'est probablement pas affecté et vous causera toujours des fuites de ressources.

Espérons que ça aide.