lançando exceção de tempo de execução no aplicativo Java

11

Estou trabalhando como contratado para projetar aplicativos Java corporativos para meu cliente no papel de líder técnico. O aplicativo será usado pelos usuários finais e haverá uma equipe de suporte que o apoiará quando sairmos.

Os outros líderes técnicos com quem estou trabalhando têm a impressão de que o tratamento de exceções deixará o código sujo. O sistema deve lançar exceções verificadas apenas da camada Serviço e o restante do código deve lançar exceções de tempo de execução de todas as outras camadas, para que não haja necessidade de lidar com as exceções não verificadas.

Qual é a necessidade de sempre lançar uma exceção não verificada em um aplicativo de negócios?

Da minha experiência no passado sobre exceções de tempo de execução:

1) Exceções não verificadas tornam o código imprevisível, pois elas não aparecem no Javadoc.

2) Lançar exceções não verificadas em um aplicativo de negócios não faz sentido, porque quando você o lança e ele segue direto no rosto dos usuários, como você o explica? Já vi muitas aplicações web que mostram o 500 -Internal Error. Contact Administratorque não significa nada para um usuário final ou para a equipe de suporte que gerencia a aplicação.

3) O lançamento de exceções de tempo de execução força os usuários da classe que lançam a exceção a percorrer o código-fonte para depurar e ver por que a exceção é lançada. Isso só pode ser evitado se o Javadoc da exceção de tempo de execução estiver excelentemente documentado, o que eu acho que nunca é um caso.

randominstanceOfLivingThing
fonte
Você pode explicar o que é nível de serviço? A camada está mais distante do usuário ou mais próxima do usuário?
Manoj R
Por que essa pergunta não se encaixa no SO?
Bjarke Freund-Hansen
@ Manoj R: Nem. Uma camada ou camada de serviço é onde são executadas as regras e a lógica de negócios, separadas dos detalhes da tecnologia DB e da apresentação do cliente.
Michael Borgwardt
6
Exceções não verificadas [...] não aparecem nem no Javadoc. => eles aparecerão se você os documentar com a @throwstag, o que, a propósito, é uma boa prática.
Assilias
4
@SureshKoya Java efetivo, item 62: documente todas as exceções geradas por cada método. Extrair: use a @throwstag Javadoc para documentar cada exceção não verificada que um método pode lançar, mas não use a palavra-chave throws para incluir exceções não verificadas na declaração do método.
assylias

Respostas:

12

As exceções verificadas são uma falha no projeto da linguagem. Eles o forçam a ter abstrações extremamente vazadas e código sujo. Eles devem ser evitados o máximo possível.

Quanto aos seus pontos:

1) As exceções marcadas tornam o código sujo e não menos imprevisível, pois são exibidos em todos os lugares .

2) Como uma exceção verificada está sendo mostrada ao usuário melhor? A única diferença real entre as exceções marcadas e as não verificadas é técnica e afeta apenas o código fonte.

3) Já ouviu falar de rastreamentos de pilha? Eles informam exatamente onde a exceção foi lançada, independentemente de estar marcada ou desmarcada. Na verdade, as exceções verificadas tendem a ser piores para a depuração, porque geralmente são agrupadas, o que leva a rastreamentos de pilha mais longos e mais feios, ou até perdidos por causa do empacotamento incorreto.

Existem dois tipos de exceções: aquelas que ocorrem "normalmente" e geralmente são tratadas muito perto de onde elas ocorrem, e aquelas que são realmente excepcionais e podem ser tratadas genericamente em uma camada muito alta (basta interromper a ação atual e registrá-la / mostrar um erro).

As exceções verificadas foram uma tentativa de colocar essa distinção na sintaxe do idioma no momento em que as exceções são definidas. Os problemas com isso são

  • A distinção depende realmente do chamador , não do código que gera a exceção
  • É completamente ortogonal ao significado semântico de uma exceção, mas vinculá-lo à hierarquia de classes obriga a misturar os dois
  • O ponto principal das exceções é que você pode decidir em que nível capturá-las sem correr o risco de perder um erro silenciosamente ou precisar poluir o código em níveis intermediários ou; exceções verificadas perdem essa segunda vantagem.
Michael Borgwardt
fonte
1. Bem, no caso de uma exceção desmarcada, você nem saberá se uma chamada pode resultar em uma desmarcada. 2. Quando você disse "É completamente ortogonal ao significado semântico de uma exceção, mas vinculá-lo à hierarquia de classes obriga a misturar os dois", suponho que você quis dizer hierarquia de chamadas em vez de hierarquia de classes.
randominstanceOfLivingThing
2
@Suresh Koya: Não, eu quis dizer hierarquia de classes, o fato de uma declaração SQLException extends Exceptionsignificar que escolher lançar um SQLExceptionporque um erro está relacionado ao acesso ao banco de dados significa automaticamente que a exceção é verificada. E você pode documentar exceções não verificadas nos comentários do Javadoc. Funciona bem para todos os outros idiomas.
Michael Borgwardt
1
@MichaelBorgwardt "As exceções verificadas são uma falha no design da linguagem", é sua opinião pessoal, se não, eu gostaria de ler mais sobre isso? Qualquer link autêntico seria uma grande ajuda.
Vipin
2
@Vipin: É minha opinião pessoal, mas uma que muitas pessoas compartilham, por exemplo, Bruce Eckel: mindview.net/Etc/Discussions/CheckedExceptions - e o fato de que nenhuma outra linguagem adotou exceções verificadas nos 20 anos desde que o Java foi introduzido fala por si ...
Michael Borgwardt
2

A meu ver, que tipo de exceção é lançada depende do que seu código está fazendo.

Lancei exceções verificadas quando espero que possam acontecer com bastante frequência (usuários inserindo dados duvidosos, por exemplo) e espero que o código de chamada lide com a situação.

Lancei exceções não verificadas / de tempo de execução (não tão frequentemente quanto verificadas) quando tenho uma situação rara que não espero que o código de chamada manipule. Um exemplo pode ser algum tipo de erro relacionado à memória estranha que eu nunca espero que ocorra. Desmarcado são os tipos de erros que você espera derrubar o aplicativo.

Nenhuma exceção deve aparecer na frente de um usuário sem algum nível de suporte. Mesmo que seja apenas um "recorte e cole esse despejo de erro em um email". Nada é mais irritante para um usuário do que saber que há um erro, mas não há detalhes ou ações que eles podem tomar para iniciar a correção.

Meu palpite é que a filosofia que você menciona vem de uma de duas fontes:

  • Programadores preguiçosos tentando evitar o trabalho.
  • Ou desenvolvedores que tiveram que dar suporte a códigos que foram para o outro lado. ou seja, manipulação de erro excessivo. O tipo de código que contém grandes quantidades de manipulação de exceções, grande parte das quais não faz nada, ou pior ainda, é usado para controle de fluxo e outros fins incorretos.
drekka
fonte
Se algo pode acontecer com bastante frequência, não é uma exceção. Mas concordou que o erro não deve ser gerado, sobre o qual o usuário não tem idéia.
Manoj R
Eu concordo com sua filosofia. Em um aplicativo que você está desenvolvendo para os usuários, qual será a necessidade de lançar uma exceção de Tempo de Execução?
randominstanceOfLivingThing
Além disso, mesmo que uma exceção de tempo de execução seja lançada, prefiro a convenção @assylias apontada.
randominstanceOfLivingThing
1

Se você não deseja exceções de tempo de execução desmarcadas, por que está usando java? Existe a possibilidade de exceções de tempo de execução em quase todos os lugares - principalmente as exceções NullPointerException, ArithmeticException, ArrayIndexOutOfBounds, etc.

E quando você ler os arquivos de log de algum sistema J2EE, como, por exemplo, uma instalação do SAP NetWeaver, verá que essas exceções acontecem literalmente o tempo todo.

Ingo
fonte
Da especificação da linguagem Java: "As classes de exceção de tempo de execução (RuntimeException e suas subclasses) estão isentas da verificação em tempo de compilação porque, no julgamento dos projetistas da linguagem de programação Java, ter que declarar tais exceções não ajudaria significativamente a estabelecer a correção Muitas das operações e construções da linguagem de programação Java podem resultar em exceções de tempo de execução. "
randominstanceOfLivingThing
1
A exceção de tempo de execução que você está falando é a que surge do Java, já que o sistema de tempo de execução Java não tem idéia do motivo pelo qual você está tentando fazer a chamada específica. Por exemplo: Uma NullPointerException surgiria se você estivesse chamando um método que é nulo. Nesse caso, o sistema de tempo de execução Java não tem a palavra certa para os loucos dos programadores e daria um tapa no chamador com uma NullPointerException. A parte triste é que o chamador vendeu o produto Netweaver para você e você, como usuário, agora é vítima de uma programação ruim. Os testadores são os mesmos culpados, pois não fizeram todos os testes de limite.
randominstanceOfLivingThing
1

Existem algumas regras para o tratamento de exceções que você deve ter em mente. Mas primeiro, é preciso lembrar que as exceções fazem parte da interface exposta pelo código; documentá-los . Isso é especialmente importante quando a interface é pública, é claro, mas também é uma boa ideia em interfaces privadas.

As exceções devem ser tratadas apenas no ponto em que o código pode fazer algo sensato com elas. A pior opção de manuseio é não fazer nada a respeito, o que deve ser feito apenas quando essa for exatamente a opção correta. (Quando tenho uma situação desse tipo no meu código, incluo um comentário nesse sentido, para não me preocupar com o corpo vazio.)

A segunda pior opção é lançar uma exceção não relacionada sem o original anexado como causa. O problema aqui é que as informações da exceção original que permitiriam o diagnóstico do problema foram perdidas; você está criando algo com o qual ninguém pode fazer nada (exceto reclamar que "não funciona" e todos sabemos como odiamos esses relatórios de erros).

Muito melhor é registrar a exceção. Isso permite que alguém descubra qual é o problema e corrija-o, mas você só deve registrar a exceção no ponto em que ela seria perdida ou relatada por uma conexão externa. Isso não ocorre porque o registro com mais frequência é um grande problema, como tal, mas porque o registro excessivo significa que o log consome mais espaço sem conter mais informações. Depois de registrar a exceção, você pode relatar uma précis ao usuário / cliente com boa consciência (desde que inclua também o tempo de geração - ou outro identificador de correlação) nesse relatório para que a versão curta possa ser correspondida com os detalhes, se necessário).

A melhor opção é, é claro, lidar completamente com a exceção, lidando com a situação de erro na sua totalidade. Se você pode fazer isso, faça de qualquer maneira! Pode até significar que você pode evitar ter que registrar a exceção.

Uma maneira de lidar com uma exceção é lançar outra exceção que fornece uma descrição de nível superior do problema (por exemplo, “ failed to initialize” em vez de “ index out of bounds”). Esse é um bom padrão, desde que você não perca as informações sobre a causa da exceção; use a exceção detalhada para inicializar a causeexceção de nível superior ou registre os detalhes (conforme discutido acima). O registro é mais apropriado quando você está prestes a cruzar um limite entre processos, como uma chamada IPC, porque não há garantia de que a classe de exceção de baixo nível esteja presente no outro extremo da conexão. Manter como causa anexa é mais apropriado ao cruzar um limite interno.

Outro padrão que você vê é pegar e soltar:

try {
    // ...
} catch (FooException e) {
    throw e;
}

Esse é um anti-padrão, a menos que você tenha restrições de tipo de outras catchcláusulas, o que significa que você não pode simplesmente deixar a exceção passar por conta própria. Então é apenas um recurso feio do Java.

Não há diferença real entre exceções verificadas e desmarcadas, exceto o fato de que você deve declarar exceções verificadas que cruzam os limites do método. Ainda é uma boa idéia documentar exceções não verificadas (com o @throwscomentário do javadoc) se você souber que elas estão sendo lançadas deliberadamente pelo seu código. Não jogue deliberadamente java.lang.Errorou suas subclasses (a menos que esteja escrevendo uma implementação da JVM).

Opinião: um caso de erro inesperado sempre representa um erro no seu código. As exceções verificadas são uma maneira de gerenciar essa ameaça, e onde os desenvolvedores usam deliberadamente exceções não verificadas como uma maneira de evitar problemas ao lidar com casos de erro, você está acumulando muitas dívidas técnicas que precisarão limpar por algum tempo se você quiser código robusto. A manipulação de erro superficial não é profissional (e observar a manipulação de erro é uma boa maneira de determinar quão bom é o programador).

Donal Fellows
fonte
0

Eu acho que você só deve lançar exceção marcada quando o aplicativo pode se recuperar. Portanto, os usuários da sua biblioteca devem capturar essas exceções e fazer o que for necessário para recuperar. Qualquer outra coisa deve ser desmarcada.

Como exemplo,

Se o seu aplicativo carregar dados de um arquivo ou de um banco de dados. Então,..

try {
  File data = new File(...);
  // parse file here
} catch (Exception ex) {
  throw new MissingDataFileException("data file not found");
}

o chamador sempre pode capturar a MissingDataFileException verificada e, em seguida, tentar carregar os dados do banco de dados.

try {
  Connection con = DriverManager.getConnection( host, username, password );
  // query data here
} catch (Exception ex) {
  throw new RuntimeException("better call Saul!");
}
Hendrix
fonte
1
Saul morreu em um acidente de carro há 3 anos. Falhou! ;-)
Bolsistas do Donal 31/03