Respostas:
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
.
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.
boolean assertionsEnabled = false; assert assertionsEnabled = true;
Geralmente, você deve sempre capturar java.lang.Error
e 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, OutOfMemoryError
mas é um problema de tempo de execução e não há razão para encerrar seu programa.
NoClassDefFoundError
ocorrem 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 Throwable
no nível superior e produzir uma mensagem de erro útil.
OutOfMemoryError
nã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 length
for uma entrada não confiável, ela deve ser validada antes da chamada new byte[]
.
NoClassDefFoundError
pode 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 ClassLoader
para verificar se ela existe, o que gera ClassNotFoundException
.
ZipError
indica 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.
java.lang.Error
ou java.lang.Throwable
no 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 String
s, o que aciona outro OOM.
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.
OutOfMemoryError
e continuar ao invés de existir prontamente não é aconselhável porque seu programa está em um estado indefinido .
Um Error
geralmente não deve ser detectado , pois indica uma condição anormal que nunca deve ocorrer .
Na especificação da API Java para a Error
classe:
Um
Error
é uma subclasse deThrowable
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 Error
ocorre, 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 Error
seja uma subclasse do Throwable
que significa que pode ser capturado por uma try-catch
cláusula, provavelmente não é realmente necessário, pois o aplicativo estará em um estado anormal quando um Error
for 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 .
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.
Error
s.
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).
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:
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.
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.