As exceções Java são um mecanismo de consistência fornecido pelo Java para identificar e responder a erros.
O mecanismo de exceção do Java pode separar o código de manuseio de exceções no programa do código comercial normal, garantir que o código do programa seja mais elegante e melhorar a robustez do programa. Ao usar exceções de maneira eficaz, a exceção pode responder claramente a estas três perguntas: o tipo de exceção responde "o que" e a exceção respostas de rastreamento de pilha "onde" e as informações de exceção respondem "por que" são lançadas.
Várias palavras -chave usadas no mecanismo de exceção Java: tente, capturar, finalmente, arremessar, arremessar.
| Palavras -chave | ilustrar |
|---|---|
| tentar | Usado para ouvir. Coloque o código a ser ouvido (código que pode lançar uma exceção) no bloco de instrução Try. Quando ocorre uma exceção no bloco de instrução Try, a exceção é lançada. |
| pegar | Usado para capturar exceções. A captura é usada para capturar exceções que ocorrem nos blocos de declaração Try. |
| Finalmente | Finalmente, os blocos de declaração sempre serão executados. Ele é usado principalmente para reciclar recursos de material (como conexões de banco de dados, conexões de rede e arquivos de disco) abertos nos blocos de tentativa. Somente após a execução do bloco finalmente retornar ou lançar declarações no bloco de tentativa ou captura. Se as declarações finais como retorno ou arremesso forem usadas, elas não voltarão para a execução e pararão diretamente. |
| lançar | Usado para lançar exceções. |
| joga | Usado em assinaturas de método para declarar exceções que podem ser jogadas pelo método. |
classe pública Demo1 {public static void main (string [] args) {try {int i = 10/0; System.out.println ("i ="+i); } catch (arithmeticexception e) {System.out.println ("Exceção pega"); System.out.println ("e.getMessage ():" + e.getMessage ()); System.out.println ("e.toString ():" + e.toString ()); System.out.println ("E.PrintStackTrace ():"); E.PrintStackTrace (); }}} Resultados em execução:
ExceptionE.getMessage (): / por zeroe.tostring (): java.lang.arithmeticexception: / por zeroe.printStacktrace (): java.lang.arithmeticexception: / por zero AT Demo1.main (Demo1.java:6)
A descrição do resultado: há uma operação com um divisor de 0 no bloco de instrução Try, e a operação lançará uma exceção de Java.lang.arithmeticexception. Por captura, a exceção é capturada.
Observando os resultados que descobrimos que o System.out.println ("i ="+i) não foi executado. Isso significa que, após uma exceção no bloco de instrução Try, o conteúdo restante no bloco de instrução Try não será mais executado.
Exemplo 2: entenda o uso básico de finalmente
Com base em "Exemplo um", adicionamos finalmente declarações.
classe pública Demo2 {public static void main (string [] args) {try {int i = 10/0; System.out.println ("i ="+i); } catch (arithmeticexception e) {System.out.println ("Exceção pega"); System.out.println ("e.getMessage ():" + e.getMessage ()); System.out.println ("e.toString ():" + e.toString ()); System.out.println ("E.PrintStackTrace ():"); E.PrintStackTrace (); } finalmente {System.out.println ("Run Finalmente"); }}} Resultados em execução:
Exceptione.getMessage (): / por zeroe.tostring (): java.lang.arithmeticexception: / por zeroe.printstacktrace (): java.lang.arithmeticexcept
Resultados: Finalmente, o bloco de declaração foi executado.
Exemplo 3: entenda o uso básico de lances e lances
Os arremessos são usados para declarar exceções jogadas, enquanto o arremesso é usado para lançar exceções.
classe myException estende a exceção {public myException () {} public myException (string msg) {super (msg); }} public class Demo3 {public static void main (string [] args) {try {test (); } catch (myException e) {System.out.println ("Catch My Exception"); E.PrintStackTrace (); }} public static void test () lança myException {try {int i = 10/0; System.out.println ("i ="+i); } catch (arithmeticexception e) {throw New MyException ("This Is MyException"); }}} Resultados em execução:
Pegue minha exceção MyException: esta é a MyException no Demo3.test (Demo3.Java:24) na Demo3.Main (Demo3.Java:13)
Resultados: MyException é uma subclasse herdada da exceção. A exceção de aritmeticexception (o divisor é 0) é gerada no bloco de instrução Try of Test (), e a exceção é capturada na captura; Então uma exceção da MyException é lançada. O método Main () captura a MyException lançada em Test ().
Estrutura de exceção Java
Diagrama de arquitetura de exceção Java:
1. Jogável
O Throwable é uma superclasse de todos os erros ou exceções no idioma Java.
O Throwable contém duas subclasses: erro e exceção. Eles geralmente são usados para indicar uma anormalidade.
O Throwable contém instantâneos do thread que executa a pilha quando o thread é criado. Ele fornece interfaces como PrintStackTrace () para obter informações como dados de rastreamento de pilha.
2. Exceção
Exceção e suas subclasses são uma forma de arremesso que aponta as condições que um aplicativo razoável deseja capturar.
3. RuntimeTexception
O RunTimeException é uma superclasse que pode lançar exceções durante a operação normal da máquina virtual Java.
O compilador não verifica as exceções de tempo de execução. Por exemplo, quando o divisor é zero, uma exceção de aritmeticexception é lançada. RuntimeException é uma superclasse da aritmeticexception. Quando o código tem um divisor de zero, ele também pode ser compilado se "não for jogado através da declaração de arremessos" ou "não é tratado através da tentativa ... Catch ...". É isso que dizemos "o compilador não verá as exceções de RuntimeException"!
Se o código gerar uma exceção de tempo de execução, ele precisará ser evitado modificando o código. Por exemplo, se o divisor for zero, você precisará evitar essa situação através do código!
4. Erro
Como a exceção, o erro também é uma subclasse de arremesso. É usado para indicar problemas sérios que um aplicativo razoável não deve tentar capturar, e a maioria desses erros são condições excepcionais.
Como o RunTimeException, o compilador não verá um erro.
O Java divide a estrutura jogável em três tipos: exceção verificada (exceção verificada), exceção de tempo de execução (tempo de execução) e erro (erro).
(1) Exceção de tempo de execução
Definição: RuntimeException e suas subclasses são chamadas de exceções de tempo de execução.
Recursos: o compilador Java não o verificará. Ou seja, quando essa exceção pode ocorrer no programa, se "não for jogado através da declaração de arremessos" ou "não é capturado com a declaração de tentativa", ela ainda será compilada e passada. Por exemplo, a exceção de aritmeticexception gerada quando o divisor é zero, a exceção indexOutOfBoundSexception gerada quando a matriz está fora dos limites, a exceção concorrente de exceção gerada pelo mecanismo de falha de falha etc. são todas as exceções do tempo de execução.
Embora o compilador Java não verifique as exceções de tempo de execução, também podemos declarar e lançá-lo através de lances ou capturá-lo através do Try-capath.
Se uma exceção de tempo de execução for gerada, ela precisará ser evitada modificando o código. Por exemplo, se o divisor for zero, você precisará evitar essa situação através do código!
(2) Exceção verificada
Definição: a própria classe de exceção e outras subclasses nas subclasses da Exceção, exceto "Exceção de tempo de execução", são consideradas exceções verificadas.
Recursos: o compilador Java verificará. Tais exceções são declaradas e lançadas através de lances ou capturadas através de Try-Catch, caso contrário elas não podem ser compiladas. Por exemplo, o CLONENOTSupportEdException é uma exceção verificada. Quando um objeto é clonado através da interface clone () e a classe correspondente do objeto não implementa a interface clonável, uma clonenotsupportedException será lançada.
Exceções que estão sendo verificadas geralmente podem ser recuperadas.
(3) erro
Definição: Classe de erro e suas subclasses.
Recursos: Como exceções de tempo de execução, o compilador não verá erros.
Ocorre um erro quando não há recursos insuficientes, falha de restrições ou outras condições que não podem continuar sendo executadas por outros programas. O próprio programa não pode corrigir esses erros. Por exemplo, o VirtualMachineError é um erro.
De acordo com a Convenção Java, não devemos implementar novas subclasses de erro!
Para as três estruturas acima, qual devemos apresentar uma exceção ou um erro? A recomendação fornecida em "Java eficaz" é: use exceções verificadas para condições que podem ser recuperadas e use exceções de tempo de execução para erros do programa.
Várias sugestões sobre manuseio de exceções
Artigo 1: Use exceções apenas para situações anormais
Recomendação: Exceções devem ser usadas apenas para condições anormais e nunca devem ser usadas para fluxos de controle normais.
A explicação é feita comparando os dois códigos abaixo.
Código 1
tente {int i = 0; while (true) {arr [i] = 0; i ++; }} catch (indexOutOfBoundSexception e) {} Código 2for (int i = 0; i <arr.length; i ++) {arr [i] = 0;} O objetivo de ambos os códigos é iterar sobre a matriz ARR e definir o valor de cada elemento na matriz como 0. O código 1 termina por exceção, que parece muito difícil de entender, o Código 2 termina pelos limites da matriz. Devemos evitar o uso do código 1 por três motivos principais:
O design original dos mecanismos de exceção é para situações anormais, portanto, poucas implementações da JVM tentam otimizar seu desempenho. Portanto, a sobrecarga de criar, jogar e capturar exceções é cara.
Colocar o código em retornos de try-capath impede que a JVM implemente certas otimizações específicas que poderiam ter sido executar.
O padrão padrão de travessia de matrizes não leva a verificações redundantes, e algumas implementações modernas da JVM otimizam -as.
De fato, os modos baseados em exceção são muito mais lentos que os modos padrão. O código de teste é o seguinte:
classe pública ASSUSTA1 {private static int [] arr = new int [] {1,2,3,4,5}; private estático int size = 10000; public static void main (string [] args) {long s1 = system.currenttimemillis (); for (int i = 0; i <tamanho; i ++) endbyRange (arr); e1 longo = System.currenttimemillis (); System.out.println ("ENDBYRANGE TIME:"+(E1-S1)+"MS"); long s2 = system.currenttimemillis (); for (int i = 0; i <tamanho; i ++) endbyException (arr); e2 longo = System.CurrentTimemillis (); System.out.println ("endbyException time:"+(e2-s2)+"ms"); } // Traverse a matriz ARR: private estático void endbyException (int [] arr) {try {int i = 0; while (true) {arr [i] = 0; i ++; //System.out.println("endByRange: arr ["+i+"] = "+arr [i]); }} catch (indexOutOfBoundSexception e) {}} // Transfira a matriz ARR: private estático void endbyRange (int [] arr) {for (int i = 0; i <arn.length; i ++) {arr [i] = 0; //System.out.println("endByException: arr ["+i+"] = "+arr [i]); }}} Resultados em execução:
TENDBYRANGE HEMPO: 8MEndByException Hora: 16ms
O resultado mostra que a velocidade de atravessar uma exceção é muito mais lenta do que atravessar uma matriz da maneira comum!
Artigo 2: Use exceções verificadas para condições recuperáveis e use exceções de tempo de execução para erros do programa.
| anormal | ilustrar |
|---|---|
| Exceção de tempo de execução | A classe RuntimeTeException e suas subclasses são chamadas exceções de tempo de execução. |
| A exceção verificada | A própria classe de exceção, bem como outras subclasses em exceção, exceto "Exceção de tempo de execução", são todas exceções verificadas. |
A diferença é que o Java Compiler verifica "Exceções verificadas" e não verifica "Exceções de tempo de execução".
Ou seja, para a exceção verificada, ela é declarada e jogada através de lances, ou capturada através do Try-Catch, caso contrário, não poderá ser compilado. Para exceções de tempo de execução, se eles "não forem jogados através da declaração de arremessos" ou "não pegos com a declaração de tentativa", eles ainda serão compilados e aprovados. Obviamente, embora o compilador Java não verifique as exceções de tempo de execução, também podemos explicar a exceção por meio de arremessos ou pegá-lo através do Try-Catch.
Rithmeticexception (por exemplo, divisor é 0), indexOutOfBoundSexception (por exemplo, matriz fora dos limites), etc. são todas as exceções de tempo de execução. Para esta exceção, devemos evitá -lo modificando o código. Para a exceção verificada, o programa pode ser restaurado para executar o processamento. Por exemplo, suponha que, como um usuário não armazena um número suficiente de chamadas, ele falhará ao tentar fazer uma chamada em uma chamada de pagamento; jogando assim uma exceção verificada.
Artigo 3: Evite o uso desnecessário de exceções verificadas
"Exceção censurada" é uma boa característica do Java. Ao contrário do código de retorno, "Exceções verificadas" forçam o programador a lidar com as condições de exceção, melhorando bastante a confiabilidade do programa.
No entanto, o uso excessivo de exceções verificadas pode tornar a API muito inconveniente. Se um método lança uma ou mais exceções verificadas, o código que chama o método deve lidar com essas exceções em um ou mais blocos de declaração de captura, ou eles devem ser lançados através da declaração de arremessos. Seja tratado através da captura ou arremessada através de declarações, adiciona um ônus desnatado aos programadores.
Duas condições devem ser atendidas para "exceções verificadas": primeiro, mesmo que a API seja usada corretamente, ela não pode impedir a ocorrência de condições de exceção. Segundo, uma vez que uma exceção é gerada, os programadores usando a API podem tomar ações úteis para processar o programa.
Artigo 4: Tente usar exceções padrão
A reutilização do código é digna de advocacia, essa é uma regra comum e as exceções não são exceção. Existem vários benefícios para reutilizar exceções existentes:
Primeiro, torna sua API mais fácil de aprender e usar, porque é consistente com os idiomas com os quais os programadores se familiarizaram.
Segundo, para programas que usam essas APIs, elas são melhor legíveis porque não estão cheias de exceções que não são familiares aos programadores.
Terceiro, quanto menos classes de exceção, menor o uso da memória e menor o tempo gasto na reimpressão dessas classes.
Várias exceções padrão Java são frequentemente as exceções. A tabela a seguir:
| anormal | Use ocasiões |
|---|---|
| IlegalargumentException | O valor do parâmetro não é apropriado |
| IlegalStateException | Os parâmetros não são inadequados |
| NullPointerException | Quando nulo é desativado, o valor do parâmetro é nulo |
| IndexOutOfBoundSexception | Subscrito cruza o limite |
| ConcurrentModificationException | Quando a modificação simultânea é proibida, o objeto detecta modificação simultânea |
| UnsupportEdOperationException | Métodos que o objeto não suporta solicitações de clientes |
Embora sejam as exceções mais comumente reutilizadas das bibliotecas da plataforma Java até o momento, outras exceções também podem ser reutilizadas sob condições de licença. Por exemplo, se você deseja implementar objetos aritméticos, como números ou matrizes complexos, seria muito apropriado reutilizar a aritmeticexception e o númeroFormatexception. Se uma exceção atender às suas necessidades, não hesite em usá -la, mas você deve garantir que a condição de lançar a exceção seja consistente com as condições descritas na documentação para a exceção. Esta reutilização deve ser baseada na semântica, não no nome!
Finalmente, certifique -se de ficar claro que não há regra que deve ser seguida ao escolher qual exceção reutilizar. Por exemplo, considerando o caso de um objeto de cartão, suponha que haja um método para negociar operações, e seu parâmetro (Handsize) é o número de cartões a serem fornecidos em uma mão. Suponha que o chamador passe um valor neste parâmetro maior que o número restante de cartas para todo o baralho. Em seguida, essa situação pode ser interpretada como uma ilegalArgumentException (o valor do Handsize é muito grande) ou uma ilegalStateException (o objeto de cartão tem muito poucos cartões em relação à solicitação do cliente).
Artigo 5: A exceção jogada deve ser adequada para a abstração correspondente
Essa situação pode ser esmagadora se uma exceção lançada por um método não tiver uma correlação óbvia com a tarefa que ele executa. Isso geralmente acontece quando um método passa uma exceção lançada por uma abstração de baixo nível. Quando isso acontece, não apenas confunde, mas também "polui" APIs de alto nível.
Para evitar esse problema, a implementação de alto nível deve capturar a exceção de baixo nível e lançar uma exceção que pode ser introduzida de acordo com a abstração de alto nível. Essa prática é chamada de "Tradução de exceção".
Por exemplo, o método Get () Get () Get () da estrutura de coleção Java é o seguinte (com base no JDK1.7.0_40):
public e get (int index) {try {return listiterator (index) .next (); } catch (nosuchElementException exc) {tiro novo indexOutOfBoundSexception ("Index:"+Index); }}ListIterator (índice) retornará o objeto Listiterator. Chamar o método próximo () do objeto pode lançar uma exceção de NosuchElementException. No método get (), lançar uma exceção de NosuchElementException será confuso. Então, Get () captura NoSuchElementException e lança uma exceção indexoutOfBoundSexception. Isto é, é equivalente à conversão de nosuchElementException em exceção indexOutOfBoundSexception.
Artigo 6: A exceção lançada por cada método deve ser documentada
Para declarar a exceção verificada separadamente e usar a tag @Throws do Javadoc para registrar com precisão as condições para que cada exceção seja lançada.
Se muitos métodos em uma classe lançarem a mesma exceção pelo mesmo motivo, é aceitável fazer documentação para esta exceção nos comentários do documento dessa classe, em vez de documentar individualmente para cada método.
Artigo 7: Inclua falha na mensagem de captura de mensagem detalhada
Em resumo, quando personalizamos ou lançamos uma exceção, devemos incluir informações relacionadas à falha.
Quando um programa falha devido a uma exceção não capturada, o sistema imprimirá automaticamente o rastreamento da pilha da exceção. Contém uma representação de string da exceção na faixa da pilha. Normalmente, ele contém o nome da classe da classe de exceção, juntamente com a mensagem detalhada a seguir.
Artigo 8: lute para manter a atomicidade na falha
Quando um objeto lança uma exceção, sempre esperamos que o objeto permaneça em um estado bem definido disponível. Isso é especialmente importante para a exceção verificada, porque o chamador geralmente espera se recuperar da exceção verificada.
De um modo geral, uma chamada de método com falha deve manter o objeto em "seu estado antes de ser chamado". Os métodos com esses atributos são chamados de "falha atômica". Pode -se entender que a falha ainda mantém atomicidade. Existem várias maneiras de um objeto manter "falha na atomicidade":
(1) Projete um objeto não mutável.
(2) Para métodos que executam operações em objetos mutáveis, a maneira mais comum de obter "falha na atomicidade" é verificar a validade dos parâmetros antes de executar a operação. Como segue (método pop em Stack.java):
public Object pop () {if (size == 0) lança new emptyStackStackException (); Resultado do objeto = elementos [-tamanho]; elementos [tamanho] = nulo; resultado de retorno;} (3) Semelhante ao método anterior, o processamento do cálculo pode ser ajustado para que qualquer falha possível da parte de cálculo ocorra antes que o estado do objeto seja modificado.
(4) Escreva um código de recuperação para explicar as falhas durante a operação e fazer com que o objeto volte ao estado antes do início da operação.
(5) Execute uma operação em uma cópia temporária do objeto e, após a conclusão da operação, copie o resultado na cópia temporária para o objeto original.
Embora "manter a falha na atomicidade de um objeto" seja o objetivo desejado, nem sempre é possível. Por exemplo, se vários threads tentarem acessar um objeto simultaneamente sem mecanismos de sincronização adequados, o objeto poderá ser deixado em um estado inconsistente.
Mesmo em situações em que "falha na atomicidade" podem ser alcançadas, nem sempre é esperado. Para algumas operações, pode aumentar significativamente a sobrecarga ou a complexidade.
A regra geral é: como parte da especificação do método, nenhuma exceção deve alterar o estado do objeto antes de chamar o método. Se essa regra for violada, a documentação da API deve indicar claramente em que estado o objeto estará.
Artigo 9: Não ignore exceções
Quando um designer de API declara que um método lançará uma exceção, ele está tentando ilustrar algo. Então, por favor, não ignore! O código para ignorar exceções é o seguinte:
tente {...} catch (alguma exceção e) {} Um bloco de captura vazio fará com que a exceção não atinja seu devido objetivo. O objetivo da exceção é forçá -lo a lidar com condições anormais. Ignorar uma exceção é como ignorar um sinal de alarme de incêndio - se o sinal de alarme de incêndio estiver desligado, ninguém verá o sinal de alarme de incêndio quando o incêndio real ocorrer. Portanto, pelo menos o bloco de captura deve conter uma descrição que explique por que é apropriado ignorar essa exceção.