Quando capturar java.lang.Error?


Respostas:


101

Geralmente, nunca.

No entanto, às vezes você precisa detectar erros específicos.

Se você estiver escrevendo um código de estrutura (carregando classes de terceiros), pode ser sensato capturar LinkageError(nenhuma definição de classe encontrada, link não satisfeito, mudança de classe incompatível).

Eu também vi alguns códigos estúpidos de terceiros lançando subclasses de Error, então você terá que lidar com eles também.

A propósito, não tenho certeza de que não seja possível recuperar OutOfMemoryError.


3
Isso eu tive que fazer exatamente para carregar as DLLs, que falharia se não fossem configuradas corretamente. Não é um erro fatal no caso desta aplicação.
Mario Ortegón

7
Às vezes, faz sentido capturar OutOfMemoryError - por exemplo, quando você está criando listas de grandes matrizes.
SpaceTrucker

3
@SpaceTrucker: essa abordagem funciona bem em aplicativos multithread ou há um risco significativo de que alocações menores em outros threads falhem por causa disso? … Presumivelmente apenas se seus arrays fossem pequenos o suficiente para serem alocados, mas não deixassem nada para mais ninguém.
PJTraill

@PJTraill Não tenho certeza sobre isso. Isso exigiria algumas amostras estatísticas do mundo real. Pensei ter visto esse código, mas não me lembro onde estava.
SpaceTrucker de

51

Nunca. Você nunca pode ter certeza de que o aplicativo será capaz de executar a próxima linha de código. Se você receber um OutOfMemoryError, não terá garantia de que poderá fazer nada de maneira confiável . Captura RuntimeException e exceções verificadas, mas nunca erros.

http://pmd.sourceforge.net/rules/strictexception.html


27
Nunca diga nunca. temos código de teste que faz uma "declaração falsa;" em seguida, captura o AssertionError para garantir que o sinalizador -ea esteja definido. Fora isso ... sim, provavelmente nunca ;-)
Programador Outlaw

3
Que tal um aplicativo de servidor que entrega solicitações para threads de trabalho. Não faria sentido capturar Throwable no thread de trabalho para detectar quaisquer erros e, pelo menos, tentar registrar o que deu errado?
Leigh

11
Nunca ... exceto quando for absolutamente necessário. Nunca é uma palavra forte e sempre há exceções às regras. Se você estiver construindo uma estrutura, não é improvável que tenha que detectar e lidar com certos erros, mesmo que seja apenas para registrar.
Robin

E os erros, por exemplo, NoSuchMethodError provenientes de métodos de biblioteca de terceiros?
ha9u63ar

@OutlawProgrammer Apenas para registro, existem outras maneiras de fazer o mesmo teste:boolean assertionsEnabled = false; assert assertionsEnabled = true;
shmosel

16

Geralmente, você deve sempre capturar java.lang.Errore gravar em um log ou exibi-lo para o usuário. Eu trabalho com suporte e vejo diariamente que os programadores não conseguem contar o que aconteceu em um programa.

Se você tiver um encadeamento daemon, deverá evitar que ele seja encerrado. Em outros casos, seu aplicativo funcionará corretamente.

Você só deve pegar java.lang.Error no nível mais alto.

Se você olhar a lista de erros, verá que a maioria pode ser tratada. Por exemplo umZipError ocorre na leitura de arquivos zip corrompidos.

Os erros mais comuns são OutOfMemoryError e NoClassDefFoundError, que na maioria dos casos são problemas de tempo de execução.

Por exemplo:

int length = Integer.parseInt(xyz);
byte[] buffer = new byte[length];

pode produzir um, OutOfMemoryErrormas é um problema de tempo de execução e não há razão para encerrar seu programa.

NoClassDefFoundErrorocorrem principalmente se uma biblioteca não estiver presente ou se você trabalhar com outra versão do Java. Se for uma parte opcional de seu programa, você não deve encerrá-lo.

Posso dar muitos outros exemplos de por que é uma boa ideia pegar Throwableno nível superior e produzir uma mensagem de erro útil.


Eu suspeito que é melhor nesses casos falhar o daemon com alertas adequados do que ter a possibilidade de ele permanecer vivo como algum fantasma etéreo em um jvm com falha, onde pode dar o falso pretexto de que está realmente vivo e fazendo algo
Andrew Norman

OutOfMemoryErrornão é um erro de tempo de execução, não há garantia de que o aplicativo possa se recuperar dele. Se você tiver sorte, poderá obter OOM, new byte[largeNumber]mas se essa alocação não for suficiente para causar OOM, pode ser disparado na próxima linha ou no próximo encadeamento. Este é um problema de tempo de execução porque se lengthfor uma entrada não confiável, ela deve ser validada antes da chamada new byte[].
Jeeyoung Kim

NoClassDefFoundErrorpode ocorrer em qualquer lugar , pois é invocado quando o código Java compilado não consegue encontrar uma classe. Se o seu JDK estiver configurado incorretamente, ele pode desencadear a tentativa de usar a java.util.*classe e é praticamente impossível programar contra ela. Se você está opcionalmente incluindo uma dependência, você deve usar ClassLoaderpara verificar se ela existe, o que gera ClassNotFoundException.
Jeeyoung Kim

1
ZipErrorindica que o arquivo jar contendo classes é um arquivo zip corrompido. Este é um problema muito sério e neste ponto você não pode confiar em nenhum código que seja executado e seria uma coisa irresponsável tentar "se recuperar" dele.
Jeeyoung Kim

2
Em geral, pode ser útil capturar java.lang.Errorou java.lang.Throwableno nível superior e tentar fazer algo com ele - digamos, registrar uma mensagem de erro. Mas nesse ponto não há garantia de que isso será executado. Se sua JVM estiver OOM, tentar registrar pode alocar mais Strings, o que aciona outro OOM.
Jeeyoung Kim

15

Em um ambiente multithread, você geralmente deseja capturá-lo! Quando você pegá-lo, registre-o e encerre o aplicativo inteiro! Se você não fizer isso, algum encadeamento que possa estar fazendo alguma parte crucial estará morto e o restante do aplicativo pensará que tudo está normal. Fora disso, muitas situações indesejadas podem acontecer. Um menor problema é que você não seria capaz de encontrar facilmente a raiz do problema, se outros threads começarem a lançar algumas exceções por causa de um thread não funcionar.

Por exemplo, geralmente o loop deve ser:

try {
   while (shouldRun()) {
       doSomething();
   }
}
catch (Throwable t) {
   log(t);
   stop();
   System.exit(1);
}

Mesmo em alguns casos, você desejaria lidar com erros diferentes de forma diferente, por exemplo, em OutOfMemoryError você seria capaz de fechar o aplicativo regularmente (talvez até mesmo liberar alguma memória e continuar), em outros, não há muito o que fazer.


1
Capturar OutOfMemoryError e continuar ao invés de existir prontamente não é aconselhável porque seu programa está em um estado indefinido .
Raedwald de

1
chamar o sistema, sair ao pegar um lançável tem a consequência indesejada de matar tudo em execução na JVM e não apenas o aplicativo em questão. Normalmente não é uma boa prática para aplicativos da web que podem ser hospedados em um servidor de aplicativos com outros aplicativos da web (sem mencionar que afetaria o próprio servidor de aplicativos).
Andrew Norman

9

Muito raramente.

Eu diria que apenas no nível superior de um tópico para TENTAR emitir uma mensagem com o motivo do término de um tópico.

Se você está em uma estrutura que faz esse tipo de coisa por você, deixe isso para a estrutura.


6

Quase nunca. Os erros são projetados para serem problemas sobre os quais os aplicativos geralmente não podem fazer nada. A única exceção pode ser lidar com a apresentação do erro, mas mesmo isso pode não ocorrer como planejado dependendo do erro.


6

Um Errorgeralmente não deve ser detectado , pois indica uma condição anormal que nunca deve ocorrer .

Na especificação da API Java para a Errorclasse:

Um Erroré uma subclasse de Throwable que indica problemas sérios que um aplicativo razoável não deve tentar detectar. A maioria desses erros são condições anormais. [...]

Um método não é obrigado a declarar em sua cláusula throws quaisquer subclasses de Error que possam ser lançadas durante a execução do método, mas não detectadas, uma vez que esses erros são condições anormais que nunca deveriam ocorrer.

Como a especificação menciona, um Erroré lançado apenas em circunstâncias que são. Provavelmente, quando Errorocorre, há muito pouco que o aplicativo pode fazer e, em algumas circunstâncias, a própria máquina virtual Java pode estar em um estado instável (como VirtualMachineError)

Embora an Errorseja uma subclasse do Throwableque significa que pode ser capturado por uma try-catchcláusula, provavelmente não é realmente necessário, pois o aplicativo estará em um estado anormal quando um Errorfor lançado pela JVM.

Há também uma seção curta sobre este tópico na Seção 11.5 A hierarquia de exceções da especificação da linguagem Java, 2ª edição .


6

Se você for louco o suficiente para criar uma nova estrutura de teste de unidade, seu executor de teste provavelmente precisará capturar java.lang.AssertionError lançado por quaisquer casos de teste.

Caso contrário, veja outras respostas.


5

E há alguns outros casos em que, se você detectar um erro, terá que relançá-lo . Por exemplo, ThreadDeath nunca deve ser capturado, pode causar um grande problema se você capturá-lo em um ambiente fechado (por exemplo, um servidor de aplicativos):

Um aplicativo deve capturar instâncias dessa classe apenas se precisar limpar depois de ser encerrado de forma assíncrona. Se ThreadDeath for capturado por um método, é importante que ele seja relançado para que o thread realmente morra.


2
O que na verdade não é um problema, porque você simplesmente não detecta Errors.
Bombe

4

Muito, muito raramente.

Eu fiz isso apenas para um caso conhecido muito específico. Por exemplo, java.lang.UnsatisfiedLinkError poderia ser lançado se dois ClassLoader independentes carregassem a mesma DLL. (Concordo que devo mover o JAR para um carregador de classe compartilhado)

Mas o caso mais comum é que você precisava fazer login para saber o que aconteceu quando o usuário veio reclamar. Você quer uma mensagem ou um pop-up para o usuário, em vez de silenciosamente morto.

Mesmo programadores em C / C ++, eles apresentam um erro e dizem algo que as pessoas não entendem antes de sair (por exemplo, falha de memória).


3

Em um aplicativo Android, estou capturando um java.lang.VerifyError . Uma biblioteca que estou usando não funciona em dispositivos com uma versão antiga do sistema operacional e o código da biblioteca gerará esse erro. Eu poderia evitar o erro, é claro, verificando a versão do sistema operacional em tempo de execução, mas:

  • O SDK mais antigo compatível pode mudar no futuro para a biblioteca específica
  • O bloco de erro try-catch faz parte de um mecanismo maior de fallback. Alguns dispositivos específicos, embora devam oferecer suporte à biblioteca, lançam exceções. Eu pego VerifyError e todas as exceções para usar uma solução de fallback.

3

é muito útil capturar java.lang.AssertionError em um ambiente de teste ...


Que valor você está tentando adicionar?
lpapp

adicionando o valor de um conjunto de testes não abortando
Jono

2

Idealmente, não devemos tratar / detectar erros. Mas pode haver casos em que precisamos fazer, com base no requisito de estrutura ou aplicação. Digamos que eu tenha um daemon de XML Parser que implementa DOM Parser que consome mais memória. Se houver um requisito como o thread do analisador, não deve ser encerrado quando ele obtém OutOfMemoryError , em vez disso, ele deve tratá-lo e enviar uma mensagem / e-mail ao administrador do aplicativo / estrutura.


1

idealmente, nunca devemos detectar Erro em nosso aplicativo Java, pois é uma condição anormal. A aplicação estaria em estado anormal e poderia resultar em engatilhamento ou dando algum resultado seriamente errado.


1

Pode ser apropriado detectar o erro nos testes de unidade que verificam se uma asserção foi feita. Se alguém desabilitar afirmações ou de outra forma excluir a afirmação que você gostaria de saber


1

Ocorre um Erro quando a JVM não está mais funcionando conforme o esperado ou está prestes a. Se você detectar um erro, não há garantia de que o bloco catch será executado, e muito menos que será executado até o fim.

Dependerá também do computador em execução, do estado atual da memória, portanto não há como testar, tentar e fazer o melhor. Você terá apenas um resultado complicado.

Você também diminuirá a legibilidade do seu código.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.