• Nenhum resultado encontrado

ESCREVENDO MELHORES CÓDIGOS

No documento LINGUAGEM DE PROGRAMAÇÃO I (páginas 53-91)

Objetivos

Aprender como tratar erros em Java.

Entender a diferença entre exceções checadas e não checadas.

Aprender o que são pacotes.

Saber como trabalhar com arquivos em Java.

Exceções

Uma exceção em Java é um evento que ocorre durante a execução normal do programa, fazendo com que o fluxo de execução do programa seja quebrado.

O que acontece em Java é que quando um método encontra um erro, ele cria um objeto para cuidar deste erro, este objeto contém informações sobre o erro, seu tipo e o estado do programa quando o erro aconteceu.

Depois que o método cria este objeto, ele repassa para o sistema de execução do Java (runtime). Esse processo de criar e repassar este objeto de erro para o runtime é chamado de levantar uma exceção (throwing an

exception).

Quando o runtime recebe este objeto, ele tenta achar algum método que trate esta exceção. Os possíveis métodos que podem tratar esta exceção deve estar na pilha de execução, isto é, deve ser um método que tenha chamado direta ou indiretamente o método levantou a exceção.

A execução de qualquer programa em Java inicia no main. Dentro do main é feita uma chamada a um método metA que por sua vez chama um método metB que então chama um método metC onde ocorreu um erro. Veja na figura 6.1 estas chamadas.

O processo natural seria quando o método metC terminasse naturalmente, então a execução voltaria para o método metB, este método então continuaria a sua execução até que em um determinado momento ele

54

Informática | Linguagem de Programação I | Volume 02

terminaria e aí a execução iria para o método metA. Este método executaria até que terminasse e então a execução voltasse para o main. Observe que poderia haver mais chamadas de métodos neste processo, isto é, o método metB poderia chamar os métodos metD e metE, antes de terminar e a execução voltar para o método metA.

Figura 6.1:Pilha de execução – seta significa chamada a um método

Fonte: Conteudista

Na figura 6.2 é possível ver como este processo de erro é executado. No método metC um erro foi gerado, o método é encerrado de forma exceptional e a execução volta para o método metB procurando se ele possui um tratamento para o erro ocorrido no método metC, veja a seta vermelha representando esta volta a partir de um encerramento anormal. Como neste exemplo o método metB não possui nenhum tratamento de erro, ele também é encerrado de forma excepcional e a execução volta para o método metA (outra seta vermelha). Como o método metA possui um tratamento de exceção, então as devidas providências são tomadas, e o método metA continua a sua execução normal, até que finalmente ele termine naturalmente e a execução volte para o main (veja a seta preta).

55

Informática | Linguagem de Programação I | Volume 02

Figura 6.2Pilha de execução – retorno (exceptional ou normal) dos métodos Fonte: Conteudista

Se por um acaso, na pilha de execução não houvesse nenhum método que fizesse o tratamento do erro, então o programa terminaria abruptamente. Portanto, sempre é interessante tratar erros nos códigos em Java para que nunca o programa termine de forma excepcional.

A sequência exibida nas figuras 6.1 e 6.2 podem ser codificadas de maneira bem simplificada como apresentada na figura 6.3. Na linha 6 é realizada a chamada do método metA, então na linha 11 é feita a chamada de metB e na linha 18 é feita a chamada de metC. Dentro do método metC (linhas 21 a 23) um erro é lançado, e não é feito nenhum tratamento dentro de metC, portanto é preciso informar que ele pode levantar uma exceção (veja na linha 21 o comando throws Exception). Como o método metB também não faz nenhum tratamento de exceção, então é necessário também colocar a informação throws Exception (linha 17). O método metA realiza o tratamento de erros utilizando o comando try/catch (linhas 10 a 14).

56

Informática | Linguagem de Programação I | Volume 02

Figura 6.3: Captura de tela do Netbeans 6.8: Código referente ao fluxo exibido nas figuras 6.1 e 6.2 Fonte: Conteudista

Algumas considerações a serem feitas sobre o código da figura 6.3: não é necessário que sejam métodos de classe, poderiam ser métodos de objetos e também podem ser utilizadas outros tipos de exceções (não apenas a da classe Exception).

Algumas exceções e erros são levantadas pelas próprias classes do Java (e sua API), como por exemplo, se você fizer uma divisão de dois números e o denominador for zero, então será lançada automaticamente uma exceção aritmética, ou então se você tentar gravar em um arquivo somente para leitura será lançada uma exceção de entrada e saída. Porém, outras exceções são levantadas pelo próprio programador do método, quando detecta uma situação anormal.

No código da figura 6.4 é apresentado um programa que realizada a leitura de um valor inteiro (linha 12) e um valor real (linha 14). Porém se o usuário digitar um valor incompatível com o que está sendo esperado o

57

Informática | Linguagem de Programação I | Volume 02

programa será encerrado prematuramente com uma mensagem de erro como exibida na figura 6.5.

Figura 6.4Captura de tela do Netbeans 6.8: Exemplo de leitura de valores numéricos Fonte: Conteudista

Na primeira linha em vermelho da figura 6.5 é possível ver que ocorreu uma exceção do tipo InputMismatchException, pois o usuário digitou a palavra “olá!” quando estava se esperando um número inteiro.

Figura 6.5 Captura de tela do Netbeans 6.8: Execução do programa da figura 6.4 com uma digitação inválida Fonte: Conteudista

Então, ao invés de deixar o programa encerrar de maneira abrupta é possível realizar o tratamento de erro utilizando o comando try/catch. A seguir está a sua sintaxe:

58

Informática | Linguagem de Programação I | Volume 02

try {

// código que pode gerar erros

} catch (TipoDaExcecao nomeDaVariavel) {

// código que será executado se o erro acima

ocorrer

}

A figura 6.6 apresenta o código da figura 6.4 adicionando a cláusula try/catch. Dentro da clásula try é colocado o código que pode gerar algum erro, porém assumindo o fluxo normal do programa, isto é, neste exemplo, é solicitada a digitação de um número inteiro, depois um real e finalmente estes dois números são impressos na tela (linhas 13 a 17). Dentro da clásula catch é colocado o código a ser executado caso aconteça o erro (linha 19) especificado pelo catch (linha 18).

Figura 6.6Captura de tela do Netbeans 6.8: Tratando a exceção InputMismatchException Fonte: Conteudista

59

Informática | Linguagem de Programação I | Volume 02

Agora se o usuário digitar um valor inválido não aparecerá mais a mensagem de erro exibida na figura 6.5, aparecerá a mensagem: “Exceção capturada: ...” conforme especificada na linha 19 da figura 6.6 e o programa encerrará de forma normal. Veja na figura 6.7 a saída da execução do programa da figura 6.6 onde o usuário digita um valor inválido.

Figura 6.7 Captura de tela do Netbeans 6.8: Execução do programa da figura 6.6 com uma digitação inválida Fonte: Conteudista

Uma outra vantagem em utilizar exceções em no código é porque torna o fluxo do programa bem mais fácil de entender, pois você não precisa ficar colocando um monte de if/else no meio do seu código só para verificar se ocorreu ou não um erro. Observe na figura 6.8 uma nova execução do código da figura 6.6, porém agora o usuário digita um valor inteiro válido e um valor real inválido – o real digitado é inválido, pois foi digitado com ponto (.), ao invés de vírgula (,). Não foi necessário colocar um if/else para a digitação do inteiro e outro if/else para a digitação do número real para testar se os valores foram corretos ou não. Pois, utilizando o tratamento de exceção, quando há um erro em qualquer parte do código da cláusula try, a execução é interrompida naquele ponto (sem executar o resto do código que está dentro da cláusula try), e passa-se a executar o código da cláusula catch correspondente ao erro capturado.

Figura 6.8 Captura de tela do Netbeans 6.8: Outra execução do programa da figura 6.6 com uma digitação inválida

60

Informática | Linguagem de Programação I | Volume 02

Fonte: Conteudista

O código da figura 6.9 foi baseado no da figura 6.6, porém agora as duas variáveis são inteiras (linha 9) e foi adicionada uma divisão (linha 16). Portanto, é possível ter o problema de divisão por zero dependendo do valor que o usuário digitar.

<Saiba Mais:

Quando você estiver utilizando o tipo float ou double, não haverá um exceção informando que houve uma divisão por zero, pois quando um número for dividido por zero o resultado será Infinity. Se por acaso, houver a tentativa de dividir zero por zero, então o resultado será NaN (abreviação de Not a Number, que significa Não é um Número). Isto é assim, pois como float e double tem representação para estes casos especiais, então nenhuma exceção é levantada.

É preciso ficar atento para estes casos, pois para isto, seria melhor utilizar um if para saber se o divisor é diferente de zero.>

Para resolver este problema, foi adicionada uma segunda cláusula catch (linhas 19 a 21). É possível adicionar quantas cláusulas catch quanto forem necessárias, porém é bom saber que se você quiser utilizar vários tipos de exceções, é importante que se uma for subclasse da outra, primeiro você inclua a subclasse e depois a superclasse, senão nunca a subclasse será utilizada.

61

Informática | Linguagem de Programação I | Volume 02

Figura 6.9 Captura de tela do Netbeans 6.8: Utilizando várias cláusulas catch Fonte: Conteudista

Na figura 6.10 está a saída da execução do programa da figura 6.9, onde o usuário digitou zero para o segundo número. Veja que apareceu o nome completo da exceção (java.lang.ArithmeticException) e o problema que aconteceu (/ by zero).

Figura 6.10 Captura de tela do Netbeans 6.8: Saída do programa 6.9 Fonte: Conteudista

Como você viu, o código conseguiu ser executado tanto quando você tratou o erro, quanto quando você não tratou esta exceção. Este tipo de exceção é chamada de não checada (unchecked exceptions), isto é, o

62

Informática | Linguagem de Programação I | Volume 02

compilador não obriga o programador a tratar este tipo de exceção, pois são as runtime exceptions.

Existem outros tipos de exceções que o compilador obriga que o programador trate ou repasse a exceção. Elas são as chamadas exceções checadas (checked exceptions) e precisam necessariamente estender a classe Exception (ou uma subclasse dela que não seja subclasse de RuntimeException).

Veja na figura 6.11 a hierarquia de classes relacionadas com tratamento de erros em Java. A classe Throwable é a classe pai de todas os erros e exceções que podem acontecer em Java. A classe Error é a classe pai de todos os erros que podem acontecer, normalmente quando um erro acontece (normalmente relacionado com a máquina virtual Java) o programa é abortado prematuramente e o programador não tem como prever este tipo de problema, portanto não são checados pelo compilador.

A classe Exception é a classe pai de todas as exceções e são sempre checadas pelo compilador, com a exceção da classe RuntimeException e suas subclasses. As exceções do tipo RuntimeException não são checadas, pois normalmente são ocasionadas por erros de programação que poderiam ser evitados, como por exemplo, acessar um ponteiro nulo, ou acessar uma posição inválida de um vetor ou matriz.

Isto é, se o programador escreveu o seu código corretamente, então nunca deveria ocasionar uma exceção do tipo RuntimeException, e por isso o compilador não checa estes tipos de exceções.

As exceções que são checadas, são aquelas exceções que são subclasses da classe Exception e acontecem por problemas que podem ser previstos. Por exemplo, ao tentar abrir um arquivo para escrita podem existir alguns problemas como: disco cheio e arquivo só para leitura. Portanto, estes erros devem ser tratados no seu código e para reforçar que o programador irá tratá-los o compilador checa estas exceções e gera um erro de compilação quando o programador não as trata ou as repassa.

63

Informática | Linguagem de Programação I | Volume 02

Figura 6.11 Parte da hierarquia de exeções em Java Fonte: Conteudista

Portanto, quando você estiver desenvolvendo a sua aplicação, você extenderá a classe Exception para criar as suas próprias exceções.

Voltando ao exemplo da classe Televisao, quando o usuário tentar trocar de canal, ou mudar o volume da televisão com ela desligada uma exceção será levantada. Então, para isto é necessário criar uma classe que estende a classe Exception. Veja na figura 6.12 a classe ExcecaoTelevisaoDesligada. A classe Exception possui um atributo que armazena um mensagem informativa sobre o erro. Portanto, a classe ExcecaoTelevisaoDesligada utiliza o construtor da classe Exception para incializar este atributo com a mensagem “Televisao desligada” concatenada com uma informação extra que foi passada para este construtor (linha 5).

Figura 6.12 Captura de tela do Netbeans 6.8: Classe ExcecaoTelevisaoDesligada Fonte: Conteudista

64

Informática | Linguagem de Programação I | Volume 02

Depois de criada a exceção é necessário utilizá-la nos métodos aumentarCanal e diminuirCanal, entre outros.

Veja a nova implementação do método aumentarCanal na figura 6.13. Na linha 73 aparece a informação que este método pode levantar uma exceção chamada ExcecaoTelevisaoDesligada.

O teste é realizado na linha 74 para saber se a televisão está ou não ligada, caso a televisão esteja desligada então uma exceção é levantada

(linha 75). Observe que foi criado um objeto da classe

ExcecaoTelevisaoDesligada passando como parâmetro para o construtor a frase “ao tentar aumentar o canal” que completa a informação de porque a exceção foi levantada, então a frase completa que estará armazenada será “Televisão desligada ao tentar aumentar o canal”, e ela será exibida quando o usuário tentar aumentar o canal com a televisão desligada. Esse objeto que foi criado e que encapsula as informações do erro ocorrido é então levantado (throw) e pode ser visto na linha 75. Preste bem atenção para não confundir o throws utilizado na linha 73 com o throw da linha 75, o primeiro (com a letra „s‟ no final) é utilizado nas assinaturas dos métodos e são apenas para informar que o método gera ou repassa aquele erro, enquanto que o throw (sem o „s‟) é utilizado onde aconteceu o problema e serve para levantar uma exceção.

Figura 6.13 Captura de tela do Netbeans 6.8: Método aumentarCanal levantando uma exceção Fonte: Conteudista

Como o método aumentaCanal faz parte da interface TV, então é necessário alterar a interface TV para que os seus métodos informem que podem levantar a exceção ExcecaoTelevisaoDesligada. Basta adicionar à todos os métodos que levantam esta exceção o seguinte complemento: “throws ExcecaoTelevisaoDesligada”.

65

Informática | Linguagem de Programação I | Volume 02

Observe na figura 6.14 um main que utiliza uma cláusula try/catch ao utilizar um objeto da classe Televisao. Na linha 8 a televisão é criada, e como padrão, ela é criada desligada. Ela é então ligada (linha 9) tem o seu canal aumentado (linha 10) e o seu volume alterado para 7 (linha 11). A televisão é desligada (linha 12) e há uma tentativa de aumentar o seu canal na linha 13, porém uma exceção é gerada pelo método aumentarCanal e o fluxo é direcionado para o catch da linha 15 e a mensagem da linha 16 é então exibida para o usuário.

Figura 6.14 Captura de tela do Netbeans 6.8: Teste gerando a ExcecaoTelevisaoDesligada Fonte: Conteudista

Na figura 6.15 é exibida a saída da execução do programa da figura 6.14. Note que a mensagem da linha 14 da figura 6.14 não foi impressa na tela, pois como aconteceu a exceção na linha 13, o fluxo foi execução foi direto para a linha 15 por causa do catch.

Figura 6.15 Captura de tela do Netbeans 6.8: Saída do programa da figura 6.14 Fonte: Conteudista

66

Informática | Linguagem de Programação I | Volume 02

Utilizando este tipo de exceção que herda da classe Exception, se você esquecer de informar no método que ele pode levantar aquela exceção, como ela é checada, então o seu programa nem compila.

Como dá mais trabalho utilizar exceções checadas, pois é necessário utilizar try/catch ou escrever os throws nas assinaturas dos métodos para fazer o programa compilar, então muitos programadores se sentem tentados a utilizar a classe RuntimeException ou uma subclasse dela, pois como não são checadas, não é necessário ter tanto trabalho.

Porém, a recomendação é que você não utilize RuntimeException, pois deixam uma falsa ilusão que o seu código não possui métodos que levantam exceções. Quem ler o seu código vai ter a impressão que não são levantadas exceções, pois não tem escrito isto nas assinaturas dos métodos. Uma vantagem de utilizar as exceções checadas é justamente porque o compilador lhe avisa que você esqueceu de uma exceção, então ou você irá tratá-la ou explicitamente dirá que não vai tratar (através do throws na assinatura do método).

Para terminar o assunto de exceções, existe uma outra cláusula que pode ser utilizada com o try/catch que é a finally. Esta cláusula é adicionada ao final de todas as catch que existam e o código que tiver dentro dela sempre será executado, independentemente se houve ou não alguma exceção.

Veja o código da figura 6.16 para ver o funcionamento da cláusula finally. Ao executar este programa será impressas as mensagens das linhas 16, 18 e 20. A linha 16 mostra a mensagem do erro ocorrido, enquanto que a mensagem da linha 18 é sempre apresentada, independentemente se houve ou não um erro e se este erro foi ou não tratado. Neste exemplo, ocorreu um erro e ele foi tratado através da cláusula catch da linha 15. Como o erro foi tratado, então prossegue a execução do código após o try/catch/finally e a mensagem da linha 20 também é impressa na tela.

67

Informática | Linguagem de Programação I | Volume 02

Figura 6.16 Captura de tela do Netbeans 6.8: Utilização da cláusula Finally Fonte: Conteudista

Na figura 6.17 você pode ver a saída da execução do programa da figura 6.16, constatando que foram exibidas as mensagens das linhas 16, 18 e 20 deste programa.

Figura 6.17 Captura de tela do Netbeans 6.8: Saída do programa da figura 6.16 Fonte: Conteudista

Imagine que na linha 11 da figura 6.16 substituíssemos o número “7” pela divisão “7 / 0”, então acontecerá um erro que não foi tratado e por isso o main terminará prematuramente e não executará o que vier após o try/catch/finally e assim não imprimirá a mensagem da linha 20. Porém, a mensagem da linha 18 será impressa mesmo assim, pois o código da clásula finally sempre é executado. Veja na figura 6.18 a execução do programa da figura 6.16 com esta modificação e constate que apenas a mensagem da

68

Informática | Linguagem de Programação I | Volume 02

linha 18 foi impressa, além da exceção aritmética divisão por zero não tratada (mensagem em vermelho).

Figura 6.18 Captura de tela do Netbeans 6.8: Saída do programa da figura 6.16 modificado Fonte: Conteudista

<Atividades de aprendizagem:

1. Crie no seu projeto a classe ExcecaoTelevisaoDesligada definida na figura 6.12.

2. Altere a interface TV para que ela possua nas assinaturas dos métodos necessários os trhows ExcecaoTelevisaoDesligada.

3. Altere a classe Televisao para levantar a exceção ExcecaoTelevisaoDesligada em todos os métodos necessários, como foi feito no método aumentarCanal exibido na figura 6.13.

4. Crie um main que utilizando try/catch/finally teste as condições de erros como foi feito no main exibido na figura 6.16.

5. Crie a exceção ExcecaoTelevisaoLigada. Ela será utilizada sempre que o programador tentar alterar a marca ou o modelo da televisão quando ela estiver ligada. Portanto, altere a interface TV e os métodos setMarca e setModelo para levantar esta exceção quando necessário.>

Pacotes

Pacotes em Java servem para agrupar classes e interfaces que estão interrelacionadas. Além de facilitar a organização no sistema de arquivos em pastas, também as protegem as classes e interfaces de acesso indevido.

Imagine as classes exibidas na figura 5.19, elas estão relacionadas e poderiam estar todas num pacote chamado grafico. Ao criar uma aplicação Java no Netbeans chamada “UtilizandoPacotes” é criado um projeto que tem

69

Informática | Linguagem de Programação I | Volume 02

já a classe principal chamada utilizandopacotes.Main. Para criar neste projeto um pacote chamado grafico, veja o passo-a-passo nas figuras de 6.19 a 6.21.

A figura 6.19 mostra como criar um pacote: você deve clicar com o botão direito do mouse, sobre “Pacotes de código-fonte”, apontar com o mouse sobre a opção “Novo” que abrirá um outro submenu que você deverá clicar com o botão esquerdo do mouse sobre a opção “Pacote Java”.

Figura 6.19 Captura de tela do Netbeans 6.8: Criando um novo pacote Fonte: Conteudista

Automaticamente é aberta a caixa de diálogo “Novo Pacote Java” apresentada na figura 6.20, onde você deverá escrever o nome do pacote, neste exemplo, “grafico” (sem acento, pois não é recomendado utilizar caracteres especiais). Para que o pacote seja realmente criado você deve clicar no botão Finalizar.

O pacote recém-criado ainda não possui nenhuma classe nem interface, por isso ele fica com o ícone cinza (veja na figura 6.21). Portanto, você irá criar dentro deste pacote as classes apresentadas na figura 5.19. Para isto, a figura 6.21 mostra como criar uma classe dentro daquele pacote. Basta clicar com o botão direito sobre o nome do pacote, apontar para a opção “Novo” e depois selecionar “Classe Java”.

70

Informática | Linguagem de Programação I | Volume 02

Figura 6.20 Captura de tela do Netbeans 6.8: Caixa de diálogo Novo Pacote Java

Fonte: Conteudista

Figura 6.21 Captura de tela do Netbeans 6.8: Criando uma classe dentro do pacote graficos Fonte: Conteudista

71

Informática | Linguagem de Programação I | Volume 02

Quando forem criadas todas as classes da figura 5.19 o seu projeto ficará como apresentado pela figura 6.22.

Figura 6.22 Captura de tela do Netbeans 6.8: Projeto UtilizandoPacotes com seus pacotes e classes

No documento LINGUAGEM DE PROGRAMAÇÃO I (páginas 53-91)

Documentos relacionados