terça-feira, 27 de julho de 2010

[Java] Descrever programas para manipular suas próprias exceções


Na linguagem de programação Java, o programador pode definir suas próprias exceções. Exceções definidas pelo programador são criadas estendendo-se a classe Exception. As classes de exceções contêm os mesmos membros que a classe “normal” contém. Abaixo segue um exemplo de uma classe de exceção definida pelo programador que contém um construtor, algumas variáveis e métodos.
public class ServerTimedOutException extends Exception {
private int porta;

// Usa o método getMessage da classe Exception para retornar o motivo da exceção
public ServerTimedOutException(String m, int p) {
super(m);
this.porta = p;
}

public int getPorta() {
return porta;
}
}

Assim, para lançar essa exceção recém criada, use a seguinte forma:
throw new ServerTimedOutException("Não foi possível conectar ao servidor", 80);
Para demonstrar como utilizar esta nova exceção, veja o exemplo a seguir. Considere um programa cliente-servidor. Nele, se houver uma tentativa de conexão e o servidor não responder por mais de 5 segundos será lançada a exceção criada acima. Assim o exemplo ficaria da seguinte forma:
public void Conecta(Servidor nomeDoServidor) thows ServerTimedOutException {
private boolean conectou;
private int portaConectar = 80;

// abrir é um método fictício para tentar abrir uma conexão e retorna true com sucesso e false caso contrário.
Conectou = abrir(nomeDoServidor, portaConectar);

if(!conectou){
throw new ServerTimedOutException("Não foi possível conectar ao servidor", portaConectar);
}
}

E para capturar a exceção lançada pelo método Conecta:
public void ProcuraServidor() {
try{
Conecta(primeiroServidor);
}
catch(ServerTimedOutException e1){
System.out.printl(“Não foi possível conectar ao primeiro servidor. Tentando conectar ao outro servidor”);
//É possivel fazer try-catch aninhados. Veja abaixo
try{
Conecta(outroServidor);
}
catch(ServerTimedOutException eOutro){
System.out.printl(“Erro: ” + eOutro.getMessage + “ conectando na porta ” + eOutro.getPorta());
}
}
}

[Java] Identificar exceções comuns


A linguagem de programação Java fornece diversas exceções predefinidas. Algumas das exceções mais comuns são:



domingo, 25 de julho de 2010

[Java] Descrever as categorias de exceções


Hierarquia das exceções

Os objetos que herdam da classe Throwable incluem descendentes diretos e indiretos. A figura abaixo ilustra a hierarquia de classes da classe Throwable e suas subclasses mais significativas. A classe Throwable possui dois descendentes diretos: Error e Exception.


Classe Error

A classe Error e suas descendentes representam situações anormais que poderiam acontecer na JVM. Errors acontecem raramente e não devem ser capturados ou lançados por aplicativos.

Classe Exception

A classe Exception e suas descendentes representam situações não comuns que podem ocorrer durante a execução de um programa (Por exemplo, sinais IllegalAccessException indicam que um determinado método não pode ser encontrado, enquanto NegativeArraySizeException indica que um programa tentou criar uma matriz com um tamanho negativo) e podem ser capturadas pelo aplicativo. A maioria dos programas lança e captura objetos que derivam da classe Exception. Uma exceção indica que ocorreu um problema, mas não um problema grave do sistema. A maioria dos programas escritos irá lançar e capturar exceções ao contrário de erros. A subclasse de Exception, RuntimeException, é reservada para as exceções que indicam o uso incorreto de uma API.

Classe RuntimeException

RuntimeException indica um problema de design ou de implementação. Ou seja, ele indica as condições que nunca deve acontecer se o programa está funcionando adequadamente. Uma exceção ArrayIndexOutOfBoundsException, por exemplo, nunca deve ser acionada se os índices de array não estender além dos limites do vetor. Isto também se aplica, por exemplo, a referenciar uma variável de objeto nulo. Como um programa corretamente projetado e implementado nunca enfrenta este tipo de exceção, é de costume deixá-la sem tratamento. Isso resulta em uma mensagem em tempo de execução, e assegura que sejam tomadas medidas para corrigir o problema, ao invés de escondê-lo onde (você acha que) ninguém vai notar.

Exceções verificadas e não verificadas

As exceções em Java diferenciam em dois grupos de exceções: exceções verificadas e exceções não verificadas. As exceções verificadas são aquelas em que é obrigatório capturar ou declarar as exceções, enquanto as não verificadas não possuem a obrigatoriedade de capturar ou declarar as exceções. Se o requisito de capturar ou declarar uma exceção verificada não for satisfeito, o compilador irá retornar uma mensagem de erro informando que a exceção deve ser capturada ou declarada. O tipo de uma exceção é que define se é uma exceção verificada ou não verificada. Assim sendo, veja a figura abaixo (lembrando que nela possuem apenas as classes mais significativas). As subclasses das classes de cor vermelha são consideradas exceções verificadas, as demais são consideradas não verificadas.


Resumindo: Todas as classes que herdam de Exception e não herdam de RuntimeException serão verificadas enquanto todas as classes que herdam de RuntimeException, Error e suas descendentes serão não verificadas.

[Java] Uso das instruções try, catch e finally


O tratamento de exceções em Java ocorre transferindo a execução normal de um programa para o devido código de tratamento de exceções quando uma exceção ocorre. Para realizar o desvio da execução do programa utiliza-se as palavras chaves try, catch ou finally.

Instrução try

A sintaxe da instrução try se dá da seguinte forma:

try {
//código passível de exceções
}


Quando uma exceção é lançada em um bloco try o restante do código após à exceção é ignorado, transferindo o fluxo de execução para o tratamento dessa exceção.

Instrução catch

A instrução que captura (recebe) e trata da exceção lançada pela instrução try é a catch. Cada instrução try pode ter zero ou mais instruções catchs. A sintaxe de uma instrução catch é:

catch(<TipoDaExceção> <exceção>) {
//código de tratamento de exceção
}


Os blocos catchs devem ser posicionados logo após o término do bloco try, sem linhas de comando ou blocos entre eles. Assim como um bloco try e um catch devem ser dispostos juntos, dois blocos catchs também devem seguir este mesmo padrão. Deve-se atentar, porém, para a ordem dos blocos catchs. Blocos catchs devem ser dispostos de forma que primeiro estejam as exceções específicas e por último as gerais, pois quando uma exceção é lançada no bloco try, os blocos catchs são verificados um por um seguindo a sequencia das linhas do código. Assim se uma exceção específica for colocada após uma geral a específica nunca será executada.

Instrução finally

Apesar das instruções try e catch serem ótimas elas não são perfeitas. Quando uma exceção é lançada em uma instrução try, todo o restante do código é descartado e o fluxo é transferido para a instrução catch, assim se for necessário, por exemplo, de uma reinicialização de variáveis, ou desalocação de recursos. A instrução finally resolve este problema, pois será sempre excecutada após os comandos (a qualquer ponto) de um bloco try, lançando ou não uma exceção. A instrução finally é tão poderosa que até mesmo antes de um comando return do bloco try ela é executada. Desta forma, o bloco finally é o perfeito bloco para se fechar sockets, e realizar qualquer operação de limpeza que seu código necessite.

Comando try-catch-finally

Quando um bloco try é escrito, pelo um bloco catch ou finally deverá ser escrito. Como mostra a tabela abaixo:

try {}

-

-

INVÁLIDO

try {}

catch {}

-

VÁLIDO

try {}

finally {}

-

VÁLIDO

try{}

catch {}

finally{}

VÁLIDO

try{}

finally{}

catch {}

INVÁLIDO

catch {}

finally{}

-

INVÁLIDO

catch {}

-

-

INVÁLIDO

finally{}

catch {}

-

INVÁLIDO

finally{}

-

-

INVÁLIDO



[Java] Identificar os usos adequados e inadequados de declarações


Como foi introduzido no post anterior, as declarações ou assertivas são utilizadas para verificar a validade de um programa quanto a falhas de lógica de programação e bugs. A instrução assert é utilizada para fazer a validação das declarações e não modificar o código após a verificação. Para que a instrução assert seja vericada durante a execução do código é necessária a habilitação da opção. Para executar o programa com assertivas digite: java -ea <NomeDaClasse>. A instrução assert avalia uma expressão boolean e determina se é verdadeira ou falsa.

Sintaxe da instrução assert

assert <expressão_booleana>;
assert <expressão_booleana> : &ltexpressão_de_detalhes>;

A primeira forma da sintaxe da instrução assert avalia a expressão booleana e lança um AssertionException se a mesma for false. A segunda forma da sintaxe avalia a expressão booleana e lança um AssertionExeption com a expressão de detalhes como a mensagem de erro se a expressão booleana for false.

Uso adequado de declarações

Utilizar declarações para documentar e validar a lógica interna de um único método:
  • Invariante interno

  • if (x > 0) {
    // Faz uma coisa
    } else {
    assert ( x == 0 );
    // Faz outra coisa, a não ser que x seja negativo
    }


  • Invariante de controle de fluxo

  • switch (naipe) {
    case Naipe.PAUS: // ...
    break;
    case Naipe.OUROS: // ...
    break;
    case Naipe.COPAS: // ...
    break;
    case Naipe.ESPADAS: // ...
    break;
    default: assert false : "Naipe indeterminado!!";
    break;
    }


  • Invariante de pós condição

  • public Object pop() {
    int size = this.getElementCount();
    if (size == 0) {
    throw new RuntimeException("Tentativa de retirar elemento de uma pilha vazia!");
    }

    Object result = /* Código para recuperar o elemento retirado */ ;

    // testa a pós condição
    assert (this.getElementCount() == size - 1);

    return result;
    }
Uso inadequado de declarações
  • Não utilize declarações para verificar os parâmetros de um método público. Bem como para validar parâmetros da linha de comando.

  • public void fazAlgo(int x) {
    assert (x > 0); // INAPROPRIADO!!
    // faz coisas com x
    }


    Como você não possui controle dos códigos que fazem chamada a um método público não é recomendado fazer a verificação dos parâmetros recebidos de um método público, pois a instrução assert pode ser desativada e sua verificação não mais será realizada. Ao invés de usar assert, utilize exceções!

  • Não utilize declarações que possam desencadear efeitos colaterais.

  • public void fazAlgumaCoisa() {
    assert (modificaAlgo());
    // ...
    }
    public boolean modificaAlgo() {
    y = x++;//Mudança no valor de variável
    return true;
    }


    Não é recomendado utilizar declarações que possam desencadear efeitos colaterais, pois a regra geral da utilização da instrução assert é de testar algo e deixar o código exatamente como antes do teste. Instruções assert nem sempre ocorrem, assim é impossível manter o controle do código se a instrução produzir efeistos colaterais.

quarta-feira, 21 de julho de 2010

[Java] Exceções e Declarações (Assertivas)


Exceções

Uma exceção é o evento que ocorre durante a execução de um programa que interrompe o fluxo normal das instruções. Para resolver os problemas ocorridos em uma exceção deve-se fazer o tratamento de exceção. O tratamento das exceções de um programa faz com que o mesmo fique mais robusto e tolerante às falhas. O tratamento de exceções foi projetado para processar erros síncronos (como por exemplo índice fora do intervalo do vetor, estouro aritmético, divisão por zero) e não erros assíncronos (como por exemplo chegadas de mensagens de rede, cliques do mouse), que ocorrem paralelamente com o fluxo de controle do programa e independente dele.

Tratamento de exceções

No tratamento de exceções primeiramente executa-se uma tarefa, depois se testa se a tarefa foi executada corretamente. Se não estiver sido executada corretamente, então se realiza o tratamento da exceção. Porém, mesclar o tratamento de exceções com a lógica do programa pode prejudicar a leitura do código, principalmente se o aplicativo for grande.

Declarações ou Assertivas

Para realizar a depuração de uma classe ou trecho de código, é útil declarar as condições verdadeiras em um certo ponto do código. A essas condições dá-se o nome de Declarações ou Assertivas (Assertions). Elas ajudam a assegurar a validade do programa identificando possíveis erros de lógica e bugs.

[Java] Criar e usar tipos enumerados


Uma enumeração é um tipo especial de classe que é introduzida pela palavra-chave enum e um nome de tipo. Assim como todas as classes as chaves { e } delimitam o corpo da declaração enum. Entre as chaves contém uma lista de constantes de enumeração separadas por vírgulas. Cada identificador em uma enum devem ser únicos.

Como todas as classes, os tipos enum são tipos por referência. Uma declaração do tipo enum pode opcionalmente incluir construtores, campos e métodos.Características da classe enum:
  1. Tipos enum são implicidamente final.
  2. Constantes enum são implicidamente static.
  3. Se tentar criar um objeto de um tipo enum irá resultar em erro de compilação.

Usar tipos enumerados

Segue abaixo exemplo de declaração e utilização de classe enum. Verifique que no exemplo a classe enum possui campo, construtor e método.
//Classe enum Naipe
public enum Naipe {
ESPADAS ("Espadas"),
COPAS ("Copas"),
PAUS ("Paus"),
OUROS ("Ouros");

// Campo
private final String nome;

// Construtor
private Naipe (String nome) {
this.nome = nome;
}

// Método
public String getNome() {
return nome;
}
}

// Classe Baralho
public class Baralho {
private Naipe naipe;
private int valor;

public Baralho(Naipe n, int v) {...}


public Naipe getNaipe() {...}
public int getValor() {...}


public String getNomeNaipe() {...}
}

// Classe TestaBaralho
public class TestaBaralho {
public static void main(String[] args) {

Baralho carta1 = new Baralho(Naipe.ESPADAS, 2);
System.out.println("A carta1 é um " + carta1.getValor()
+ " de " + carta1.getNaipe().getNome());

// Isto não compilará.
// Baralho carta2 = new Baralho(47, 2);
}
}


terça-feira, 20 de julho de 2010

[Java] Abstração e métodos abstratos


Normalmente quando pensamos em classes associamos à possível criação de objetos daquele tipo. Porém exitem casos em que não necessitamos de classes para a criação de objetos daquele tipo. Classes com este propósito são as chamadas classes abstratas. Classes abstratas são comumente utilizadas para servir como "ponto de encontro" entre duas ou mais classes diferentes, porém com algumas características e métodos semelhantes. Para tornar mais claro o entendimento, considere o diagrama UML abaixo:


Neste exemplo a classe abstrata Empregado serviu como ponto de encontro para as classes Assalariado, Comissionado e Horista. Foi omitido do diagrama os métodos get/set.

Uma classe abstrata é composta por PELO MENOS 1 método abstrato. Um método abstrato é aquele que não possui implementação e declarado com a palavra-chave abstract. Toda classe abstrata deve ser declarada usando a palavra-chave abstract. As subclasses de uma classe abstrata DEVEM implementar TODOS os métodos abstratos.

Veja o abaixo a implementação do diagrama UML do exemplo.
// Classe Abstrata
public abstract class Empregado {
private String nome;
private String sobrenome;
private String cpf;
public Empregado(){ }
public Empregado(String n, String s, String c){...}
public String getNome(){...}
public void setNome(String n){...}
public String getSobrenome(){...}
public void setSobrenome(String s){...}
public String getCpf(){...}
public void setCpf(String c){...}
//Método Abstrato
public abstract double salario();
}
//Classe Assalariado
public class Assalariado extends Empregado {
private double salario;

public double getSalario() {...}
public void setSalario(double s) {...}

//Implementa o método abstrato
public double salario() {
return getSalario();
}

}
//Classe Comissionado
public class Comissionado extends Empregado {

private double totalVenda;
private double taxaComissao;
public double getTotalVenda() {...}
public void setTotalVenda(double tv) {...}
public double getTaxaComissao() {...}
public void setTaxaComissao(double tc) {...}

//Implementa o método abstrato
public double salario() {
return getTotalVenda() * getTaxaComissao();
}
}
//Classe Horista
public class Horista extends Empregado {
private double precoHora;
private double horasTrabalhadas;
public double getPrecoHora() {...}
public void setPrecoHora(double ph) {...}
public double getHorasTrabalhadas() {...}
public void setHorasTrabalhadas(double ht) {...}

//Implementa o método abstrato
public double salario() {
return getPrecoHora() * getHorasTrabalhadas();
}
}

Apesar das classes abstratas não puderem ser instanciadas é possível declarar variáveis da classe abstrata para armazenar referências de qualquer classe concreta (contanto que seja uma subclasse dela). E só! :)

[Java] Descrever a operação completa de construção e de inicialização de objeto


A operação de construção e inicialização de objeto já foi tratado neste post.

[Java] Descrever os conceitos de sobrecarga de construtores e método


Este assunto já foi tratado anteriormente por este post.

[Java] Usar os modificadores de acesso protegidos e padrão


Os modificadores de acesso protegido e padrão (protected e default) já foram tratados anteriormente neste post.