Est-il possible de construire un extrait de code dans Java qui rendrait un hypothétique Java.lang.ChuckNorrisException
impossible à capturer?
Les pensées qui me viennent à l’esprit utilisent par exemple des intercepteurs ou programmation orientée aspect .
Je n’ai pas essayé cela, donc je ne sais pas si le JVM restreindrait quelque chose comme ceci, mais vous pourriez peut-être compiler du code qui jette ChuckNorrisException
, mais lors de l'exécution, fournissez un définition de classe de ChuckNorrisException
qui ne s'étend pas de Throwable .
MISE À JOUR:
Ça ne marche pas Il génère une erreur de vérificateur:
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.
MISE À JOUR 2:
En fait, vous pouvez faire en sorte que cela fonctionne si vous désactivez le vérificateur de code octet! (-Xverify:none
)
MISE À JOUR 3:
Pour ceux qui suivent de chez eux, voici le script complet:
Créez les classes suivantes:
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.");
}
}
}
Compiler des classes:
javac -cp . TestVillain.Java ChuckNorrisException.Java
Courir:
Java -cp . TestVillain
Gotcha!
The end.
Mettez en commentaire les extensions "RuntimeException" et recompilez ChuckNorrisException.Java
uniquement :
javac -cp . ChuckNorrisException.Java
Courir:
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.
Exécuter sans vérification:
Java -Xverify:none -cp . TestVillain
The end.
Exception in thread "main"
Après avoir réfléchi à cela, j'ai créé avec succès une exception incalculable. J'ai cependant choisi de l'appeler JulesWinnfield
plutôt que Chuck, car il s'agit d'une exception mère-champignon-nuage-pondeuse. En outre, ce n'est peut-être pas exactement ce que vous aviez à l'esprit, mais vous ne pouvez certainement pas vous en empêcher. Observer:
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! Exception non capturée.
Sortie:
courir:
Dites 'Quoi' encore! Je te défie! Je double osez-vous!
Résultat Java: 8
BUILD SUCCESSFUL (temps total: 0 secondes)
Quand j'aurai un peu plus de temps, je verrai si je ne peux pas proposer autre chose aussi.
Aussi, vérifiez ceci:
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)
{
}
}
Provoque un débordement de pile - là encore, les exceptions restent non capturées.
Avec une telle exception, il serait évidemment obligatoire d’utiliser une System.exit(Integer.MIN_VALUE);
du constructeur car c’est ce qui arriverait si vous lançiez une telle exception;)
N'importe quel code peut attraper Throwable. Donc non, quelle que soit l'exception que vous créez, elle sera une sous-classe de Throwable et sera sujette à être attrapée.
public class ChuckNorrisException extends Exception {
public ChuckNorrisException() {
System.exit(1);
}
}
(Certes, techniquement cette exception n'est jamais lancée, mais un ChuckNorrisException
correct ne peut pas être lancé - il vous lance en premier.)
Toute exception que vous lancez doit être étendue à Throwable pour pouvoir toujours être interceptée. Donc, la réponse est non.
Si vous voulez rendre la tâche difficile, vous pouvez remplacer les méthodes getCause(), getMessage()
, getStackTrace()
, toString()
pour lancer un autre Java.lang.ChuckNorrisException
.
Ma réponse est basée sur l’idée de @ jtahlborn, mais c’est un programme entièrement fonctionnel Java , qui peut être empaqueté dans un fichier JAR et même déployé sur votre serveur d’applications préféré en tant que une partie d'un application web .
Tout d’abord, définissons la classe ChuckNorrisException
afin qu’elle ne plante pas la JVM dès le début (Chuck adore vraiment la destruction de machines virtuelles Java 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);
}
}
La classe Expendables
va maintenant la construire:
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);
}
}
}
Et enfin, la classe Main
qui donne un coup de pied:
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");
}
}
}
Compilez-le et lancez-le avec la commande suivante:
Java -Xverify:none -cp .:<path_to_javassist-3.9.0.GA.jar> chuck.Main
Vous obtiendrez la sortie suivante:
before
finally
Pas de surprise - c'est un coup de pied rond après tout :)
Dans le constructeur, vous pouvez démarrer un thread qui appelle de manière répétée originalThread.stop (ChuckNorisException.this)
Le fil pourrait capturer l'exception à plusieurs reprises, mais continuerait à la lancer jusqu'à ce qu'elle meure.
Toutes les exceptions dans Java doivent sous-classe Java.lang.Throwable
, et bien que cela puisse ne pas être une bonne pratique, vous pouvez intercepter chaque type d'exception de la manière suivante:
try {
//Stuff
} catch ( Throwable T ){
//Doesn't matter what it was, I caught it.
}
Voir la documentation Java.lang.Throwable pour plus d'informations.
Si vous essayez d'éviter exceptions cochées (celles qui doivent être explicitement gérées), vous souhaiterez sous-classer Error ou RuntimeException.
En réalité, la réponse acceptée n’est pas très agréable, car Java doit être exécuté sans vérification, c’est-à-dire que le code ne fonctionnerait pas dans des circonstances normales.
AspectJ à la rescousse pour le vraie solution!
Classe d'exception:
package de.scrum_master.app;
public class ChuckNorrisException extends RuntimeException {
public ChuckNorrisException(String message) {
super(message);
}
}
Aspect:
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;
}
}
Exemple d'application:
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!");
}
}
Sortie:
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)
Une variante du thème est le fait surprenant que vous pouvez générer des exceptions vérifiées non déclarées à partir de code Java. Comme il n'est pas déclaré dans la signature des méthodes, le compilateur ne vous laissera pas attraper l'exception elle-même, bien que vous puissiez l'attraper sous le nom Java.lang.Exception.
Voici une classe d'assistance qui vous permet de lancer n'importe quoi, déclaré ou non:
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;
}
}
Maintenant, throw SneakyThrow.sneak(new ChuckNorrisException());
lève une exception ChuckNorrisException, mais le compilateur se plaint
try {
throw SneakyThrow.sneak(new ChuckNorrisException());
} catch (ChuckNorrisException e) {
}
attraper une exception qui n'est pas levée si ChuckNorrisException est une exception vérifiée.
Le seul ChuckNorrisException
s dans Java devrait être OutOfMemoryError
et StackOverflowError
.
Vous pouvez réellement les "intercepter" de la manière qu'un catch(OutOfMemoryError ex)
sera exécuté au cas où une exception serait levée, mais ce bloc renverra automatiquement l'exception à l'appelant.
Je ne pense pas que public class ChuckNorrisError extends Error
fasse l'affaire mais vous pouvez l'essayer. Je n'ai trouvé aucune documentation sur l'extension Error
Is it possible to construct a snippet of code in Java that would make a hypothetical Java.lang.ChuckNorrisException uncatchable?
Oui, et voici la réponse: concevez votre Java.lang.ChuckNorrisException
de sorte qu'il ne s'agisse pas d'une instance de Java.lang.Throwable
. Pourquoi? Un objet non transférable est par définition impossible à saisir car vous ne pouvez jamais attraper quelque chose qui ne peut jamais être jeté.
Vous pouvez garder Chuck Norris interne ou privé et l'encapsuler ou l'avaler ...
try { doChuckAction(); } catch(ChuckNorrisException cne) { /*do something else*/ }
Deux problèmes fondamentaux liés à la gestion des exceptions dans Java sont qu’il utilise le type d’une exception pour indiquer si une action doit être prise en fonction de celle-ci, et que tout ce qui prend une action en fonction d’une exception (par exemple, "catch" es est supposé résoudre la condition sous-jacente. Il serait utile de disposer d'un moyen par lequel un objet d'exception pourrait décider des gestionnaires à exécuter et si les gestionnaires qui ont exécuté jusqu'ici ont suffisamment nettoyé les choses pour que la méthode actuelle réponde à ses conditions de sortie. Bien que cela puisse être utilisé pour créer des exceptions "non interceptables", deux utilisations plus importantes consisteraient à (1) créer des exceptions qui ne seront considérées comme gérées que si elles sont capturées par un code qui sait réellement comment les traiter, et (2) permettent pour un traitement judicieux des exceptions qui se produisent dans un bloc finally
(si un FooException
pendant un bloc finally
pendant le déroulement d'un BarException
, les deux exceptions doivent se propager dans la pile d'appels ; les deux doivent pouvoir être capturés, mais le déroulement doit se poursuivre jusqu’à ce que les deux aient été capturés) Malheureusement, je ne pense pas qu'il y aurait un moyen de faire fonctionner le code de gestion des exceptions existant sans casser des choses.
Il est facilement possible de simuler une exception non interceptée sur le thread actuel. Cela déclenchera le comportement habituel d'une exception non capturée et fera donc le travail sémantiquement. Cependant, cela n'arrêtera pas nécessairement l'exécution du thread en cours, car aucune exception n'est réellement levée.
Throwable exception = /* ... */;
Thread currentThread = Thread.currentThread();
Thread.UncaughtExceptionHandler uncaughtExceptionHandler =
currentThread.getUncaughtExceptionHandler();
uncaughtExceptionHandler.uncaughtException(currentThread, exception);
// May be reachable, depending on the uncaught exception handler.
Ceci est réellement utile dans des situations (très rares), par exemple lorsque la gestion correcte de Error
est requise, mais que la méthode est invoquée à partir d'un cadre capturant (et ignorant) tout Throwable
.
Appelez System.exit (1) dans la finalize
et lancez simplement une copie de l'exception parmi toutes les autres méthodes afin que le programme se ferme.