Baixe o app para aproveitar ainda mais
Prévia do material em texto
1 Java Básico + Orientação a Objetos Por: Raphaela Galhardo Fernandes raphaela@j2eebrasil.com.br JavaRN - http://javarn.dev.java.net J2EEBrasil - http://www.j2eebrasil.com.br Natal, Maio de 2006 Sumário 1. Introdução 4 2. Estrutura de um Código Java 6 2.1 Arquivo Fonte 6 2.2 Classe 6 2.3 Método 7 3. Primeiro Programa em Java 7 4. Elementos Básicos de um Arquivo Fonte .java 9 5. Identificadores e Palavras Reservadas 10 5.1 Convenção da Linguagem 11 6. Variáveis 11 6.1 Declaração de Variáveis 12 6.2 Variáveis que Referenciam Objetos 13 7. Outras Seqüências de Escape 16 8. Separadores 16 9. Operadores 17 9.1 Operadores Unários 17 9.2 Operadores Aritméticos 19 9.3 Operadores de Deslocamento 20 9.4 Operadores de Comparação 21 9.5 Operadores Bit-a-Bit 22 9.6 Operadores Lógicos de Curto-Circuito 22 9.7 Operador de Atribuição 24 10. Estruturas de Controle e Fluxo 24 10.1 Estrutura de Seleção if 24 10.2 Estrutura de Seleção if/else 25 10.3 Estrutura de repetição while 27 10.4 Estrutura de repetição do/while 27 10.5 Estrutura de repetição for 28 10.6 Estrutura de seleção múltipla switch 29 10.7 Interrompendo os Fluxos de Controle 30 11. Vetores/Arrays 30 11.1 Vetores/Arrays Multidimensionais 33 11.2 Estrutura for Aprimorada 34 12. Programação Orientada a Objetos 36 12.1 Abstração 36 12.2 Objetos 37 12.3 Classes 37 12.4 Passagem de Argumentos para Métodos 39 12.5 Lista de Argumentos de Comprimento Variável (Varargs) 43 12.6 Retorno de Métodos 44 12.7 Construtores 45 3 12.8 Sobrecarga de Métodos 46 13. Igualdade entre Objetos 47 13.1 Método equals() 49 14. Encapsulamento 52 15. Herança 54 15.1 Como Definir o Relacionamento de Herança 56 15.2 Exemplo de Implementação de Herança em Java 61 16. Polimorfismo 65 16.1 Redefinição de Métodos 68 17. Classes e Métodos Abstratos 69 18. Interfaces 73 18.1 Breve Comparação entre Classe Abstrata e Interface 79 19. Modificadores 79 19.1 Private 79 19.2 Public 80 19.3 Protected 80 19.4 Package 80 19.5 Static 80 19.6 Final 82 19.7 Abstract 82 19.8 Transient 82 19.9 Synchronized 82 20. Redefinição de Métodos e Modificadores 82 21. Wrappers de Tipos 83 21.1 Autoboxing e Auto-Unboxing 85 22. Constantes em Java 86 23. Referências Bibliográficas 91 4 1. Introdução Java é uma linguagem de programação que começou a ser desenvolvida pela Sun Microsystems[1] por volta de 1991. Possui uma estrutura bastante parecida com as linguagens C e C++. Java é orientada a objetos, portanto, inclui os benefícios de reutilização de código, extensibilidade e aplicações dinâmicas. Sintetizando, programar em Java significar criar classes (formadas por variáveis e métodos) que encapsulam funcionalidades em objetos reutilizáveis e que podem ser carregados dinamicamente. Uma outra característica bastante importante dessa linguagem é o fato dela ser portável, ou seja, uma aplicação implementada em Java pode ser executada em diversos sistemas operacionais. Java é uma linguagem que é primeiramente compilada e em seguida interpretada. A Figura 1 apresenta um esquema de compilação e execução de um programa escrito nela. O primeiro passo é a construção de um arquivo com o código fonte, que deve ter como extensão .java. No caso do exemplo da Figura 1, o arquivo foi chamado de HelloWorld.java. Em seguida, o código fonte é compilado e um programa fonte em bytecodes (HelloWorld.class) é gerado. Durante a compilação, há uma checagem de erros do código fonte. O fonte em bytecodes só será gerado se nenhum erro tiver sido detectado. Por fim, qualquer dispositivo que execute Java será capaz de interpretar este novo arquivo fonte e executar a aplicação. Os bytecodes são lidos e executados (ou seja, interpretados) pela Máquina Virtual Java (JVM – Java Virtual Machine) em um computador, em um celular, etc., além de serem independentes de plataforma. Figura 1 - Esquema de compilação e execução de um programa Java 5 A geração dos bytecodes ou, para o exemplo, do arquivo HelloWorld.class é feita a partir da execução do comando javac HelloWorld.java . Então, o próximo passo é fazer com que a JVM execute o arquivo HelloWorld.java, através do comando java HelloWorld.class . Dessa maneira, a JVM traduz o código compilado para uma linguagem que a máquina entenda e o programa é executado. Resumindo, um programa Java é um conjunto de instruções a serem interpretadas por uma JVM. Como já citado, os bytecodes são independentes de plataforma. Dessa forma, os programas em Java podem executar em qualquer plataforma de hardware ou software que possua uma versão da JVM. A Máquina Virtual Java é definida como uma máquina imaginária implementada através da emulação em um software executado em uma máquina real. Ela possui uma arquitetura que permite garantir segurança. Quando um programa Java é executado, seus bytecodes são verificados pela JVM para que estejam de acordo com os seus requisitos de segurança, impedindo a execução de código com alguma irregularidade. Assim, códigos fonte com instruções que acessem áreas restritas da memória ou até mesmo recursos do hardware não são executados pela JVM. 6 2. Estrutura de um Código Java A estrutura geral de um código Java está apresentada na Figura 2. Figura 2 - Estrutura Geral de um Código em Java 2.1 Arquivo Fonte Um arquivo de código fonte possui a definição de uma classe. Uma classe representa uma parte do programa. Ela define atributos (variáveis), o comportamento de um objeto e possui a declaração básica apresentada na Listagem 1. Os limites da definição da classe são dados pelas chaves {}. Listagem 1 - Formato Geral da Declaração de uma Classe class Passaro{ } 2.2 Classe Uma classe é formada por um ou mais métodos. Os métodos definem as funcionalidades da classe, ou seja, o que se pode fazer com os objetos dessa classe. No caso da classe da Listagem 1, a classe Passaro possui dois métodos: voar e cantar, como mostrado na Listagem 2. Os métodos devem ser declarados dentro da classe e seus delimitadores também são as chaves {}. 7 Listagem 2 - Métodos da Classe class Passaro{ void cantar(){ } void voar(){ } } 2.3 Método Um método é formado por declarações e instruções que juntas representarão uma funcionalidade da classe. Como se pode observar na Listagem 3, as declarações e instruções são separadas por ponto-vírgula. Listagem 3 - Instruções do Método class Passaro{ void cantar(){ declaracao; instrução1; } void voar(){ declaração; instrução1; instrução2; } } 3. Primeiro Programa em Java O primeiro exemplo a ser apresentado deve ser escrito em um arquivo denominado PrimeiroExemplo.java, que conterá uma classe também chamada de PrimeiroExemplo. Quando o comando java PrimeiroExemplo.class é executado, a JVM inicia e procura um método que parece com o descrito na Listagem 4. Esse método é denominado método principal da classe. 8 Listagem 4 - public static void main public static void main (String[] args){ //Código do programa a ser executado } O próximo passo é a JVM executar o código que se encontra entre as chaves ({}) do método principal. Toda aplicação Java tem ao menos uma classe e no mínimo um método principal ou métodomain. É importante saber que não existe um método main por classe, mas sim por aplicação. A Listagem 5 apresenta o primeiro exemplo deste material escrito em Java. Listagem 5 - PrimeiroExemplo.Java public class PrimeiroExemplo{ public static void main (String[] args){ System.out.println (“Primeiro Exemplo em Java”); } } Agora será explicado o código fonte presente na Listagem 5. Algumas coisas ainda ficarão obscuras, mas tenha em mente que este é o primeiro exemplo a ser apresentado e explicações futuras esclarecerão as possíveis dúvidas. A primeira linha declara o nome da classe implementada: public class PrimeiroExemplo{ A palavra public indica que a classe terá um nível de acesso público, ou seja, que ela será acessível de qualquer classe. A palavra class indica que uma classe está sendo declarada e seu nome é PrimeiroExemplo. A { delimita o limite inicial da classe. A segunda linha declara o método principal da classe: public static void main (String[] args){ A palavra public já foi descrita, portanto permite que o método seja acessado publicamente. A palavra static será descrita posteriormente. O lugar onde fica a palavra void é onde se deve indicar o tipo de retorno da classe. Neste caso, não há tipo de retorno, dessa maneira o método é declarado como void. O conjunto String[] args presentes entre () são os argumentos do método. Neste exemplo, o método possui um 9 único argumento (um vetor de Strings denominado args. A { delimita o limite inicial do método. A terceira linha consiste no conteúdo do método principal, formado apenas por um comando: System.out.println (“Primeiro Exemplo em Java”); O System.out.println é usado para exibir algo na saída padrão, por padrão, na linha de comando. Será exibido o que tiver entre (“”), no caso, Primeiro Exemplo em Java. No final deste comando, tem-se um ; , que o finaliza. Por fim, há duas “fecha chaves” } } , que delimitam o fim do método e da classe, respectivamente. 4. Elementos Básicos de um Arquivo Fonte .java Os elementos básicos (porém não obrigatórios) de um arquivo fonte .java são: declaração de pacotes, declaração de importação de classes e definições de classes. Os pacotes são criados para organização dos arquivos fontes da aplicação Java. Servem para agrupar classes afins. Declarar um pacote em uma classe significa definir a que pacote ela pertence. A sintaxe da declaração de um pacote é package nome_pacote; ,ou seja, inicia com a palavra chave package, seguida do nome do pacote (pode ser formado por várias partes separadas por ponto) e finalizada por um ponto e vírgula (;). É bastante comum empresas colocarem como nomes de pacotes o nome do seu domínio invertido, como: package br.com.j2eebrasil.meuPacote; . Assim, os arquivos que contém as classes devem estar dentro de um diretório br\com\j2eebrasil\meuPacote . A declaração de importação de classes é feita de duas maneiras: importando uma classe específica ou importando todas as classes pertencentes a um pacote. A sintaxe básica da declaração de importação de uma classe específica é import nome_pacote.nome_classe; . Semelhante à declaração de pacotes, inicia-se com a palavra chave import; seguida do nome do pacote, um ponto e do nome da classe; e finalizada por um ponto e vírgula (;). No caso da importação de todas as classes de um pacote a sintaxe é import nome_pacote.*; , ou seja, o nome da classe é substituído pelo asterisco (*). A Listagem 6 exemplifica a declarações de pacotes, de importação e definição de classe. 10 Listagem 6 - Elementos de um Arquivo Fonte // Declaração de Pacote package br.com.j2eebrasil.meuPacote // Declaração de Importação de Classe import java.util.Random; //Importação de classe específica import java.sql.*; //Importação de um pacote inteiro import java.util.Date; import java.sql.Date; // Definição da Classe public class NomeClasse {...} 5. Identificadores e Palavras Reservadas Um identificador em um programa Java consiste em uma palavra (de tamanho ilimitado) que nomeia uma variável, um método, uma classe ou um rótulo/label. Identificadores podem ser formados por letras e números, mas apenas iniciar com um dos três caracteres: letra, cifrão ($), ou sublinhado (_). Os demais caracteres que formam o identificador podem ser desses três tipos ou um dígito. A Listagem 7 apresenta exemplos de identificadores válidos. Já a Listagem 8 mostra exemplos de identificadores inválidos. Observe que os identificadores são case sensitives, ou seja, há diferença quando formados por letras maiúsculas e minúsculas. Listagem 7 - Identificadores Válidos x y z America _9_i$to_EH_meio_esquisito total_1+2+3 $4outroExemplo exemploCOMmuitasPALAVRAS Listagem 8 - Identificadores Inválidos 4_naoPodeComecarComDigito !naoPodeComecarComCaracteresEspeciais 11 As palavras reservadas ou palavras chaves são aquelas que não podem ser utilizadas como identificadores. Java possui diversas palavras reservadas, entre elas as listadas na Listagem 9. Listagem 9 - Palavras Reservadas abstract boolean break byte case catch char class continue default do double else extends final finally float for if implements import instanceof int long native interface new null package private public protected return short static super switch this throw synchronized throws transient try void volatile while 5.1 Convenção da Linguagem Na linguagem Java é utilizada a seguinte convenção para formação de identificadores: • Constantes com todas as letras em maiúsculo: CONSTANTE ; • Variáveis começam com letra minúscula: variável ; • Classes começam com letra maiúscula: Classe ; • Métodos começam com letra minúscula: metodo(), metodo2(int a); • Se o nome for composto, cada nome começa com letra maiúscula: variávelComNomeComposto . 6. Variáveis Variáveis são valores que são atribuídos dinamicamente, ou seja, não têm um valor pré-definido. Podem ser definidos pelo programador, bem como por qualquer método. As variáveis podem pertencer à classe (variáveis globais) ou aos métodos (variáveis locais). Também podem ser utilizadas como argumentos de métodos e como tipos de retornos. 12 6.1 Declaração de Variáveis Para evitar confusões na manipulação das variáveis de um programa, como por exemplo, atribuir o valor de um objeto Cadeira a uma variável que corresponde a um valor numérico, os tipos das variáveis devem ser declarados. Declarar significa identificar se as variáveis são números inteiros, números de ponto flutuante, etc., ou até mesmo se são objetos de uma outra classe. As variáveis enquadram-se em duas categorias: primitivas ou referências a objetos. As variáveis primitivas representam valores fundamentais, isto é, inteiros, booleanos (verdadeiro ou falso), de ponto flutuante. Já o segundo tipo diz respeito às variáveis que referenciam outros objetos diferentes. Toda variável é formada por um tipo e um tamanho. Na Tabela 1 estão listados os tipos de variáveis primitivas com seus respectivos tamanhos em bits e faixa de valores assumidos. Tabela 1- Tipos Primitivos Tipo Tamanho (Em bits) Faixa de valores assumidos boolean 8 true ou false char 16 2 bytes – Unicode (0 – 65535) byte 8 -128 a 127 short 16 -32768 a 32767int 32 -2.147.483.648 a 2.147.483.647 long 64 -9.223.372.036.854.775.808 a + 9.223.372.036.854.775.808 float 32 -3.40292347E+38 a +3.40292347E+38 double 64 -1.79769313486231570E+308 a +1.79769313486231570E+308 É importante saber que o compilador Java não permitirá que um valor de tamanho grande seja atribuído a uma variável de tipo de tamanho menor. Por exemplo, na Listagem 10, o valor da variável x é 24 que é um valor que caberia em uma variável do tipo byte. Conceitualmente é fácil de perceber isso, porém o compilador não sabe que o valor 24 cabe em uma variável byte, ele sempre vai achar que esse tipo de atribuição (de um valor int a um valor byte) vai significar em atribuir um valor a uma variável byte de tamanho maior do que esse tipo suporta, o que provocaria overflow. 13 Listagem 10 - Exemplo de Atribuição de Valor de Tamanho Maior em Variável de Tamanho Menor int x = 24; byte b = x; 6.1.1 Exemplos A Listagem 11 apresenta declarações de tipos primitivos com suas respectivas atribuições de valores. As variáveis podem ser declaradas e inicializadas em uma única linha de comando ou então, inicialmente declaradas e posteriormente receberem valores. Na última declaração, float f = 72.5f, após o valor de ponto flutuante tem a letra f. Esse f é necessário para que o Java saiba que o valor é do tipo float, pois para ele números de ponto flutuante são interpretados como double. Listagem 11 – Exemplo: Atribuição de Valores int x; x = 234; byte b = 89; boolean isPermitido = true; double d = 1235.56; char c = ‘R’; int z = x; boolean isAcessado; isAcessado = false; boolean ligado; ligado = isPermitido; long big = 3456789; float f = 72.5f; 6.2 Variáveis que Referenciam Objetos A declaração de variáveis primitivas já foi apresentada. Agora é hora de apresentar como declarar variáveis que referenciam objetos. A Listagem 12 mostra um segundo programa em Java. Antes de explicar como se declara objetos, observe no código da Listagem 12 que Java não impõe formatação de código fonte, ou seja, a separação entre tokens é feita apenas por espaços em branco, por exemplo, é possível declarar cada variável do programa em uma linha ou então declarar todas em uma única linha. 14 Antes da declaração da variável numPortas aparece a palavra chave private, fazendo com que a variável tenha acesso privado, ou seja, tornando-a acessível apenas dentro da definição da própria classe. Já antes da declaração da variável numJanelas aparece a palavra chave public, que é um modificador de acesso público, permitindo que a variável seja acessada de qualquer classe. O código fonte abaixo também mostra como se comentar o código Java. O comentário a ser escrito em uma única linha deve ser precedido por duas barras //. Já um comentário que se estenda por várias linhas, deve ser escrito entre /**/. A classe Casa do código da Listagem 12 é formada por 4 variáveis (duas inteiras, uma boleana e uma de ponto flutuante), por dois métodos (um que define o número de portas da casa e o outro que retorna o número de janelas) e um método principal. A primeira linha do método principal exibe na tela a String “ Criação de uma casa” seguida de uma quebra de linha. O espaço em branco antes da palavra “Criação” corresponde a um TAB. Normalmente, os caracteres em uma String são exibidos exatamente como são colocados entre as aspas duplas. Entretanto, na mensagem a ser exibida pelo método principal os caracteres \t e \n não são enviados para a saída. Quando uma barra invertida encontra-se juntamente com uma string de caracteres, o caractere seguinte a \ é combinada à mesma para formar uma seqüência de escape. A seqüência de escape \t é o caractere do TAB, já a seqüência \n é o caractere de nova linha. O segundo comando do método principal refere-se à declaração de um objeto. É importante distinguir que uma variável primitiva possui um tamanho em bits que a representa, porém uma variável que referencia um objeto não, ela possui um tamanho em bits que representa um caminho para se obter o objeto, ou seja, um endereço de memória. O último comando do método principal, int numJan = c.getNumJanelas(), atribui a variável inteira numJan o número de janelas do objeto Casa obtido a partir da chamada ao método getNumJanelas() (que retorna um valor também inteiro). A chamada a um método de um objeto é feita utilizando o operador ponto (.), ou seja, c.getNumJanelas() . Nesse caso, significa que o objeto referenciado pela variável c invoca o método getNumJanelas(). Listagem 12 - Segundo Programa em Java public class Casa (){ //Declarações das variáveis da classe private int numPortas = 3; public int numJanelas; 15 boolean grande = true; double valor; /* Declarações dos métodos da classe */ void setNumPortas(int numero){ numPortas = numero; } int getNumJanelas(){ return numJanelas; } public static void main (String args[]){ System.outr.print(“\t Criação de uma casa \n”); Casa c = new Casa(); int numJan = c.getNumJanelas(); } } 6.2.1 Declarar, Criar e Atribuir Valor a um Objeto Declarar, criar e atribuir valor a um objeto significa, por exemplo, escrever a seguinte linha de código: Casa casa = new Casa(); A declaração consiste no trecho Casa casa, o que faz a JVM alocar espaço em memória para a referência ao objeto do tipo Casa. A criação do objeto é feita a partir do trecho new Casa(); . Neste ponto, a JVM aloca espaço em memória para o novo objeto Casa que acaba de ser criado. O operador igual (=) faz a ligação entre a referência em memória ao objeto Casa. 6.2.2 Inicialização de Variáveis As variáveis de uma classe, também conhecidas como variáveis globais, se, após a sua declaração, não forem inicializadas pelo programador (como por exemplo, a variável numPortas da Listagem 12 que recebe o valor 3), recebem valores padrões. Os valores padrões estão apresentados na Tabela 2. 16 Tabela 2 - Valores Padrões de Variáveis Globais Tipo Valor Inicial byte 0 short 0 int 0 long 0L float 0.0f double 0.0d char ‘\u0000’ boolean false reference null 7. Outras Seqüências de Escape Esta seção destina-se apenas a mostrar outras seqüências de escape que não foram apresentadas na seção anterior. Veja a Tabela 3. Tabela 3 - Seqüências de Escape Seqüência de Escape Descrição \n Indica nova linha, posicionando o cursor de tela no início da próxima linha. \t Indica tabulação horizontal, movendo o cursor da tela para a próxima parada de tabulação. \r Posiciona o cursor de tela no início da linha atual. Qualquer saída de caracteres após essa seqüência sobrescreve a saída anterior de caracteres na linha atual. \\ Usada para imprimir o caractere de barra invertida. \” Usada para imprimir o caractere de aspas duplas. 8. Separadores A Tabela 4 apresenta os separadores que podem ser utilizados ao se programar em Java. 17 Tabela 4 - Separadores Separador Descrição () Separador utilizado para: delimitar parâmetros em chamadas de métodos; modificar precedência em expressões; identificar tipo-alvo em comandos; delimitar condicionais em comandos. {} Separador para delimitar blocos de código e inicialização de vetores. [] Separador para declarar e manusear vetores. ; Finalizar de comando. . Separador utilizado para selecionarcampos e métodos de um objeto. , Utilizado na declaração de variáveis e na estrutura de controle for. 9. Operadores A linguagem Java possui diversas categorias de operadores, entre eles: unário, aritmético, deslocamento, comparação, bit-a-bit, lógico de curto-circuito, atribuição. 9.1 Operadores Unários Os operadores unários são aqueles que trabalham apenas com um único operando. São sete os operadores unários, como pode ser visualizado na Tabela 5. Tabela 5 - Operadores Unários Operador Descrição ++ Operador de incremento de variável. A posição do operador em relação à variável deve ser considerada. -- Operador de decremento de variável. A posição do operador em relação à variável deve ser considerada. + Mais unário. Não afeta o valor numérico. - Menos unário. Afeta o valor numérico, negando-o. ~ Operador de inversão de bit-a-bit. Em um valor binário, converte todos os bits com valor 1 no valor 0 e vice-versa. ! Operador do não booleano. Inverte o valor de uma expressão do tipo boolean. () Operador para realização de cast. A operação de casting é utilizada quando é desejado converter tipos de dados em uma expressão. Esse tipo de conversão só é possível quando os tipos de dados são tipos compatíveis entre si. Pode ser aplicado tanto a tipos primitivos como a objetos. 18 A Listagem 13 apresenta exemplos de usos dos operadores unários. Listagem 13 - Exemplos de Uso de Operadores Unários int a = 2; //Declaração da variável inteira a. Inicializada com o valor 2. System.out.println(a++); /*Escreve na tela o valor 2 e depois incrementa o valor da variável a para 3. Primeiro utiliza a variável, depois incrementa*/ int b = 3; //Declara variável inteira b inicializada com valor igual a 3. System.out.print(++b); /*Escreve na tela o valor 4. Primeiro incrementa o valor da variável, depois utiliza*/ a = 10; //Variável a recebe novo valor 10. int w = a--; /*A variável w é declarada e inicializada com o valor 10 e o valor da variável a passa a ser 9. Primeiro utiliza a variável, depois decrementa*/ System.out.println(--a); /*Escreve na tela o valor 8. Primeiro decrementa, depois Utiliza. A possuía valor igual a 9.*/ double c = 43.5; //Declara variável doublé c inicializada com valor igual a //43.5 double d = +c; //Variável double d recebe valor da variável c, mantendo o sinal. d = -d; /*Variável d assume seu próprio valor com sinal negativo, ou seja -43.5*/ boolean bT = true; //Declaração da variável booleana com valor true boolean bF = false; //Declaração da variável booleana com valor false !bT; //bT passa a assumir valor false !bF; //bF passa a assumir valor true 19 double numD = 10.0; int numero = (int)numD; //Casting do tipo double para o tipo int 9.2 Operadores Aritméticos Os operadores aritméticos são aqueles utilizados na programação de cálculos aritméticos. Veja a Tabela 6. Tabela 6 - Operadores Aritméticos Operador Descrição + Adição. Realiza a adição entre valores numéricos. O operador + também pode ser utilizado para concatenar Strings. - Subtração. Realiza a subtração entre valores numéricos. * Multiplicação. Realiza a multiplicação entre valores numéricos. / Divisão. Realiza a divisão entre valores numéricos. % Módulo. Retorna o resto da divisão de dois números. A Listagem 14 apresenta exemplos de usos dos operadores aritméticos. Listagem 14 - Exemplos de Uso de Operadores Aritméticos int a = 10; int b = 3; int c = 17; int d = a + b; //A variável d é declarada e inicializada com o valor 13 (10 + 3). int e = c – b ; //A variável e é declarada e inicializada com o valor 14 (17 - 3). int f = c % 5; //f é declarada e recebe o valor 2 (Resto da divisão 17 / 5). int g = 23 / 5; /*g é declarada e recebe o valor 4. O resultado da divisão é na realidade 4.6, porém a divisão inteira produz um quociente inteiro. Não há arredondamento. A parte fracionária é simplesmente truncada.*/ double h = (a + b) * g; //h é declara e inicializada com 52 ( (10 + 3) * 4) Os operadores aritméticos possuem a precedência mostrada na Tabela 7. A ordem da precedência em Java é feita da esquerda para direita. 20 Tabela 7 - Precedência dos Operadores Aritméticos Operador Precedência () Os operadores em expressões contidas dentro de pares de parênteses são avaliados primeiro. Se os parênteses estiverem aninhados, a expressão no par mais interno será avaliada primeiramente. Se existe pares não aninhados em um mesmo nível, eles são avaliados da esquerda para direita. *, /, % São avaliados em segundo lugar. Havendo vários, são avaliados da esquerda para direita. + ou - Avaliados por último. Havendo vários, são avaliados da esquerda para direita. 9.3 Operadores de Deslocamento A operação de deslocamento consiste simplesmente em mover bits para a direita ou para a esquerda. Por exemplo: Dado Original: 0001100110 Deslocamento de um bit para a direita: 0000110011 Deslocamento de um bit para a esquerda: 0011001100 A Tabela 8 apresenta os operadores de deslocamento. Tabela 8 - Operadores de Deslocamento Operador Descrição << Operador de deslocamento à esquerda. >> Operador de deslocamento à direita com sinal. >>> Operador de deslocamento à direita sem sinal. O valor resultante do deslocamento possui a mesma quantidade de bits do valor original. Os bits que forem deslocados para fora da representação são descartados. Já aqueles novos que entram na representação assumem o valor 0, caso os operadores sejam << ou >>>. No caso do operador >>, os novos bits adicionados assumem o valor do bit mais significativo antes do deslocamento, por exemplo: Dado Original: 1001100110 Deslocamento >>: 1100110011 21 9.4 Operadores de Comparação Os operadores de comparação retornam valores booleanos. Veja a Tabela 9 Tabela 9 - Operadores de Comparação Operador Descrição == Compara se dois valores são iguais. != Compara se dois valores são diferentes. > Compara se um valor é maior que o outro. < Compara se um valor é menor que o outro. >= Compara se um valor é maior ou igual a o outro. <= Compara se um valor é menor ou igual a o outro. Os operadores descritos acima, em relação aos tipos primitivos, comparam os seus valores. Já em relação aos objetos, os valores comparados são as referências dos objetos (tipicamente endereços em memória). A comparação semântica entre objetos, por exemplo, se duas Strings são iguais, é feita a partir do método equals(). Veja exemplo do uso dos operadores de comparação na Listagem 15. Listagem 15 - Exemplos de Uso de Operadores de Comparação int a = 10; int b = 3; if (a == b){} //Compara se a é igual a b if (a != b){} //Compara se a é diferente de b if (a > b){} //Compara se a é maior que b if (a < b){} //Compara se a é menor que b if (a >= b){} //Compara se a é maior ou igual a b if (a <= b){} //Compara se a é menor ou igual a b String s1 = new String(“Teste”); String s2 = new String(“Teste”); System.out.println(s1 == s2); /*Compara se o endereço em memória da variável s1 é igual ao endereço da variável s2. Escreve na tela false*/ System.out.println(s1.equals(s2)); /* Compara se o conteúdo da variável s1 é igual ao conteúdo da variável s2. Escreve na tela true */ 22 9.5 Operadores Bit-a-Bit Os operadores bit-a-bitrealizam, respectivamente, as operações lógicas AND, XOR e OR bit-a-bit. Eles estão listados na Tabela 10. Tabela 10 - Operadores Bit-a-Bit Operador Descrição & Operador do AND lógico. ^ Operador do XOR lógico. | Operador do OR lógico. Quando esses operadores são aplicados a operandos booleanos, os operados são considerados como um único bit. 9.6 Operadores Lógicos de Curto-Circuito Os operadores de curto-circuito são utilizados para realizar também operações AND e OR, porém apenas para valores booleanos. Esses operadores estão na Listagem 11. Tabela 11 - Operadores Lógicos de Curto-Circuito Operador Descrição && Conjunção. Operador de curto-circuito do AND lógico. || Disjunção. Operador de curto-circuito do OR lógico. A diferença desses dois operadores para os operadores & e | bit-a-bits é que em algumas situações o operando que se encontra do lado direito pode não ser avaliado. Por exemplo, os códigos presentes na Listagem 16 e na Listagem 17. Listagem 16 - Exemplo Operando de Curto-Circuito: & e && String a = null; //Declara uma String nula de nome a String b = new String(“Testando”); //Declara e inicializa uma String de nome b if (a != null & b.equals(“Testando”) ){ //Teste com operador & System.out.println(“Olá Pessoal”); } 23 if (a != null && b.equals(“Testando”) ){ //Teste com operador && System.out.println(“Olá Pessoal”); } Em todos os testes da Listagem 16, o resultado será o mesmo, ou seja, nada será escrito na tela. Considere o primeiro operando como sendo a != null e o segundo operando como sendo b.equals(“Testando”). As diferenças são as seguintes: • No teste que utiliza o operador &, ambos operandos serão avaliados, ou seja, vai ser testado se a é diferente de null e se a String b é igual à String Testando, o que resultará em um resultado falso. • No teste que utiliza o operador &&, apenas o primeiro operando será avaliado. Testa-se a é diferente de null. Como essa condição é falsa, não há mais necessidade de se avaliar os demais operandos, já que na operação lógica AND, se qualquer uma das condições for falsa, a expressão toda será falsa. Listagem 17 - Exemplo Operando de Curto-Circuito: | e || String a = null; //Declara uma String nula de nome a String b = new String(“Testando”); //Declara e inicializa uma String de nome b if (a == null | b.equals(“Testando”) ){ //Teste com operador | System.out.println(“Olá Pessoal”); } if (a == null || b.equals(“Testando”) ){ //Teste com operador || System.out.println(“Olá Pessoal”); } Nos testes da Listagem 17, o resultado também será o mesmo, ou seja, será escrito na tela a String Olá Pessoal. Considere o primeiro operando como sendo a == null e o segundo operando como sendo b.equals(“Testando”). As diferenças são as seguintes: • No teste que utiliza o operador |, ambos operandos serão avaliados, ou seja, vai ser testado se a é igual a null e se a String b é igual à String Testando, o que resultará em um resultado verdadeiro. • No teste que utiliza o operador ||, apenas o primeiro operando será avaliado. Testa-se a é igual à null. Como essa condição é verdadeira, não há mais necessidade de se avaliar os demais operandos, já que na operação lógica OR, se qualquer uma das condições for verdadeira, a expressão toda será verdadeira. 24 9.7 Operador de Atribuição Os operadores de atribuição, como o próprio nome já diz, atribuem um valor a uma variável. Eles estão apresentados na Tabela 12. Tabela 12 - Operadores de Atribuição Operador Descrição = Atribui um valor qualquer a uma variável. += Atribui a uma variável o resultado da soma de seu valor atual a um valor qualquer. -= Atribui a uma variável o resultado da subtração de seu valor atual de um valor qualquer. *= Atribui a uma variável o resultado da multiplicação entre seu valor atual e um valor qualquer. /= Atribui a uma variável o resultado da divisão de seu valor atual por um valor qualquer. %= Atribui a uma variável a o resto da divisão de seu valor atual por um valor qualquer. Exemplos de uso podem ser vistos na Listagem 18. Listagem 18 - Exemplo Operandos de Atribuição int a = 2; a += 3; //a assume o valor 5 (2 + 3) int b = 4; //b assume o valor 8 (4 * 2) b *=2; int c = 10; //c assume o valor 1 (Resto da divisão 10/3) c %= 3; 10. Estruturas de Controle e Fluxo Uma estrutura de controle é aquela que executa blocos de código delimitados por chaves “{...}”. Todas as variáveis criadas dentro de um bloco de estrutura de controle são vistas apenas dentro desse bloco. 10.1 Estrutura de Seleção if Na estrutura de seleção IF, apenas uma condição é avaliada. Caso o resultado da avaliação da condição seja verdadeiro (true), um bloco de instruções relacionado à 25 estrutura if é executado. Caso seja falso (false), esse bloco de código não será executado e o fluxo de controle segue adiante. A sintaxe básica dessa estrutura está apresentada na Listagem 19. Listagem 19 - Sintaxe da Estrutura IF if (condicao){ //Conjunto de comandos comando_1; comando_2; } Para exemplificar observe o código presente na Listagem 20. Neste exemplo, a primeira condição testa se x > 9.5. Como anteriormente x foi declarado e inicializado com valor 10.54, a condição é verdadeira e o comando para escrever x é maior que 9.5 na tela é executado. A segunda condição verifica se a String nome possui conteúdo igual a David, porém nome tem o conteúdo Raphaela, dessa forma, o comando que escreveria O nome é David não será executado. Listagem 20 – Exemplo de uso da Estrutura IF double x = 10.54; String nome = “Raphaela”; //Primeira condição if (x > 9.5){ System.out.println(“x é maior que 9.5”); } //Segunda condição If (nome.equals(“David”)){ System.out.println(“O nome é David”); } 10.2 Estrutura de Seleção if/else No caso da estrutura de seleção IF/ELSE, se uma condição for avaliada como verdadeira, um conjunto de instruções será executado, caso contrário, um outro conjunto de instruções será executado. A Listagem 21 apresenta a sintaxe básica da estrutura if/else. 26 Listagem 21 - Sintaxe da Estrutura IF/ELSE if (condicao){ //Conjunto de instruções para condição VERDADEIRA instrucao_1; instrucao_2; }else{ //Conjunto de instruções para condição FALSA instrucao_3; instrucao_4; instrucao_5; } O exemplo apresentado na Listagem 22 ilustra a estrutura de seleção if/else. Neste caso, a condição é se x % 2 == 0, ou seja, se o resto da divisão do valor da variável x por 2 for igual a zero, então escreva na tela x é par, se não escreva x é ímpar. Listagem 22 – Exemplo de uso da Estrutura IF/ELSE if (x % 2 == 0){ System.out.println(“x é par”); }else{ System.out.println(“x é ímpar”); } A estrutura if/else também pode ser utilizada de forma aninhada, testando vários casos, colocando estruturas if/else dentro de outras estruturas if/else. Veja o exemplo da Listagem 23. Listagem 23 – Exemplo de uso da Estrutura IF/ELSE Aninhada if (notaAluno >= 9){ System.out.println(“A”); }else if (notaAluno >= 8){ System.out.println(“B”); }else if (notaAluno >= 7){ System.out.println(“C”); }else if (notaAluno >= 6){ System.out.println(“D”); 27 }else{ System.out.println(“E”); } 10.3 Estrutura de repetição while Uma estrutura de repetição especifica que uma ação será repetida enquantoalguma condição permanecer verdadeira. No caso da estrutura de repetição while, a sintaxe básica é mostrada na Listagem 24. As instruções dentro das {} da estrutura while ficam sendo executadas até que a condição se torne falsa. A Listagem 25 apresenta um exemplo de uso. No exemplo, a variável contador terá seu valor incrementado de um até que assuma o valor 29. Listagem 24 - Sintaxe da Estrutura WHILE while (condicao){ //Conjunto de instruções para condição VERDADEIRA instrucao_1; instrucao_2; } Listagem 25 - Exemplo de uso da Estrutura WHILE int contador = 1; while (contador < 30){ System.out.println(“Contador no número: ” + contador ); contador = contador + 1; } 10.4 Estrutura de repetição do/while A estrutura de repetição do/while é exatamente igual à estrutura while. A diferença é que a condição é verificada ao final do bloco, de forma que ele é executado pelo menos uma vez. A sintaxe básica e um exemplo de uso encontram-se, respectivamente, na Listagem 26 e na Listagem 27. 28 Listagem 26 - Sintaxe da Estrutura DO/WHILE do{ /* Conjunto de instruções para condição VERDADEIRA. Executado pelo menos uma vez.*/ instrucao_1; instrucao_2; }while (condicao) Listagem 27 - Exemplo de uso da Estrutura DO/WHILE int valor = 1; do { total += valor; valor++; } while (valor <= 100) System.out.println(“Soma é “ + total); 10.5 Estrutura de repetição for A estrutura de repetição for implementa repetições baseada em índices. A sintaxe básica pode ser encontrada na Listagem 28. No exemplo da Listagem 29, as instruções contidas no bloco do for serão executadas até que a variável contador assuma valor 10. Essa variável é inicializada com o valor 2 e é incrementada de uma unidade a cada laço. Listagem 28 - Sintaxe da Estrutura FOR for ( [iniciações]; [condição];[incremento]){ instrucao_1; instrucao_2; } Listagem 29 - Exemplo de uso da estrutura FOR int x = 0; for (int contador = 2; contador <= 10; contador++){ x += contador; System.out.println(“Valor de x = ” + x); System.out.println(“Valor do contador = ” + contador); } 29 10.6 Estrutura de seleção múltipla switch A estrutura de seleção múltipla switch é usada quando o algoritmo tem uma série de decisões a tomar em que uma variável ou condição é testada separadamente para cada um dos vários valores integrais constantes que ela pode assumir e ações diferentes são tomadas. A sintaxe básica pode ser vista na Listagem 30. Exemplo de uso na Listagem 31. Listagem 30 - Sintaxe da Estrutura SWITCH switch (expressão) { case cte1: [ comandos1; break; ] case cte2: [ comandos2; break; ] ... default: comandos; } Listagem 31 - Exemplo de uso da estrutura SWITCH int x = 0; switch(x){ case 1: System.out.println(“x = 1”); break; case 2: System.out.println(“x = 2”); break; case 3: System.out.println(“x = 3”); break; default: System.out.println(“x não é igual a 1 nem a 2 nem a 3”); } 30 10.7 Interrompendo os Fluxos de Controle A interrupção das estruturas de controle pode ser feita usando: • return – interrompe a execução do método em questão, retornando o controle para o método chamador. • break – interrompe a execução do fluxo atual saindo para a linha de comando imediatamente posterior ao final do bloco de execução. • continue - desvia a execução do programa para o início do bloco de execução, evitando a execução dos trechos posteriores a chamada do continue. 11. Vetores/Arrays Um vetor ou array é um conjunto de posições seqüenciais na memória que possuem o mesmo nome e o mesmo tipo. Pode ser formado por tipos primitivos ou objetos (referências). As suas principais características são: Tamanho fixo. É preciso criar um novo vetor e copiar o conteúdo do antigo para o novo. Vetores não podem ser redimensionados depois de criados; • Quantidade máxima de elementos obtida através da propriedade length (comprimento do vetor); • Verificados em tempo de execução. Tentativa de acessar índice inexistente provoca um erro na execução; • Tipo definido. Pode-se restringir o tipo dos elementos que podem ser armazenados. Para declarar um vetor em Java, utilizada-se os colchetes ([]), como visto na Listagem 32. Observa-se que basta adicionar [] na declaração da variável, podendo ser antes ou depois do nome da variável. Neste exemplo, um vetor de inteiros e um vetor de valores double são declarados. Listagem 32 – Declaração de um Vetor int[] vetorInt_1; double vetorInt_2[]; Os vetores ocupam espaço em memória. Além de definir o tipo do vetor, é necessário definir o número de elementos que ele vai suportar. Isso é feito a partir do operador new, durante a inicialização do vetor. No exemplo da Listagem 33, são 31 alocados 10 elementos para o vetor de inteiros vetorInt. Quando o tamanho do vetor é definido, automaticamente, as variáveis numéricas do tipo de dados primitivo são inicializadas com zero; as variáveis booleanas são inicializadas com false; e as referências (variáveis de tipos não-primitivo) são inicializadas com null. Listagem 33 – Inicialização de um Vetor 1 int[] vetorInt; //Declara o vetor vetorInt = new int[10]; //Inicializa o vetor, alocando 10 posições em memória O tamanho do vetor pode ser alocado informando o número de dados entre colchetes ou informando os elementos do vetor entre chaves, separados por vírgula, como apresentado na Listagem 34. Neste último caso, a quantidade de elementos do vetor é definida pela quantidade de elementos definida entre chaves. Além disso, a inicialização de um vetor usando os valores entre chaves só pode ser feita na linha de declaração do vetor. Listagem 34 – Inicialização de um Vetor 2 int[] vetorInt = new int[10]; //Declara e inicializa vetor de 10 posições String[] vetorString = {“Sol”, “Mar”, “Terra”, “Lua”}; Os elementos de um vetor são acessados fornecendo o nome do vetor seguido pelo número da posição do elemento particular entre colchetes([]). O primeiro elemento do vetor encontra-se na posição zero. No exemplo da Listagem 35, uma variável vetorD é declarada como um vetor de double de 3 elementos. Durante a declaração e inicialização do vetor, todos os elementos recebem o valor zero. Em seguida, cada elemento do vetor recebe um novo valor. Por fim, a frase “O terceiro elemento do vetor é o 3.6” é exibida na tela. O valor 3.6 é exibido a partir do acesso ao terceiro elemento do vetor, de índice igual a 2. Listagem 35 – Acessando Elementos de um Vetor double vetorD = new double[3]; //Declaração e inicialização do vetor. //Todos os elementos recebem o valor 0.0 vetorD[0] = 2.4; //Primeira posição do vetor vetorD[1] = 2.0; //Segunda posição do vetor vetorD[2] = 3.6; //Terceira posição do vetor System.out.println(“O terceiro elemento do vetor é o = ” + vetorD[2] ); 32 A Listagem 36 apresenta um exemplo de uso com vetor. No método principal, inicialmente, um vetor de nome n é declarado e inicializado com 10 posições. Em seguida, utilizando a estrutura de um loop for, percorre-se todos os elementos do vetor e atribui a eles o valor da variável de controle do for (no caso, o valor da variável i) multiplicada por 2. Observa-se que na condição da estrutura do for, o valor de i deve ser menor que n.length. A expressão n.length determina o comprimento do vetor. Nestecaso, como o vetor tem tamanho igual a 10, o laço do for será executado até que o valor da variável dontrole i seja menor que 10. Listagem 36 – Exemplo que Utiliza Vetor – Elementos: Inteiros public class Vetor{ public static void main( String args[] ){ int n[]; //Declara referência para um vetor n = new int[10]; //Aloca dinamicamente o vetor //Atribuição de valores ao vetor for (int i = 0; i < n.length; i++){ n[i] = i * 2; } } } A Listagem 37 apresenta um exemplo com vetor, onde esse vetor é formado por elementos que são objetos do tipo Casa. Listagem 37 – Exemplo que Utiliza Vetor – Elementos: Objetos public class VetorObjeto{ public static void main( String args[] ){ Casa c[]; //Declara o vetor c como sendo do tipo Casa c= new Casa[3]; //Aloca dinamicamente o vetor. Elementos //inicializados com null Casa c1 = new Casa(); //Atribuição de valores ao vetor c[0] = new Casa(); 33 c[1] = new Casa(); c[2] =c1; //Acesso aos elementos do vetor c1.setNumPortas(4); //Atribui o valor 4 ao nº de portas do 3º //elemento do vetor int numJan = c[2].getNumJanelas(); //Atribui à variável numJan o //nº de janelas do 2º elemento //do vetor } } 11.1 Vetores/Arrays Multidimensionais Os vetores que possuem dois subscritos são utilizados na representação de tabelas de valores organizadas em linhas e colunas. A identificação de um elemento da tabela é feita através da especificação dos dois subscritos. Convencionalmente, o primeiro subscrito indica a linha do elemento e o segundo, a sua coluna. Os vetores que possuem apenas dois subscritos são chamados de vetores bidimensionais. Já os vetores multidimensionais são aqueles que possuem mais de um subscritos, ou seja, podem ter também dois ou mais de dois. A linguagem Java não suporta diretamente vetores multidimensionais, porém um vetor de um único subscrito pode ser declarado e possuir elementos que são outros vetores de também apenas um subscrito, portanto, atingindo o mesmo efeito. Um exemplo de vetor bidimensional pode ser visto na Listagem 38. Listagem 38 – Exemplo que utiliza vetor bidimensional public class VetorBidimensional{ public static void main( String args[] ){ int vb[][]; //Declara referência para um vetor bidimensional vb = new int[2][3]; //Aloca dinamicamente o vetor com //duas linhas e três colunas //Atribuição de valores ao vetor bidimensional vb vb[0][0] = 1; //Elemento da 1ª linha e da 1ª coluna vb[0][1] = 2; //Elemento da 1ª linha e da 2ª coluna 34 vb[0][2] = 15; //Elemento da 1ª linha e da 3ª coluna vb[1][0] = 13; //Elemento da 2ª linha e da 1ª coluna vb[1][1] = 12; //Elemento da 2ª linha e da 2ª coluna vb[1][2] = 5; //Elemento da 2ª linha e da 3ª coluna /* Vetor bidimensional declarado e inicializado com valores entre chaves */ int vb_2[][] = { { 10, 11, 12}, { 20, 21, 22} }; //Escrita na tela dos elementos de vb e vb_2 for ( int i = 0; i < 2; i++ ){ for ( int j = 0; j < 3; j++ ){ System.out.println(“vb[” + i + “][” + j “] = ” vb[i][j]); System.out.println(“vb_2[” + i + “][” + j “] = ” vb_2[i][j]); } } } } 11.2 Estrutura for Aprimorada Antes da versão do Java 5.0, os elementos de um vetor somente eram percorridos através de uma instrução for controlada por contador, como mostrado no exemplo da Listagem 39. Listagem 39 – Exemplo que percorre vetor com for controlado por contador public class Vetor{ public static void main( String args[] ){ int vetor[] = {43, 75, 867, 89, 20}; int soma = 0; //Soma valores ao vetor for (int i = 0; i < vetor.length; i++){ soma += vetor[i]; } 35 System.out.println(“Soma = ” + soma); } } Com a estrutura do for aprimorada introduzida no Java 5.0, é possível percorrer os elementos de um vetor sem utilizar um contador. A sua sintaxe está apresentada na Listagem 40. O parâmetro é formado por um tipo e um identificador, por exemplo: int i e nomeDoVetor é o vetor que se deseja percorrer. O tipo do parâmetro corresponde ao tipo dos elementos do vetor. Listagem 40 – Sintaxe da Estrutura for Aprimorada for (parâmetro:nomeDoVetor) instrução A Listagem 41 apresenta a versão do código presente na Listagem 40 utilizando a estrutura aprimorada do for. Observa-se que essa estrutura simplifica o código para percorrer um vetor. Essa estrutura somente pode ser utilizada para acessar elementos do vetor. Se houver a necessidade de modificar elementos do vetor, deve-se utilizar a estrutura do for tradicional controlada por contador. Listagem 41 – Exemplo que percorre vetor com estrutura for aprimorada public class Vetor{ public static void main( String args[] ){ int vetor[] = {43, 75, 867, 89, 20}; int soma = 0; //Soma valores ao vetor for (int i : vetor){ soma += i; } System.out.println(“Soma = ” + soma); } } 36 12. Programação Orientada a Objetos A programação orientada a objetos tem como objetivo principal atingir um desenvolvimento interativo e incremental, criação rápida de protótipos, códigos reutilizáveis e extensíveis através do encapsulamento dos dados e uma modelagem do problema a ser solucionado. Esta seção destina-se a descrever conceitos relacionados à orientação a objetos. 12.1 Abstração “Uma abstração denota as características essenciais de um objeto que o distingue de todas as outras espécies de objetos e assim provê limites conceituais bem definidos, sempre relativos à perspectiva de um observador.” A Figura 3 apresenta um exemplo de abstração. Neste exemplo, a figura do gato é vista por dois observadores diferentes: a vovó e a veterinária. Ambos observadores sabem que o que estão vendo é um gato, porém cada um deles vê o gato com um propósito diferente, ou seja, a vovó vê o gato como um animal para brincar e fazer carinho, já a veterinário o vê como um objeto de estudo. Resumindo, a figura do gato é identificada e distinguida das outras espécies de objetos existentes, porém apresenta significado diferente dependendo do observador (vovó ou veterinária). Figura 3 - Abstração 37 12.2 Objetos Os objetos são abstrações de dados do mundo real, com nomes de operações e um estado local. Em outras palavras, um objeto é qualquer coisa, real ou abstrata, sobre a qual armazenamos dados e operações que manipulam os dados. O estado interno de um objeto é descrito por seus atributos que somente são acessados ou modificados a partir de operações definidas pelo criador do objeto. 12.3 Classes Uma classe é um modelo para um objeto. Um conjunto de objetos que possuem o mesmo tipo pode ser agrupado em uma classe. Uma classe define o comportamento dos objetos, através de métodos, e quais estados ele é capaz de manter, através de atributos. A Listagem 42 apresenta a sintaxe básica de uma classe. A definição da classe é feita a partir da palavra chave class seguida do nome da classe (definida pelo programador). Listagem 42 - Sintaxe Geral de uma Classe class nomeClasse{ //Atributos //Métodos } Observa-se que uma classe é formada por um conjunto de atributos e métodos. Os atributos podem ser definidos como características que mudam de indivíduo para indivíduo. Jáos métodos representam o comportamento de um objeto. Na estrutura de um método encontra-se a codificação que utiliza os atributos do objeto para realizar seus objetivos, também podendo receber informações externas (parâmetros) quando os atributos da classe não são suficientes para a realização de suas atividades. Cada classe deve ser definida dentro de um arquivo com extensão .java que possui o mesmo nome da classe. Um arquivo fonte só pode ser composto de uma única classe. A Listagem 43 exibe um exemplo de uma classe de nome Retangulo. Essa classe deve pertencer ao arquivo Retângulo.java. Ela é formada por um conjunto de atributos do tipo float e um único método sem retorno chamado translacao. Esse método recebe informação externa, ou seja, recebe como argumento os parâmetros do tipo float x e y. 38 Listagem 43 – Exemplo de uma Classe class Retangulo{ //Atributos da Classe float orig_x, orig_y; float altura, largura; //Método da classe public void translacao (float x, float y) { orig_x = x; orig_y = y; } } Já a Listagem 44 apresenta exemplos de instanciação de objetos da classe Retangulo. Neste exemplo, dois objetos do tipo Retangulo são criados, recebem valores para alguns de seus atributos e possuem seu método translacao invocado. Observa-se que os objetos são do mesmo tipo, porém apresentam atributos comportamentos de translação diferentes. Listagem 44 – Exemplo de Objetos //Declara e inicializa um objeto do tipo Retangulo Retangulo ret_1; ret_1 = new Retangulo(); //Cria um outro objeto do tipo Retangulo Retangulo ret_2 = new Retangulo(); //Atribui valores a atributos do objeto ret_1 ret_1.orig_x = 10; ret_1.orig_y = 100; //Invoca método do objeto ret_1, passando como argumento os valores 0,0 ret_1.translacao (0, 0); //Atribui valores a atributos do objeto ret_2 ret_2.orig_x = 20; ret_2.orig_y = 87; //Invoca método do objeto ret_2, passando como argumento os valores 5,5 ret_2.translacao (5, 5); 39 12.4 Passagem de Argumentos para Métodos Existem duas maneiras de se passar argumentos/parâmetros para métodos: passagem por valor ou passagem por referência. Na passagem do argumento por valor, uma cópia do valor do argumento é feita e passada para o método chamador. Neste caso, alterações na cópia do método chamado não afetam o valor da variável original no método de chamada. Já na passagem por referência, o método chamado é capaz de acessar diretamente os dados do método chamador e alterar esses dados se o método chamado assim o escolher. Em Java, não é permitido ao programador escolher se deseja passar o argumento por valor ou por referência. Os argumentos são sempre passados por valor. No caso dos tipos primitivos, uma cópia do valor da variável é passada. Dessa forma, as modificações na cópia desse valor feitas dentro do método não influenciam o valor da variável original. Já no caso de variáveis do tipo referências a objetos, uma cópia da referência original é passada ao método. Essa cópia da referência aponta para o mesmo objeto que o apontado pela referência original. Referências geralmente são implementadas como endereços de objetos, quando uma referência é passada como parâmetro, uma cópia deste endereço é criada e apesar de ser uma cópia este endereço aponta para o mesmo objeto, podendo assim modificá-lo. Um exemplo de passagem por valor pode ser visto na Listagem 45. Neste exemplo, a classe Retangulo possui o método translacao que recebe dois argumentos do tipo float. Ambos os argumentos são passados por valor, já que são de tipo de dados primitivo. No método principal, cria-se o objeto ret do tipo Retângulo. Em seguida, declara-se duas variáveis do tipo float (x1 e y1) que serão passadas como argumentos para o método translacao do objeto ret. No final do método translacao, as variáveis passadas como argumentos recebem o valor zero. Essa alteração é vista apenas dentro do método chamado e não também pelo método chamador. Isso pode ser comprovado, ao ver o resultado das duas linhas finais do método principal, que irão escrever na tela: O valor da variável x1 é 15.5 O valor da variável y1 é 10.0 Ou seja, a alteração de atribuir zero aos argumentos recebidos pelo método translacao não é vista pelo método chamador. 40 Listagem 45 – Exemplo de Passagem por Valor class Retangulo{ //Atributos da Classe float orig_x, orig_y; float altura, largura; //Método da classe public void translacao (float x, float y) { //Realiza translação orig_x = x; orig_y = y; //Altera valores dos argumentos x = 0.0; y = 0.0; } public static void main( String args[] ){ Retangulo ret = new Retangulo(); float x1 = 15.5; float y1 = 10.5; ret.translacao(x1, y1); System.out.println(“O valor de x1 é ” + x1); System.out.println(“O valor de y1 é ” + y1); } } Para exemplificar a passagem da cópia da referência, considere os vetores que são tratados em Java como objetos. Neste caso, quando passados aos métodos são passadas cópias de suas referências. Assim, um método chamado pode acessar os elementos dos vetores originais do chamador. Na Listagem 46, pode-se exemplificar a passagem da cópia de uma variável primitiva e a passagem de uma referência utilizando um vetor. A classe Vetor é formada por uma variável global mensagem do tipo String, um método sem retorno chamado inicializa que não recebe nenhum argumento, um método sem retorno chamado modificaVetor que recebe como argumento um vetor (passado por referência), um 41 método sem retorno chamado modificaElemento que recebe como argumento um elemento do tipo inteiro (passado por valor) e um método chamado getMensagem que retorna um objeto do tipo String. No método inicializa, um vetor v é declarado com 5 elementos (1, 2, 3, 4, 5). Em seguida, a variável mensagem recebe o valor: Os valores originais do vetor são: 1 2 3 4 5 O próximo passo é a invocação do método modificaVetor passando v como argumento. Esse método faz com que todos os elementos do vetor sejam multiplicados por 3 e essa modificação vai ser vista pelo método chamador, já que o vetor foi passada uma cópia da referência. A seguir, a variável mensagem passa a ser: Os valores originais do vetor são: 1 2 3 4 5 Os valores do vetor após modificação são: 3 6 9 12 15 Por fim, o segundo elemento do vetor v[2] é passado para o método modificaElemento. Dentro deste método, o argumento passado é multiplicado por 3. O que se poderia esperar era que v[2] passasse do valor 9 para o valor 27, mas não é o que acontece, já que uma cópia de v[2] é passada ao método, portanto não altera em nada no valor do elemento de v. Dessa forma, a variável mensagem passa a ser: Os valores originais do vetor são: 1 2 3 4 5 Os valores do vetor após modificação são: 3 6 9 12 15 O valor de v[2] é 9 O método principal da classe cria um objeto do tipo vetor, invoca o método inicializa e escreve na tela o conteúdo final da variável mensagem, que é o valor exibido anteriormente. Listagem 46 – Exemplo de Passagem por Referência class Vetor{ //Variável global da classe String mensagem; //Método de inicialização do vetor public void inicializa(){ //Declaração e inicialização do vetor int v[] = {1, 2, 3, 4, 5}; 42 //Mensagem a ser escrita na tela mensagem = “Os valores originais do vetor são: ”; for (int i = 0; i < v.length; i++ ){ mensagem+= “ ” + v[i]; } /* Chamada ao método modificaVetor, passando v1 como argumento. Passagem por referência */ modificaVetor( v ); mensagem += “\nOs valores do vetor após modificação são: “; for (int i = 0; i < v.length; i++ ){ mensagem += “ ” + v[i]; } modificaElemento( v[2] ); mensagem += “\n O valor de v[2] é =” + v[2]; } //Modifica todos os elementos do vetor. Multiplica todos por 3 public void modificaVetor( int v1[] ){ for (int j = 0; j < v1.length; j++ ){ v1[j] *= 3; } } //Modifica um elemento, multiplicando-o por 3 public void modificaElemento( int elem ){ elem *= 3; } //Retorna a mensagem public String getMensagem(){ return mensagem; } public static void main( String args[] ){ Vetor v = new Vetor(); v.inicializa(); System.out.println( v.getMensagem() ); 43 } } 12.5 Lista de Argumentos de Comprimento Variável (Varargs) Lista de argumentos de comprimento variável (Varargs) é um recurso disponibilizado pela versão do Java 5.0. Com esse recurso, é possível criar um método com um número não especificado de argumentos. Para isso, basta declarar como parâmetro um tipo de argumento seguido por reticências (...), indicando que o método recebe um número variável de argumentos desse tipo específico. As reticências só podem aparecer uma única vez na lista de argumentos do método e juntamente com seu tipo devem ser colocados no final da lista de argumentos. A Listagem 47 mostra a método media que recebe uma lista de argumentos de comprimento variável do tipo double. Esse método é invocado três vezes no método principal da classe TesteVarargs e com lista de argumentos de tamanhos diferentes. Listagem 47 – Exemplo de Uso de Varargs public classe TesteVarargs(){ public static double media (double... numeros){ double total = 0.0; for (int i = 0; i < numeros.length; i++){ total += números[i]; } return total/numeros.length; } public static void main (String args[] ){ double d1 = 10.0; double d2 = 20.0; double d3 = 30.0; double d4 = 40.0; double media1 = + media(d1, d2); System.out.print(“Média entre ” + d1 + “ e ” + d2 + “=” + media1); double media2 = + media(d1, d2, d3); 44 System.out.print(“Média entre ” + d1 + “, ” + d2 + “ e ” + d3 “=” + media2); double media3 = + media(d1, d2, d3, d4); System.out.print(“Média entre ” + d1 + “, ” + d2 + “, ” + d3 + “ e ” + d4 + “ =” + media3); } } 12.6 Retorno de Métodos Os métodos que possuem a necessidade de retornar valores devem ser declarados com tipo de retorno. Além disso, o valor retornado deve ser do tipo declarado. A Listagem 48 apresenta um exemplo de método sem retorno. Este tipo de método é identificado pela palavra chave void. Já a Listagem 49 mostra um exemplo de um método que retorna um valor do tipo int. Observe que o método deve ser declarado com o tipo de retorno int e o retorno é feito através da palavra chave return. Listagem 48 – Exemplo de Método sem Retorno void metodoSemRetorno(){ //Método sem retorno } Listagem 49 – Exemplo de Método com Retorno int metodoComRetorno(){ //Método com retorno return 10; } Um método pode declarar apenas um tipo de retorno. Se for desejado que ele retorne mais de um valor, é possível declarar o tipo de retorno como sendo um vetor. Por exemplo, no método da Listagem 50 é necessário retornar dos valores do tipo int. Para isso, um vetor de inteiros com duas posições é declarado e possuem os valores que devem ser retornados. 45 Listagem 50 – Exemplo de Método com Mais de um Valor como Retorno int[] getValores(){ int retornos[] = new int[2]; retornos[0] = 9; retornos[1] = 23; return retornos; } Em Java não é necessário utilizar o valor de retorno de um método, por exemplo, a Listagem 51 e a Listagem 52 ilustram o uso do valor de retorno e o não uso ao se invocado o método presente na Listagem 50, respectivamente. Listagem 51 – Exemplo de Uso de Valor de Retorno int[] a = getValores(); Listagem 52 – Exemplo de Não Uso de Valor de Retorno getValores(); 12.7 Construtores Um método construtor é aquele que é chamado quando um objeto de uma classe é instanciado. É utilizado para fazer inicializações necessárias das variáveis de instância da classe, ou seja, inicializar o estado de um objeto da classe. Um construtor possui o mesmo nome da classe e não possui tipo de retorno, já que quando invocado a partir do operador new o retorno será a instância. Se nenhum construtor for definido pela classe, o compilador Java cria automaticamente um construtor vazio sem argumentos. Na Listagem 53, a classe ExemploConstrutor possui três tipos de construtores diferentes. Cada uma com suas respectivas listas de parâmetros e inicializações distintas. Uma classe que apresenta mais de um construtor, ou seja, possui construtores com listas de argumentos distintas, possui construtores sobrecarregados. Listagem 53 – Exemplo de Classe com Construtores Definidos public class ExemploConstrutor{ private int var_1; private double var_2; private String var_3; 46 public ExemploConstrutor(){ var_1 = 10; var_3 = “Exemplo Construtor sem Argumentos”; } public ExemploConstrutor(int valor){ var_1 = valor; var_2 = 15.0; } public ExemploConstrutor(int valor1, String valor2){ var_1 = valor1; var_3 = valor2; } } 12.8 Sobrecarga de Métodos A sobrecarga de um método consiste em utilizar um mesmo nome para vários métodos, porém cada método possui uma lista de parâmetros distinta. Vários métodos podem ser sobrecarregados em uma única classe. A Listagem 54 exibe alguns métodos sobrecarregados. Observa-se que todos os métodos possuem o mesmo nome e tipo de retorno, porém as listas de parâmetros deles são diferentes entre si. É importante saber que ao realizar sobrecarga de métodos apenas é permitido alterar o seu tipo de retorno, se e somente se, os métodos com mesmos nomes tiverem listas de parâmetros distintas. Listagem 54 – Exemplo de Sobrecarga de Métodos public class ExemploSobrecarga(){ public void metodoSobrecarregado(int param1){} public void metodoSobrecarregado(String param1){} public void metodoSobrecarregado(int param1, String param2){} public void metodoSobrecarregado(String param1, int param2){} public void metodoSobrecarregado(double param1, String param2, int param3){} } 47 13. Igualdade entre Objetos A comparação entre dois tipos primitivos em Java se faz a partir do operador ==, já em relação a objetos a utilização desse operador vai fazer com que os valores de suas referências à memória sejam comparados e não o seu conteúdo semântico. Assim, utilizar o operador == para comparar objetos pode gerar desigualdades para objetos idênticos. No caso das Strings, para resolver este problema basta utilizar o método equals(), como mostrado anteriormente. Para compreender o porquê de dois objetos iguais serem diferentes quando comparados com o operador == é necessária a introdução de dois conceitos: Pilha ou Memória Stack (Pilha) e Heap de Objetos. Assim como em outras linguagens de programação, em Java, as estruturas de dados usadas durante a execução do programa na memória são mapeadas em duas partes: a memória stack e a memória heap. A memória stack (tambémconhecida como pilha) possui uma estrutura organizada de maneira seqüencial, em que os dados são colocados uns sobre os outros. Os dados primitivos e as referências a objetos são armazenados na pilha. A memória heap consiste em uma estrutura de armazenamento não ordenada, mas ampla, que pode ser acessada diretamente. Ela contém os objetos na linguagem Java. O acesso direto a esses objetos somente é feito porque se sabe onde o dado desejado está localizado a partir de sua referência armazenada na memória stack. Para melhor entender esses conceitos, observe os exemplos presentes na Listagem 55 e Listagem 56. A Listagem 55 apresenta uma classe Teste com uma única variável e dois métodos. Já a Listagem 56 possui a TesteStackHeap com um método principal onde algumas variáveis são instanciadas. No método principal, uma variável double é criada e recebe o valor 12.5, uma variável do tipo int é instanciada e recebe o valor 23, dois objetos do tipo Teste são instanciados e atribuídos as variáveis teste1 e teste2, que guardarão suas referências e uma outra variável do tipo Teste é declarada e recebe o conteúdo da variável teste2. Listagem 55 – Exemplificando memória stack e heap de objetos public class Teste(){ private int var1; public void setVar1(int valor){ var1 = valor; } public int getVar1(){ 48 return var1; } } Listagem 56 – Exemplificando memória stack e heap de objetos public class Teste (){ public static void main(String args[]){ Teste teste1 = new Teste(); Teste teste2 = teste1; int b = 23; double a = 12.5; Teste teste3 = new Teste(); } } A Figura 4 mostra como ficaria a configuração do stack e do o heap após a execução do método principal da Listagem 56. Observa-se que no stack as variáveis primitivas e as referências aos objetos são armazenadas. Já o heap possui os dois objetos instanciados. Como as variáveis teste1 e teste2 possuem a mesma referência, elas apontam para o mesmo objeto no heap (Objeto 1). Figura 4 - Ilustração de Stack e Heap Após a apresentação desses conceitos, pode-se concluir que um objeto pode ser referenciado por várias variáveis, ou seja, podem existir várias posições na memória stack com o mesmo conteúdo (mesma referência). Dessa forma, comparar objetos a partir do operador == implicará em comparar os valores de suas referências na memória stack, mas na verdade o que se deseja é comparar se dois objetos são iguais no que diz respeito a possuírem os mesmos valores para os seus dados internos. 49 Em Java, a comparação do conteúdo interno dos objetos é feita através do método equals() que retorna verdadeiro se eles forem iguais e falso, caso contrário. Para algumas classes (String, ArrayList), a API Java já disponibiliza esse método implementado. Para as classes criadas pelo programador, esse método deve ser implementado e seu código conterá a comparação entre os dados relevantes dos objetos a serem comparados. 13.1 Método equals() Todas as classes escritas em Java são filhas da classe Object, ou seja, todas as classes herdam os seus métodos. Na classe Object é definido que a comparação entre dois objetos é feita a partir do método equals(), também herdado. Esse método possui a seguinte assinatura: public boolean equals(Object obj) Para comparar dois objetos de uma mesma classe, deve-se sobrescrever o método equals() de maneira que ele possua a funcionalidade de comparar se o objeto recebido é igual ao atual. Para exemplificar o uso do método equals() considere a classe Aluno presente na Listagem 57. Essa classe é formada por duas variáveis (matricula e nome) e quatro métodos de acesso a elas. Listagem 57 – Exemplificando o método equals: Classe Aluno public class Aluno(){ private int matricula; private String nome; public int getMatricula(){ return matricula; } public void setMatricula(int matricula){ this.matricula = matricula; } public String getNome(){ return nome; } 50 public void setNome(String nome){ this.nome = nome; } } A Listagem 58 apresenta uma aplicação que compara objetos da classe Aluno. Inicialmente, duas variáveis (aluno1 e aluno2) do tipo Aluno são criadas e recebem valores para seus atributos. Em seguida aluno1 é comparada a aluno2 e como o resultado é falso é escrito na tela Aluno 1 != Aluno 2. Verifica-se que eles são realmente diferentes, pois não possuem o mesmo conteúdo. Após a comparação entre essas duas variáveis, duas outras são instanciadas (aluno3 e aluno4) e recebem também valores para seus atributos. Dessa vez ambas variáveis possuem mesmo conteúdo, ou seja, valores iguais para seus atributos. Logo em seguida, a variável aluno3 é comparada a aluno4. E o resultado é Aluno 3 != Aluno 4, implicando em novamente um resultado falso e em as variáveis serem verdadeiras. Dessa forma, verifica-se que a comparação não funcionou como o desejado, afinal se dois objetos possuem o mesmo conteúdo, então, a comparação entre eles é para retornar que são iguais. O resultado de que aluno3 é diferente de aluno4 se deve ao fato de o método equals() que os compara ser herdado da classe Object e apenas realizar uma comparação entre as referências dos objetos e não a comparação de atributo a atributo. Portanto, a solução é sobrescrever o método equals(), implementando-o de acordo com as necessidades que indicam se dois objetos da classe Aluno são iguais ou não. Neste caso, dois objetos Aluno são iguais se possuírem todos os seus atributos iguais. Assim, a Listagem 59 apresenta uma nova implementação da classe Aluno, incluindo a implementação do método equals(), que fará com que a comparação entre aluno3 e aluno4 retorne verdadeira. Listagem 58 – Exemplificando o método equals public class ComparaAlunos(){ public static void main(String args[]){ Aluno aluno1 = new Aluno(); Aluno aluno2 = new Aluno(); aluno1.setMatricula(12345); aluno1.setNome(“Carlos”); aluno2.setMatricula(54321); aluno2.setNome(“Janaina”); 51 //Comparando aluno1 com aluno2 if (aluno1.equals(aluno2)){ System.out.println(“Aluno 1 = Aluno 2”); }else{ System.out.println(“Aluno 1 != Aluno 2”); } Aluno aluno3 = new Aluno(); Aluno aluno4 = new Aluno(); aluno3.setMatricula(12345); aluno3.setNome(“Carlos”); aluno4.setMatricula(12345); aluno4.setNome(“Carlos”); //Comparando aluno3 com aluno4 if (aluno3.equals(aluno4)){ System.out.println(“Aluno 3 = Aluno 4”); } else{ System.out.println(“Aluno 3 != Aluno 4”); } } } Listagem 59 – Exemplificando o método equals: Classe Aluno com equals() public class Aluno(){ private int matricula; private String nome; public int getMatricula(){ return matricula; } public void setMatricula(int matricula){ this.matricula = matricula; } public String getNome(){ 52 return nome; } public void setNome(String nome){ this.nome = nome; } public boolean equals(Object obj){ Aluno alun = (Aluno)obj; if (matricula == alun.getMatricula()){ if (nome.equals(alun.getNome)){ return true; } } return false; } } 14. Encapsulamento O encapsulamento determina que os atributos de uma classe devem ser privativos dela, de forma a garantir a confiabilidade das informações registradas em uma instância de uma classe. Em outras palavras, é o processo de ocultar todos
Compartilhar