Неуловимый ChuckNorrisException

Можно ли построить фрагмент кода в Java, который сделал бы гипотетический java.lang.ChuckNorrisException неуловимый?

Мысли, которые приходили на ум, используют, например, перехватчики или аспектно-ориентированное программирование.

17 ответов

Решение

Я не пробовал это, поэтому я не знаю, будет ли 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.

Закомментируйте "extends 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"

Обдумав это, я успешно создал неуловимое исключение. Я решил назвать это 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");
    }
}

И вуаля! Неисследованное исключение.

Выход:

бежать:

Скажи "Что" снова! Попробуй! Я дважды смею тебя!

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

    }
}

Вызывает переполнение стека - опять же, исключения остаются необработанными.

С таким исключением, очевидно, было бы обязательно использовать System.exit(Integer.MIN_VALUE); от конструктора, потому что это то, что произойдет, если вы бросили такое исключение;)

Любой код может поймать Throwable. Так что нет, какое бы исключение вы ни создавали, оно будет подклассом Throwable и может быть поймано.

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

(Конечно, технически это исключение на самом деле никогда не выдается, а ChuckNorrisException не может быть брошено - это бросает тебя первым.)

Любое исключение, которое вы выбрасываете, должно расширять Throwable, чтобы его всегда можно было поймать. Так что ответ - нет.

Если вы хотите усложнить обработку, вы можете переопределить методы getCause(), getMessage(), getStackTrace(), toString() бросить другого java.lang.ChuckNorrisException,

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

Прежде всего, давайте определимся ChuckNorrisException класс, чтобы он не разбивал JVM с самого начала (Чак действительно любит разбивать JVM, кстати)

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

Не удивительно, ведь это удар с разворота:)

В конструкторе вы можете запустить поток, который несколько раз вызывает originalThread.stop (ChuckNorisException.this)

Поток мог бы перехватить исключение несколько раз, но продолжал бы выбрасывать его, пока он не умрет.

Все исключения в Java должны быть подклассами java.lang.Throwableи, хотя это может не быть хорошей практикой, вы можете поймать каждый тип исключения следующим образом:

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

См. Документацию java.lang.Throwable для получения дополнительной информации.

Если вы пытаетесь избежать проверенных исключений (тех, которые должны быть явно обработаны), тогда вы захотите создать подкласс Error или RuntimeException.

На самом деле принятый ответ не так хорош, потому что Java нужно запускать без проверки, то есть код не будет работать в нормальных условиях.

AspectJ на помощь для реального решения!

Исключительный класс:

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)

Вариант на тему - удивительный факт, что вы можете бросить необъявленные проверенные исключения из кода 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 является проверенным исключением.

Единственный ChuckNorrisExceptions в Java должен быть OutOfMemoryError а также StackruError,

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

Я не думаю что public class ChuckNorrisError extends Error делает трюк, но вы могли бы попробовать. Я не нашел документации о расширении Error

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, Зачем? Невыбираемый объект по определению неуловим, потому что вы никогда не сможете поймать что-то, что никогда не может быть брошено.

Вы можете оставить Чака Норриса внутренним или частным и заключить его в капсулу или проглотить его...

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

Две фундаментальные проблемы с обработкой исключений в Java состоят в том, что она использует тип исключения, чтобы указать, должно ли действие быть предпринято на его основе, и что все, что предпринимает действие, основанное на исключении (то есть "поймает" его), как предполагается, разрешает основное условие. Было бы полезно иметь средство, с помощью которого объект исключения мог бы решить, какие обработчики должны выполняться, и выполнили ли обработчики, которые выполнялись до сих пор, достаточно вещей, чтобы настоящий метод удовлетворял условиям его выхода. Хотя это может быть использовано для создания "неуловимых" исключений, есть два более значительных применения: (1) создавать исключения, которые будут считаться обработанными, только когда они пойманы кодом, который фактически знает, как с ними работать, и (2) разрешить для разумной обработки исключений, которые происходят в finally блок (если FooException во время finally блок во время разматывания BarExceptionоба исключения должны распространяться вверх по стеку вызовов; оба должны быть уловимы, но раскручивание должно продолжаться до тех пор, пока оба не будут пойманы). К сожалению, я не думаю, что был бы какой-то способ заставить существующий код обработки исключений работать таким образом, не ломая вещи.

Легко можно смоделировать необработанное исключение в текущем потоке. Это вызовет обычное поведение необработанного исключения и, таким образом, выполнит работу семантически. Это, однако, не обязательно остановит выполнение текущего потока, так как никакое исключение фактически не выдается.

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,

Вызовите System.exit(1) в finalizeи просто выбросить копию исключения из всех других методов, чтобы программа вышла.

Другие вопросы по тегам