Mostrando postagens com marcador Java. Mostrar todas as postagens
Mostrando postagens com marcador Java. Mostrar todas as postagens

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.

sexta-feira, 7 de maio de 2010

[Java] Definir herança, polimorfismo, sobrecarga, sobrescrita e invocação de método virtual (parte 3/3)


Sobrecarga de métodos

O tema da sobrecarga de métodos já foi tratado neste post.

Sobrescrita de métodos

Sobrescrever um método é a possibilidade de uma subclasse (classe filha) modificar todo ou parte do comportamento herdado de sua superclasse (classe pai). Para sobrescrever um método basta utilizar o mesmo nome, tipo de retorno e os argumentos do método da superclasse e realizar as devidas modificações (customizações) na subclasse.

Se um método de uma superclasse for declarado como private, o mesmo não poderá ser sobrescrito pelas suas subclasses, pois membros de uma subclasse não acessam, membros private diretamente de sua superclasse.

Métodos sobrescritos não podem reduzir a acessibilidade, ou seja, se um método foi declarado como public na superclasse o método sobrescrito não pode ser declarado como private na subclasse.

Um método ou atributo de uma superclasse (em qualquer nível) pode ser invocado utilizando-se a palavra-chave super. Exemplo:

public class Empregado {

private String nome;
private double salario;
private Date dataNasc;

public String getInformacoes() {
return "Nome: " + nome
+ "\nSalario: " + salario;
}
}

public class Gerente extends Empregado {

private String departamento;

public String getInfomacoes() {
// chamada à superclasse
return super.getInformacoes()
+ “\nDepartamento: " + departamento;
}
}

Invocação de método virtual

Assumindo o exemplo anterior (Empregado e gerente), e que:
Empregado e = new Empregado();
Gerente g = new Gerente();

Se for invocado e.getInformacoes e g.getInformacoes serão invocados diferentes métodos. Para o objeto do tipo Empregado será chamado o método getInformacoes da classe Empregado enquanto para o objeto do tipo Gerente será chamado o método getInformacoes da classe Gerente. Isto parece óbvio, certo? O que na verdade não é tão óbvio é o que acontece quando tem-se a seguinte situação:
Empregado e = new Gerente();
e.getInformacoes();

O comum seria pensar que o método invocado nesta situação seria aquele da classe Empregado. Não o(a) culpo por pensar assim (eu também pensei assim por um tempo ;D ), mas neste caso recebe-se o comportamento associado com o tipo em tempo de execução da variável, ou seja, o tipo do objeto referenciado pela variável (neste caso seria Gerente) e não com o tipo em tempo de compilação da variável. Esta característica é muito importante em linguagens orientadas a objetos geralmente referida como invocação virtual de método (virtual method invocation).

quinta-feira, 6 de maio de 2010

[Java] Definir herança, polimorfismo, sobrecarga, sobrescrita e invocação de método virtual (parte 2/3)


Polimorfismo

Em POO (programação orientada a objetos), o polimorfismo é a capacidade de permitir que referências de tipos de classes mais abstratas representem o comportamento das classes concretas que elas referenciam. Enquanto um objeto tem apenas uma forma (aquela que é dada quando ele é construído) uma variável é polimórfica porque pode referenciar a objetos de diferentes formas. A linguagem de programação Java, como a maioria das linguagens orientadas a objeto, permite o polimorfismo. Utilizando o exemplo do post anterior:

Animal anim = new Cavalo();

Usando a variável acima anim da forma que está você só conseguirá acessar as partes do objeto que forem parte de Animal, as partes que forem específicas de Cavalo estaram escondidas. Isto acontece porque para o compilador a variável anim é um Animal e não um Cavalo. Assim o exemplo a seguir não é válido:

anim.raca = "Mangalarga";

Coleções heterogêneas

Em Java também é possível criar coleções de objetos que tem uma classe em comum. Essas coleções são chamadas de coleções homogêneas. Exemplo:
Agenda[] nome = new Agenda[3];
nome[0] = new Agenda("Carina");
nome[1] = new Agenda("Cristina");
nome[2] = new Agenda("Juliana");
A linguagem de programação Java tem uma classe chamada Object e todas as classes em Java extendem essa classe Object. Assim você pode fazer coleções de todos os tipos de elementos devido ao polimorfismo. Essas coleções de vários tipos são chamadas coleções heterogêneas. Exemplo:
Animal[] habitat = new Animal[3];
habitat[0] = new Peixe();
habitat[1] = new Cavalo();
habitat[2] = new Aguia();
Pode parecer estranho criar um objeto do tipo Cavalo e usar uma variável do tipo Animal para referenciá-lo, mas isto é possivel em Java e você poderá querer ter este efeito.

Argumentos Polimórficos

Um método pode ser escrito para receber como parâmetro um objeto de uma superclasse, no nosso exemplo a classe Animal, e receber sem problemas qualquer objeto da subclasse que foi declarada como parâmetro do método. Assim, em nosso exemplo, podemos escrever um método que recebe como parâmetro um objeto da classe Animal e receber na verdade um objeto da classe Cavalo como mostra o exemplo:
// Na classe Animal
public String determinaPorte (Animal a){
//Determina a partir do pesoMedio do animal qual é seu porte
}
...

// Em alguma parte de outra classe

Cavalo c = new Cavalo();
...
String porte = determinaPorte (c);
Esta chamada ao método determinaPorte é legal porque Cavalo é um Animal.

Operador instanceof

Sabendo que é possivel passar como parâmetros objetos que sejam filhos daquele determinado na escrita do método, algumas vezes é necessário a determinação de que tipo de objeto é realmente aquele. Para esse propósito temos o operador instanceof. Levando em consideração o diagrama de classe do post anterior, veja o exemplo abaixo:
public void metodoExemplo (Animal a){
if (a instanceof Cavalo){
// Faz alguma coisa com Cavalo
}
if (a instanceof Peixe){
// Faz alguma coisa com Peixe
}
if (a instanceof Aguia){
// Faz alguma coisa com Aguia
}
else {
// Faz alguma coisa com Animal
}
}

Conversão de Objetos

Em circunstâncias que você recebeu uma referência para uma classe pai e já foi determinado usando o operador instanceof que o objeto é, na verdade, uma subclasse particular. É possível restorar a funcionalidade completa daquele objeto utilizando a conversão de objetos (casting). Veja o exemplo abaixo:
public void metodoExemplo (Animal a){
if (a instanceof Cavalo){
Cavalo c = (Cavalo)a; // Casting
}
// Restante do método
}
Como já explicado, se não for feito o cast, ao se tentar executar a.getRaca() iria retornar em erro, pois para o compilador o método getRaca() não é acessível na classe Animal.

Observações sobre a conversão de objetos:
  • A conversão de um objeto de uma subclasse para o tipo de sua classe pai (de baixo para cima) é sempre permitida e de fato não necessita de cast explícito.
  • Para conversões "de cima para baixo" (conversão de um objeto de uma classe pai para uma subclasse) só é possível se a classe destino for alguma subclasse da classe atual.
  • Se o compilador permite o cast, então o tipo do objeto é checado em tempo de execução.

terça-feira, 6 de abril de 2010

[Java] Definir herança, polimorfismo, sobrecarga, sobrescrita e invocação de método virtual (parte 1/3)


Herança

É comum em programação orientada a objetos a criação de um modelo para alguma coisa (por exemplo um Animal), e depois precisar de um modelo especializado do original. Por exemplo, pode ser necessária a modelagem de um Peixe ou de um Cavalo que seriam um tipo de Animal, porém com algumas características a mais ou diferenciadas.

Considere o exemplo abaixo.
public class Animal {

public String habitoAlimentar = "";
public String reproducao = "";
public double pesoMedio = 0.0;

public Animal(){}

public Animal(String habitoAlimentar, String reproducao,
double pesoMedio){
...
}

public String detalhaAnimal(){
...
}
}

public class Cavalo {

public String habitoAlimentar = "";
public String reproducao = "";
public double pesoMedio = 0.0;

public String raca = "";
public String corPelagem = "";

public Cavalo(){}

public Cavalo(String habitoAlimentar, String reproducao,
double pesoMedio, String raca, String corPelagem){
...
}

public String detalhaAnimal(){
...
}

}
Percebe-se pelo exemplo que houve a duplicação de variáveis entre as classes Animal e Cavalo. Além disso pode também existir vários métodos que sejam válidos para ambos. Para evitar esse tipo de repetições desnecessárias é recomendada a criação de subclasses daquelas já existentes (utilização de herança).

Em linguagens orientadas a objeto o mecanismo da herança permite ao programador definir uma classe com termos de uma classe anteriormente definida. Em java é utilizada a palavra-chave extends para este propósito como mostra o exemplo abaixo.
public class Cavalo extends Animal {
private String raca = "";
private String corPelagem = "";
}
Descrevendo a classe Cavalo como "é um" Animal não é apenas um modo conveniente de descrever a relação entre as duas classes faz com que o programador se lembre que Cavalo tem todos os membros, tanto atributos como métodos, da classe pai Animal. Isso também significa que se uma operação é válida para Animal então ela também é válida para Cavalo. Assim se a classe pai Animal possui o método detalhaAnimal a classe Cavalo também a possui.

Veja abaixo como seria o diagrama de classe para as classes Animal e Cavalo usando herança.



Sintaxe para uma classe que herda outra:
[modificador] class [nomeDaClasse] extends [nomeDaClassePai]{
[declarações]
}
Vale lembrar que uma herança em Java os contrutores não são herdados, assim se uma classe pai não houver um construtor padrão (sem parâmetros) a classe filha deve implementar uma chamada ao construtor da classe pai com os parâmetros necessários para o contrutor da classe pai. Exemplo: Se a classe pai possui apenas um construtor na forma pai(int x){} a classe filha deve obrigatoriamente implementar um construtor da seguinte forma: filha(int n){ super(n) }

Herança Simples


A linguagem de programação Java permite que uma classe extenda apenas a uma outra classe. A esta restrição damos o nome de Herança simples. Apesar de possiur a restrição de herança simples, java pode realizar tarefa semelhante a herança múltipla com a utilização de interfaces da seguinte forma:
  • Uma classe implementando várias interfaces
  • Uma interface extendendo várias interfaces
Interfaces

Interfaces são conjuntos de métodos vazios (não implementados) e constantes. As interfaces não possuem atributos e interfaces são, de forma simplificada, como roteiros a serem seguidos pelas classes que a implementam. Quando uma classe implementa certa interface ela deve implementar cada um dos seus métodos.

[Java] Passar argumentos para o método principal para uso em um programa


Após todos estes posts pode-se perceber que a base de um programa Java possui a seguinte estrutura:
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World");
}
}
No blog já foi explicado o significado das palavras-chave public, static e void. Porém ainda não foi explicado sobre as palavras-chave main e este vetor args que é passado como parâmetro para o método main. A palavra-chave main serve para indicar ao compilador que este método chamado main será o executável. O vetor de String args passado como argumento para este método main pode ou não ser utilizado. Para passar argumentos (parâmetros) para o método main basta fazer como no exemplo abaixo.

java HelloWorld [argumento1] [argumento2] [argumento3]

Agora como utilizar este argumento no método main? Muito simples! Como foi dito anteriormente os argumentos são na verdade um vetor de String chamado args, então para utilizá-lo é exatamente da mesma forma que utilizamos matrizes unidimensionais (vetores). Veja um exemplo do uso de argumentos abaixo.
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World");
if (args.length>0)
System.out.println(args[0]);
}
}
Lembre-se sempre de verificar se a posição do vetor de argumentos que você deseja utilizar é existente. Como não é obrigatória a passagem de argumentos, se você em seu programa não fizer essa verificação e tentar utilizar alguma posição não existente o programa retornará um erro de estouro de vetor (Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: [númeroDaPosiçãoInexistente]).

[Java] Criar uma matriz multidimensional


Da mesma forma que funcionam as matrizes de matrizes que vimos neste post. Matrizes multidimensionais são matrizes de matrizes com mesmo tamanho. Ficou complicado? Então observe a figura abaixo.


Para melhorar a visualização, utilizade e usabilidade de uma matriz multidimensional procure entende-la como uma tabela. Assim, esta mesma matriz mostrada acima seria entendida da seguinte forma.


As matrizes com duas e três dimensões são as mais utilizadas no dia-a-dia. Entretanto, matrizes multidimensionais podem ter, obviamente, mais de três dimensões. Para declarar matrizes de três ou mais dimensões deve-se apenas acrescentar mais colchetes em sua declaração. Veja o exemplo abaixo de uma matriz com quatro dimensões para compreender melhor.
int[][][][] matrizQuatroDimensoes = new int [2][2][2][2];
A matriz deste exemplo pode ser vista da seguinte forma.


Matrizes multidimensionais também pode ser declarada e inicializada de forma simplificada. Considerando uma matriz com quatro dimensões, veja o exemplo abaixo.

int matrizQuatroDimensoes[][][][]={ { {{1, 2},{3, 4}},
{{5, 6},{7, 8}}
},
{ {{9, 10},{11, 12}},
{{13, 14},{15, 16}}
}
}
Lembrando que você não precisa colocar tantos espaços e quebra de linhas como foi feito acima. Fiz dessa forma apenas para melhor vizualizar.

quarta-feira, 31 de março de 2010

[Java] Determinar o número de elementos de uma matriz


Para determinar o número de elementos de uma matriz basta verificar o valor da variável de instância lenght (comprimento) da matriz desejada. A variável length armazena o número de elementos de uma matriz.

Exemplo


System.out.println("O número de elementos é: "+ matriz.lenght);

[Java] Explicar como inicializar os elementos de uma matriz


Este assunto já foi abortado neste post.

[Java] Explicar porque os elementos de uma matriz são inicializados


Os elementos de uma matriz devem ser inicializados para "reservar" o espaço de memória da matriz. Quando uma matriz é inicializada, é reservado na memória o espaço necessário para todos os elementos da matriz. Se uma referência a uma antiga matriz for utilizada para reservar uma nova matriz, a anterior será perdida e posteriormente "limpada" da memória pelo garbage collector.

[Java] Declarar e criar matrizes de tipos de primitivas, de classes e de matrizes


Declarando matrizes

Matrizes servem para agrupar valores de dados de um mesmo tipo e finalidade. Como foi visto neste post matrizes podem ser declaradas para qualquer tipo de primitiva, mas além de tipos de primitivas podem também ser declaradas como classes ou também outras matrizes (matriz de matriz).

Quando as matrizes são declaradas cria-se um espaço na memória por referência, como pode ser visto na última figura do post passado.

Declarando matrizes de objetos

Matrizes de objetos funciona da mesma maneira que as matrizes de primitivas, porém ao invés de conter um valor de tipo primitivo, contém um objeto. Veja no exemplo abaixo como declarar, instanciar e inicializar esse tipo de matriz.
Data[] vetorDatas;
vetorDatas = new Data[3];
vetorDatas[0] = new Data(07, 04, 1986);
vetorDatas[1] = new Data(03, 11, 1985);
vetorDatas[2] = new Data(31, 03, 2010);
Matrizes de objetos podem ser declarados, instanciados e inicializados como da forma acima, mas também da seguinte forma:
Data[] vetorData = {new Data(07, 04, 1986),
new Data(03, 11, 1985),
new Data(31, 03, 2010)};
Declarando matrizes de matrizes

Da mesma forma que matrizes de primitivas e matrizes de objetos, funcionam as matrizes de matrizes. Matrizes de matrizes são comumente chamadas também de matrizes multidimensionais. Veja a figura abaixo para visualizar sua utilidade e funcionamento.


No exemplo acima, se tentar acessar a posição vetorDeVetor[1][5] (que não existe) irá retornar uma exceção do tipo java.lang.ArrayIndexOutOfBoundsException. Informando justamente que o índice que foi acessado excede a matriz.