Baixe o app para aproveitar ainda mais
Prévia do material em texto
Aula 10: Relacionamentos entre Classes e entre Objetos Objetivos § Recordando... Nas aulas passadas, criamos algumas aplicações Java em que instanciamos objetos a partir de classes que havíamos previamente declarado. Essas classes possuíam atributos que, após a instanciação dos objetos, eram tratados como variáveis de instância do objeto. Ou seja, cada atributo ou método era, até agora, visto como um atributo ou método do objeto instanciado. Hoje veremos que os atributos e métodos podem pertencer somente à classe do objeto, sendo comuns aos diferentes objetos que tenham sido instanciados de uma mesma classe. Antes, entretanto, vamos reforçar o conceito de operadores unários de incremento e decremento e de operadores compostos de atribuição. § Operadores compostos de atribuição A linguagem Java possui vários operadores compostos de atribuição para abreviar expressões de atribuição. Qualquer instrução na forma variável = variável operador expressão; onde o operador seja +, -, *, / ou %, pode ser escrita na forma: variável operador= expressão; Por exemplo, a instrução: c = c + 3; pode ser abreviada com o operador composto de atribuição de adição: c += 3; Suponha: int c = 3, d = 5, e = 4, f = 6, g = 12; += c += 7 ou c = c + 7 resulta c = 10 -= d -= 4 ou d = d – 4 resulta d = 1 *= e *= 5 ou e = e * 5 resulta e = 20 /= f /= 3 ou f = f / 3 resulta f = 2 %= g %= 9 ou g = g % 9 resulta g = 3 § Reforçar o uso dos operadores compostos de atribuição § Reforçar o uso dos operadores unários de incremento e decremento § Compreender o conceito e o uso de atributos e métodos Static § Reforçar conceitos sobre arrays unidimensionais e multidimensionais § Passar arrays como argumentos de métodos § Entender o conceito de coleções e aprender a usar a classe Arrays § Aprender a criar pacotes de classes § Operadores de Incremento e Decremento Java tem dois operadores unários para adicionar 1 ou subtrair 1 de uma variável numérica – são o operador de incremento unário “++” e o operador de decremento unário “--". Um programa pode incrementar de 1 o valor de uma variável utilizando o operador de incremento, em vez das expressões “c = c+1” ou “c += 1”. Quando o operador de incremento ou decremento é colocado antes de uma variável é chamado operador de pré-incremento ou de pré-decremento, respectivamente. Quando é posto depois, é chamado operador de pós-incremento ou pós- decremento. Pré- incremento ++a Incrementa a de 1 e depois utiliza o novo valor na expressão. Pós- incremento a++ Utiliza o valor atual na expressão e depois incrementa a de 1. Pré- decremento --a Decrementa a de 1 e depois utiliza o novo valor na expressão. Pós- decremento a-- Utiliza o valor atual na expressão e depois decrementa a de 1. A classe Incremento, a seguir, mostra as variações acima: public class Incremento{ public static void main( String args[] ) { int c = 5 , d = 5; // inicia variáveis com 5 System.out.printf( "%d, %d, %d\n", c, c++, c ); System.out.printf( "%d, %d, %d\n", d, ++d, d ); } } § Atributos static Até o momento só havíamos aprendido como definir atributos de instância. Cada objeto tinha seus próprios atributos e uma modificação nos atributos de um objeto não afetava os atributos de outros objetos. Neste tópico iremos apreender como definir atributos de classe. Esses atributos são os mesmos para todos os objetos de uma classe. Eles são, portanto, compartilhados pelos objetos. Uma mudança em um destes atributos é visível por todos os objetos instanciados dessa classe. Atributos de classe também são chamados de atributos static. Para exemplificar, definiremos uma classe Robot que usa um atributo static como se fosse um contador, para saber quantos objetos robots foram criados (instanciados). //Classe Robot class Robot { public int x, y; // posição do robot public static int contador; //contador de instancias public Robot(int x,int y){ this.x = x; this.y = y; contador++; } } A seguir a classe de teste Principal: class Principal { public static void main(String args[]) { Robot.contador=0; //inicializando variavel static Robot r1,r2; System.out.println(Robot.contador); r1 = new Robot(10,12); System.out.println(Robot.contador); r2 = new Robot(11,13); System.out.println(Robot.contador); } //main method } //class Principal Apesar de termos exemplificado com um inteiro, você poderia ter usado uma classe no lugar desse atributo, naturalmente tomando o cuidado de chamar new antes de usá-lo. § Métodos static Métodos static também são chamados de métodos de classes. Estes métodos só podem operar sobre atributos que também sejam static. Assim: public static void metodoA() {... Exemplificando, vamos criar um método static para incrementar o contador: public static void incrementa(){ contador++; } Não esqueça de alterar o construtor para, agora, chamar o novo método: incrementa(); //substituindo o comando: contador++ Após fazer as alterações, compile e teste. § Vetores e matrizes Em exemplos passados, já usamos vetores (arrays). Java dá suporte a vetores e matrizes de diversas formas. Por exemplo, podemos formar um vetor com as notas de cinco alunos de uma sala de aula: float nota[] = { 6.5, 9.1, 4.2, 1.8, 6.4 }; Neste caso nota[0] é a nota do primeiro aluno, isto é, 6.5, nota[1] é a nota do segundo, ou seja, 9.1, e assim por diante. Para usarmos de vetores e matrizes precisamos cumprir três etapas: 1. Declarar o vetor ou a matriz. Para isto, declaramos o nome da variável e acrescentamos um par de colchetes antes ou depois. Por exemplo: int compra[]; double mat[][],elemento[][][]; int []nota; 2. Reservar espaço de memória e definir o tamanho. É preciso definir o tamanho do vetor, isto é, a quantidade total de elementos que terá de armazenar. Em seguida é necessário reservar espaço de memória para armazenar os elementos. Isto é feito de maneira simples pelo operador new: compra = new int[9]; // vetor de int nota = new double[35]; // vetor de double mat = new float[20][30]; // matriz de float meusObjetos = new Object[10] // vetor de referências 3. Armazenar elementos no vetor ou matriz. Para armazenar uma informação em um dos elementos de um vetor ou matriz, é necessário fornecer um índice que indique a posição desse elemento. Por exemplo, para armazenar um valor na quarta posição do vetor nota, fazemos o seguinte: nota[3] = 7.5; Os índices começam em zero e vão até o número de posições reservadas, menos um. No vetor nota, por exemplo, os índices válidos vão de 0 até 34. Ao tentar atribuir um valor a um elemento cujo índice esteja fora desse intervalo, ocorrerá um erro, impedindo a execução. Quando desejamos criar um vetor com valores atribuídos de modo estático podemos fazer como no primeiro exemplo acima, onde foi declarado um vetor nota e o mesmo já foi iniciado com as notas de cinco alunos. Veja mais exemplos: // 12 primeiros termos da seqüência de Fibonacci: long fibonacci[] = {1,1,2,3,5,8,13,21,34,55,89,144}; // Tabela de sen(n*pi/6), n=0,1,2,...5 float seno[] = {0.000,0.500,0.866,1.000,0.866,0.500}; // Tabela de log(1+n), n=0,2...99: double tlog[] = new double[100]; for(int n=0; n<100; n++) tlog[i] = Math.log(1+n); // Matriz dos coeficientes double a[][] = { {1,2,3}, {0,1,3}, {0,0,-1} }; Para exemplificar o uso de métodos/atributos estáticos e vetores, vamos alterar a classe Robot, criando um método static (guarda ()) para guardar, em um atributo static (o array robots[]), uma referência paracada robot instanciado. Teremos agora o seguinte código para a classe Robot: class Robot { public int x, y; // posição do robot public static int contador; //contador de instancias public static Robot robots[] = new Robot[10]; public Robot(int posicao, int x,int y){ this.x = x; this.y = y; incrementa(); guarda(posicao,this); } public int getX(){ return x; } public int getY(){ return y; } public void setX(int x){ this.x = x; } public void setY(int y){ this.y = y; } public static void incrementa(){ contador++; } public static void guarda (int posicao, Robot este){ robots[posicao] = este; } } Agora, na classe Principal, após criar cada objeto robot, chamamos o método de cada objeto para pegar a sua respectiva posição, a partir das referências dos objetos que estão guardadas no array de objetos da classe Robot. Veja o código: //Classe principal class Principal { public static void main(String args[]) { Robot.contador=0; //inicializando variavel static Robot r; int i; for (i=0;i<10;i++) { // cria robots r = new Robot(i,i*2,i*2); } for (i=0;i<10;i++) { // imprime posicoes System.out.printf( "Posicao do robot %d: x=%d y=%d\n",i, Robot.robots[i].getX(),Robot.robots[i].getY()); } } //main method } //class Principal Em aulas passadas, vimos como declarar, instanciar e iniciar arrays de uma ou mais dimensões, de diferentes formas. Recorde algumas delas: Formas alternativas de fazer O que está sendo feito int c[ ]=new int [10]; int c[ ]; c = new int[10]; Instanciar um array float [ ] a, b; float a [ ]; float b [ ]; Instanciar dois arrays int k[ ] = {2, 4, 6, 8, 10}; k[0]=2; k[1]=4; k[2]=6; k[3]=8; k[4]=10; Instanciar e iniciar um array com valores int m [ ] [ ] = {{1,3,5}{7,9,0}}; int m [ ] [ ]; m=new int [2][3]; m[0]={1,3,5}; m[1]={7,9,0}; Instanciar e iniciar com valores um array bidimensional (matriz) int n [ ] [ ]; n = new int [2] [ ]; n[0]=new int[3]; n[1]=new int[5]; Instanciar um array bidimensional c/ linhas de tamanhos diferentes § Passando arrays como argumentos de métodos Para passar um array como argumento para um método, especifique o nome do array sem colchetes. Por ex., para uma array declarado como: int valor[] = int[10] A chamada de um método trataValor que recebesse esse array, seria: trataValores(valor); Já a declaração do método com o argumento formal, teria a forma: void trataValores(int a[]); Caso desejássemos passar apenas um elemento do array, faríamos: trataValor(valor[4]); //passa o quinto elemento Neste caso, o método deveria ser declarado como: void trataValor(int b); Em muitas linguagens de programação, existem duas maneiras de passar argumentos em chamadas de métodos: passagem por valor e passagem por referência. Ao contrário dessas linguagens, o Java não permite escolher passar por valor ou por referência – todos os argumentos são passados por valor. Uma chamada de método pode passar dois tipos de valores para um método: as cópias de valores primitivos, int por exemplo, e as cópias de referências para objetos , inclusive referências a arrays. Objetos não podem ser passados para os métodos. Quando um método modifica um parâmetro do tipo primitivo, as alterações no parâmetro não têm nenhum efeito no valor original do argumento no método chamador. Isto também é verdadeiro para os parâmetros de tipo por referência. Se o método chamado modificar um parâmetro de tipo por referência atribuindo a ele a referência a outro objeto, o parâmetro referenciará o novo objeto, mas a referência armazenada na variável do chamador ainda referencia o objeto original. § Coleções Podemos armazenar vários objetos ou valores de tipos primitivos em um array e manipular este array como sendo uma única entidade. Um array pode ser encapsulado em uma classe, possibilitando a criação de métodos específicos de manipulação do array encapsulado. Apesar da flexibilidade que pode ser obtida com o encapsulamento de um array em uma classe, algumas necessidades úteis não podem ser implementadas de forma trivial ou eficiente. Como, por exemplo: . o tamanho do array não pode ser modificado depois do array criado; . um array somente pode conter elementos de um único tipo, exceto se considerarmos os mecanismos de herança e polimorfismo; . algumas operações não podem ser realizadas de maneira simples em array como inserir e excluir um determinado elemento. Para facilitar a manipulação, a linguagem Java possui classes e interfaces que poupam trabalho do programador, oferecendo mecanismos para agrupar e processar objetos em conjuntos, denominados genericamente como coleções. Essas classes permitem a manipulação de estruturas de dados complexas de forma transparente para o programador. Todas essas classes fazem parte do pacote java.util. Resumindo, uma coleção é uma estrutura de dados, na realidade um objeto, que pode armazenar referências a outros objetos. Normalmente as coleções contêm referências a objetos que são todos do mesmo tipo. As interfaces de estrutura das coleções declaram as operações a serem realizadas nessas coleções: Collection, Set, List, Map, Queue, .... § Classe Arrays A classe Arrays fornece métodos static de alto nível para manipular arrays, como sort para ordenar o array, binarySearch para pesquisar o array ordenado, equals para comparar arrays e fill para colocar valores no array. O aplicativo a seguir mostra a utilização de alguns desses métodos: import java.util.Arrays; // Classe Arrays import java.util.Scanner; // Classe Scanner public class UsandoArrays { private int intArray[] = { 1, 2, 3, 4, 5, 6 }; private double doubleArray[] = {8.4,9.3,0.2,7.9,3.4}; private int intArrayCheio[], intArrayCopia[]; public UsandoArrays(){ //construtor intArrayCheio = new int[10]; intArrayCopia = new int[intArray.length]; Arrays.fill (intArrayCheio, 7);// preenche com 7s Arrays.sort (doubleArray); // ordena doubleArray System.arraycopy (intArray, 0, intArrayCopia, 0, intArray.length ); } public void mostraArrays() { System.out.print( "doubleArray ordenado: " ); for (double doubleValue : doubleArray) System.out.printf ("%.1f; ", doubleValue); System.out.print( "\nintArray: " ); for ( int intValue : intArray ) System.out.printf( "%d ", intValue ); System.out.print( "\nintArrayCheio: "); for ( int intValue : intArrayCheio ) System.out.printf( "%d ", intValue ); System.out.print( "\nintArrayCopia: " ); for ( int intValue : intArrayCopia ) System.out.printf( "%d ", intValue ); System.out.println( "\n" ); } public int pesquisaInt( int value ){ return Arrays.binarySearch(intArray, value ); } public void mostraIgualdade(){ boolean b = Arrays.equals(intArray, intArrayCopia); System.out.printf("intArray %s intArrayCopia\n", ( b ? "==" : "!=" )); b = Arrays.equals( intArray, intArrayCheio ); System.out.printf( "intArray %s intArrayCheio\n", ( b ? "==" : "!=" ) ); } public static void main( String args[] ) { UsandoArrays meuArray = new UsandoArrays(); meuArray.mostraArrays(); meuArray.mostraIgualdade(); Scanner entrada = new Scanner( System.in ); System.out.printf("\nEntre com valor a procurar: "); int val = entrada.nextInt(); int location = meuArray.pesquisaInt(val); String lixo = entrada.nextLine() ; if ( location >= 0) System.out.printf( "Valor %d encontrado na posicao %d do intArray\n", val, location ); else System.out.printf( "Valor %d nao foi encontrado em intArray\n",val); } } § Criando Pacotes Aplicações e projetos complexos necessitam de organização das classes que utilizam, de forma que fique claro a qual aplicação ou projeto as classes pertencem. Esta necessidade de organização é mais aparente quando se deseja compartilhar classes ou instalá-las em outro computador. Java provê um mecanismo de agrupamento de classes em pacotes – packages – com o qual podemos criar grupos de classes relacionadas. Para a criação desses pacotes, basta uma declaração de pertinência ao pacote em cada classe e a organização das classes em um diretório. Para criar uma pacote basta, portanto, criar um diretório e colocar lá os códigos- fonte das classes que desejamos que façam parte desse pacote. Como exemplo, vamos considerar as classes Data, Hora e, ainda, a classe DataHora que encapsula uma data e uma hora. Para aglutinar essas classes em um pacote, primeiramente devemos criar um diretório DataHora e, a seguir, armazenar as classes dentro desse diretório. Cada classe do pacote deve ter, no seu início, a palavra-chave package seguida do nome do diretório (pacote) ao qual a classe pertence. Veja para a classe Data: package DataHora; // indica o pacote da classe public class Data { byte dia; // acessibilidade default é package byte mes; // acessibilidade default é package short ano; // acessibilidade default é package public Data(byte d, byte m, short a){ dia = d; mes = m; ano = a; } public String toString(){ return dia + "/" + mes + "/" + ano; } } A seguir listamos a classe Hora que encapsula os dados de uma hora: package DataHora; // indica o pacote da classe public class Hora { byte hora; // acessibilidade default é package byte minuto; // acessibilidade default é package byte segundo; // acessibilidade default é package public Hora(byte h, byte m, byte s){ hora = h; minuto = m; segundo = s; } public String toString(){ return hora + ":" + minuto + ":" + segundo; } } A terceira classe do pacote DataHora é a classe de nome DataHora: package DataHora; public class DataHora { private Data novaData; private Hora novaHora; public DataHora(byte d, byte mes, short a, byte h, byte min, byte s){ novaData = new Data(d, mes, a); novaHora = new Hora(h, min, s); } public String toString(){ String resulta = novaHora.hora + ":" + novaHora.minuto + ":" + novaHora.segundo; resulta += " de " + novaData.dia + " de "; switch (novaData.mes){ case 1: resulta += "Janeiro"; break; case 2: resulta += "Fevereiro"; break; case 3: resulta += "Março"; break; case 4: resulta += "Abril"; break; case 5: resulta += "Maio"; break; case 6: resulta += "Junho"; break; case 7: resulta += "Julho"; break; case 8: resulta += "Agosto"; break; case 9: resulta += "Setembro"; break; case 10: resulta += "Outubro"; break; case 11: resulta += "Novembro"; break; case 12: resulta += "Dezembro"; break; } resulta += " de " + novaData.ano; return resulta; } } Note que a classe DataHora acima, faz referência a atributos das classes Data e Hora (hora, dia,etc.), que possuem acessibilidade default (package). As classes Data, Hora e DataHora pertencem ao pacote DataHora. A classe de teste DemoDataHora utiliza as classes Data, Hora e DataHora, mas não faz parte do pacote DataHora (coloque-a em outro diretório). Veja: import DataHora.Hora; import DataHora.Data; import DataHora.DataHora; public class DemoDataHora { public static void main (String args[]){ Hora meiodia=new Hora ((byte)12,(byte)00,(byte)00); Data hoje=new Data ((byte)24,(byte)10,(short)2006); DataHora agora=new DataHora ((byte)24,(byte)10, (short)2006,(byte)19,(byte)50,(byte)00); System.out.println (meiodia); System.out.println (hoje); System.out.println (agora); } } § Exercícios 1. Crie uma classe Aluno, que deve possuir, além do nome, uma nota (inicialmente 0). Crie também uma classe Disciplina que possua nomes da matéria e do professor. Nessa classe deve haver um array (static) com todas as referências para os alunos da disciplina. A aplicação de teste deve ler os nomes do professor e da matéria e instanciar um objeto da classe Disciplina com os valores lidos para os atributos. Em seguida, deve ler o nome de 10 diferentes alunos e, para cada aluno, instanciar um objeto da classe Aluno. Ao instanciar o aluno, a aplicação deve guardar sua referência no vetor da classe Disciplina. Depois disso, a aplicação deve pedir um nome de aluno, procurar o objeto aluno que possui esse nome e alterar sua nota para um valor fornecido pelo usuário. 2. Nas classes do pacote DataHora do exemplo, defina os atributos das classes Data e Hora como private e tente compilar a aplicação observando o erro gerado. Faça as alterações nas classes para viabilizar essa nova implementação que preserva mais o encapsulamento. 3. Altere a aplicação de exemplos com arrays dada na aula, de forma a introduzir um método comparaArrays que receba como parâmetros dois arrays quaisquer e retorne um valor booleano indicando se os arrays são efetivamente iguais ou não.
Compartilhar