2009-11-08 9 views
12

Si funciono con la siguiente prueba, falla:¿Por qué puedo "falsificar" el rastro de la pila de una excepción en Java?

public class CrazyExceptions { 
    private Exception exception; 

    @Before 
    public void setUp(){ 
     exception = new Exception(); 
    } 

    @Test 
    public void stackTraceMentionsTheLocationWhereTheExceptionWasThrown(){ 
     String thisMethod = new Exception().getStackTrace()[0].getMethodName(); 
     try { 
      throw exception; 
     } 
     catch(Exception e) { 
      assertEquals(thisMethod, e.getStackTrace()[0].getMethodName()); 
     } 
    } 
} 

con el siguiente error:

Expected :stackTraceMentionsTheLocationWhereTheExceptionWasThrown 
Actual :setUp 

traza La pila es simple y llanamente mentira.

¿Por qué no se reescribe la traza de la pila cuando se lanza la excepción? No soy un desarrollador de Java, y tal vez me falta algo aquí.

+0

No sé si hace 6 años podríamos establecer el rastro de pila como "causa" de la excepción, pero es mejor usar esto después de crear la nueva excepción y antes de lanzarla: Exception.initCause (Throwable), donde se configura la causa stacktrace con setStackTrace(). –

Respuesta

19

El seguimiento de la pila se crea cuando se crea una instancia de la excepción, no cuando se lanza. Esto se especifica el comportamiento del Java Language Specification

20.22.1 public Throwable() 

This constructor initializes a newly created Throwable object with null as 
its error message string. Also, the method fillInStackTrace (§20.22.5) is 
called for this object. 

.... 

20.22.5 public Throwable fillInStackTrace() 

This method records within this Throwable object information about the 
current state of the stack frames for the current thread. 

No sé qué lo hicieron de esa manera, pero si la especificación define así, por lo menos es constante en todas las diversas máquinas virtuales de Java.

Sin embargo, puede actualizarlo llamando al exception.fillInStackTrace() manualmente.

También tenga en cuenta que debe usar Thread.currentThread().getStackTrace() en lugar de usar new Exception().getStackTrace() (estilo incorrecto).

+0

No usé 'Thread.currentThread(). GetStackTrace()' porque 'Thread.currentThread(). GetStackTrace() [0] .getMethodName' siempre es' getStacktrace' ... –

+0

+1 para señalar el manual 'fillInStackTrace' cosa. En C# obtenemos ese comportamiento por defecto y podemos simplemente 'throw;' cuando necesitamos mantener el seguimiento de la pila cuando se reinicia. –

+0

Es 'getStackTrace() [1]', nada más. – mhaller

1

Porque no solicitó que se reescriba esa traza de pila. Se configuró cuando lo creó en el método setUp, y nunca hizo nada para modificarlo.

La clase de excepción no le da ninguna oportunidad de establecer el nombre del método; es inmutable Así que no hay forma de que yo sepa de dónde podrías restablecer el nombre del método, a menos que quisieras recurrir a algo atroz como la reflexión.

Su anotación @Test no me dice si está usando JUnit o TestNG, porque no puedo ver la importación estática, pero en cualquier caso puede ejecutar una prueba para ver si una excepción en particular es lanzada por usando el miembro "esperado" en la anotación @Test.

+0

Estoy usando jUnit, pero no estaba tratando de probar las excepciones que se lanzan. Utilicé la prueba solo para ilustrar mi pregunta. –

+0

No estoy seguro de cuál es la pregunta: "¿Cómo modifico el método de un seguimiento de pila?" – duffymo

0

Creo que la suposición es que no creará una excepción a menos que esté en el proceso de lanzarla, entonces, ¿por qué pagar el precio para obtener el seguimiento de la pila dos veces?

Sería difícil recrear la traza de la pila al tirarla, ya que eso es solo enviar el objeto.

La excepción debe configurarse por completo antes del lanzamiento, por lo que parte de la instanciación es obtener el seguimiento de la pila.

ACTUALIZACIÓN:

Puede llamar fillInStackTrace para resolver esto: http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Throwable.html#fillInStackTrace%28%29

9

La pila de la excepción se completa en el momento de la creación de la excepción. De lo contrario, sería imposible atrapar una excepción, manejarlo y volver a lanzarlo. La stacktrace original se perdería.

Si quiere forzar esto, debe llamar al exception.fillInStackTrace() explícitamente.

+1

En C# obtenemos ese comportamiento de forma predeterminada y podemos simplemente 'throw;' cuando necesitamos mantener el seguimiento de la pila al reiniciar. Es por eso que me confundí. –

+0

-1: Sabemos por .NET que este no es el caso. Ciertamente, hay una razón para cuando se captura el seguimiento de la pila, pero no es porque no se puede gestionar cuando se captura por una declaración 'throw e;'. –

+0

@ 280Z28, el punto es que en Java no habría forma, ya que no tiene una construcción de lenguaje como throw ;. – Yishai

1

No querrá lanzar una excepción para alterar la pista de pila o no podría volver a lanzar una excepción de forma segura.

public void throwsException() { 
    throw new RuntimeException(); 
} 

public void logsException() { 
    try { 
     throwsException(); 
    } catch (RuntimeException e) { 
     e.printStrackTrace(); 
     throw e; // doesn't alter the exception. 
    } 
} 

@Test 
public void youCanSeeTheCauseOfAnException(){ 
    try { 
     logsException(); 
    } catch(Exception e) { 
     e.printStrackTrace(); // shows you the case of the exception, not where it was last re-thrown. 
    } 
} 
0

El seguimiento de la pila en la excepción corresponde a la operación "nueva", nada más.

Cuestiones relacionadas