2009-11-08 10 views
12

Jeśli uruchomić następujący test, to nie:Dlaczego mogę "podrobić" ślad stosu wyjątku w Javie?

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()); 
     } 
    } 
} 

z powodu następującego błędu:

Expected :stackTraceMentionsTheLocationWhereTheExceptionWasThrown 
Actual :setUp 

Stos ślad jest tylko flat out kłamie.

Dlaczego ślad stosu nie został przepisany po zgłoszeniu wyjątku? Nie jestem programistą Java i może brakuje mi tutaj czegoś.

+0

Nie wiem, czy 6 lat temu mogliśmy ustawić ślad stosu "przyczyny" wyjątku, ale lepiej jest użyć tego po utworzeniu nowego wyjątku i przed jego wyrzuceniem: Exception.initCause (Throwable), gdzie konfigurujesz przyczyna stacktrace z setStackTrace(). –

Odpowiedz

19

Ślad stosu jest tworzona, gdy wyjątek jest tworzony, gdy nie jest on wyrzucony. To jest określone zachowanie 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. 

nie wiem dlaczego zrobili to w ten sposób, ale jeżeli specyfikacja definiuje ją tak, to przynajmniej spójne na wszystkich różnych Java VM.

Można ją jednak odświeżyć, dzwoniąc pod numer exception.fillInStackTrace() ręcznie.

Należy również pamiętać, że należy używać Thread.currentThread().getStackTrace() zamiast używać new Exception().getStackTrace() (zły styl).

+0

Nie użyłem 'Thread.currentThread(). GetStackTrace()' ponieważ 'Thread.currentThread(). GetStackTrace() [0] .getMethodName' jest zawsze' getStacktrace' ... –

+0

+1 za wskazanie instrukcji rzecz 'fillInStackTrace'. W języku C# domyślnie otrzymujemy takie zachowanie, a my możemy po prostu "rzucić", gdy musimy zachować śledzenie stosu podczas ponownego wysyłania. –

+0

To 'getStackTrace() [1]', nic więcej. – mhaller

1

Ponieważ nie prosiłeś o przepisanie tego śladu stosu. Został on ustawiony, gdy utworzyłeś go w metodzie setUp i nigdy nie zrobiłeś nic, by to zmienić.

Klasa wyjątków nie daje żadnej możliwości ustawienia nazwy metody; jest niezmienny. Więc nie ma sposobu, aby wiedzieć, gdzie można ponownie ustawić nazwę metody, chyba że chcesz uciekać się do czegoś haniebnego jak odbicie.

Twoja adnotacja @Test nie mówi mi, czy używasz JUnit lub TestNG, ponieważ nie widzę statycznego importu, ale w każdym przypadku możesz uruchomić test, aby sprawdzić, czy dany wyjątek został zgłoszony przez użycie "oczekiwanego" członka w adnotacji @Test.

+0

Używam jUnit, ale nie próbowałem testować wyjątków, które są zgłaszane. Użyłem testu, aby zilustrować moje pytanie. –

+0

Nie jestem pewien, co to jest pytanie: "Jak zmienić metodę ze śledzenia stosu?" – duffymo

0

Myślę, że założenie jest, że nie będzie instancji wyjątek, chyba że jesteś w procesie rzucania go, dlaczego więc zapłacić cenę, aby uzyskać ślad stosu dwa razy?

Byłoby trudno odtworzyć ślad stosu podczas rzucania go, jako że jest po prostu wysłanie przedmiotu na zewnątrz.

Wyjątkiem powinny być w pełni skonfigurować przed rzutem, więc część instancji jest dostać ślad stosu.

UPDATE:

Można zadzwonić fillInStackTrace aby rozwiązać ten problem: http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Throwable.html#fillInStackTrace%28%29

9

Stos wyjątków jest wypełniany podczas tworzenia wyjątku. W przeciwnym razie byłoby to niemożliwe połowu wyjątek obsłużyć go i przekaż go. Oryginalny ślad stosu zostałby zgubiony.

Jeśli chcesz wymusić to trzeba zadzwonić exception.fillInStackTrace() wyraźnie.

+1

W języku C# domyślnie otrzymujemy takie zachowanie i możemy po prostu "rzucić", gdy zachodzi potrzeba śledzenia stosu podczas ponownego wysyłania. Dlatego się zdezorientowałem. –

+0

-1: Wiemy od .NET, że tak nie jest. Z pewnością istnieje uzasadnienie dla przechwytywania śladu stosu, ale nie dzieje się tak dlatego, że nie można nim zarządzać po przechwyceniu przez instrukcję 'throw e;'. –

+0

@ 280Z28, chodzi o to, że w języku Java nie byłoby sposobu, ponieważ nie ma takiej konstrukcji języka, jak rzut ;. – Yishai

1

Nie chcesz rezygnować z wyjątku, aby zmienić ścieżkę stosu lub nie możesz bezpiecznie rzucić wyjątku.

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

Ślad stosu w wyjątku odpowiada operacji "nowej", nic więcej.

Powiązane problemy