Chuck InacessívelNorrisException

596

É possível construir um trecho de código em Java que tornaria um hipotético java.lang.ChuckNorrisExceptioninacessível?

Pensamentos que vieram à mente estão usando, por exemplo, interceptadores ou programação orientada a aspectos .

Max Charas
fonte
2
usando a sugestão do link @jschoen fornecido (desabilite o verificador de código de bytes), você pode lançar algo que não se estende ao Throwable! descrito na minha resposta abaixo.
jtahlborn
4
Este trecho da resposta da aioobe resume muito bem a pergunta @jschoen: sim."
Dan Is Fiddling Por favor
2
@ Max - Você pode elaborar usos práticos para isso?
Vineet Bhatia
3
que tal uma exceção que se refaz no finalize()?
Lie Ryan

Respostas:

314

Eu não tentei isso, então não sei se a JVM restringiria algo assim, mas talvez você possa compilar o código que lança ChuckNorrisException, mas em tempo de execução forneça uma definição de classe ChuckNorrisExceptionque não estenda Throwable .

ATUALIZAR:

Isso não funciona. Ele gera um erro de verificação:

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.

ATUALIZAÇÃO 2:

Na verdade, você pode fazer isso funcionar se desativar o verificador de código de bytes! ( -Xverify:none)

ATUALIZAÇÃO 3:

Para os que seguem de casa, aqui está o script completo:

Crie as seguintes classes:

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

Classes de compilação:

javac -cp . TestVillain.java ChuckNorrisException.java

Corre:

java -cp . TestVillain
Gotcha!
The end.

O comentário "estende RuntimeException" e recompila ChuckNorrisException.javaapenas :

javac -cp . ChuckNorrisException.java

Corre:

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.

Executar sem verificação:

java -Xverify:none -cp . TestVillain
The end.
Exception in thread "main"
jtahlborn
fonte
18
OK, e daí se você pegar em Objectvez de Throwable, então? (O compilador não vai permitir isso, mas desde que já tenha desativado o verificador, talvez se poderia cortar o bytecode para fazê-lo.)
Ilmari Karonen
11
De acordo com o que você pode jogar em Java, ainda é possível capturar coisas que não se estendem por jogáveis, mas jogar e pegá-las é um comportamento indefinido.
VolatileDream
8
@dzieciou Eles podem ser verdadeiros juntos. Você pode capturá-los usando sua versão do ambiente Java na sua versão específica do seu sistema operacional no seu tipo de processador. Mas se não for especificado no padrão se PODE ser capturado, isso será chamado de comportamento indefinido, porque outras implementações de Java podem optar por torná-lo não capturável.
precisa
2
Hmmph. Eu esperava que, para 176 votos positivos, você tivesse escrito algum código JNI que corrige toda a pilha de chamadas para repetir sua exceção (chamada pelo ctor, é claro).
Kdgregory
3
Ao fazer tudo isso, também é uma ótima idéia ficar de pé em uma perna, dar um tapinha na cabeça e esfregar a barriga enquanto assobia dixie ...;);)
Glen Best
120

Depois de refletir sobre isso, criei com sucesso uma exceção incontornável. Eu escolhi nomeá-lo JulesWinnfield, no entanto, ao invés de Chuck, porque é uma exceção da mãe que coloca a nuvem em cogumelo. Além disso, pode não ser exatamente o que você tinha em mente, mas certamente não pode ser capturado. 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! Exceção não capturada.

Resultado:

corre:

Diga o que de novo! Atreva-se! Eu duvido você!

Resultado Java: 8

CONSTRUÇÃO BEM SUCEDIDA (tempo total: 0 segundos)

Quando eu tiver um pouco mais de tempo, vou ver se não consigo pensar em outra coisa também.

Além disso, verifique isso:

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

    }
}

Causa um estouro de pilha - novamente, as exceções permanecem não detectadas.

MikeTheLiar
fonte
32
+1 para usar o estouro de pilha na sua resposta. Brincadeira, resposta muito boa.
21712 Josiah
7
Uma "exceção inacessível" adequada garantiria que todos os blocos finalmente encerrados fossem executados sem capturas intermediárias. Matar o sistema não gera uma exceção - apenas mata o sistema.
Supercat 15/12
4
Como você "joga" o JulesWinfield? O sistema não será interrompido antes de ser lançado?
Supercat
6
@mikeTheLiar: O sistema sai durante o construtor, não é? A afirmação throw new Whatever()é realmente duas partes:, Whatever it = new Whatever(); throw it;e o sistema morre antes de chegar à segunda parte.
Supercat
5
@mikeTheLiar você realmente pode pegar Jules ou Vincent com bastante facilidade ... se você conseguir jogá-lo. É fácil criar uma exceção que você não pode lançar:class cn extends exception{private cn(){}}
John Dvorak
85

Com essa exceção, seria obviamente obrigatório usar a System.exit(Integer.MIN_VALUE);partir do construtor, porque é isso que aconteceria se você lançasse uma exceção;)

Korgen
fonte
32
+1; IMO, esta é a única solução possível. Uma exceção incontornável é deve encerrar o programa ...
home
7
Não, não seria o que acontece quando você lança uma exceção. Uma exceção não capturada encerrará um único encadeamento, não sairá da jvm; em alguns contextos, o próprio System.exit causará uma SecurityException - nem todo código pode desligar um programa.
Jsefx
3
Você pode usar em while(true){}vez de System.exit().
Piotr Praszmo
2
na verdade, você pode impedir o System.exit()trabalho instalando um gerenciador de segurança que não o permite. isso transformaria o construtor em uma exceção diferente (SecurityException), que poderia ser detectada.
jtahlborn
5
Umm, tecnicamente você nunca lançou uma exceção. Você nem construiu o objeto para lançar ainda!
Thomas Eding
46

Qualquer código pode pegar Throwable. Portanto, não, qualquer exceção que você criar será uma subclasse de Throwable e estará sujeita a ser capturada.

Nathan Hughes
fonte
11
A hangprópria Throwable tentaria capturar ChuckNorrisException: P
PermGenError
35
public class ChuckNorrisException extends Exception {
    public ChuckNorrisException() {
        System.exit(1);
    }
}

(Concedido, tecnicamente , essa exceção nunca é realmente lançada, mas ChuckNorrisExceptionnão é possível lançar uma adequada - ela lança você primeiro.)

fofo
fonte
4
Um colega meu sugeriu aderir 'a (;;) {}', pois sentiu que uma chamada 'System.exit (1)' poderia gerar uma Exceção de Segurança. Estou votando nessa por criatividade!
Phil Street
Eu concordo com o final da sua resposta. Nunca mexa com ChuckNorris, Exception ou não.
Benj 29/07
28

Qualquer exceção que você lançar tem que estender Throwable, para que possa sempre ser capturada. Então a resposta é não.

Se você quiser torná-lo difícil de lidar, você pode substituir métodos getCause(), getMessage(), getStackTrace(), toString()para jogar outro java.lang.ChuckNorrisException.

mirelon
fonte
2
Hmm, catch (Throwable t) chama algum método ou muda o objeto? Pode ser possível fazer com que uma cláusula catch lance ainda uma exceção, tornando-a impossível.
Colton
1
Eu acho que catch(Throwable t)apenas armazena em variável para minhas sugestões se aplicam somente no próximo bloco quando o usuário deseja para lidar com a exceção
mirelon
24

Minha resposta é baseada na idéia de @ jtahlborn, mas é um programa Java totalmente funcional , que pode ser empacotado em um arquivo JAR e até implantado no seu servidor de aplicativos favorito como parte de um aplicativo da web .

Antes de tudo, vamos definir a ChuckNorrisExceptionclasse para não travar a JVM desde o início (Chuck realmente ama travar as 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);
    }
}

Agora vai a Expendablesclasse para construí-lo:

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

E finalmente a Mainturma para chutar um bumbum:

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 e execute-o com o seguinte comando:

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

Você obterá a seguinte saída:

before
finally

Não é surpresa - afinal, é um pontapé redondo :)

Incêndios
fonte
muito agradável! Eu não fiz muito com a manipulação de definição de classe. você ainda precisa do "verificar: nenhum" na linha de comando?
precisa saber é o seguinte
@jtahlborn Sim, tente lançar um objeto que não seja descendente de Throwable falha sem "verificar: nenhum".
Wildfire
ah, eu tenho a impressão de que isso de alguma forma contornou essa restrição. Então, como isso é diferente da minha resposta?
jtahlborn
2
A principal diferença é que é código java de trabalho, sem tempo de compilação de hackers
Wildfire
15

No construtor, você pode iniciar um thread que chama repetidamente originalThread.stop (ChuckNorisException.this)

O encadeamento poderia capturar a exceção repetidamente, mas continuaria a lançá-la até morrer.

Peter Lawrey
fonte
13

Não. Todas as exceções em Java devem ser subclasses java.lang.Throwablee, embora isso não seja uma boa prática, você pode capturar todos os tipos de exceção da seguinte forma:

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

Consulte a documentação java.lang.Throwable para obter mais informações.

Se você estiver tentando evitar exceções verificadas (aquelas que devem ser tratadas explicitamente), convém subclassificar Error ou RuntimeException.

VolatileDream
fonte
9

Na verdade, a resposta aceita não é tão boa porque o Java precisa ser executado sem verificação, ou seja, o código não funcionaria em circunstâncias normais.

AspectJ para o resgate da solução real !

Classe de exceção:

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

Aplicação de exemplo:

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

Resultado:

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)
kriegaex
fonte
8

Uma variante do tema é o fato surpreendente de que você pode lançar exceções verificadas não declaradas do código Java. Como não está declarado na assinatura dos métodos, o compilador não permitirá que você capture a exceção em si, mas pode capturá-la como java.lang.Exception.

Aqui está uma classe auxiliar que permite lançar qualquer coisa, declarada ou não:

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

Agora throw SneakyThrow.sneak(new ChuckNorrisException());lança um ChuckNorrisException, mas o compilador reclama

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

sobre como capturar uma exceção que não é lançada se ChuckNorrisException for uma exceção verificada.

Hans-Peter Störr
fonte
6

Os únicos ChuckNorrisExceptions em Java devem ser OutOfMemoryErrore StackOverflowError.

Você pode realmente "capturá-los" da maneira que um catch(OutOfMemoryError ex)será executado caso a exceção seja lançada, mas esse bloco retrocederá automaticamente a exceção para o chamador.

Eu não acho que public class ChuckNorrisError extends Errorisso funcione, mas você pode tentar. Não encontrei nenhuma documentação sobre a extensãoError

usr-local-ΕΨΗΕΛΩΝ
fonte
2
O erro ainda se estende ao Throwable, portanto, não há como impedir a captura. Isso ocorre por design da linguagem Java.
JasonM1
1
@ JasonM1 Acho que o OP não pediu uma exceção realmente "inatacável", e quis dizer que o erro se propaga mesmo que você o pegue. Assim, qualquer Throwable é capturável, mas estes dois acabará por se propagar, não importa o que você faz
ΕΨΗΕΛΩΝ usr-local-
Para ser complicado, ChuckNorrisException poderia estender Throwable diretamente, então não seria nem Exceção nem Erro!
JasonM1
4
O erro não se propaga, mesmo se você o pegar, não tenho certeza de onde você tirou essa ideia.
precisa
3
Eu acho que você está bem confuso sobre Erros, são exceções normais, como tudo que se estende Throwable ou até Throwable, por si só.
bestsss 21/12/12
6

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

Sim, e aqui está a resposta: Crie o seu de java.lang.ChuckNorrisExceptionforma que não seja uma instância do java.lang.Throwable. Por quê? Um objeto que não pode ser jogado é inacessível por definição, porque você nunca pode capturar algo que nunca pode ser lançado.

Thomas Eding
fonte
2
Mas então não é uma exceção.
DOLBI
8
@dolbi: Não consigo encontrar lugar na pergunta do OP de que os estados java.lang.ChuckNorrisExceptiondevem ser uma exceção, muito menos jogáveis ​​#
Thomas Eding
1
Eu acho que não está indicado, mas está implícito. Você é um matemático :-), não é?
DOLBI
3

Você pode manter ChuckNorris interno ou privado e encapsulá-lo ou segui-lo ...

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

Jay
fonte
7
Não acredito que a ideia fosse pegá-lo. Eu acho que a idéia é impedir que seja pego.
Patrick Roberts
Corrija-me se eu estiver errado, mas se você o tornar interno, não poderá alcançá-lo sem reflexão.
21412 Jay
5
sim, mas contanto que você possa capturar Exception ou Throwable, a visibilidade do tipo real é irrelevante.
Keiths
3

Dois problemas fundamentais com o tratamento de exceções em Java são que ele usa o tipo de exceção para indicar se uma ação deve ser executada com base nela e que qualquer coisa que execute uma ação com base em uma exceção (por exemplo, "pega") é considerada como resolvida a condição subjacente. Seria útil ter um meio pelo qual um objeto de exceção pudesse decidir quais manipuladores devem ser executados e se os manipuladores que executaram até agora limparam as coisas o suficiente para que o presente método satisfaça suas condições de saída. Embora isso possa ser usado para tornar exceções "inatacáveis", dois usos maiores seriam (1) criar exceções que só serão consideradas manipuladas quando forem capturadas pelo código que realmente sabe lidar com elas,finallyFooExceptiondurante um finallybloco durante o desenrolamento de a BarException, ambas as exceções devem propagar a pilha de chamadas; ambos devem ser capturáveis, mas o desenrolamento deve continuar até que ambos sejam capturados). Infelizmente, acho que não haveria maneira de fazer com que o código de tratamento de exceções existente funcionasse dessa maneira sem interromper as coisas.

supercat
fonte
uma ideia interessante, mas não acho que o código de baixo nível saiba o que uma exceção específica "significa" para o chamador, portanto, não acho que faria sentido para o lançador decidir quais manipuladores devem ser executados.
jtahlborn
@jtahlborn: No momento, o lançador decide quais manipuladores de exceção devem executar através da escolha do tipo de exceção. Isso torna quase impossível lidar com alguns cenários de maneira limpa. Entre outras coisas: (1) se uma exceção ocorre enquanto um finallybloco está limpando de uma exceção anterior, é bem possível que uma exceção, na ausência da outra, possa ser algo com o qual se espera que o código manipule e continue, mas lidar com um e ignorar o outro seria ruim. Porém, não há mecanismo para produzir uma exceção composta que ambos os manipuladores processariam.
Supercat
@jtahlborn: Além disso, torna muito difícil permitir que as exceções que ocorrem nos retornos de chamada sejam tratadas pela camada de aplicação externa. Se a exceção do retorno de chamada estiver agrupada em outro tipo de exceção, o tipo de exceção de retorno de chamada não poderá ser usado na camada externa para decidir se deve capturá-la; se não estiver encapsulado, uma exceção "acidental" da camada intermediária poderá ser confundida com uma que ocorre no retorno de chamada. Se um objeto de exceção quebrada for informado quando foi passado para a camada de aplicativo externa, ele poderá começar a responder aos tipos de exceções quebradas.
Supercat
Eu não estava discutindo seus outros pontos, apenas a declaração sobre o objeto de exceção que decide quais manipuladores serão executados. até certo ponto, os tipos de exceção já fazem isso, mas parece que você queria algo mais dinâmico, com o qual eu discordava. Eu acho que seu principal argumento (que você meio que vem de lado) é capturar o máximo de informações possível na parte inferior e deixar as camadas superiores verem e trabalharem com todas essas informações. Neste ponto geral, eu concordo com você, no entanto, o diabo está nos detalhes / implementação.
jtahlborn
@jtahlborn: Minha intenção não era fazer com que os métodos virtuais implementassem algo particularmente "dinâmico", mas basicamente dizia "Existe uma condição do tipo indicado que deve ser adotada". Uma coisa que esqueci de mencionar, porém, é que deveria haver um meio pelo qual o código que as chamadas Foopodem distinguir entre uma exceção que Foose lançou ou deliberadamente quer fingir que se lançou, de uma que Foonão esperava que acontecesse quando foi chamando algum outro método. É sobre isso que a noção de exceções "verificadas" deve ser "sobre".
Supercat
1

É facilmente possível simular uma exceção não capturada no encadeamento atual. Isso acionará o comportamento regular de uma exceção não capturada e, portanto, executará o trabalho semanticamente. No entanto, não necessariamente interromperá a execução do encadeamento atual, pois nenhuma exceção é realmente lançada.

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

Isso é realmente útil em situações (muito raras), por exemplo, quando o Errormanuseio adequado é necessário, mas o método é chamado a partir de uma estrutura que captura (e descarta) qualquerThrowable .

DST
fonte
0

Chame System.exit (1) em finalize, e basta lançar uma cópia da exceção de todos os outros métodos, para que o programa saia.

Demi
fonte