ChuckNorrisException inalcanzable

596

¿Es posible construir un fragmento de código en Java que pueda hacer que un hipotético sea java.lang.ChuckNorrisExceptionindescifrable?

Los pensamientos que se me ocurrieron están utilizando, por ejemplo, interceptores o programación orientada a aspectos .

Max Charas
fuente
2
Usando la sugerencia del enlace que @jschoen proporcionó (deshabilite el verificador de código de bytes), puede lanzar algo que no se extienda. descrito en mi respuesta a continuación.
jtahlborn
44
Este extracto de la respuesta de aioobe resume la pregunta que @jschoen enlazó bastante bien: "Es decir, su pregunta puede interpretarse como 'Si una JVM se desvía de la especificación, puede hacer cosas extrañas como arrojar primitivas' y la respuesta es, por supuesto, si."
Dan Is Fiddling By Firelight
2
@Max - ¿Puedes explicar los usos prácticos para esto?
Vineet Bhatia
3
¿ finalize()Qué tal una excepción que se vuelva a lanzar en el ?
Lie Ryan

Respuestas:

314

No lo he intentado, así que no sé si la JVM restringiría algo como esto, pero tal vez podría compilar código que arroje ChuckNorrisException, pero en tiempo de ejecución proporcionar una definición de clase ChuckNorrisExceptionque no extienda Throwable .

ACTUALIZAR:

No funciona Genera un error de verificador:

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.

ACTUALIZACIÓN 2:

En realidad, puede hacer que esto funcione si deshabilita el verificador de código de bytes. ( -Xverify:none)

ACTUALIZACIÓN 3:

Para aquellos que siguen desde casa, aquí está el script completo:

Crea las siguientes clases:

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.");
        }
    }
}

Compilar clases:

javac -cp . TestVillain.java ChuckNorrisException.java

Correr:

java -cp . TestVillain
Gotcha!
The end.

Comente "extiende RuntimeException" y recompile ChuckNorrisException.javasolo :

javac -cp . ChuckNorrisException.java

Correr:

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.

Ejecutar sin verificación:

java -Xverify:none -cp . TestVillain
The end.
Exception in thread "main"
jtahlborn
fuente
18
OK, ¿y qué pasa si atrapas en Objectlugar de Throwableentonces? (El compilador no lo permitirá, pero como ya hemos deshabilitado el verificador, tal vez uno podría hackear el
código de bytes
11
De acuerdo con lo que puede lanzar en Java , aún puede atrapar cosas que no se extienden, pero arrojarlas y atraparlas es un comportamiento indefinido.
VolatileDream
8
@dzieciou Pueden ser verdad juntos. Es posible que pueda capturarlos utilizando su versión del entorno Java en su versión específica de su sistema operativo en su tipo de procesador. Pero si no se especifica en el estándar si PUEDE ser atrapado, se llama comportamiento indefinido, porque otras implementaciones de Java podrían optar por hacerlo no capturable.
heinrich5991
2
Hmmph Esperaba que para 176 votos a favor, hubieras escrito un código JNI que parchee la pila completa de llamadas para volver a lanzar tu excepción (llamada por el ctor, por supuesto).
kdgregory
3
Al hacer todo esto, también es una buena idea pararse sobre una pierna, acariciarse la cabeza y frotarse la barriga mientras silba a Dixie ...;);)
Glen Best
120

Después de haber reflexionado sobre esto, he creado con éxito una excepción inalcanzable. Elegí nombrarlo JulesWinnfield, sin embargo, en lugar de Chuck, porque es una excepción de hongo-nube-colocación-madre. Además, puede que no sea exactamente lo que tenía en mente, pero ciertamente no se puede atrapar. Observar:

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 voila! Excepción no capturada.

Salida:

correr:

¡Dí que otra vez! ¡Yo Te reto! ¡Te recontra reto!

Resultado Java: 8

CONSTRUIR CON ÉXITO (tiempo total: 0 segundos)

Cuando tenga un poco más de tiempo, veré si no puedo pensar en otra cosa también.

Además, mira esto:

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 desbordamiento de la pila; nuevamente, las excepciones permanecen sin capturar.

MikeTheLiar
fuente
32
+1 por usar Stack Overflow en tu respuesta. Es broma, muy buena respuesta.
Josiah
77
Una "excepción no capturable" adecuada garantizaría que todos los bloques que se cerraron finalmente se ejecutarían sin ninguna captura intermedia. Matar el sistema no arroja una excepción, solo mata el sistema.
supercat
44
¿Cómo se "tira" el JulesWinfield? ¿No se detendrá el sistema antes de lanzarlo?
supercat
66
@mikeTheLiar: El sistema se cierra durante el constructor, ¿no es así? La declaración throw new Whatever()es realmente dos partes: Whatever it = new Whatever(); throw it;y el sistema muere antes de llegar a la segunda parte.
supercat
55
@mikeTheLiar en realidad puedes atrapar a Jules o Vincent con bastante facilidad ... si logras lanzarlo. Es fácil crear una excepción que no puedes lanzar:class cn extends exception{private cn(){}}
John Dvorak
85

Con tal excepción, obviamente sería obligatorio usar un System.exit(Integer.MIN_VALUE);del constructor porque esto es lo que sucedería si lanzara tal excepción;)

Korgen
fuente
32
+1; OMI, esta es la única solución posible. Una excepción que no se puede capturar es finalizar el programa ...
inicio
77
No, no sería lo que sucede cuando lanzas tal excepción. Una excepción no detectada terminará un solo subproceso, no saldrá de la jvm, en algunos contextos System.exit incluso causará una SecurityException, no se permite que cada pieza de código cierre un programa.
josefx
3
Puedes usar en while(true){}lugar deSystem.exit() .
Piotr Praszmo
2
en realidad, puede evitar que System.exit()funcione instalando un administrador de seguridad que no lo permita. eso convertiría al constructor en una excepción diferente (SecurityException), que podría detectarse.
jtahlborn
55
Umm, técnicamente nunca lanzaste una excepción. ¡Aún no has construido el objeto para lanzar!
Thomas Eding
46

Cualquier código puede atrapar Throwable. Entonces, no, cualquier excepción que cree será una subclase de Throwable y estará sujeta a ser atrapada.

Nathan Hughes
fuente
11
Throwable lo haría hangen un intento de atrapar a ChuckNorrisException: P
PermGenError
35
public class ChuckNorrisException extends Exception {
    public ChuckNorrisException() {
        System.exit(1);
    }
}

(De acuerdo, técnicamente , esta excepción nunca se lanza, pero ChuckNorrisExceptionno se puede lanzar una adecuada , te lanza primero).

mullido
fuente
44
Un colega mío había sugerido que se quedara 'for (;;) {}' porque sentía que una llamada 'System.exit (1)' podía generar una excepción de seguridad. ¡Voy a votar por la creatividad!
Phil Street
Estoy de acuerdo con el final de tu respuesta. Nunca te metas con ChuckNorris, Excepción o no.
Benj
28

Cualquier excepción que arrojes tiene que extender Throwable, por lo que siempre se puede atrapar. Entonces la respuesta es no.

Si desea hacer que sea difícil de manejar, puede reemplazar los métodos getCause(), getMessage(), getStackTrace(), toString()para lanzar otra java.lang.ChuckNorrisException.

mirelon
fuente
2
Hmm, catch (Throwable t) ¿llama a algún método o muta el objeto? Puede ser posible causar una cláusula catch para lanzar una excepción adicional para hacerlo imposible.
Colton
1
Creo que catch(Throwable t)solo lo almacena en variable, por lo que mis sugerencias solo se aplican en el siguiente bloque cuando el usuario quiere hacer frente a la excepción
mirelon
24

Mi respuesta se basa en la idea de @ jtahlborn, pero es un programa Java totalmente funcional , que puede empaquetarse en un archivo JAR e incluso implementarse en su servidor de aplicaciones favorito como parte de una aplicación web .

En primer lugar, definamos la ChuckNorrisExceptionclase para que no bloquee JVM desde el principio (a Chuck realmente le encanta chocar JVMs 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);
    }
}

Ahora pasa a Expendablesclase para construirlo:

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

Y finalmente la Mainclase para patear un trasero:

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

Compile y ejecútelo con el siguiente comando:

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

Obtendrá el siguiente resultado:

before
finally

No es de extrañar, es una patada giratoria después de todo :)

Fuego fatuo
fuente
¡muy agradable! No he hecho mucho con la manipulación de la definición de clase. ¿todavía necesita el "verificar: ninguno" en la línea de comandos?
jtahlborn
@jtahlborn Sí, el intento de lanzar un objeto que no sea descendiente de Throwable falla sin "verificar: ninguno".
Wildfire
Oh, tengo la impresión de que esto de alguna manera evitó esa restricción. Entonces, ¿cómo es esto diferente de mi respuesta?
jtahlborn
2
La principal diferencia es que funciona el código Java sin piratería en tiempo de compilación
Wildfire
15

En el constructor, puede iniciar un hilo que llama repetidamente originalThread.stop (ChuckNorisException.this)

El hilo podría atrapar la excepción repetidamente, pero seguiría lanzándolo hasta que muera.

Peter Lawrey
fuente
13

No. Todas las excepciones en Java deben ser de subclase java.lang.Throwable, y aunque puede que no sea una buena práctica, puede detectar todo tipo de excepción de esta manera:

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

Consulte la documentación de java.lang.Throwable para obtener más información.

Si está tratando de evitar las excepciones marcadas (las que deben manejarse explícitamente), entonces querrá subclasificar Error o RuntimeException.

Sueño volátil
fuente
9

En realidad, la respuesta aceptada no es tan buena porque Java debe ejecutarse sin verificación, es decir, el código no funcionaría en circunstancias normales.

¡AspectJ al rescate por la solución real !

Clase de excepción:

package de.scrum_master.app;

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

Aspecto:

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

Aplicación de muestra:

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!");
    }
}

Salida:

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)
kriegaex
fuente
8

Una variante del tema es el hecho sorprendente de que puede lanzar excepciones comprobadas no declaradas del código Java. Como no se declara en la firma de métodos, el compilador no le permitirá detectar la excepción en sí, aunque puede capturarla como java.lang.Exception.

Aquí hay una clase auxiliar que te permite lanzar cualquier cosa, declarada o no:

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

Ahora throw SneakyThrow.sneak(new ChuckNorrisException());arroja una ChuckNorrisException, pero el compilador se queja en

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

sobre la captura de una excepción que no se produce si ChuckNorrisException es una excepción marcada.

Hans-Peter Störr
fuente
6

Los únicos ChuckNorrisExceptions en Java deberían ser OutOfMemoryErrory StackOverflowError.

De hecho, puedes "atraparlos" de la manera que catch(OutOfMemoryError ex) se ejecutará a en caso de que se lance la excepción, pero ese bloque automáticamente arrojará la excepción a la persona que llama.

No creo que eso public class ChuckNorrisError extends Errorfuncione, pero podrías intentarlo. No encontré documentación sobre extenderError

usr-local-ΕΨΗΕΛΩΝ
fuente
2
El error aún se extiende Throwable, por lo que no hay forma de evitar que lo atrape. Eso es por diseño del lenguaje Java.
JasonM1
1
@ JasonM1 No creo que el OP haya pedido una excepción realmente "incognoscible", y quise decir que el Error se propaga incluso si lo detecta. Por lo tanto, cualquier Throwable es capturable pero estos dos finalmente se propagará no importa lo que haces
ΕΨΗΕΛΩΝ usr-local-
Para ser complicado, ChuckNorrisException podría extender Throwable directamente, ¡entonces no sería ni Excepción ni Error!
JasonM1
44
El error no se propaga incluso si lo detecta, no estoy seguro de dónde sacó esa idea.
jtahlborn
3
Creo que estás muy confundido acerca de Erros, son excepciones normales como todo lo que se extiende Throwable o incluso Throwable, en sí mismo.
bestsss
6

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

Sí, y aquí está la respuesta: diseñe su java.lang.ChuckNorrisExceptiontal que no sea una instancia dejava.lang.Throwable . ¿Por qué? Un objeto que no se puede lanzar es inalcanzable por definición porque nunca se puede atrapar algo que nunca se puede lanzar.

Thomas Eding
fuente
2
Pero entonces no es una excepción.
dolbi
8
@dolbi: No puedo encontrar lugar en la pregunta del OP que indique que los estados java.lang.ChuckNorrisExceptiondeben ser una excepción, y mucho menos arrojables
Thomas Eding
1
Supongo que no está indicado, pero está implícito. Usted es matemático :-), ¿no es así?
dolbi
3

Puede mantener a ChuckNorris interno o privado y encapsularlo o tragarlo ...

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

Arrendajo
fuente
77
No creo que la idea fuera atraparlo. Creo que la idea es evitar que sea atrapado.
Patrick Roberts
Corrígeme si estoy equivocado pero si lo haces interno no puedes llegar a él sin reflexionar.
Jay
55
sí, pero siempre que pueda detectar Exception o Throwable, la visibilidad del tipo real es irrelevante.
KeithS
3

Dos problemas fundamentales con el manejo de excepciones en Java son que usa el tipo de una excepción para indicar si se debe tomar una acción basada en ella, y que cualquier cosa que tome acción basada en una excepción (es decir, "atraparlo") se supone que se resuelve La condición subyacente. Sería útil tener un medio por el cual un objeto de excepción podría decidir qué manejadores deberían ejecutarse, y si los manejadores que se han ejecutado hasta ahora han limpiado las cosas lo suficiente como para que el presente método satisfaga sus condiciones de salida. Si bien esto podría usarse para hacer excepciones "imposibles de capturar", dos usos más grandes serían (1) hacer excepciones que solo se considerarán manejadas cuando estén atrapadas por un código que realmente sepa cómo lidiar con ellas,finallyFooExceptionfinallybloque durante el desenrollado de a BarException, ambas excepciones deben propagarse por la pila de llamadas; ambos deben ser atrapables, pero el desenrollado debe continuar hasta que ambos hayan sido atrapados). Desafortunadamente, no creo que haya alguna forma de hacer que el código existente de manejo de excepciones funcione de esa manera sin romper las cosas.

Super gato
fuente
es una idea interesante, pero no creo que el código de bajo nivel sepa lo que una excepción particular "significa" para la persona que llama, por lo que no creo que alguna vez tenga sentido que el lanzador decida qué controladores deben ejecutar.
jtahlborn
@jtahlborn: En este momento, el lanzador decide qué manejadores de excepciones deben ejecutarse mediante la elección del tipo de excepción. Esto hace que sea casi imposible manejar algunos escenarios limpiamente. Entre otras cosas: (1) si ocurre una excepción mientras un finallybloque se está limpiando de una excepción anterior, es muy posible que cualquiera de las excepciones, en ausencia de la otra, sea algo que el código esperaría manejar y continuar, pero que manejar uno e ignorar al otro sería malo. Sin embargo, no existe un mecanismo para producir una excepción compuesta que ambos manejadores procesen.
supercat
@jtahlborn: Además, hace que sea muy difícil permitir que la capa de aplicación externa maneje las excepciones que ocurren dentro de las devoluciones de llamada. Si la excepción de devolución de llamada está envuelta en otro tipo de excepción, el tipo de excepción de devolución de llamada no se puede utilizar en la capa externa para decidir si debe capturarla; si no está envuelto, una excepción de capa media "accidental" puede confundirse con una que ocurre en la devolución de llamada. Si un objeto de excepción envuelto se le dijera cuando se pasó a la capa de aplicación externa, entonces podría comenzar a responder a los tipos de excepciones envueltas.
supercat
No estaba discutiendo sus otros puntos, solo la declaración sobre el objeto de excepción que decide qué controladores se ejecutarán. hasta cierto punto, los tipos de excepción ya lo hacen, pero parece que querías algo más dinámico, con lo que no estaba de acuerdo. Creo que su argumento principal (que es como si estuviera de lado) es capturar tanta información como sea posible en la parte inferior y dejar que las capas superiores vean y trabajen con toda esa información. en este punto general, estoy de acuerdo con usted, sin embargo, el diablo está en los detalles / implementación.
jtahlborn
@jtahlborn: Mi intención no era que los métodos virtuales implementaran algo particularmente "dinámico", sino que esencialmente dijera "¿Hay una condición del tipo indicado sobre la que se debe actuar"? Sin embargo, una cosa que olvidé mencionar es que debería haber un medio a través del cual el código que las llamadas Foopuedan distinguir entre una excepción que se Foolanzó o deliberadamente quiere fingir que se lanzó, de una que Foono esperaba que ocurriera cuando era llamando a otro método. De eso debería tratarse la noción de excepciones "controladas".
supercat
1

Es posible simular fácilmente una excepción no detectada en el hilo actual. Esto activará el comportamiento regular de una excepción no detectada y, por lo tanto, hará el trabajo semánticamente. Sin embargo, no necesariamente detendrá la ejecución del subproceso actual, ya que no se produce ninguna excepción.

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

Esto es realmente útil en situaciones (muy raras), por ejemplo, cuando Errorse requiere un manejo adecuado , pero el método se invoca desde un marco que captura (y descarta) cualquiera Throwable.

dst
fuente
0

Llame a System.exit (1) en el finalize, y simplemente arroje una copia de la excepción de todos los demás métodos, para que el programa salga.

Demi
fuente