2012-03-20 16 views
29

Sono curioso perché il metodo fillInStackTrace di java.lang.Throwable è pubblico?Perché il metodo Throwable.fillInStackTrace() è pubblico? Perché qualcuno dovrebbe usarlo?

Questo metodo sostituisce la traccia di stack originale con quella dal punto in cui è chiamata, rimuovendo le informazioni necessarie per localizzare l'eccezione. Potrebbe essere usato per offuscare, ma senza molto sforzo, dal momento che la nuova traccia di stack avrebbe indirizzato al codice di offuscamento. Il modo migliore sarebbe semplicemente nascondere l'eccezione o lanciare quella nuova.

Ma non riesco a trovare nessuna ragionevole caso per chiamare questo metodo su esistente Throwable. Quindi la domanda è: perché questo metodo è pubblico? C'è qualche senso dietro?

risposta

36

Una ragione è per le prestazioni. Lanciare e catturare un'eccezione è economico; la parte costosa è filling in the stack trace. Se si esegue l'override di fillInStackTrace() per non eseguire alcuna operazione, anche la creazione di un'eccezione diventa economica.

Con poche eccezioni, è possibile utilizzare le eccezioni per flow control, che possono rendere il codice più leggibile in determinate situazioni; è possibile utilizzarli quando si implementano le lingue JVM dove è necessario more advanced flow control e sono utili se si sta scrivendo uno actors library.

+0

Questo è interessante, ma non è forse un concetto di Java per controllare il flusso tramite eccezioni? –

+1

In generale si, ma come puoi vedere se segui i collegamenti, a volte è molto utile. – sbridges

+3

Dubito che questo uso fosse l'intenzione dei progettisti – Attila

4

Ecco un caso ragionevole: nascondere i dettagli dell'applicazione dall'utente non autorizzato. Ad esempio, non si desidera esporre la traccia di stack di eccezione generata quando la chiave di licenza è scaduta perché semplifica il lavoro dell'hacker.

+1

Ma perché in tal caso mostrare traccia dello stack comunque? –

+0

@lechlukasz: non * è * necessario per mostrare la traccia dello stack. A volte è sufficiente controllare il file di log in cui vengono stampate tutte le eccezioni. Ho personalmente rotto alcune applicazioni protette (solo per interessi sportivi) e ho visto che usavano questo trucco in aggiunta all'offuscamento. – AlexR

18

Un possibile uso legittimo sta creando un'eccezione in un luogo diverso da quello in cui si sta effettivamente effettuando il lancio. Ad esempio, potresti avere un'applicazione in cui fornisci una funzione di plugin per generare eccezioni personalizzate. Poiché lo stacktrace è compilato in per la costruzione, la traccia dell'eccezione includerà probabilmente informazioni fuorvianti (includerà le chiamate nel factory delle eccezioni personalizzate). pertanto, l'applicazione chiamerebbe fillInStackTrace() nell'eccezione dopo la generazione dal factory personalizzato per fornire una traccia di stack più accurata al destinatario finale dell'eccezione.

Questo rejected bug indica che non sei l'unico confuso dalla necessità di questo metodo.

1

Un simile question fornisce la risposta.

In base al JLS, per impostazione predefinita, lo stacktrace viene compilato quando viene creata l'istanza dell'eccezione. Tuttavia, ci sono momenti in cui potresti voler "resettare" lo stack in una posizione più utile durante il rethrowing.

Per consentire questa funzionalità, abbiamo fillInStackTrace()

EDIT

Bene, oggi ho imparato la specifica libreria standard di Java è stato rimosso dalla JLS dopo la revisione 1. Come la prefazione alla seconda edizione:

The specifications of the libraries are now far too large to fit into this volume, and 
they continue to evolve. Consequently, API specifications have been removed from 
this book. The library specifications can be found on the Web; this specification 
now concentrates solely on the Java programming language proper. 

Come tale, abbiamo davvero bisogno di guardare più da vicino la javadoc per capirlo. L'API è qui sorprendentemente allentata.Le intere porzioni rilevanti sono dall'intestazione, in cui si afferma:

Instances of two subclasses, Error and Exception, are conventionally used to indicate 
that exceptional situations have occurred. Typically, these instances are freshly 
created in the context of the exceptional situation so as to include relevant 
information (such as stack trace data). 

E java.lang.Throwable.printStackTrace() è

Prints this throwable and its backtrace to the standard error stream. 

La mia convinzione è che questo è quello di consentire implementors VM per fornire qualunque è l'implementazione più performante/appropriata per il loro contesto. Con questo contesto, avere il pubblico esplicito fillInStackTrace() ha ancora più senso.

Per impostazione predefinita, le macchine virtuali forniscono la posizione migliore per il backtrace dello sforzo. Rendere pubblico fillInStackTrace() consente agli sviluppatori di perfezionare ulteriormente.

-

un montaggio finale :)

sono riuscito a rintracciare un collegamento mancante alla relevant bits of the JLS First Edition e mi piacerebbe offrire un ulteriore motivo per cui il metodo è pubblico. È meglio o peggio una spiegazione comune in Java.

Era sempre stato così.

+0

Puoi spiegare quali informazioni aggiuntive sono dietro al tuo link oltre la risposta di jtahlborn a questa domanda? Il JLS indica qualcosa sull'uso di 'fillInStackTrace'? –

1

Forse si desidera rilevare un'eccezione e lanciarne una nuova che non incapsula la precedente eccezione. getStackTrace da quello catturato e imposta lo stackTrace per quello nuovo.

Questo sarebbe anche lavorare quando si ha un'eccezione Singleton, un'istanza volta come:

private static Exception theException = new MyException(); 

che viene generata più volte. Ciò imposterà lo stackTrack su qualcosa correlato all'eccezione rilevata senza la necessità di creare ogni volta una nuova eccezione. Penso che questo sia il modo in cui Scala fa il suo continuare e rompere i costrutti.

try { 
    .... 
} catch (OtherException oe) { 
    theException.fillStackTrace(oe.getStackTrace()); 
    theException.setMessage(...); //etc 
    throw theException; 
} 

Uso eccezioni statici può essere difficile se può essere lanciata più volte contemporaneamente (cioè in filetti)

+1

Nella migliore delle ipotesi, questo è offuscamento del codice. Le eccezioni dovrebbero essere trattate come se fossero immutabili. L'OtherException dovrebbe semplicemente essere incapsulato come causa di una nuova eccezione. Scala utilizza tracce di stack vuote per il controllo di flusso non locale come un altro esempio per lo stesso motivo spiegato nel mio post, ma non in questo modo. –

+0

Le eccezioni non sono immutabili, quindi non si desidera, ad esempio, assegnarle a una statica. –

5

Un caso uso legittimo per Throwable.fillInStackTrace() è per il flusso di controllo non locale:

Ad esempio, all'interno del mio framework TrueZIP, utilizzo una complessa catena di decoratori per i controller del file system. Uno dei controller in questa catena è un'istanza della classe FsLockController. Questo oggetto è responsabile della gestione di ReentrantReadWriteLock per il file system. Ora un controller di file system da qualche parte più in profondità in questa catena potrebbe rilevare che è necessario un blocco di scrittura, ma solo un blocco di lettura è stato acquisito da FsLockController. Poiché non è possibile aggiornare un blocco di lettura a un blocco di scrittura senza dead-locking, è necessario generare un'eccezione, che è un'istanza della classe FsNeedsWriteLockException. FsLockController decorerà quindi questa eccezione, rilascerà il blocco di lettura e acquisirà il blocco di scrittura prima di riprovare l'operazione.

Questo tipo di flusso di controllo non locale funziona in realtà molto bene, ma c'è una cosa da considerare: lanciare e catturare un'eccezione è economica, ma la compilazione o l'esame della traccia dello stack è costosa. Ora, poiché questo particolare tipo di eccezione viene solo gettato e catturato all'interno di questa catena di controller del file system, non ho bisogno di una traccia dello stack e quindi posso tranquillamente sovrascrivere Throwable.fillInStackTrace() con un corpo del metodo vuoto per sopprimere questa operazione costosa .

Il codice sorgente per la classe base di questo tipo di eccezione può essere visto here.

Problemi correlati