Quando catturare java.lang.Error?


Risposte:


101

In genere, mai.

Tuttavia, a volte è necessario rilevare errori specifici.

Se stai scrivendo codice framework-ish (caricando classi di terze parti), potrebbe essere saggio prendere LinkageError (nessuna definizione di classe trovata, collegamento insoddisfatto, cambio di classe incompatibile).

Ho anche visto alcuni stupidi codici di terze parti che lanciano sottoclassi Error, quindi dovrai gestire anche quelli.

A proposito, non sono sicuro che non sia possibile riprendersi OutOfMemoryError.


3
Quello che dovevo fare esattamente per caricare le DLL, che non avrebbe funzionato se non fossero state configurate correttamente. Non è un errore fatale in caso di questa applicazione.
Mario Ortegón

7
A volte ha senso rilevare OutOfMemoryError, ad esempio quando si creano elenchi di array di grandi dimensioni.
SpaceTrucker

3
@SpaceTrucker: questo approccio funziona bene nelle applicazioni multithread o c'è un rischio significativo che allocazioni più piccole in altri thread falliscano a causa di ciò? ... presumibilmente solo se i tuoi array erano abbastanza piccoli da essere allocati, ma non lasciavano nulla per nessun altro.
PJTraill

@PJTraill non ne sono sicuro. Ciò richiederebbe alcuni campioni statistici del mondo reale. Pensavo di aver visto un codice del genere, ma non ricordo dove fosse.
SpaceTrucker

51

Mai. Non si può mai essere sicuri che l'applicazione sia in grado di eseguire la riga di codice successiva. Se ottieni un messaggio OutOfMemoryError, non hai alcuna garanzia che sarai in grado di fare qualcosa in modo affidabile . Cattura RuntimeException e controllato le eccezioni, ma mai gli errori.

http://pmd.sourceforge.net/rules/strictexception.html


27
Mai dire mai. abbiamo codice di test che fa un "asserire falso;" quindi rileva l'eccezione AssertionError per assicurarsi che il flag -ea sia impostato. Oltre a questo ... sì, probabilmente mai ;-)
Programmatore fuorilegge

3
Che ne dici di un'applicazione server che passa le richieste ai thread di lavoro. Non ha senso catturare Throwable sul thread di lavoro per intercettare eventuali errori e almeno provare a registrare cosa è andato storto?
Leigh

11
Mai ... tranne quando è assolutamente necessario. Mai è una parola forte e ci sono sempre eccezioni alle regole. Se stai costruendo un framework, non è così improbabile che dovrai rilevare e gestire determinati errori, anche se si tratta solo di log.
Robin

Che ne dici di errori ad esempio NoSuchMethodError che proviene da metodi di libreria di terze parti?
ha9u63ar

@OutlawProgrammer Per la cronaca, ci sono altri modi per eseguire lo stesso test:boolean assertionsEnabled = false; assert assertionsEnabled = true;
shmosel

16

Generalmente dovresti sempre prenderlo java.lang.Errore scriverlo in un registro o mostrarlo all'utente. Lavoro in supporto e vedo ogni giorno che i programmatori non possono dire cosa è successo in un programma.

Se si dispone di un thread daemon, è necessario impedire che venga terminato. In altri casi la tua applicazione funzionerà correttamente.

Dovresti catturare solo java.lang.Erroral livello più alto.

Se guardi l'elenco degli errori vedrai che la maggior parte può essere gestita. Ad esempio aZipError verifica quando si leggono file zip corrotti.

Gli errori più comuni sono OutOfMemoryErroreNoClassDefFoundError , che sono entrambi nella maggior parte dei casi problemi di runtime.

Per esempio:

int length = Integer.parseInt(xyz);
byte[] buffer = new byte[length];

può produrre un file OutOfMemoryError ma è un problema di runtime e non c'è motivo per terminare il programma.

NoClassDefFoundErrorsi verificano principalmente se una libreria non è presente o se si lavora con un'altra versione di Java. Se è una parte facoltativa del programma, non interrompere il programma.

Posso fornire molti altri esempi del motivo per cui è una buona idea catturare Throwableil livello più alto e produrre un utile messaggio di errore.


Sospetto che sia meglio in quei casi fallire il demone con gli avvisi adeguati, quindi avere la possibilità che rimanga in vita come un fantasma eterico su una jvm fallita dove potrebbe dare il falso pretesto che è davvero vivo e sta facendo qualcosa
Andrew Norman

OutOfMemoryErrornon è un errore di runtime non esiste alcuna garanzia che l'applicazione possa ripristinarlo. Se sei fortunato, potresti ottenere OOM, new byte[largeNumber]ma se tale allocazione non fosse sufficiente per causare OOM, potrebbe essere attivato nella riga successiva o nel thread successivo. Questo è un problema di runtime perché se lengthè un input non attendibile dovrebbe essere convalidato prima di chiamare new byte[].
Jeeyoung Kim

NoClassDefFoundErrorpuò verificarsi ovunque , poiché viene invocato quando il codice java compilato non riesce a trovare una classe. Se il tuo JDK è configurato in modo errato, può innescarsi dal tentativo di utilizzare la java.util.*classe ed è praticamente impossibile programmarlo. Se opzionalmente includi una dipendenza, dovresti usarla ClassLoaderper verificare se esiste, che genera ClassNotFoundException.
Jeeyoung Kim

1
ZipErrorindica che il file jar contenente le classi è un file zip danneggiato. Questo è un problema abbastanza serio ea questo punto non puoi fidarti di alcun codice che viene eseguito e sarebbe una cosa irresponsabile tentare di "recuperare" da esso.
Jeeyoung Kim

2
In generale, potrebbe essere utile catturare java.lang.Erroro java.lang.Throwableal massimo livello e tentare di fare qualcosa con esso, ad esempio registrare un messaggio di errore. Ma a quel punto non vi è alcuna garanzia che ciò venga eseguito. Se la tua JVM è OOM, il tentativo di log potrebbe allocare più messaggi di posta elettronica Stringche innesca un altro OOM.
Jeeyoung Kim

15

Nell'ambiente multithread, la maggior parte delle volte si desidera catturarlo! Quando lo prendi, registralo e chiudi l'intera applicazione! Se non lo fai, qualche thread che potrebbe svolgere una parte cruciale sarebbe morto e il resto dell'applicazione penserà che tutto sia normale. Oltre a ciò, possono verificarsi molte situazioni indesiderate. Un problema più piccolo è che non saresti in grado di trovare facilmente la radice del problema, se altri thread iniziassero a lanciare alcune eccezioni a causa di un thread non funzionante.

Ad esempio, di solito il ciclo dovrebbe essere:

try {
   while (shouldRun()) {
       doSomething();
   }
}
catch (Throwable t) {
   log(t);
   stop();
   System.exit(1);
}

Anche in alcuni casi, vorresti gestire diversi Errori in modo diverso, ad esempio, su OutOfMemoryError potresti chiudere l'applicazione regolarmente (magari anche liberare un po 'di memoria e continuare), su altri, non c'è molto che puoi fare.


1
Catturare OutOfMemoryError e continuare piuttosto che esistere prontamente non è saggio perché il tuo programma è quindi in uno stato indefinito .
Raedwald

1
sistema di chiamata, l'uscita dopo aver catturato un lanciabile ha la conseguenza involontaria di uccidere tutto ciò che è in esecuzione sulla JVM e non solo l'applicazione in questione. Di solito non è una buona pratica per le applicazioni web che potrebbero essere ospitate su un server app con altre applicazioni web (per non parlare del fatto che avrebbe un impatto sul server app stesso).
Andrew Norman

9

Molto raramente.

Direi solo al livello più alto di un thread per TENTARE di emettere un messaggio con il motivo della morte di un thread.

Se ti trovi in ​​un framework che fa questo genere di cose per te, lascialo fare al framework.


6

Quasi mai. Gli errori sono progettati per essere problemi per i quali le applicazioni generalmente non possono fare nulla. L'unica eccezione potrebbe essere quella di gestire la presentazione dell'errore, ma anche questo potrebbe non andare come previsto a seconda dell'errore.


6

Di Errorsolito non dovrebbe essere catturato , poiché indica una condizione anormale che non dovrebbe mai verificarsi .

Dalla specifica dell'API Java per la Errorclasse:

An Errorè una sottoclasse di Throwable che indica problemi seri che un'applicazione ragionevole non dovrebbe cercare di individuare. La maggior parte di questi errori sono condizioni anormali. [...]

Un metodo non è tenuto a dichiarare nella sua clausola throws eventuali sottoclassi di Error che potrebbero essere lanciate durante l'esecuzione del metodo ma non catturate, poiché questi errori sono condizioni anormali che non dovrebbero mai verificarsi.

Come menzionato nella specifica, un Errorviene lanciato solo in circostanze che sono Probabili, quando si Errorverifica, l'applicazione può fare molto poco e in alcune circostanze, la Java Virtual Machine stessa potrebbe trovarsi in uno stato instabile (come VirtualMachineError)

Sebbene an Errorsia una sottoclasse, Throwableciò significa che può essere catturato da una try-catchclausola, ma probabilmente non è realmente necessario, poiché l'applicazione si troverà in uno stato anormale quando un Errorviene lanciato dalla JVM.

C'è anche una breve sezione su questo argomento nella Sezione 11.5 The Exception Hierarchy of the Java Language Specification, 2nd Edition .


6

Se sei abbastanza pazzo da creare un nuovo framework di unit test, il tuo test runner dovrà probabilmente catturare java.lang.AssertionError lanciato da qualsiasi test case.

Altrimenti, vedi altre risposte.


5

E ci sono un paio di altri casi in cui se rilevi un errore, devi rilanciarlo . Ad esempio ThreadDeath non dovrebbe mai essere catturato, può causare un grosso problema se lo prendi in un ambiente contenuto (ad es. Un application server):

Un'applicazione dovrebbe catturare istanze di questa classe solo se deve essere ripulita dopo essere stata terminata in modo asincrono. Se ThreadDeath viene rilevato da un metodo, è importante che venga rilanciato in modo che il thread muoia effettivamente.


2
Che in realtà non è un problema perché semplicemente non prendi le Errors.
Bombe

4

Molto, molto raramente.

L'ho fatto solo per un caso noto molto molto specifico. Ad esempio, java.lang.UnsatisfiedLinkError potrebbe essere lanciato se due ClassLoader di indipendenza caricano la stessa DLL. (Sono d'accordo che dovrei spostare il JAR su un classloader condiviso)

Ma il caso più comune è che hai bisogno di loggarti per sapere cosa è successo quando l'utente si è presentato a lamentarsi. Vuoi un messaggio o un popup per l'utente, piuttosto che silenziosamente morto.

Anche i programmatori in C / C ++, fanno apparire un errore e dicono qualcosa che la gente non capisce prima che esca (es. Errore di memoria).


3

In un'applicazione Android sto catturando java.lang.VerifyError . Una libreria che sto utilizzando non funzionerà su dispositivi con una vecchia versione del sistema operativo e il codice della libreria genererà un tale errore. Ovviamente potrei evitare l'errore controllando la versione del sistema operativo in fase di esecuzione, ma:

  • Il più vecchio SDK supportato potrebbe cambiare in futuro per la libreria specifica
  • Il blocco dell'errore try-catch fa parte di un meccanismo di fallback più grande. Alcuni dispositivi specifici, sebbene dovrebbero supportare la libreria, generano eccezioni. Prendo VerifyError e tutte le eccezioni per utilizzare una soluzione di fallback.

3

è abbastanza utile catturare java.lang.AssertionError in un ambiente di test ...


Che valore stai cercando di aggiungere?
lpapp

aggiungendo il valore di una suite di test che non si interrompe
Jono

2

Idealmente non dovremmo gestire / rilevare errori. Ma ci possono essere casi in cui dobbiamo farlo, in base ai requisiti del framework o dell'applicazione. Supponiamo che io abbia un demone XML Parser che implementa DOM Parser che consuma più memoria. Se c'è un requisito come il thread Parser non dovrebbe essere morto quando ottiene OutOfMemoryError , invece dovrebbe gestirlo e inviare un messaggio / posta all'amministratore dell'applicazione / framework.


1

idealmente non dovremmo mai rilevare l'errore nella nostra applicazione Java in quanto è una condizione anormale. L'applicazione si troverebbe in uno stato anormale e potrebbe causare il danneggiamento del veicolo o dare un risultato gravemente errato.


1

Potrebbe essere appropriato rilevare errori all'interno di unit test che verificano che venga fatta un'asserzione. Se qualcuno disabilita le asserzioni o elimina in altro modo l'asserzione, vorresti sapere


1

Si verifica un errore quando la JVM non funziona più come previsto o è in procinto di farlo. Se si rileva un errore, non vi è alcuna garanzia che il blocco catch verrà eseguito e ancor meno che verrà eseguito fino alla fine.

Dipenderà anche dal computer in esecuzione, dallo stato della memoria corrente, quindi non c'è modo di testare, provare e fare del tuo meglio. Avrai solo un risultato fastidioso.

Eseguirai anche il downgrade della leggibilità del codice.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.