Можно ли построить фрагмент кода в Java , который сделал бы гипотетическую java.lang.ChuckNorrisException
неуловимую?
Мысли, которые пришли в голову, используют, например, перехватчики или аспектно-ориентированное программирование .
finalize()
?
Можно ли построить фрагмент кода в Java , который сделал бы гипотетическую java.lang.ChuckNorrisException
неуловимую?
Мысли, которые пришли в голову, используют, например, перехватчики или аспектно-ориентированное программирование .
finalize()
?
Ответы:
Я не пробовал этого, поэтому я не знаю, будет ли 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"
Object
вместо Throwable
, тогда? (Компилятор не допустит этого, но, поскольку мы уже отключили верификатор, возможно, для этого можно взломать байт-код.)
Обдумав это, я успешно создал неуловимое исключение. Однако я решил назвать его 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)
{
}
}
Вызывает переполнение стека - опять же, исключения остаются необработанными.
JulesWinfield
? Разве система не остановится до того, как ее бросят?
throw new Whatever()
на самом деле состоит из двух частей: Whatever it = new Whatever(); throw it;
и система умирает до того, как достигнет второй части.
class cn extends exception{private cn(){}}
С таким исключением, очевидно, будет обязательно использовать конструктор System.exit(Integer.MIN_VALUE);
from, потому что это то, что произойдет, если вы бросите такое исключение;)
while(true){}
вместо System.exit()
.
System.exit()
работе, установив менеджер безопасности, который запрещает это. это превратит конструктор в другое исключение (SecurityException), которое может быть перехвачено.
Любой код может поймать Throwable. Так что нет, какое бы исключение вы ни создавали, оно будет подклассом Throwable и может быть поймано.
hang
сам в попытке поймать ChuckNorrisException: P
public class ChuckNorrisException extends Exception {
public ChuckNorrisException() {
System.exit(1);
}
}
(Конечно, технически это исключение никогда не генерируется, но правильное ChuckNorrisException
не может быть выброшено - оно выбрасывает вас первым.)
Любое исключение, которое вы выбрасываете, должно расширять Throwable, чтобы его всегда можно было поймать. Так что ответ - нет.
Если вы хотите , чтобы сделать его трудно обрабатывать, вы можете переопределить методы getCause(), getMessage()
, getStackTrace()
, toString()
чтобы бросить другие java.lang.ChuckNorrisException
.
catch(Throwable t)
сохраняет только в переменную, поэтому мои предложения применяются только в следующем блоке, когда пользователь хочет справиться с исключением
Мой ответ основан на идее @ 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 является проверенным исключением.
Единственные ChuckNorrisException
s в Java должны быть OutOfMemoryError
и StackOverflowError
.
Вы действительно можете «поймать» их в том смысле, что 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
. Почему? Невыбираемый объект по определению неуловим, потому что вы никогда не сможете поймать то, что никогда не бросите.
java.lang.ChuckNorrisException
должны быть исключением, не говоря уже о бросаемом
Вы можете оставить Чака Норриса внутренним или частным и заключить его в капсулу или проглотить его ...
try { doChuckAction(); } catch(ChuckNorrisException cne) { /*do something else*/ }
Две фундаментальные проблемы с обработкой исключений в Java заключаются в том, что в ней используется тип исключения, чтобы указать, следует ли предпринимать действия на его основе, и что предполагается, что все, что предпринимает действие на основе исключения (то есть «ловит» его), разрешает основное условие. Было бы полезно иметь средство, с помощью которого объект исключения мог бы решить, какие обработчики должны выполняться, и выполнили ли обработчики, которые выполнялись до сих пор, достаточно вещей, чтобы существующий метод удовлетворял условиям его выхода. Хотя это может быть использовано для создания «неуловимых» исключений, два более масштабных применения будут заключаться в том, чтобы (1) создавать исключения, которые будут рассматриваться как обработанные, только когда они обнаружены кодом, который фактически знает, как с ними работать,finally
FooException
finally
блока при разматывании a BarException
оба исключения должны распространяться вверх по стеку вызовов; оба должны быть подловлены, но раскручивание должно продолжаться до тех пор, пока оба не будут пойманы). К сожалению, я не думаю, что был бы какой-то способ заставить существующий код обработки исключений работать таким образом, не ломая вещи.
finally
очистки блока от более раннего исключения, вполне возможно, что любое исключение, в отсутствие другого, может быть чем-то, что код будет обрабатывать и продолжать, но что обращаться с одним и игнорировать другое было бы плохо. Однако нет механизма для создания составного исключения, которое будут обрабатывать оба обработчика.
Foo
может различать исключение, которое Foo
либо выдает себя, либо намеренно хочет притвориться, что оно выбрасывает сам, от того, которое Foo
не ожидалось, когда оно было вызывая какой-то другой метод. Это то, что понятие «проверенных» исключений должно быть «примерно».
Легко можно смоделировать необработанное исключение в текущем потоке. Это вызовет обычное поведение необработанного исключения и, таким образом, выполнит работу семантически. Это, однако, не обязательно остановит выполнение текущего потока, так как никакое исключение фактически не выдается.
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
.