Нерозбірливий ChuckNorrisException


596

Чи можливо побудувати фрагмент коду на Java, який зробив би гіпотетичним java.lang.ChuckNorrisExceptionнерозбірливим?

Думки, які прийшли вам на думку, використовують, наприклад, перехоплювачі або орієнтоване на аспекти програмування .



2
використовуючи пропозицію із наданого посилання @jschoen (вимкніть перевірку байтового коду), ви можете кинути щось, що не розширює Throwable! описаний у моїй відповіді нижче.
jtahlborn

4
Цей уривок з відповіді aioobe підсумовує питання @jschoen, пов'язане досить добре: "Тобто, ваше запитання можна інтерпретувати як" Якщо JVM відхиляється від специфікації, чи може це робити дивні речі, такі як кидання примітивів ", і відповідь, звичайно, так."
День вигадує Firelight

2
@Max - Чи можете ви детальніше ознайомитись із практичним використанням цього?
Vineet Bhatia

3
як щодо виключення, яке перекидає себе на finalize()?
Лежать Раян

Відповіді:


314

Я не пробував цього, тому не знаю, чи обмежив би JVM щось подібне, але, можливо, ви могли б скласти код, який кидає ChuckNorrisException, але під час виконання надати визначення класу, ChuckNorrisExceptionяке не поширює Throwable .

ОНОВЛЕННЯ:

Це не працює. Він генерує помилку верифікатора:

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.

ОНОВЛЕННЯ 2:

Насправді ви можете змусити це працювати, якщо вимкнути перевірку байтового коду! ( -Xverify:none)

ОНОВЛЕННЯ 3:

Для тих, хто йде з дому, ось повний сценарій:

Створіть такі класи:

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

Компілюйте класи:

javac -cp . TestVillain.java ChuckNorrisException.java

Виконати:

java -cp . TestVillain
Gotcha!
The end.

Прокоментуйте "розширює RuntimeException" і перекомпілюйте ChuckNorrisException.javaлише :

javac -cp . ChuckNorrisException.java

Виконати:

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.

Запустити без перевірки:

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

18
Гаразд, що робити, якщо ви ловите Objectзамість Throwableцього? (Компілятор не дозволить цього, але оскільки ми вже відключили перевіряючий, можливо, хтось може зламати байт-код, щоб це зробити.)
Ilmari Karonen

11
Згідно з тим, що ви можете кинути на Java, ви все одно можете ловити речі, які не розширюються, але їх кидати і ловити - це невизначена поведінка.
VolatileDream

8
@dzieciou Вони можуть бути правдою разом. Можливо, ви зможете зафіксувати їх за допомогою вашої версії середовища Java у вашій конкретній версії операційної системи для вашого типу процесора. Але якщо в стандарті не вказано, чи МОЖЕ бути зловленим, це називається невизначеною поведінкою, оскільки інші реалізації Java можуть вибрати, щоб зробити це неможливим.
heinrich5991

2
Хммф. Я сподівався, що за 176 анонсів ви написали якийсь код JNI, який мавпи виправляє весь стек викликів, щоб повторно скинути виняток (звичайно, зателефонував ctor).
kdgregory

3
Роблячи все це, також чудова ідея стати на одній нозі, погладити голову і потерти животик, свистячи дикси ...;);)
Glen Best

120

Поміркувавши це, я успішно створив невловимий виняток. Я вирішив назвати це JulesWinnfield, проте, а не Чак, тому що це один виняток гриби-хмари, що лежить мама. Крім того, це може бути не саме те, що ви мали на увазі, але це, безумовно, не може бути спіймане. Дотримуйтесь:

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! Неперехоплене виключення.

Вихід:

запустити:

Скажи знову "Що"! Я смію тебе! Я подвійно наважуюся на тебе!

Результат Java: 8

БУДІВ УСПІШНО (загальний час: 0 секунд)

Коли у мене ще трохи часу, я побачу, чи не можу я придумати ще щось.

Також перевірте це:

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)
    {

    }
}

Викликає переповнення стека - знову ж таки, винятки залишаються незрозумілими.


32
+1 за використання переповнення стека у вашій відповіді. Жартую, справді гарна відповідь.
Йосія

7
Власне "нерозбірливе виняток" забезпечить виконання всіх блоків, що в остаточному підсумку, виконуватись без жодного втручання, що втручається. Вбивство системи не є винятком - воно просто вбиває систему.
supercat

4
Як ти "кидаєш" JulesWinfield? Невже система не зупиниться перед викидом?
supercat

6
@mikeTheLiar: Система виходить під час конструктора, чи не так? Заява throw new Whatever()справді складається з двох частин: Whatever it = new Whatever(); throw it;і система вмирає, перш ніж вона досягне другої частини.
supercat

5
@mikeTheLiar ти насправді можеш досить легко спіймати Жуля чи Вінсента ... якщо ти зумієш його кинути. Легко створити виняток, який ви не можете кинути:class cn extends exception{private cn(){}}
Джон Дворак

85

За таким винятком, очевидно, було б обов'язково використовувати a System.exit(Integer.MIN_VALUE);від конструктора, тому що це станеться, якщо ви викинете такий виняток;)


32
+1; ІМО - це єдино можливе рішення. Виняток, який не можна назвати, слід припинити програму ...
додому

7
Ні, це було б не те, що відбувається, коли кидаєш такий виняток. Невиконаний виняток завершить один потік, він не вийде з jvm, в деяких контекстах сам System.exit навіть спричинить SecurityException - не кожен фрагмент коду дозволений для вимкнення програми.
josefx

3
Ви можете використовувати while(true){}замість System.exit().
Пьотр Працмо

2
насправді ви можете запобігти System.exit()роботі, встановивши менеджер безпеки, який забороняє його працювати. це перетворило б конструктор на інший виняток (SecurityException), який може бути спійманий.
jtahlborn

5
Гм, технічно ви ніколи не кидали винятку. Ви ще навіть не сконструювали об'єкт, щоб кинути його!
Томас Едінг

46

Будь-який код може зловити Throwable. Тож ні, незалежно від того, який ви створюєте виняток, буде підкласом Throwable, і він буде підлягати спійманню.


11
Кидаюча hangсама б у спробі зловити ChuckNorrisException: P
PermGenError

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

(Звичайно, технічно цей виняток ніколи насправді не кидається, але власне ChuckNorrisExceptionне можна кидати - він кидає вас першим.)


4
Мій колега запропонував дотримуватися "for (;;) {}", оскільки він вважав, що виклик "System.exit (1)" може призвести до виключення безпеки. Я голосую за творчість!
Філ-стріт

Я згоден з закінченням вашої відповіді. Ніколи не возиться з ChuckNorris, виняток чи ні.
Бендж

28

Будь-який виняток, який ви кинете, повинен поширювати Throwable, тому його завжди можна зловити. Тож відповідь - ні.

Якщо ви хочете ускладнити обробку, ви можете замінити методи getCause(), getMessage() , getStackTrace(), toString()щоб кинути інші java.lang.ChuckNorrisException.


2
Хм, ловити (Throwable t) викликати будь-які методи чи іншим чином мутувати об'єкт? Можливо, може викликати застереження про вилов для подальшого викиду з нього виключення, що робить його неможливим.
Колтон

1
Я думаю, що catch(Throwable t)зберігає його лише в змінній, тому мої пропозиції застосовуються лише в наступному блоці, коли користувач хоче впоратися з винятком
mirelon

24

Моя відповідь заснована на ідеї @ jtahlborn, але це повністю працююча програма Java , яку можна упакувати у файл JAR і навіть розгорнути на ваш улюблений сервер додатків у складі веб-програми .

Перш за все, давайте визначимо ChuckNorrisExceptionклас, щоб він не збивав JVM з самого початку (Чак дуже любить збій 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);
    }
}

Тепер іде Expendablesклас для його побудови:

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

І, нарешті, Mainклас поштовх прикладом:

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

Складіть і запустіть його за допомогою наступної команди:

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

Ви отримаєте наступний вихід:

before
finally

Не дивно - адже це кінець круглим будинком :)


дуже хороший! я мало що робив із маніпуляцією визначення класу. чи все ще вам потрібно в командному рядку "перевірити: немає"?
jtahlborn

@jtahlborn Так, спроба кинути об'єкт, який не є нащадком брошуваних, не вдається без "перевірити: немає".
Wildfire

о, у мене склалося враження, що це якось подолало цю обмеження. так чим це відрізняється від моєї відповіді?
jtahlborn

2
Основна відмінність полягає в тому, що він працює з кодом Java без злому часу компіляції
Wildfire

15

У конструкторі ви можете запустити потік, який неодноразово викликає originalThread.stop (ChuckNorisException.this)

Нитка може виловлювати виняток неодноразово, але продовжуватиме її кидати, поки вона не відмирає.


13

Ні. Усі винятки в Java повинні містити підклас java.lang.Throwable, і хоча це може бути недоброю практикою, ви можете вловлювати кожен тип винятку, як наприклад:

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

Дивіться java.lang.Throwable документації .

Якщо ви намагаєтеся уникати перевірених винятків (тих, які повинні бути явно оброблені), тоді вам потрібно буде підклас Помилка або RuntimeException.


9

Насправді прийнята відповідь не така приємна, тому що Java потрібно запускати без перевірки, тобто код не працює в звичайних обставинах.

Аспект J на ​​допомогу для реального рішення !

Клас винятку:

package de.scrum_master.app;

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

Аспект:

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

Зразок застосування:

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

Вихід:

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

Варіантом теми є дивовижний факт, що ви можете викидати незадекларовані перевірені винятки з коду Java. Оскільки це не задекларовано в підписі методів, компілятор не дозволить вам зловити виняток, хоча ви можете впізнати його як java.lang.Exception.

Ось клас помічників, який дозволяє кидати що-небудь, заявлене чи ні:

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

Тепер throw SneakyThrow.sneak(new ChuckNorrisException());кидає ChuckNorrisException, але компілятор скаржиться на

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

про вилучення виключення, яке не викидається, якщо ChuckNorrisException є перевіреним винятком.


6

Єдиним ChuckNorrisExceptions на Java повинен бути OutOfMemoryErrorі StackOverflowError.

Ви насправді можете «зловити» їх засобами, які а catch(OutOfMemoryError ex) виконують у випадку, якщо виняток буде кинуто, але цей блок автоматично повторно скине виняток для абонента.

Я не думаю, що public class ChuckNorrisError extends Errorце робить трюк, але ви можете спробувати. Я не знайшов жодної документації про продовженняError


2
Помилка все ще поширюється на Throwable, так що жодним чином не запобігти її вловлюванню. Це - дизайн мови Java.
JasonM1

1
@ JasonM1 Я не думаю, що ОП запитувала насправді "нерозбірливий" виняток, і я мав на увазі, що Помилка поширюється, навіть якщо ви її зловили. Таким чином, будь-яку оброблювану книгу можна знайти, але ці два з часом розмножуватимуться незалежно від того, що ви робите
usr-local-ΕΨΗΕΛΩΝ

Якщо бути хитрою, ChuckNorrisException може поширити Throwable безпосередньо, тоді це не буде ні Виняток, ні Помилка!
JasonM1

4
Помилка не поширюється, навіть якщо ви її зловили, я не впевнений, звідки у вас ідея.
jtahlborn

3
Я думаю, що ви добре плутаєтесь щодо Ерроса, вони є нормальними винятками, як усе, що розширює Throwable або навіть Throwable.
bestsss

6

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

Так, і ось відповідь: Створіть java.lang.ChuckNorrisExceptionтак, щоб він не був екземпляром java.lang.Throwable. Чому? Об'єкт, який не можна захистити, за визначенням неможливий для неможливості, тому що ніколи не можна зловити те, що ніколи не можна кинути.


2
Але тоді це не виняток.
dolbi

8
@dolbi: Я не можу знайти місця в питанні ОП про те, що держави java.lang.ChuckNorrisExceptionповинні бути винятком, не кажучи вже про перекидання
Томас Едінг

1
Я здогадуюсь, це не заявлено, але це мається на увазі. Ви математик :-), чи не так?
dolbi

3

Ви можете тримати ЧакНорріса внутрішнім або приватним і інкапсулювати його або набрякати ...

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


7
Я не вірю, що ідея полягала в тому, щоб її спіймати. Я думаю, що ідея полягає у тому, щоб не допустити її спіймання.
Патрік Робертс

Виправте мене, якщо я помиляюся, але якщо ви зробите це внутрішнім, ви не можете дійти до нього без роздумів.
Джей

5
так, але поки ви можете зловити виняток або Throwable, видимість фактичного типу не має значення.
KeithS

3

Дві основні проблеми, пов’язані з обробкою винятків у Java, полягають у тому, що він використовує тип винятку для вказівки, чи слід вживати дії на його основі, і що припускається вирішити все, що вживає дій, заснованих на винятку (тобто "зловити його"). основна умова. Було б корисно мати засоби, за допомогою яких об'єкт виключення міг би вирішити, які обробники повинні виконуватись, і чи обробники, які виконали до цього часу, очистили речі достатньо, щоб даний метод задовольнив його умови виходу. Хоча це може використовуватися для винятку "нерозбірливих" винятків, двома більшими можливостями було б (1) робити винятки, які будуть вважатися обробленими лише тоді, коли вони будуть спіймані за кодом, який насправді знає, як з ними боротися,finallyFooExceptionпід часfinallyблок під час розмотування a BarException, обидва винятки повинні поширювати стек виклику; обидва повинні підлягати виловленню, але розмотування слід продовжувати до тих пір, поки обидва не потраплять). На жаль, я не думаю, що не було б ніякого способу змусити існуючий код обробки обставин таким чином працювати, не порушуючи речі.


цікава ідея, але я не думаю, що код низького рівня міг би знати, що конкретний виняток "означає" для абонента, тому я не думаю, що коли-небудь має сенс вирішувати, які обробники повинні виконувати.
jtahlborn

@jtahlborn: Прямо зараз кидач вирішує, які обробники винятків повинні виконувати через вибір винятку. Це робить все, але неможливо, чітко обробляти деякі сценарії. Серед іншого: (1) якщо виняток виникає під час finallyочищення блоку від попереднього винятку, цілком можливо, що будь-який виняток, за відсутності іншого, може бути чимось, з яким кодом можна було б обробляти і продовжувати, але поводження з одним і ігнорування іншого було б погано. Однак немає механізму, щоб створити складене виключення, яке обробляли б обидва обробники.
supercat

@jtahlborn: Крім того, це робить дуже важким дозволити винятки, що виникають у межах зворотних викликів, обробляти зовнішній шар програми. Якщо виняток зворотного дзвінка обгорнуто іншим типом винятків, виняток типу зворотного дзвінка не може бути використаний у зовнішньому шарі при вирішенні питання щодо його вибору; якщо він не завершений, виняток середнього шару "випадковий" може бути помилково сприйнятий для зворотного виклику. Якщо обговорений винятковий об'єкт було сказано, коли він передається до зовнішнього шару програми, він може почати відповідати на типи обгортаних винятків.
supercat

Я не сперечався з вашими іншими пунктами, просто твердження про об'єкт виключення, який вирішує, які обробники виконуватимуть. певною мірою типи винятків вже роблять це, але це здається, що ви хотіли чогось більш динамічного, з чим я не погоджувався. я думаю, що основним вашим аргументом (який ви начебто приїжджаєте збоку) є збирання якомога більшої кількості інформації в нижній частині та дозволення верхнім шарам бачити та працювати з усією цією інформацією. в цьому загальному питанні я згоден з вами, проте чорт у деталях / реалізації.
jtahlborn

@jtahlborn: Моя мета не полягала в тому, щоб віртуальні методи реалізовували щось особливо "динамічне", а по суті сказати "Чи є умова зазначеного типу, щодо якої слід діяти". Одне, що я забув згадати, - це те, що має бути засіб, за допомогою якого код, який викликає, Fooможе відрізняти виняток, який Fooабо кинув себе, або навмисно хоче зробити вигляд, що кинув себе, від того, якого Fooне очікував, що станеться, коли він виклик якогось іншого методу. Ось яким має бути поняття "перевірені" винятки "про".
supercat

1

Легко можна симулювати невиконаний виняток у поточній нитці. Це призведе до регулярної поведінки неприхованого винятку, і, таким чином, робота буде виконана семантично. Однак це не обов'язково зупинить виконання поточного потоку, оскільки насправді не виняток.

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

Це насправді корисно в (дуже рідкісних) ситуаціях, наприклад, коли потрібне правильне Errorповодження, але метод застосовується з рамки, яка збирає (і відкидає) будь-яку Throwable.


0

Зателефонуйте System.exit (1) в finalizeі просто викиньте копію винятку з усіх інших методів, щоб програма вийшла з програми.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.