Este artigo se concentra em alguns mal -entendidos na seleção e no uso de exceções Java. Espero que os leitores possam dominar alguns dos pontos e princípios do manuseio de exceções e prestar atenção ao resumo e indução. Somente lidando com exceções, podemos melhorar as qualidades básicas dos desenvolvedores, melhorar a robustez do sistema, aprimorar a experiência do usuário e melhorar o valor do produto.
Mal -entendimento 1. Escolha anormal
Figura 1. Classificação de anomalia
A Figura 1 descreve a estrutura da exceção. De fato, todos sabemos que as anormalidades são divididas em detectar anormalidades e anormalidades não detectadas, mas na prática, a aplicação dessas duas anormalidades é confusa. Como exceções não detectadas são fáceis de usar, muitos desenvolvedores pensam que a detecção de exceções é inútil. De fato, os cenários de aplicação anormais podem ser resumidos da seguinte forma:
1. O código da chamada não pode continuar sendo executado e precisa ser encerrado imediatamente. Existem muitas possibilidades para essa situação, como o servidor não está conectado, os parâmetros estão incorretos, etc. Exceções não detectadas são aplicáveis nesses momentos e não há necessidade de chamar captura e processamento explícitos de código, e o código é conciso e claro.
2. O código de chamada precisa de processamento e recuperação adicionais. Se o SQLEXCIPCECT for definido como exceções não detectadas, os desenvolvedores acreditarão naturalmente que a SQLEXCECTION não exige a chamada de captura e processamento explícito de código, o que levará a situações graves, como não fechar a conexão, não reverter a transação e os dados sujos no banco de dados. É precisamente porque o SQLEXCECTION é definido como detecção de exceções que os desenvolvedores serão impulsionados para capturar e limpar explicitamente os recursos após o código gerar uma exceção. Obviamente, após a limpeza dos recursos, você pode continuar a lançar exceções não detectadas para impedir a execução do programa. De acordo com a observação e a compreensão, a detecção de exceções pode ser aplicada principalmente às classes de ferramentas. Java Learning Group 669823128
Incomposição 2: Exiba exceções diretamente na página ou cliente.
É comum imprimir exceções diretamente no lado do cliente. Tomando o JSP como exemplo, uma vez que o código é executado, o contêiner imprime as informações da pilha de exceção diretamente na página por padrão. De fato, da perspectiva do cliente, qualquer exceção não tem significado prático e a maioria dos clientes não consegue entender as informações de exceção. O desenvolvimento de software também deve tentar evitar apresentar a exceção diretamente aos usuários.
Listagem 1
<strong> pacote </strong> com.ibm.dw.sample.exception;/*** RuntimeException Custom* Adicione o atributo do código de erro*/<strong> public </strong> <strong> classe </strong> <strong> runtimeException </strong> <strong> estende </strong> <strong> java </strong> <strong> </strong> </strong> <strong> strong <strong> <strong> java </strong> <strong> </strong> <strong> <strong> strong <strong> strott <strong> strott </strong> </strong> </strong> </strong> <strong> strong <strong> <strong> java </strong>. <strong> public </strong> <strong> static </strong> <strong> final </strong> inteiro genérico = 1000000; // Código de erro <strong> privado </strong> Inteiro ErrorCode; <strong> public </strong> <strong> RUNTimeException </strong> (Errorcode Inteiro, causa de arremesso) {<strong> this </strong> (ErrorCode, <strong> null </strong>, causa); } <strong> public </strong> <strong> RUNTimeException </strong> (mensagem de string, causa de arremesso) {// use o código de erro geral <strong> this </strong> (genérico, mensagem, causa); } <strong> public </strong> <strong> RUNTimeException </strong> (Errorcode Inteiro, mensagem de string, causa de arremesso) {<strong> super </strong> (mensagem, causa); <strong> isto </strong> .errorCode = errorCode; } <strong> public </strong> inteiro <strong> getErrorCode </strong> () {<strong> retornar </strong> errorCode; }}Como mostra o código de exemplo, introduza códigos de erro nas exceções. Depois que ocorre uma exceção, apenas apresentamos o código de erro da exceção ao usuário ou convertemos o código de erro em um prompt mais compreensível. De fato, o código de erro aqui também contém outra função, e os desenvolvedores também podem saber com precisão que tipo de exceção ocorreu com base no código de erro.
Equívoco 3: Poluição da hierarquia de código
Frequentemente, dividimos o código em diferentes hierarquias, como serviço, lógica de negócios, dao etc. A camada DAO conterá métodos de realização de exceções, conforme mostrado na Listagem 2:
Listagem 2
<strong> public </strong> Cliente <strong> recuperaçãoCustomerbyId </strong> (Long Id) <strong> tiro </strong> sqLexception {// consulte o banco de dados baseado em id}À primeira vista, não há problema com o código acima, mas se você pensa cuidadosamente da perspectiva do acoplamento de design, a SQLEXCECTION aqui poluga o código de chamada superior. A camada de chamada precisa usar explicitamente o Try-Catch para capturar ou jogá-lo ainda mais no nível mais alto. De acordo com o princípio do isolamento do design, podemos modificá -lo adequadamente para:
Listagem 3
<strong>public</strong> Customer <strong>retrieveCustomerById</strong>(Long id) { <strong>try</strong>{ //Query the database based on ID}<strong>catch</strong>(SQLException e){ //Use non-detected exception encapsulation to detect exceptions, reduce hierarchical coupling<strong>throw</strong> <strong>new</strong> RUNTimeException (SQLERRORCODE, E); } <strong> finalmente </strong> {// feche a conexão e limpe os recursos}}Equívoco 4: ignore exceções
O manuseio de exceção a seguir está apenas emitindo a exceção ao console e não faz sentido. Além disso, uma exceção aparece aqui e o programa não interrompe, e o código de chamada continua sendo executado, resultando em mais exceções.
Listagem 4
<strong> public </strong> <strong> void </strong> <strong> recuperebjectbyId </strong> (longo id) {<strong> tente </strong> {//) um código que lança sqLexception} <strong> captura </strong> (sqLexception ex) {/** ** A pessoa que sabe que a excepção é que a exceção é significativa. * No ambiente de produção, a pilha de erros precisa ser emitida para o log. * E o programa continua a executar após o processo de captura, o que levará a outros problemas*/ ex.PrintStackTrace (); }}Pode ser reconstruído:
Listagem 5
<strong> <strong> </strong> <strong> void </strong> <strong> recuperaroBjectById </strong> (longo id) {<strong> tente </strong> {//) um código que lança sqLexception} <strong> catch </strong> (sqLexception ex) {<strong> throw </strong> <strong> stlttime </strong> (sqLexception ex) {<strong> tiro </strong> <strong> </strong> runnime " ex); } <strong> finalmente </strong> {// Limpe o ResultSet, instrução, conexão etc}}Esse mal-entendido é relativamente básico e, em circunstâncias normais, você não cometerá esse erro de baixo nível.
Equívoco 5: Inclua exceções nos blocos de declaração de loop
Conforme mostrado no código a seguir, a exceção está contida no bloco de instrução FOR LOOP.
Listagem 6
<strong> para </strong> (<strong> int </strong> i = 0; i <100; i ++) {<strong> tente </strong> {} <strong> Catch </strong> (xxxException e) {//…. }}Todos sabemos que o manuseio de exceções ocupa os recursos do sistema. À primeira vista, todos pensaram que não cometeriam esse erro. De outra perspectiva, um loop é executado na classe A, e o método da classe B é chamado no loop, mas o método chamado na classe B contém blocos de instrução, como o Try-Catch. A hierarquia da classe desapareceu e o código é exatamente o mesmo que acima.
Equívoco 6: Use a exceção para capturar todas as exceções em potencial
Vários tipos diferentes de exceções são lançados durante a execução de um método. Por uma questão de simplicidade do código, a exceção da classe base é usada para capturar todas as exceções em potencial, como mostrado no exemplo a seguir:
Listagem 7
<strong> public </strong> <strong> void </strong> <strong> recuperebjectbyId </strong> (long id) {<strong> tente </strong> {//… Código chama que lança ioexception //… Código chama que lança SqLexception} <strong> Catch </strong> (Exceção e) {// All Protending Excessio Se vários níveis capturarem isso, as informações válidas da exceção original serão perdidas <strong> Throw </strong> <strong> novo </strong> RuntimeException ("Exception <strong> em </strong> recuperar objectbyId", e); }}Pode ser reconstruído
Listagem 8
<strong> public </strong> <strong> void </strong> <strong> recuperebjectbyId </strong> (longo id) {<strong> experimente </strong> {//..lembom, que lança a hora de execução, ioexception, sqlexception} <strong> catch </strong> (ioexception e) {// apenas captura ioException} <strong> cate </strong> (ioexception e) {// apenas catch ioException} <strong> catch RUNTimeException (/*Especifique o código de erro correspondente à ioexception aqui*/code, "Exception <strong> em </strong> recuperebjectbyId", e); } <strong> Catch </strong> (sqLexception e) {// Apenas captura o SQLEXCECTIONE <STROW> Throw </strong> <strong> novo </strong> run timeException (/*especifique o código de erro correspondente ao sqLexception aqui*/code, "Exception <strong> em </strong> recuperadoBtyid", e); }}Equívoco 7: O encapsulamento de vários níveis lança exceções não detectadas
Se sempre insistirmos que diferentes tipos de exceções devem usar instruções de captura diferentes, a maioria dos exemplos pode ignorar esta seção. No entanto, se apenas uma parte das chamadas de código lançar mais de uma exceção, geralmente não é necessário escrever uma instrução de captura para cada tipo diferente de exceção. Para o desenvolvimento, qualquer exceção é suficiente para explicar o problema específico do programa.
Listagem 9
<strong> tente </strong> {// A RuntimeException, IoExeption ou outros podem ser jogados; // Observe a diferença entre aqui e o mal -entendido seis, aqui está um pedaço de código que lança várias exceções. Os itens acima são vários segmentos de código, cada um jogando diferentes exceções} <strong> Catch </strong> (<strong> exceção </strong> e) {// Como sempre, converta a exceção em tempo de execução, mas o E aqui é na verdade uma instância de tempo de execução da DeException e foi encapsulada no código anterior. RunTimeException (/**/code,/**/, e);}Se convertermos todas as exceções em tempo de execução da USCECCECTIVA, como mostrado no exemplo acima, quando o tipo de exceção já é o tempo de execução do tempo de execução, fazemos outro encapsulamento. A RuntimeTexception foi reencapsulada novamente e as informações válidas transportadas pelo tempo de execução originais foram perdidas.
A solução é que podemos adicionar verificações relevantes à classe RunTimeException para confirmar que o parâmetro jogável não é uma instância do RuntimeException. Nesse caso, copie o atributo correspondente para a instância recém -criada. Ou use diferentes blocos de declaração de captura para capturar o RuntimeException e outras exceções. Método de preferência pessoal 1, os benefícios são evidentes.
Equívoco 8: Exceção de impressão em vários níveis
Vamos primeiro olhar para o exemplo a seguir, que define as 2 classes A e B. Classe B, o código B é chamado na classe A e as exceções de Classe A e Classe B e Imprima.
Listagem 10
<strong> public </strong> <strong> classe </strong> <strong> a </strong> {<strong> private </strong> <strong> static </strong> logger logger = loggerFactory.getLogger (A.Class); <strong> public </strong> <strong> void </strong> <strong> Process </strong> () {<strong> tente </strong> {// Instantia a classe B, você pode mudar para outros métodos de injeção, como b b = <strong> novo </strong> b (); B.Process (); // Outro código pode causar exceção} <strong> capturar </strong> (xxxxException e) {// Se o método do processo da classe B lançar uma exceção, a exceção estará em B a classe for impressa e também será impressa aqui, então Logger.error (e); <strong> Throw </strong> <strong> novo </strong> RUNTimeException (/*Código de erro*/ErrorCode,/*Informações de exceção*/msg, e); }}} <strong> public </strong> <strong> classe </strong> <strong> b </strong> {<strong> privado </strong> <strong> static </strong> logger logger = loggerFactory.getLogger (b.class); <strong> public </strong> <strong> void </strong> <strong> Process </strong> () {<strong> tente </strong> {// código que pode lançar exceção} <strong> catch </strong> (xxxxException e) {logger.error (e); <strong> Throw </strong> <strong> novo </strong> RUNTimeException (/*Código de erro*/ErrorCode,/*Informações de exceção*/msg, e); }}}A mesma exceção será impressa 2 vezes. Se o nível for um pouco mais complicado, é uma dor de cabeça não considerar o desempenho do sistema de impressão de logs e simplesmente localizar problemas específicos nos logs de exceção.
De fato, os logs de impressão exigem apenas capturar e imprimir na camada mais externa do código. A impressão de exceção também pode ser escrita como AOP e tecida na camada mais externa do quadro.
Equívoco 9: o problema que as informações contidas nas exceções não podem ser totalmente localizadas
Exceções não apenas permitem que os desenvolvedores saibam o que está errado, mas com mais frequência os desenvolvedores também precisam saber o que causa o problema. Sabemos que Java .Lang.Exception possui um construtor de parâmetros de tipo de string, e essa sequência pode ser personalizada em informações rápidas fáceis de entender.
Os desenvolvedores de informações personalizadas simples podem saber apenas onde a exceção aparece, mas em muitos casos os desenvolvedores precisam saber mais sobre quais parâmetros estão causando essa exceção. No momento, precisamos anexar as informações do parâmetro do método chamadas para as informações personalizadas. O exemplo a seguir lista apenas o caso de um parâmetro. No caso de vários parâmetros, você pode escrever uma classe de ferramentas para organizar essa string.
Listagem 11
public <strong> void </strong> recuperar objectbyId (longo id) {<strong> tente </strong> {//..os algum código que lança sqlexception} <strong> catch </strong> (sqLexception ex) {// Adicionar informações de parâmetro para excepcionar informações <strong> </strong> <strong> <strong> <strong> runTimeException " Id: "+ id, ex); }}Equívoco 10: não pode prever possíveis anormalidades
Durante o processo de escrita, devido à falta de profunda compreensão do código de chamada, é impossível determinar com precisão se o código chamado produzirá exceções, portanto o processamento será ignorado. Depois que um bug de produção foi gerado, lembrei -me de adicionar a recuperação de exceção a uma determinada peça de código e não conseguia sequer apontar com precisão a causa da exceção. Isso exige que os desenvolvedores não apenas saibam o que estão fazendo, mas também para saber o máximo possível o que os outros fizeram e quais resultados possíveis podem ser causados e considerar o processo de processamento de toda a aplicação de uma perspectiva global. Essas idéias afetarão nossa escrita e processamento de código.
Equívoco 11: Uso misto de várias bibliotecas de log de terceiros
Atualmente, existem mais e mais bibliotecas de log de terceiros Java. Várias estruturas serão introduzidas em um grande projeto, e essas estruturas contarão com a implementação de diferentes bibliotecas de log. O problema mais problemático é não introduzir todas as bibliotecas de log necessárias, o problema é que as bibliotecas de log introduzidas são incompatíveis. Se for fácil resolver no estágio inicial do projeto, você poderá reintroduzir todas as bibliotecas de log do seu código conforme necessário ou alterar para uma estrutura. Mas esse tipo de custo não é acessível para todos os projetos e quanto maior o risco à medida que o projeto progride.
Como podemos efetivamente evitar problemas semelhantes? A maioria das estruturas agora levou em consideração problemas semelhantes. Eles podem configurar propriedades ou arquivos XML, parâmetros ou classes de implementação de log de varredura de tempo de execução na biblioteca Lib e somente quando o aplicativo está em execução, podemos determinar qual biblioteca de log específica aplicar.
De fato, com base no princípio de não exigir vários níveis de impressão de log, podemos simplificar muitas classes que originalmente chamavam de código de impressão de log. Em muitos casos, podemos usar interceptores ou filtros para imprimir logs para reduzir o custo da manutenção e migração do código.
Conclusão
O exposto acima é uma experiência e um resumo puramente pessoais. As coisas são dialéticas e não há princípios absolutos. O princípio mais eficaz é adequado para você. Espero que a explicação e análise acima possam ser úteis para você.