java - ChuckNorrisException incomparable
exception-handling aop (17)
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;)
¿Es posible construir un fragmento de código en Java que haría que una java.lang.ChuckNorrisException
hipotética sea incatchable?
Los pensamientos que vinieron a la mente están utilizando, por ejemplo, interceptores o programación orientada a aspectos .
Cualquier código puede atrapar Throwable. Así que no, cualquier excepción que cree será una subclase de Throwable y estará sujeta a ser capturada.
Cualquier excepción que lances tiene que extender Throwable, para que siempre pueda ser atrapada. Así que la respuesta es no.
Si desea que sea difícil de manejar, puede anular los métodos getCause(), getMessage()
, getStackTrace()
, toString()
para lanzar otra java.lang.ChuckNorrisException
.
Después de haber ponderado esto, he creado con éxito una excepción incatchable. Sin embargo, JulesWinnfield
por JulesWinnfield
, en lugar de Chuck, porque es una excepción de nube de hongo que pone. Además, puede que no sea exactamente lo que tenías en mente, pero ciertamente no se puede detectar. 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 captada.
Salida:
correr:
¡Dí que otra vez! ¡Yo Te reto! ¡Te recontra reto!
Resultado de Java: 8
CONSTRUYE EXITOSO (tiempo total: 0 segundos)
Cuando tenga un poco más de tiempo, veré si también se me ocurre algo más.
También, 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 pila: de nuevo, las excepciones permanecen sin ser detectadas.
Dos problemas fundamentales con el manejo de excepciones en Java son que utiliza el tipo de excepción para indicar si se debe tomar acción en función de ella, y que cualquier cosa que tome acción en base a una excepción (es decir, "captura" se supone que se resolverá) La condición subyacente. Sería útil tener un medio por el cual un objeto de excepción pueda decidir qué manejadores deben ejecutar, y si los manejadores que han ejecutado hasta el momento han limpiado las cosas lo suficiente para que el método actual satisfaga sus condiciones de salida. Si bien esto se podría usar para hacer excepciones "incatchable", dos usos más importantes serían (1) hacer excepciones que solo se considerarán manejadas cuando sean capturadas por un código que realmente sepa cómo tratarlas, y (2) permitir para el manejo sensato de las excepciones que se producen en un bloque finally
(si se FooException
una FooException
durante un bloque finally
durante el BarException
de una BarException
, ambas excepciones deberían propagarse hacia arriba en la pila de llamadas; ambas deberían ser detectables, pero la desenvolvimiento debería continuar hasta que se hayan detectado) . Desafortunadamente, no creo que haya ninguna manera de hacer que el código existente de manejo de excepciones funcione de esa manera sin romper las cosas.
En el constructor, puedes iniciar un hilo que repetidamente llama originalThread.stop (ChuckNorisException.this)
El hilo podría atrapar la excepción repetidamente, pero seguiría tirándolo hasta que muera.
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.
¡Aspecto 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 ejemplo:
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)
Es fácilmente posible simular una excepción no detectada en el hilo actual. Esto activará el comportamiento regular de una excepción no detectada y, por lo tanto, hace el trabajo semánticamente. Sin embargo, no necesariamente detendrá la ejecución del subproceso actual, ya que no se lanza 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 se requiere un manejo adecuado de Error
, pero el método se invoca desde un marco que captura (y descarta) cualquier Throwable
.
Las únicas ChuckNorrisException
s en Java deben ser OutOfMemoryError
y Error
.
En realidad, puede "atraparlos" en el sentido de que se ejecutará un catch(OutOfMemoryError ex)
en caso de que se produzca la excepción, pero ese bloque volverá a enviar automáticamente la excepción a la persona que llama.
No creo que public class ChuckNorrisError extends Error
hace el truco, pero podrías intentarlo. No encontré documentación sobre la extensión del Error
Llame a System.exit (1) en la finalize
, y simplemente lance una copia de la excepción de todos los otros métodos, para que el programa salga.
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 clase ChuckNorrisException
para que no se bloquee la JVM desde el principio (a Chuck realmente le gusta estrellarse 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);
}
}
Ahora va la clase Expendables
para construirla:
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 clase Main
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á la siguiente salida:
before
finally
No es de extrañar, después de todo es una patada circular.
No he intentado esto, así que no sé si la JVM restringiría algo como esto, pero tal vez podría compilar el código que arroja ChuckNorrisException
, pero en tiempo de ejecución proporcione una definición de clase de ChuckNorrisException
que 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 byte. ( -Xverify:none
)
ACTUALIZACIÓN 3:
Para los 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.");
}
}
}
Clases de compilación:
javac -cp . TestVillain.java ChuckNorrisException.java
Correr:
java -cp . TestVillain
Gotcha!
The end.
Comente "extiende RuntimeException" y ChuckNorrisException.java
compilar ChuckNorrisException.java
solamente :
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"
No. Todas las excepciones en Java deben ser subclases java.lang.Throwable
, y aunque no sea una buena práctica, puede capturar todo tipo de excepción de la siguiente 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 excepciones marcadas (las que deben manejarse explícitamente), entonces querrá hacer una subclase de Error, o RuntimeException.
Puede mantener ChuckNorris interno o privado y encapsularlo o abandonarlo ...
try { doChuckAction(); } catch(ChuckNorrisException cne) { /*do something else*/ }
Una variante del tema es el hecho sorprendente de que puede lanzar excepciones comprobadas no declaradas desde el código Java. Dado que no está declarado en la firma de métodos, el compilador no le permitirá capturar la excepción, aunque sí puede capturarla como java.lang.Exception.
Aquí hay una clase de ayuda 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());
lanza una ChuckNorrisException, pero el compilador se queja en
try {
throw SneakyThrow.sneak(new ChuckNorrisException());
} catch (ChuckNorrisException e) {
}
acerca de la captura de una excepción que no se produce si ChuckNorrisException es una excepción comprobada.
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.ChuckNorrisException
modo que no sea una instancia de java.lang.Throwable
. ¿Por qué? Un objeto que no se puede tirar es indiscutible por definición porque nunca puedes atrapar algo que nunca puede ser lanzado.
public class ChuckNorrisException extends Exception {
public ChuckNorrisException() {
System.exit(1);
}
}
(Por supuesto, técnicamente esta excepción nunca se lanza realmente, pero no se puede lanzar una ChuckNorrisException
adecuada ChuckNorrisException
primero te lanza a ti).