ChuckNorrisException non recuperabile


596

È possibile costruire un frammento di codice in Java che renderebbe un ipotetico java.lang.ChuckNorrisExceptionirraggiungibile?

I pensieri che mi sono venuti in mente stanno usando, ad esempio, intercettori o programmi orientati all'aspetto .



2
usando il suggerimento dal link @jschoen fornito (disabilita il verificatore del codice byte) puoi lanciare qualcosa che non estende Throwable! descritto nella mia risposta di seguito.
jtahlborn,

4
Questo estratto della risposta di Aioobe riassume abbastanza bene la domanda che @jschoen ha collegato: "Cioè, la tua domanda può essere interpretata come" Se una JVM si discosta dalle specifiche, può fare cose strane come lanciare primitivi "e la risposta è ovviamente, sì."
Dan Fiddling By Firelight,

2
@Max - Puoi approfondire gli usi pratici per questo?
Vineet Bhatia,

3
che ne dici di un'eccezione che si ripropone sul finalize()?
Lie Ryan,

Risposte:


314

Non ho provato questo, quindi non so se la JVM limiterebbe qualcosa del genere, ma forse potresti compilare il codice che genera ChuckNorrisException, ma in fase di esecuzione fornire una definizione di classe di ChuckNorrisExceptioncui non si estende Throwable .

AGGIORNARE:

Non funziona Genera un errore verificatore:

Exception in thread "main" java.lang.VerifyError: (class: TestThrow, method: ma\
in signature: ([Ljava/lang/String;)V) Can only throw Throwable objects
Could not find the main class: TestThrow.  Program will exit.

AGGIORNAMENTO 2:

In realtà, puoi farlo funzionare se disabiliti il ​​verificatore del codice byte! ( -Xverify:none)

AGGIORNAMENTO 3:

Per quelli che seguono da casa, ecco lo script completo:

Creare le seguenti classi:

public class ChuckNorrisException
    extends RuntimeException // <- Comment out this line on second compilation
{
    public ChuckNorrisException() { }
}

public class TestVillain {
    public static void main(String[] args) {
        try {
            throw new ChuckNorrisException();
        }
        catch(Throwable t) {
            System.out.println("Gotcha!");
        }
        finally {
            System.out.println("The end.");
        }
    }
}

Compilare le classi:

javac -cp . TestVillain.java ChuckNorrisException.java

Correre:

java -cp . TestVillain
Gotcha!
The end.

Commenta "estende RuntimeException" e ricompila ChuckNorrisException.javasolo :

javac -cp . ChuckNorrisException.java

Correre:

java -cp . TestVillain
Exception in thread "main" java.lang.VerifyError: (class: TestVillain, method: main signature: ([Ljava/lang/String;)V) Can only throw Throwable objects
Could not find the main class: TestVillain.  Program will exit.

Esegui senza verifica:

java -Xverify:none -cp . TestVillain
The end.
Exception in thread "main"

18
OK, e se prendessi Objectinvece di Throwable, allora? (Il compilatore non lo consentirà, ma poiché abbiamo già disabilitato il verificatore, forse si potrebbe hackerare il bytecode per farlo.)
Ilmari Karonen,

11
Secondo Cosa puoi lanciare in Java puoi ancora catturare cose che non si estendono gettabili, ma lanciarle e catturarle è un comportamento indefinito.
VolatileDream,

8
@dzieciou Possono essere veri insieme. Potresti essere in grado di catturarli utilizzando la tua versione dell'ambiente Java sulla tua versione specifica del tuo sistema operativo sul tuo tipo di processore. Ma se non è specificato nello standard se PUO 'essere catturato, si chiama comportamento indefinito, perché altre implementazioni di Java potrebbero scegliere di renderlo non catchaable.
Heinrich5991,

2
Hmmph. Speravo che per 176 voti, avessi scritto del codice JNI che correggesse l'intero stack di chiamate per riproporre la tua eccezione (chiamata naturalmente dal ctor).
kdgregory,

3
Quando fai tutto questo, è anche una grande idea stare su una gamba, picchiettare la testa e massaggiarti la pancia mentre fischi dixie ...;);)
Glen Best,

120

Dopo aver riflettuto su questo, ho creato con successo un'eccezione irraggiungibile. Ho scelto di chiamarlo JulesWinnfield, invece, piuttosto che Chuck, perché è un'eccezione madre fungo-nuvola-posa-nuvola. Inoltre, potrebbe non essere esattamente quello che avevi in ​​mente, ma sicuramente non può essere colto. Osservare:

public static class JulesWinnfield extends Exception
{
    JulesWinnfield()
    {
        System.err.println("Say 'What' again! I dare you! I double dare you!");
        System.exit(25-17); // And you shall know I am the LORD
    }
}


public static void main(String[] args)
{       
    try
    {
        throw new JulesWinnfield();
    } 
    catch(JulesWinnfield jw)
    {
        System.out.println("There's a word for that Jules - a bum");
    }
}

Et voilà! Eccezione non rilevata.

Produzione:

correre:

Dì di nuovo "Cosa"! Ti sfido! Ti do il doppio coraggio!

Risultato Java: 8

BUILD SUCCESSFUL (tempo totale: 0 secondi)

Quando avrò ancora un po 'di tempo, vedrò se non riesco a trovare anche qualcos'altro.

Inoltre, controlla questo:

public static class JulesWinnfield extends Exception
{
    JulesWinnfield() throws JulesWinnfield, VincentVega
    {
        throw new VincentVega();
    }
}

public static class VincentVega extends Exception
{
    VincentVega() throws JulesWinnfield, VincentVega
    {
        throw new JulesWinnfield();
    }
}


public static void main(String[] args) throws VincentVega
{

    try
    {
        throw new JulesWinnfield();
    }
    catch(JulesWinnfield jw)
    {

    }
    catch(VincentVega vv)
    {

    }
}

Provoca un overflow dello stack: di nuovo, le eccezioni rimangono non rilevate.


32
+1 per l'utilizzo di Stack Overflow nella risposta. Sto scherzando, davvero un'ottima risposta.
Giosia

7
Una "eccezione irrintracciabile" corretta garantirebbe l'esecuzione di tutti i blocchi che racchiudono infine senza intercettazioni. Uccidere il sistema non fa eccezione: uccide solo il sistema.
Supercat,

4
Come si "lancia" il JulesWinfield? Il sistema non si arresterà bruscamente prima di essere lanciato?
supercat

6
@mikeTheLiar: il sistema esce durante il costruttore, no? L'affermazione throw new Whatever()è in due parti: Whatever it = new Whatever(); throw it;e il sistema muore prima di raggiungere la seconda parte.
supercat

5
@mikeTheLiar in realtà puoi catturare Jules o Vincent abbastanza facilmente ... se riesci a lanciarlo. È facile creare un'eccezione che non puoi lanciare:class cn extends exception{private cn(){}}
John Dvorak il

85

Con una tale eccezione sarebbe ovviamente obbligatorio usare un System.exit(Integer.MIN_VALUE);dal costruttore perché questo è ciò che accadrebbe se si lanciasse una tale eccezione;)


32
+1; IMO questa è l'unica soluzione possibile. Un'eccezione irrinunciabile dovrebbe terminare il programma ...
home

7
No, non sarebbe quello che succede quando si lancia un'eccezione del genere. Un'eccezione non rilevata interromperà un singolo thread, non uscirà da jvm, in alcuni contesti System.exit stesso provocherà anche un'eccezione SecurityException - non tutti i frammenti di codice possono chiudere un programma.
josefx,

3
Puoi usare while(true){}invece di System.exit().
Piotr Praszmo,

2
in realtà, è possibile impedire il System.exit()funzionamento installando un gestore della sicurezza che non lo consente. ciò trasformerebbe il costruttore in un'eccezione diversa (SecurityException), che potrebbe essere rilevata.
jtahlborn,

5
Umm, tecnicamente non hai mai lanciato un'eccezione. Non hai ancora costruito l'oggetto da lanciare!
Thomas Eding,

46

Qualsiasi codice può essere intercettato. Quindi no, qualunque eccezione tu crei sarà una sottoclasse di Throwable e sarà soggetta ad essere catturata.


11
Gettabile da hangsolo sarebbe nel tentativo di catturare ChuckNorrisException: P
PermGenError il

35
public class ChuckNorrisException extends Exception {
    public ChuckNorrisException() {
        System.exit(1);
    }
}

(Certo, tecnicamente questa eccezione non viene mai effettivamente lanciata, ma un vero e proprio ChuckNorrisExceptionnon può essere lanciato - ti lancia prima.)


4
Un mio collega mi aveva suggerito di rimanere "for (;;) {}" poiché riteneva che una chiamata "System.exit (1)" potesse generare un'eccezione di sicurezza. Sto votando questo per creatività!
Phil Street,

Sono d'accordo con la fine della tua risposta. Non scherzare mai con ChuckNorris, eccezione o no.
Benj,

28

Qualsiasi eccezione lanciata deve estendere Throwable, in modo che possa essere sempre catturata. Quindi la risposta è no.

Se vuoi renderlo difficile da gestire, puoi ignorare i metodi getCause(), getMessage(), getStackTrace()e toString()lanciarne un altro java.lang.ChuckNorrisException.


2
Hmm, catch (Throwable t) chiama qualche metodo o muta in altro modo l'oggetto? Potrebbe essere possibile far sì che una clausola catch prenda un'eccezione per renderlo impossibile.
Colton,

1
Penso che catch(Throwable t)lo memorizzi solo in variabile, quindi i miei suggerimenti si applicano solo nel blocco successivo quando l'utente vuole far fronte all'eccezione
mirelon

24

La mia risposta si basa sull'idea di @ jtahlborn, ma è un programma Java perfettamente funzionante , che può essere impacchettato in un file JAR e persino distribuito al tuo server delle applicazioni preferito come parte di un'applicazione web .

Prima di tutto, definiamo la ChuckNorrisExceptionclasse in modo che non blocchi JVM dall'inizio (Chuck ama davvero il crash JVM BTW :)

package chuck;

import java.io.PrintStream;
import java.io.PrintWriter;

public class ChuckNorrisException extends Exception {

    public ChuckNorrisException() {
    }

    @Override
    public Throwable getCause() {
        return null;
    }

    @Override
    public String getMessage() {
        return toString();
    }

    @Override
    public void printStackTrace(PrintWriter s) {
        super.printStackTrace(s);
    }

    @Override
    public void printStackTrace(PrintStream s) {
        super.printStackTrace(s);
    }
}

Ora va in Expendablesclasse per costruirlo:

package chuck;

import javassist.*;

public class Expendables {

    private static Class clz;

    public static ChuckNorrisException getChuck() {
        try {
            if (clz == null) {
                ClassPool pool = ClassPool.getDefault();
                CtClass cc = pool.get("chuck.ChuckNorrisException");
                cc.setSuperclass(pool.get("java.lang.Object"));
                clz = cc.toClass();
            }
            return (ChuckNorrisException)clz.newInstance();
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }
}

E infine la Mainclasse per calciare un calcio:

package chuck;

public class Main {

    public void roundhouseKick() throws Exception {
        throw Expendables.getChuck();
    }

    public void foo() {
        try {
            roundhouseKick();
        } catch (Throwable ex) {
            System.out.println("Caught " + ex.toString());
        }
    }

    public static void main(String[] args) {
        try {
            System.out.println("before");
            new Main().foo();
            System.out.println("after");
        } finally {
            System.out.println("finally");
        }
    }
}

Compilalo ed eseguilo con il seguente comando:

java -Xverify:none -cp .:<path_to_javassist-3.9.0.GA.jar> chuck.Main

Otterrai il seguente output:

before
finally

Nessuna sorpresa - dopo tutto è un calcio circolare :)


molto bella! non ho fatto molto con la manipolazione della definizione di classe da solo. hai ancora bisogno di "verifica: nessuno" sulla riga di comando?
jtahlborn,

@jtahlborn Sì, il tentativo di lanciare un oggetto che non discende da Throwable fallisce senza "verifica: nessuno".
Wildfire,

oh, ho avuto l'impressione che questo in qualche modo aggirasse quel vincolo. quindi in cosa differisce dalla mia risposta?
jtahlborn,

2
La differenza principale è che funziona codice java senza hacking in fase di compilazione
Wildfire,

15

Nel costruttore è possibile avviare un thread che chiama ripetutamente originalThread.stop (ChuckNorisException.this)

Il thread potrebbe catturare ripetutamente l'eccezione ma continuerebbe a lanciarla fino alla sua morte.


13

No. Tutte le eccezioni in Java devono essere suddivise in sottoclassi java.lang.Throwablee, sebbene possa non essere una buona pratica, puoi prendere ogni tipo di eccezione in questo modo:

try {
    //Stuff
} catch ( Throwable T ){
    //Doesn't matter what it was, I caught it.
}

Vedere la documentazione di java.lang.Thable per ulteriori informazioni.

Se stai cercando di evitare le eccezioni verificate (che devono essere gestite in modo esplicito), ti consigliamo di sottoclassare Error o RuntimeException.


9

In realtà la risposta accettata non è così piacevole perché Java deve essere eseguito senza verifica, cioè il codice non funzionerebbe in circostanze normali.

AspectJ in soccorso per la vera soluzione !

Classe di eccezione:

package de.scrum_master.app;

public class ChuckNorrisException extends RuntimeException {
    public ChuckNorrisException(String message) {
        super(message);
    }
}

Aspetto:

package de.scrum_master.aspect;

import de.scrum_master.app.ChuckNorrisException;

public aspect ChuckNorrisAspect {
    before(ChuckNorrisException chuck) : handler(*) && args(chuck) {
        System.out.println("Somebody is trying to catch Chuck Norris - LOL!");
        throw chuck;
    }
}

Applicazione di esempio:

package de.scrum_master.app;

public class Application {
    public static void main(String[] args) {
        catchAllMethod();
    }

    private static void catchAllMethod() {
        try {
            exceptionThrowingMethod();
        }
        catch (Throwable t) {
            System.out.println("Gotcha, " + t.getClass().getSimpleName() + "!");
        }
    }

    private static void exceptionThrowingMethod() {
        throw new ChuckNorrisException("Catch me if you can!");
    }
}

Produzione:

Somebody is trying to catch Chuck Norris - LOL!
Exception in thread "main" de.scrum_master.app.ChuckNorrisException: Catch me if you can!
    at de.scrum_master.app.Application.exceptionThrowingMethod(Application.java:18)
    at de.scrum_master.app.Application.catchAllMethod(Application.java:10)
    at de.scrum_master.app.Application.main(Application.java:5)

8

Una variante del tema è il fatto sorprendente che è possibile generare eccezioni controllate non dichiarate dal codice Java. Dato che non è dichiarato nella firma dei metodi, il compilatore non ti permetterà di catturare l'eccezione stessa, sebbene tu possa prenderla come java.lang.Exception.

Ecco una classe di supporto che ti consente di lanciare qualsiasi cosa, dichiarata o meno:

public class SneakyThrow {
  public static RuntimeException sneak(Throwable t) {
    throw SneakyThrow.<RuntimeException> throwGivenThrowable(t);
  }

  private static <T extends Throwable> RuntimeException throwGivenThrowable(Throwable t) throws T {
    throw (T) t;
  }
}

Ora throw SneakyThrow.sneak(new ChuckNorrisException());genera una ChuckNorrisException, ma il compilatore si lamenta

try {
  throw SneakyThrow.sneak(new ChuckNorrisException());
} catch (ChuckNorrisException e) {
}

sulla cattura di un'eccezione che non viene generata se ChuckNorrisException è un'eccezione controllata.


6

Le uniche ChuckNorrisExceptions in Java dovrebbero essere OutOfMemoryErroreStackOverflowError .

Puoi effettivamente "catturarli" nel modo in cui a catch(OutOfMemoryError ex) verrà eseguito nel caso in cui venga generata l'eccezione, ma quel blocco riproporrà automaticamente l'eccezione al chiamante.

Non credo che sia public class ChuckNorrisError extends Erroril trucco, ma potresti provarlo. Non ho trovato documentazione sull'estensioneError


2
L'errore continua a essere lanciato, quindi non c'è modo di impedirlo. Questo è in base alla progettazione del linguaggio Java.
JasonM1,

1
@ JasonM1 Non credo che l'OP abbia richiesto un'eccezione in realtà "irraggiungibile", e intendevo dire che l'errore si propaga anche se lo si rileva. Quindi, qualsiasi Throwable è catturabile ma questi due alla fine si propagheranno indipendentemente da quello che fai
usr-local-ΕΨΗΕΛΩΝ

Per essere ingannevoli, ChuckNorrisException potrebbe estendere direttamente Throwable, quindi non sarebbe né eccezione né errore!
JasonM1,

4
L'errore non si propaga anche se lo prendi, non sono sicuro di dove ti sia venuta quell'idea.
jtahlborn,

3
Penso che tu sia ben confuso riguardo a Erros, sono normali eccezioni come tutto ciò che estende Throwable o persino Throwable, stesso.
bestsss

6

Is it possible to construct a snippet of code in java that would make a hypothetical java.lang.ChuckNorrisException uncatchable?

Sì, ed ecco la risposta: progetta il tuo in modo java.lang.ChuckNorrisExceptiontale che non sia un'istanza di java.lang.Throwable. Perché? Un oggetto non lanciabile è irraggiungibile per definizione perché non puoi mai catturare qualcosa che non può mai essere lanciato.


2
Ma allora non fa eccezione.
Dolbi,

8
@dolbi: non riesco a trovare posto nella domanda del PO che gli stati java.lang.ChuckNorrisExceptiondebbano essere un'eccezione, figuriamoci gettabili
Thomas Eding,

1
Immagino che non sia dichiarato, ma è implicito. Sei un matematico :-), vero?
dolbi il

3

Puoi mantenere ChuckNorris interno o privato e incapsularlo o trascinarlo ...

try { doChuckAction(); } catch(ChuckNorrisException cne) { /*do something else*/ }


7
Non credo che l'idea fosse di prenderlo. Penso che l'idea sia quella di impedire che venga catturato.
Patrick Roberts,

Correggimi se sbaglio, ma se lo rendi interno non puoi raggiungerlo senza riflettere.
Jay,

5
sì, ma finché puoi rilevare l'eccezione o il lancio, la visibilità del tipo effettivo è irrilevante.
Keith

3

Due problemi fondamentali con la gestione delle eccezioni in Java sono che utilizza il tipo di un'eccezione per indicare se un'azione deve essere basata su di essa e che si presume che tutto ciò che agisce in base a un'eccezione (ad esempio "intercettalo") si risolva la condizione sottostante. Sarebbe utile avere un mezzo con cui un oggetto di eccezione possa decidere quali gestori dovrebbero eseguire e se i gestori che hanno eseguito finora hanno ripulito le cose abbastanza perché il presente metodo soddisfi le sue condizioni di uscita. Mentre questo potrebbe essere usato per fare eccezioni "irraggiungibili", due usi più grandi sarebbero (1) fare eccezioni che saranno considerate gestite solo quando vengono catturate da un codice che effettivamente sa come gestirle,finallyFooExceptionfinallyblocco durante lo svolgimento di a BarException, entrambe le eccezioni dovrebbero propagarsi nello stack di chiamate; entrambi dovrebbero essere catchable, ma lo svolgimento dovrebbe continuare fino a quando entrambi non saranno stati catturati). Sfortunatamente, non penso che ci sarebbe alcun modo per far funzionare il codice di gestione delle eccezioni in quel modo senza rompere le cose.


un'idea interessante, ma non credo che il codice di basso livello saprebbe cosa significa "un'eccezione particolare" per il chiamante, quindi non credo che avrebbe senso per il lanciatore decidere quali gestori dovrebbero eseguire.
jtahlborn,

@jtahlborn: in questo momento, il lanciatore decide quali gestori di eccezioni devono eseguire tramite la scelta del tipo di eccezione. Questo rende quasi impossibile gestire alcuni scenari in modo pulito. Tra le altre cose: (1) se si verifica un'eccezione mentre un finallyblocco si sta ripulendo da un'eccezione precedente, è del tutto possibile che entrambe le eccezioni, in assenza dell'altra, possano essere qualcosa che il codice si aspetterebbe di gestire e continuare, ma che maneggiare l'uno e ignorare l'altro sarebbe male. Non esiste alcun meccanismo, tuttavia, per produrre un'eccezione composita che entrambi i gestori elaborerebbero.
Supercat,

@jtahlborn: Inoltre, rende molto difficile consentire che le eccezioni che si verificano all'interno dei callback vengano gestite dal livello dell'applicazione esterna. Se l'eccezione del callback è racchiusa in un altro tipo di eccezione, il tipo di eccezione del callback non può essere utilizzato nel livello esterno nel decidere se catturarlo; se non è racchiuso, un'eccezione "accidentale" di livello intermedio può essere scambiata per una che si verifica nel callback. Se un oggetto eccezione avvolta veniva detto quando veniva passato al livello dell'applicazione esterna, poteva quindi iniziare a rispondere ai tipi di eccezioni avvolte.
supercat

non stavo discutendo degli altri tuoi punti, ma solo l'affermazione sull'oggetto eccezione che decideva su quali gestori verranno eseguiti. in una certa misura i tipi di eccezione lo fanno già, ma sembra che tu volessi qualcosa di più dinamico, con cui non ero d'accordo. penso che il tuo argomento principale (di cui stai arrivando lateralmente) è quello di catturare quante più informazioni possibili in basso e lasciare che i livelli superiori vedano e funzionino con tutte quelle informazioni. su questo punto generale sono d'accordo con te, tuttavia il diavolo è nei dettagli / implementazione.
jtahlborn,

@jtahlborn: La mia intenzione non era che i metodi virtuali implementassero qualcosa di particolarmente "dinamico", ma essenzialmente dicessero "Esiste una condizione del tipo indicato su cui agire". Una cosa che ho dimenticato di menzionare, tuttavia, è che dovrebbe esserci un mezzo attraverso il quale il codice che le chiamate Foopossono distinguere tra un'eccezione che Foosi è generata o che vuole deliberatamente far finta di essersi lanciata, da quella che Foonon si aspettava che accadesse quando era chiamando qualche altro metodo. Ecco cosa dovrebbe essere la "nozione" di eccezioni "controllate".
supercat

1

È facilmente possibile simulare un'eccezione non rilevata sul thread corrente. Ciò attiverà il comportamento regolare di un'eccezione non rilevata, e quindi farà il lavoro semanticamente. Tuttavia, non interromperà necessariamente l'esecuzione del thread corrente, poiché non viene generata alcuna eccezione.

Throwable exception = /* ... */;
Thread currentThread = Thread.currentThread();
Thread.UncaughtExceptionHandler uncaughtExceptionHandler =
    currentThread.getUncaughtExceptionHandler();
uncaughtExceptionHandler.uncaughtException(currentThread, exception);
// May be reachable, depending on the uncaught exception handler.

Ciò è effettivamente utile in situazioni (molto rare), ad esempio quando Errorè richiesta una corretta gestione, ma il metodo è invocato da un framework che cattura (e scarta) qualsiasi Throwable.


0

Chiama System.exit (1) in finalize, e lancia una copia dell'eccezione da tutti gli altri metodi, in modo che il programma venga chiuso.

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.