No capítulo anterior, vimos que os objetos são
criados (instanciados) a partir de modelos, denominados classes.
Comentamos a necessidade de um método construtor, importante para a inicialização dos objetos.
Também vimos que um programa Java deve obrigatoriamente apresentar um método main em alguma classe. Esse método é responsável pela execução do programa em si, ou seja, a criação de objetos e sua posterior utilização.
Criamos a classe Contador, responsável pelo armazenamento e manipulação de um contador. A partir da classe Contador, foi sugerida a criação de uma classe Relógio, que deveria armazenar e manipular horas, minutos e segundos.
A listagem abaixo sugere uma implementação da classe Relógio.
import java.io.*; class Relogio { private int hora,minuto,segundo; // Construtor public Relogio(int h,int m,int s) { hora = h; minuto = m; segundo = s; } // Incrementa um segundo public void incrementa() { if(++segundo>59) { segundo = 0; if(++minuto>59) { minuto = 0; if(++hora>23) hora = 0; } } } // Decrementa um segundo public void decrementa() { if(--segundo<0) { segundo = 59; if(--minuto<0) { minuto = 59; if(--hora<0) hora = 23; } } } // Informa a hora atual public void informa() { System.out.println("Hora atual: "+hora+":"+minuto+":"+segundo); } public static void main(String args[]) { Relogio r1; r1 = new Relogio(14,15,10); r1.informa(); r1.incrementa(); r1.informa(); for(int c=0;c<20;++c) r1.decrementa(); r1.informa(); } } |
Exercício:
|
2. Experimentando a criação de objetos no BlueJ
A grande vantagem de um ambiente de aprendizado como o BlueJ
é que ele permite o instanciamento de qualquer objeto, independentemente
da presença ou não do método main.
Para verificar isso, crie um novo projeto e digite o programa anterior no BlueJ. A seguir, compile a classe Relogio e clique com o botão direito sobre a caixa que a representa para apresentar o menu:
Devemos agora prestar um pouco mais de atenção no que aparece no menu:
Ao selecionarmos a primeira opção, o BlueJ apresenta a seguinte tela, onde cada campo deverá ser preenchido com os valores numéricos desejados para hora, minuto e segundo. O primeiro campo é o nome da instância, ou seja, o nome de variável desejado. O BlueJ sugere um nome genérico, baseado no nome da classe e em quantas instâncias existem até agora. Portanto, a primeira instância será relogio_1, a segunda será relogio_2 e assim por diante.
Observe que imediatamente será criado o objeto relogio_1 e este deverá estar visível na área inferior da janela:
Se clicarmos com o botão direito sobre o objeto recém criado, serão apresentados todos os métodos public que este oferece:
Experimente selecionar algumas vezes os métodos decrementa() e incrementa(). Depois selecione o método informa() e verifique que os valores foram atualizados corretamente. Note que o método informa() irá causar o surgimento de uma nova janela, a BlueJ terminal window, pois foi utilizado o método println(), que exige a presença de um console (terminal de texto):
Note que a presença do método informa() não é essencial dentro do BlueJ, uma vez que existe a opção Inspect: esta opção exibe em uma janela separada todos os atributos do objeto selecionado:
3. Melhorando a classe Relogio: Sobrecarga
Como pudemos constatar, a classe Relogio funciona muito bem:
armazena corretamente as horas e permite o incremento e decremento de tempo.
Mas seria bastante interessante se fosse possível inicializá-la
sem especificar a hora completa, não ?
Exemplo: queremos acertar o relógio para 14 horas, 0 minutos e 0 segundos:
new Relogio(14,0,0)
Não parece um desperdício sempre utilizar uma chamada tão
longa ?
A linguagem Java oferece um recurso muito interessante para resolver este problema: a sobrecarga de métodos (method overloading). Dizemos que um método está sobrecarregado quando existem várias cópias deste método, cada uma com um conjunto de parâmetros diferentes.
Como exemplo, iremos sobrecarregar o construtor da classe Relogio, fazendo com que o relógio possa ser inicializado de três maneiras diferentes:
import java.io.*; class Relogio { private int hora,minuto,segundo; // Construtores public Relogio(int h,int m,int s) { hora = h; minuto = m; segundo = s; } public Relogio(int h,int m) { hora = h; minuto = m; segundo = 0; } public Relogio(int h) { hora = h; minuto = 0; segundo = 0; } ... |
Se olharmos para esse código com atenção, fica evidente que há uma maneira melhor de inicializar as variáveis. Por exemplo, podemos criar um método acertaHora(h,m,s) e chamá-lo de dentro dos construtores.
A criação deste método ainda tem a vantagem adicional de tornar possível um novo acerto de hora no futuro (após a criação do objeto):
import java.io.*; class Relogio { private int hora,minuto,segundo; // Construtores public Relogio(int h,int m,int s) { acertaHora(h,m,s); } public Relogio(int h,int m) { acertaHora(h,m,0); } public Relogio(int h) { acertaHora(h,0,0); } // Método para acertar o horário public void acertaHora(int h,int m,int s) { hora = h; minuto = m; segundo = s; } |
Para verificar isso, altere a classe de acordo com o código acima. Não esqueça de antes remover o objeto já criado, para evitar problemas... Compile a classe. Agora, se clicarmos novamente com o botão direito sobre ela, serão apresentadas todas as novas opções de construtores. Experimente com alguns valores e verifique que aqueles não informados são inicializados corretamente com 0.
Exercícios:
|
4. Métodos de classe e de instância
A grande maioria das linguagens orientadas a objetos permite
a declaração de dois tipos de métodos: de classe e de instância.
Métodos de instância são aqueles que estão disponíveis
quando existe uma instância, pois manipulam os atributos contidos nesta.
Então, todos os métodos que escrevemos até agora são
de instância.
Mas imagine que queremos estender a classe Relogio, permitindo que ela seja capaz de calcular e retornar a diferença entre duas horas:
class Relogio { ... // Método para calcular a diferença em segundos entre duas horas public int difHora(int h1,int m1,int s1,int h2,int m2,int s2) { int dif; // Calculos ... return dif; } |
Olhando com atenção, notamos que esse método não utiliza nenhum atributo da instância (hora, minuto ou segundo). Então, teoricamente ele poderia ser utilizado independentemente da existência ou não de instâncias, certo ? Para tanto, declara-se o método como static, indicando que é um método de classe, ou seja, não depende de nenhum atributo de instância:
class Relogio { ... // Método para calcular a diferença em segundos entre duas horas public static int difHora(int h1,int m1,int s1,int h2,int m2,int s2) { int dif; // Calculos (invente alguma coisa) ... return dif; } |
Ao compilarmos novamente a classe (você evidentemente já digitou o código, certo ?) e clicarmos sobre a caixa, verificamos que o método difHora(...) está disponível juntamente com os demais métodos, ou seja, pode ser utilizado mesmo sem que exista uma instância:
Para utilizar o método de classe em qualquer parte de um programa Java, utilize a sintaxe abaixo, ou seja, NomeDaClasse.metodo(..)
... public static void main(String args) { ... int dif; dif = Relogio.difHora(15,10,5,20,10,2); System.out.println("A diferença entre as duas horas é: "+dif); ... } |
Se você prestar atenção, verá que já usava um método de classe sem saber: System.out.println(...) é uma chamada para o método println(...), dentro do objeto "out", presente na classe System (funções gerais do sistema).
5. Variáveis de classe e de instância
Todas as variáveis que usamos até agora são
variáveis de instância, ou seja, existe uma cópia de cada
variável em cada instância que criamos. Cada uma pode armazenar
um valor diferente, dependendo da instância. Porém, assim como
existem métodos de classe, também existem variáveis
de classe. Para que serviria uma variável
de classe ?
Ei, não seria ótimo se cada instância
da classe Relogio soubesse quantas outras existem ?
A melhor maneira de fazer isso é declarar uma variável de classe (static) para armazenar a quantidade de instâncias já criadas.
E como fazer isso ?
Simples, inicializamos esta variável com zero (na declaração) e no construtor incrementamos esse valor. Como é uma variável de classe, quando a utilizarmos em diversas instâncias, na verdade estaremos atualizando a mesma variável:
class Relogio { private int hora, minuto, segundo; private static int total = 0; // qtd. de relogios // Construtores public Relogio(int h,int m,int s) { total++; acertaHora(h,m,s); } ... // idem para os demais construtores |
Para acreditar em mim, crie uma porção de relógios e inspecione o conteúdo das variáveis de qualquer um deles. Você deverá ver algo como:
Repare que a variável total aparece separada, pois é um static field, ou seja, uma variável de classe. Variáveis de classe também são comumente utilizadas para armazenar constantes úteis ao programa inteiro, por exemplo, armazenar a quantidade de horas em um dia:
class Relogio { private int hora, minuto, segundo; private static int total = 0; // qtd. de relogios public static final int HORAS_DIA = 24; ... |
Lembre-se que constantes são declaradas com o modificador final.
Se queremos que uma constante possa ser utilizada por outras
classes (como no exemplo acima), a declaramos como public.
Caso contrário, só será utilizável nesta classe.
Exercício:
|
Não OUSE deixar o laboratório se hover alguma dúvida com relação a:
|