Diferencias

Muestra las diferencias entre dos versiones de la página.

Enlace a la vista de comparación

patrones:excepciones [2012/09/23 00:03]
admin [La solución definitiva]
patrones:excepciones [2016/07/03 20:02] (actual)
Línea 1: Línea 1:
 ====== Tratamiento de Excepciones ====== ====== Tratamiento de Excepciones ======
-El tratamiento de excepciones demasiadas veces se realiza de forma errónea. Veamos ahora la forma de hacer un correcto tratamiento de excepciones.+El tratamiento de excepciones en Java demasiadas veces se realiza de forma errónea. Veamos ahora la forma de hacer un correcto tratamiento de excepciones.
  
 ===== Excepciones en Java ===== ===== Excepciones en Java =====
-Empecemos recordando los dos tipos de excepciones que hay en Java((Hay una tercera llamada <javadoc jdk7 >java.lang.Error</javadoc> que no trataremos aqui ))+Empecemos recordando los dos tipos de excepciones que hay en Java(( Hay realmente mas como <javadoc jdk7 >java.lang.Error</javadoc> pero que no trataremos aqui ))
   * [[#Excepciones Checked]]: Objetos de la clase <javadoc jdk7>java.lang.Exception|Exception</javadoc> o cualquier otra clase que hereda de ella, excepto si heredan de <javadoc jdk7>java.lang.RuntimeException|RuntimeException</javadoc>.   * [[#Excepciones Checked]]: Objetos de la clase <javadoc jdk7>java.lang.Exception|Exception</javadoc> o cualquier otra clase que hereda de ella, excepto si heredan de <javadoc jdk7>java.lang.RuntimeException|RuntimeException</javadoc>.
   * [[#Excepciones Unchecked]]: Objetos de la clase <javadoc jdk7>java.lang.RuntimeException|RuntimeException</javadoc> o de cualquiera otra clase que herede de ella.   * [[#Excepciones Unchecked]]: Objetos de la clase <javadoc jdk7>java.lang.RuntimeException|RuntimeException</javadoc> o de cualquiera otra clase que herede de ella.
Línea 104: Línea 104:
 <note important> <note important>
 La Checked Exceptions forman parte del flujo lógico de nuestro programa y al programar debemos tenerlas en cuenta y tratarlas adecuadamente no viéndolas como un problema del que debemos deshacernos. La Checked Exceptions forman parte del flujo lógico de nuestro programa y al programar debemos tenerlas en cuenta y tratarlas adecuadamente no viéndolas como un problema del que debemos deshacernos.
-</note> 
- 
-<note warning> 
-Lo que nunca se debe hacer con una Checked Exception es imprimir la traza del error y/o seguir con la ejecución. Si la forma correcta de tratar excepción es //no hacer nada//, ¿para que imprimes la traza? y si hay que hacer algo con el excepción ¿era necesario imprimir la traza?. 
- 
-Así que los siguientes trozos de código son erroneos: 
-<code java 1> 
-try { 
-    double c=matematicas.dividir(-1.6, 0); 
-} catch (Exception ex) { 
-    //Ignorar la excepción 
-} 
-</code> 
- 
-<code java 1> 
-try { 
-    double c=matematicas.dividir(-1.6, 0); 
-} catch (Exception ex) { 
-    Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); 
-} 
-</code> 
- 
-<code java 1> 
-try { 
-    double c=matematicas.dividir(-1.6, 0); 
-} catch (Exception ex) { 
-    ex.printStackTrace(); 
-} 
-</code> 
- 
 </note> </note>
  
Línea 144: Línea 114:
 Por ejemplo en el caso anterior de la división por cero, es un error de programación que alguien nos pase como dividendo un cero, en ese caso lanzaremos un <javadoc>java.lang.RuntimeException|RuntimeException</javadoc>. Por ejemplo en el caso anterior de la división por cero, es un error de programación que alguien nos pase como dividendo un cero, en ese caso lanzaremos un <javadoc>java.lang.RuntimeException|RuntimeException</javadoc>.
  
-Al ser las unchecked Exceptions errores en el programa , poco podemos hacer por arreglarlas. Por ello no es obligatorio tratarlas con un ''try-catch''. Lo único que podemos hacer en la mayoría de casos es hacer que el programa acabe fallando y que se muestre un mensaje de error al usuario. Es decir, si el programa funciona mal , es mejor que no siga funcionando antes que hacer que continúe funcionado pero usando datos erróneos, ya que ne ese caso los resultados que generara también podrían ser erróneos.+Al ser las unchecked Exceptions errores en el programa , poco podemos hacer por arreglarlas. Por ello no es obligatorio tratarlas con un ''try-catch''. Lo único que podemos hacer en la mayoría de casos es hacer que el programa acabe fallando y que se muestre un mensaje de error al usuario. Es decir, si el programa funciona mal , es mejor que no siga funcionando antes que hacer que continúe funcionado pero usando datos erróneos, ya que en ese caso los resultados que generara también podrían ser erróneos.
  
 <note important> <note important>
Línea 186: Línea 156:
 </note> </note>
  
 +==== Mas problemas ====
 +Veamos ahora otro problema de las excepciones.Hay excepciones unchecked , que según su significado,  deberían haber sido del tipo checked ya que no son debido a errores del programa (lo contrario del caso anterior).
  
 +La excepción <javadoc jee6>javax.validation.ConstraintViolationException|ConstraintViolationException</javadoc> se lanza cuando alguna validación de una entidad no se ha cumplido. Se suele utilizar para indicar que el usuario ha introducido algún dato erroneo en la aplicación. Entonces, ¿porqué es una excepción unchecked? Según lo que se está explicando no hay duda de que debería ser del tipo Checked ya que no se lanza por un error de programación sino a causa de un dato mal introducido por el usuario.
  
-==== Formas erróneas de tratar las excepciones ====+El problema que se genera con ésto es que la excepción <javadoc jee6>javax.validation.ConstraintViolationException|ConstraintViolationException</javadoc> no sabemos que método la va a lanzar a no ser que nos leamos la documentación, pero es importante capturarla para poder mostrar un mensaje al usuario. Si hubiera sido declarada como Checked, el propio compilador nos avisaría de cuando capturarla. Todo ésto nos puede llevar a que el usuario acabe viendo nuestra página de error de la aplicación simplemente porque se ha dejado un campo sin rellenar.
  
-=== Perder la excepción original ===+<note important> 
 +Otro problema de Java es que hay APIs que lanzan UnChecked Exceptions cuando deberían haber sido Cheked Exceptions. 
 +</note> 
 + 
 +La solución a este problema consiste simplemente en leerse la documentación para ver cuando se lanza una excepción unchecked , que debería haber sido Checked, para capturarla y hacer el correcto tratamiento con ella. 
 + 
 +Mas información sobre la excepción <javadoc jee6>javax.validation.ConstraintViolationException|ConstraintViolationException</javadoc> y como solucionar el problema lo tenemos en [[unidades:07_arquitectura:02_excepciones]] 
 +===== Formas erróneas de tratar las excepciones ===== 
 +Veamos ahora una serie de errores que se cometen al tratar las excepciones. 
 + 
 +==== Perder la excepción original ====
 Quizás otro origen del desconocimiento de las excepciones es que hasta Java 1.4 no se podía encapsular una excepción dentro de otra. Lo podemos ver en la clase ''Throwable'' de la que hereda Exception. Quizás otro origen del desconocimiento de las excepciones es que hasta Java 1.4 no se podía encapsular una excepción dentro de otra. Lo podemos ver en la clase ''Throwable'' de la que hereda Exception.
   * [[http://docs.oracle.com/javase/1.3/docs/api/java/lang/Throwable.html|Throwable en Java 1.3]]: No existen un constructor que acepte como argumento otra excepción ni el método ''Throwable getCause()'' que obtiene la excepción encapsulada.   * [[http://docs.oracle.com/javase/1.3/docs/api/java/lang/Throwable.html|Throwable en Java 1.3]]: No existen un constructor que acepte como argumento otra excepción ni el método ''Throwable getCause()'' que obtiene la excepción encapsulada.
Línea 208: Línea 191:
 Por ejemplo la excepción <javadoc jdk7>java.sql.SQLException|SQLException</javadoc>  dispone de los métodos <javadoc jdk7>java.sql.SQLException#getSQLState()|getSQLState()</javadoc> y <javadoc jdk7>java.sql.SQLException#getErrorCode()|getErrorCode()</javadoc> que contiene información sobre el error concreto que ha ocurrido en la base de datos. Usando el código anterior también lo habríamos perdido. Por ejemplo la excepción <javadoc jdk7>java.sql.SQLException|SQLException</javadoc>  dispone de los métodos <javadoc jdk7>java.sql.SQLException#getSQLState()|getSQLState()</javadoc> y <javadoc jdk7>java.sql.SQLException#getErrorCode()|getErrorCode()</javadoc> que contiene información sobre el error concreto que ha ocurrido en la base de datos. Usando el código anterior también lo habríamos perdido.
  
-=== Crear una excepción inutil === +==== Crear una excepción inutil ==== 
-Otro segundo error es crear una nueva excepción que no aporta nada respecto a ''RuntimeException'' y usarla en vez de ''RuntimeException''+Otro segundo error es crear una nueva excepción **generica** (( No me refiero a excepciones concretas que tienen significado específico como ''NullPointerException'' )) que no aporta nada respecto a ''RuntimeException'' (( Lo mismo se aplica a Exception )) y usarla en vez de ''RuntimeException''
  
 <code java 1> <code java 1>
Línea 231: Línea 214:
 En la línea 4 se lanza la nueva excepción ''MiExcepcion'' que no hace nada que no hiciera ya ''RuntimeException'' e incluso seguimos perdiendo la excepción original. En la línea 4 se lanza la nueva excepción ''MiExcepcion'' que no hace nada que no hiciera ya ''RuntimeException'' e incluso seguimos perdiendo la excepción original.
  
-=== Pasar el problema hacia arriba === +==== Pasar el problema hacia arriba ==== 
-La última forma errónea de tratar la excepciones es simplemente hacer que el método superior a su vez declare que lanza esas excepciones checked pero que deberían haber sido uncheked. Con ésto lo único que hacemos es expandir el problema hacia arriba sin solucionarlo.+La siguiente forma errónea de tratar la excepciones es simplemente hacer que el método superior a su vez declare que lanza esas excepciones checked pero que deberían haber sido uncheked. Con ésto lo único que hacemos es expandir el problema hacia arriba sin solucionarlo.
  
 <code java 1> <code java 1>
Línea 246: Línea 229:
 En la línea 2 vemos ahora como el método ''Main'' ahora también lanza una excepción checked. En la línea 2 vemos ahora como el método ''Main'' ahora también lanza una excepción checked.
  
 +==== Ignorarla ====
 +Este es el peor error que podemos cometer al tratar una excepción.Consiste simplemente en ignorar la excepción y que continúe la ejecución del programa. Esto es tan peligroso ya que si se ha producido algún fallo en el programa lo más seguro es hacer que se detenga cuanto antes y no seguir haciendo operaciones con datos que quizás sean erróneos.
  
 +<code java 1>
 +try {
 +    double c=matematicas.dividir(-1.6, 0);
 +} catch (Exception ex) {
 +
 +}
 +</code>
 +
 +
 +Vemos en la línea 4 como no se realiza ninguna acción al capturar la excepción, por lo tanto la ejecución seguirá como si no hubiera pasado nada.
 +
 +==== Log ====
 +Acabamos de ver en el apartado anterior que nunca (( Siempre hay //excepciones// para todo )) se debe ignorar la excepción. Una forma de ignorarla pero que aparenta ser mejor solución es guardar en el log la excepción que se ha producido pero sin hacer nada mas.
 +
 +<code java 1>
 +try {
 +    double c=matematicas.dividir(-1.6, 0);
 +} catch (RuntimeException ex) {
 +    Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
 +}
 +</code>
 +
 +En la línea 4 guardamos en el log la excepción pero el programa sigue ejecutándose. Como ya hemos comentado no debería seguir la ejecución del programa puesto que los datos pueden haber quedado en un estado erróneo.
 +
 +La idea de guardar en el log la excepción es buena por si misma. Sin embargo no suele ser necesario guardarla explícitamente ya que en muchas ocasiones ya se guarda automáticamente. Por ejemplo en aplicaciones Web , el propio contenedor al detectar ((Siempre que no la detengamos antes )) una excepción guardará en el log la excepción que se ha producido.
 +
 +==== Consola  ====
 +Otra forma erronea de guardar las excepciones consiste en mostrar la traza del error por la consola.
 +
 +<code java 1>
 +try {
 +    double c=matematicas.dividir(-1.6, 0);
 +} catch (RuntimeException ex) {
 +    ex.printStackTrace();
 +}
 +</code>
 +
 +En la línea 4 mostramos por la consola la traza de la excepción pero el programa sigue ejecutándose. Como ya se ha dicho no debería seguir la ejecución del programa puesto que los datos pueden haber quedado en un estado erróneo.
 +
 +En aplicaciones Web o aplicaciones de Ventanas , esta opción tiene menos sentido aun que la opción del log, ya que guardando la traza en un fichero de log está mucho más accesible para poder averiguar el origen del problema que mostrándolo por consola ya que la consola la puede cerrar el usuario y perder toda la traza o simplemente que no tengamos acceso a la consola.
 +
 +==== Imprimirla ====
 +Por último pero no por ello menos usada está la //técnica// de imprimir el mensaje de la excepción.
 +
 +<code java 1>
 +try {
 +    double c=matematicas.dividir(-1.6, 0);
 +} catch (RuntimeException ex) {
 +    System.out.println("Falló al dividir");
 +}
 +</code>
  
 +Esta forma tiene los mismos problemas del anterior de imprimirla por consola pero ésta es aun peor, hemos perdido toda la información de la excepción.
  
 ===== Mejoras en el tratamiento de excepciones ===== ===== Mejoras en el tratamiento de excepciones =====
Línea 339: Línea 376:
 Es decir que ahora la traza para una ''RuntimeException'' es como debería ser ,ya que no queda encapsulada por ninguna otra excepción. Es decir que ahora la traza para una ''RuntimeException'' es como debería ser ,ya que no queda encapsulada por ninguna otra excepción.
  
 +<note tip>
 +Por los ejemplos puede parecer un poco exagerado querer que no se encapsulen las excepciones pero pongo aquí un ejemplo de excepciones en Hibernate:
 +
 +  Exception in thread "main" org.hibernate.MappingException: Could not get constructor for org.hibernate.persister.entity.SingleTableEntityPersister
 +  at org.hibernate.persister.internal.PersisterFactoryImpl.create(PersisterFactoryImpl.java:185)
 +  at org.hibernate.persister.internal.PersisterFactoryImpl.createEntityPersister(PersisterFactoryImpl.java:135)
 +  at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:367)
 +  at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1740)
 +  at curshibernatet03e01.CursHibernateT03E01.main(CursHibernateT03E01.java:23)
 +  Caused by: org.hibernate.HibernateException: Unable to instantiate default tuplizer [org.hibernate.tuple.entity.PojoEntityTuplizer]
 +  at org.hibernate.tuple.entity.EntityTuplizerFactory.constructTuplizer(EntityTuplizerFactory.java:138)
 +  at org.hibernate.tuple.entity.EntityTuplizerFactory.constructDefaultTuplizer(EntityTuplizerFactory.java:188)
 +  at org.hibernate.tuple.entity.EntityMetamodel.<init>(EntityMetamodel.java:341)
 +  at org.hibernate.persister.entity.AbstractEntityPersister.<init>(AbstractEntityPersister.java:502)
 +  at org.hibernate.persister.entity.SingleTableEntityPersister.<init>(SingleTableEntityPersister.java:144)
 +  at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
 +  at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
 +  at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
 +  at java.lang.reflect.Constructor.newInstance(Constructor.java:525)
 +  at org.hibernate.persister.internal.PersisterFactoryImpl.create(PersisterFactoryImpl.java:163)
 +  ... 4 more
 +  Caused by: java.lang.reflect.InvocationTargetException
 +  at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
 +  at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
 +  at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
 +  at java.lang.reflect.Constructor.newInstance(Constructor.java:525)
 +  at org.hibernate.tuple.entity.EntityTuplizerFactory.constructTuplizer(EntityTuplizerFactory.java:135)
 +  ... 13 more
 +  Caused by: org.hibernate.PropertyNotFoundException: Could not find a getter for domicilio in class curshibernatet03e01.Cliente
 +  at org.hibernate.property.BasicPropertyAccessor.createGetter(BasicPropertyAccessor.java:316)
 +  at org.hibernate.property.BasicPropertyAccessor.getGetter(BasicPropertyAccessor.java:310)
 +  at org.hibernate.mapping.Property.getGetter(Property.java:320)
 +  at org.hibernate.tuple.entity.PojoEntityTuplizer.buildPropertyGetter(PojoEntityTuplizer.java:436)
 +  at org.hibernate.tuple.entity.AbstractEntityTuplizer.<init>(AbstractEntityTuplizer.java:200)
 +  at org.hibernate.tuple.entity.PojoEntityTuplizer.<init>(PojoEntityTuplizer.java:82)
 +  ... 18 more
 +  
 +Realmente la información mas útil la encontramos justamente en la primera excepción que se produce. Sin embargo está justamente al final de toda la traza: 
 +
 +  PropertyNotFoundException: Could not find a getter for domicilio in class curshibernatet03e01.Cliente
 +  
 +Así que si no se hubiera encapsulado la excepción se vería en la primera línea en vez de ir a buscarla al final de la traza.
 +
 +</note>
 ==== La solución definitiva ==== ==== La solución definitiva ====
 La solución definitiva es que las excepciones checked tenga también una traza //limpia// como las unchecked. Antes hemos comentado que es imposible pero hay un truco en Java que permite hacerlo, no voy a explicar como funciona el truco simplemente voy a explicar como hacerlo. La solución definitiva es que las excepciones checked tenga también una traza //limpia// como las unchecked. Antes hemos comentado que es imposible pero hay un truco en Java que permite hacerlo, no voy a explicar como funciona el truco simplemente voy a explicar como hacerlo.
Línea 384: Línea 465:
  
 Por fin con este ultimo truco hemos conseguido simplificar la traza con todas las excepciones. Por fin con este ultimo truco hemos conseguido simplificar la traza con todas las excepciones.
- 
-<note> 
-Puede parecer un poco exagerado querer limpiar la traza para dejarla mas clara , pero en programas muy extensos se acumulan muchas excepciones dentro de otras excepciones y al final queda poco claro que ha ocurrido viendo la traza. 
- 
-Pero si no quieres complicar el código, usa simplemente la solución anterior a ésta que queda todo más estándar. 
-</note> 
  
 ===== Referencias ===== ===== Referencias =====
Línea 398: Línea 473:
   * [[http://docs.oracle.com/javase/tutorial/essential/exceptions/runtime.html|The Java Tutorials.Unchecked Exceptions—The Controversy]]   * [[http://docs.oracle.com/javase/tutorial/essential/exceptions/runtime.html|The Java Tutorials.Unchecked Exceptions—The Controversy]]
   * [[http://www.gamlor.info/wordpress/2010/02/throwing-checked-excpetions-like-unchecked-exceptions-in-java/|Throwing Checked Exceptions Like Unchecked Exceptions in Java]]   * [[http://www.gamlor.info/wordpress/2010/02/throwing-checked-excpetions-like-unchecked-exceptions-in-java/|Throwing Checked Exceptions Like Unchecked Exceptions in Java]]
 +  * [[http://www.manageability.org/blog/stuff/exceptional-exception-handling-techniques|13 Exceptional Exception Handling Techniques]]
patrones/excepciones.1348351410.txt.gz · Última modificación: 2016/07/03 20:19 (editor externo)
Ir hasta arriba
CC Attribution-Noncommercial-Share Alike 3.0 Unported
chimeric.de = chi`s home Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0