2009-11-08 10 views
12

Se corro il seguente test, non riesce:Perché è possibile "falsificare" la traccia dello stack di un'eccezione in 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 il seguente errore:

Expected :stackTraceMentionsTheLocationWhereTheExceptionWasThrown 
Actual :setUp 

Lo stack trace è solo flat out mentendo.

Perché la traccia dello stack non viene riscritta quando viene generata l'eccezione? Non sono uno sviluppatore Java e forse mi manca qualcosa qui.

+0

Non so se 6 anni fa potremmo impostare lo stack trace "causa" dell'eccezione, ma è meglio usarlo dopo aver creato la nuova eccezione e prima di lanciarlo: Exception.initCause (Throwable), dove si configura la causa stacktrace con setStackTrace(). –

risposta

19

L'analisi dello stack si crea quando l'eccezione viene creata un'istanza, non quando si è gettato. Questo è specificato comportamento 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. 

Non so il motivo per cui hanno fatto in questo modo, ma se la specifica definisce così, è almeno coerente su tutti i vari Java VM.

Tuttavia, è possibile aggiornarlo chiamando exception.fillInStackTrace() manualmente.

Si noti inoltre che è necessario utilizzare Thread.currentThread().getStackTrace() anziché utilizzare new Exception().getStackTrace() (stile non valido).

+0

Non ho usato 'Thread.currentThread(). GetStackTrace()' perché 'Thread.currentThread(). GetStackTrace() [0] .getMethodName' è sempre' getStacktrace' ... –

+0

+1 per indicare il manuale la cosa 'fillInStackTrace'. In C# otteniamo quel comportamento di default e possiamo semplicemente "lanciare" quando è necessario mantenere la traccia dello stack quando si rilancia. –

+0

È 'getStackTrace() [1]', niente di più. – mhaller

1

Perché non hai chiesto di riscrivere quella traccia di stack. È stato impostato quando lo hai creato nel metodo setUp e non hai mai fatto nulla per alterarlo.

La classe Exception non offre alcuna possibilità di impostare il nome del metodo; è immutabile. Quindi non c'è modo che io sappia dove è possibile reimpostare il nome del metodo, a meno che non si voglia ricorrere a qualcosa di atroce come un riflesso.

L'annotazione @Test non mi dice se si sta utilizzando JUnit o TestNG, perché non riesco a vedere l'importazione statica, ma in entrambi i casi è possibile eseguire un test per verificare se una particolare eccezione viene generata da utilizzando il membro "previsto" nell'annotazione @Test.

+0

Sto usando jUnit, ma non stavo provando a verificare la presenza di eccezioni. Ho usato il test solo per illustrare la mia domanda. –

+0

Non sono sicuro di quale sia la domanda: "Come modifico il metodo da una traccia di stack?" – duffymo

0

Penso che l'ipotesi sia che non si istanzia un'eccezione a meno che non si stia procedendo a lanciarla, quindi perché pagare il prezzo per ottenere la traccia dello stack due volte?

Sarebbe difficile ricreare la traccia di stack mentre viene lanciata, poiché questo è solo l'invio dell'oggetto.

L'eccezione deve essere completamente impostata prima del lancio, quindi parte dell'istanza è ottenere la traccia dello stack.

UPDATE:

È possibile chiamare fillInStackTrace per risolvere questo: http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Throwable.html#fillInStackTrace%28%29

9

Lo stacktrace dell'eccezione viene compilato al momento della creazione dell'eccezione. Altrimenti sarebbe impossibile per catturare un'eccezione, gestirla e rilanciarla a. Lo stacktrace originale si perderebbe.

Se si desidera forzare questo è necessario chiamare exception.fillInStackTrace() esplicitamente.

+1

In C# otteniamo questo comportamento di default e possiamo semplicemente "lanciare" quando è necessario mantenere la traccia dello stack quando si rilancia. Ecco perché mi sono confuso. –

+0

-1: Sappiamo da .NET che questo non è il caso. C'è sicuramente qualche motivazione per quando viene catturata la traccia dello stack, ma non è perché non può essere gestita quando viene catturata da un'istruzione 'throw e;'. –

+0

@ 280Z28, il punto è che in java non ci sarebbe alcun modo, in quanto non ha un tale linguaggio costruttivo come lanciare. – Yishai

1

Non si vorrebbe lanciare un'eccezione per alterare la traccia di stack o non è possibile ripetere un'eccezione in modo sicuro.

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

La traccia di stack nell'eccezione corrisponde all'operazione "nuova", nient'altro.

Problemi correlati