Classes e Objetos

Propaganda
Rascunho de aulas
Disciplina: Estudo de Linguagem de Montagem /
Laboratório de Linguagens de Montagem
Ciência da Computação – 3o semestre
Professor: José Cláudio Vahl Júnior ([email protected])
Aulas 04 (14/03/06)
Classes e Objetos
Uma classe é um componente de programa que descreve a "estrutura" e o
"comportamento" de um grupo de objetos semelhantes — isto é, as informações que
caracterizam o estado desses objetos e as ações (ou operações) que eles podem
realizar. Os objetos de uma classe — também chamados de instâncias da classe —
são criados durante a execução do programa.
Uma classe é formada, essencialmente, por construtores de objetos dessa
classe, variáveis e métodos. A criação de um objeto dessa classe consiste na criação
de cada uma das variáveis do objeto, especificadas na classe. Os valores
armazenados nessas variáveis determinar o estado do objeto. Uma variável de um
objeto é também chamada de um "atributo" desse objeto.
Objetos podem receber mensagens, sendo uma mensagem basicamente uma
chamada a um método específico de um objeto, que realiza uma determinada
operação, em geral dependente do estado desse objeto. A execução de uma
chamada a um método de um objeto pode modificar o estado desse objeto, isto é,
modificar os valores dos seus atributos, e pode retornar um resultado.
O programa abaixo, ilustra a definição de classes, a criação de objetos e a
chamada de métodos em Java.
public class Ponto {
int x, y;
Ponto ( int a, int b ) {
x = a;
y = b;
}
void move ( int dx, int dy ) {
x += dx;
y += dy;
}
double distancia ( Ponto p ) {
int dx = this.x - p.x;
int dy = this.y - p.y;
double dblDistancia = Math.sqrt( dx * dx + dy * dy );
return dblDistancia;
}
}
1
public class TestePonto {
public static void main( String[] args ) {
Ponto p1 = new Ponto( 0, 0 );
Ponto p2 = new Ponto( 10, 20 );
p1.move( 3, 25 );
p2.move( 1, 14 );
System.out.println( p1.distancia( p2 ) );
}
}
Algoritmo 1 : Exemplo de definição de classe
Declaração de classe
O programa apresentado no Algoritmo 1 define 2 classes: a classe Ponto e a
classe TestePonto. A classe Ponto especifica atributos e métodos para a
representação de pontos no plano cartesiano: os atributos x e y, que representam as
coordenadas de um ponto, e os métodos move e distancia.
A classe TestePonto define um único método: main. A execução de um
programa Java é sempre iniciada com a execução do método main, que deve estar
definido em alguma classe do programa.
O método main, no exemplo acima, contém comandos que ilustram a criação
de objetos da classe Ponto e a chamada de métodos de objetos dessa classe.
Uma declaração (ou definição) de classe é introduzida pela palavra reservada
class, seguida do nome e do corpo da classe. O corpo da classe consiste em um
trecho de programa delimitado pelos símbolos "{" e "}", contendo declarações de
variáveis (atributos) e declarações de métodos.
Uma declaração de método é composta por um cabeçalho e pelo corpo do
método. O cabeçalho de uma declaração de método especifica as seguintes
informações:
•
•
•
o tipo do valor retornado em uma chamada a esse método;
o nome do método;
a lista (possivelmente vazia) dos parâmetros do método, delimitada por
parênteses — cada parâmetro tem um nome e um tipo, o qual determina
o conjunto dos valores que podem ser atribuídos a esse parâmetro, em
uma chamada de método.
A informação sobre os tipos dos parâmetros de um método, juntamente com
o tipo do valor retornado pelo método, é denominada de assinatura do método. A
assinatura de um método determina sua "interface", ou seja, em que contextos e
com quais argumentos o método pode ser usado.
O método move, na classe Ponto, tem dois argumentos, dx e dy, ambos do
tipo int. Esses parâmetros representam os deslocamentos a serem adicionados às
coordenadas x e y, de maneira que o ponto se mova para uma nova posição. O tipo
do valor retornado pelo método move é void — isso significa que, de fato, uma
2
chamada a esse método não retorna nenhum valor, mas apenas modifica o estado
do objeto que recebe uma mensagem comandando a execução desse método.
O método distancia tem apenas um parâmetro, p, do tipo Ponto. Um
argumento usado em uma chamada a esse método deve ser, portanto, uma
expressão cujo valor é uma referência a um objeto da classe Ponto (ou a um objeto
de uma subclasse dessa classe, como veremos mais adiante).
O tipo Ponto é um exemplo de um tipo-classe — toda declaração de classe
em um programa introduz também um novo tipo, que pode ser usado como tipo de
variáveis, ou tipo de parâmetros ou do valor de retorno de métodos, declarados
nesse programa.
Uma chamada ao método distancia retorna um valor do tipo double, que
representa a distância entre o objeto que recebeu a chamada a esse método e o
objeto referenciado pelo argumento usado nessa chamada.
O cabeçalho da declaração do método main, na classe TestePonto, tem a
forma padrão da assinatura desse método em qualquer programa Java:
public static void main( String[] args ) {
Essa assinatura especifica o nome do método (main), o tipo do valor
retornado por uma chamada a esse método (void) e a lista de parâmetros do
método (( String[] args )). O parâmetro do método main, embora não seja
usado nesse exemplo, deve ser sempre declarado, obrigatoriamente, na assinatura
desse método. O tipo desse parâmetro — String[] —, assim como o seu uso, serão
explicados mais adiante.
As palavras reservadas static e public especificam modificadores de
classes, variáveis ou métodos. Por enquanto, basta saber que o modificador static
indica, nesse caso, que o método main não é associado a nenhum objeto,
diferentemente, por exemplo, dos métodos move e distancia, da classe Ponto, que
são associados a objetos dessa classe. Diz-se, nesse caso, que main é um método de
classe, ou um método estático. Declarações de métodos estáticos, em Java,
equivalem a declarações de funções ou de procedimentos (esse último caso, se o
método, tal como main, não retorna nenhum valor).
Criação de objetos
O corpo do método main do programa mostrado no algoritmo 1 começa com
duas declarações de variáveis: de nomes p1 e p2, e de tipo Ponto. Em Java, uma
declaração de variável é também um comando, que especifica a criação da variável
declarada. A execução do método main começa, portanto, com a execução da
declaração de p1, seguida da execução da declaração de p2.
Cada uma dessas declarações especifica também o valor a ser armazenado na
variável (objeto) quando ela é criada. O valor a ser armazenado em p1 é o valor
resultante da avaliação da expressão de criação de objeto "new Ponto()". A
3
avaliação dessa expressão cria um objeto da classe Ponto, e retorna um valor que é
uma referência para o objeto criado — ou seja, o endereço de memória onde esse
objeto é armazenado. A figura 1 abaixo ilustra a representação de um objeto da
classe Ponto e uma variável p que contém uma referência para esse objeto.
Objeto da classe Ponto
p
x
0
y
0
Figura 1 : Representação de objetos
Para cada objeto da classe Ponto que é criado, são criadas duas variáveis, x e
y, internamente a esse objeto. Variáveis como x e y são chamadas de variáveis de
instância.
Conforme já vimos, a especificação de um valor inicial para a variável —
comumente chamada de inicialização da variável — é parte opcional da sua
declaração. Se nenhum valor inicial for especificado, é usado, implicitamente, um
determinado valor padrão (também chamado de valor default), que depende do tipo
da variável. Para tipos-classe, o valor inicial padrão é null, que indica referência a
nenhum objeto.
A inicialização das variáveis p1 e p2 usa o que é chamado, em Java, de um
construtor de objetos (ou simplesmente, construtor). O construtor Ponto, usado na
inicialização de p1 e p2, é declarado na classe Ponto. Um construtor pode ser visto
como um método de criação de um objeto. A execução de uma chamada a new tem o
efeito de criar um objeto e, em seguida, executar o corpo do construtor especificado,
fornecendo como resultado uma referência ao objeto criado.
O uso de um construtor deve ter o mesmo nome da classe, e defere da
declaração de um método, além disso, por não especificar nenhum tipo para o
resultado.
Chamada de método
Os três últimos comandos do método main, no algoritmo 1, ilustram
chamadas de métodos. Uma chamada de método especifica um objeto —
denominado de objeto alvo da chamada —, um método a ser executado, e
argumentos usados na execução desse método.
Por exemplo, em "p1.move( 3, 25 )", o objeto alvo é p1, o método é move
(da classe Ponto) e os argumentos são dados pelas expressões 3 e 25. O objeto alvo
4
de uma chamada de um método passa a ser denominado, durante a execução desse
método, de objeto corrente.
A execução de uma chamada a um determinado método m, em Java, consiste
em avaliar as expressões que fornecem os valores dos argumentos passados para m
nessa chamada, atribuir esses valores aos parâmetros de m correspondentes e,
finalmente, executar o bloco de comandos que constitui o corpo desse método. Esse
mecanismo de passagem de parâmetros é conhecido como passagem de parâmetros
por valor.
Uma chamada de método é semelhante a uma chamada de procedimento ou
função. A diferença fundamental é que, no caso de uma chamada de método:
1. Qualquer uso de atributos durante a execução desse método é referente a
atributos do objeto corrente, isto é, do objeto alvo da chamada. Por
exemplo, na execução da chamada de método "p1.move( 3, 25 )", a
atribuição de x += dx faz com que a variável x do objeto referido por p1
seja modificada (passando a conter o valor contido anteriormente em x,
acrescido de 3), e a atribuição y += dy faz com que a variável y desse
objeto seja modificada (passando a conter o valor antes contido em y,
acrescido de 25).
2. Existe uma expressão que, durante a execução de um método, denota o
objeto alvo da chamada a esse método. Em Java, essa expressão consiste
da palavra-chave this. Por exemplo, durante a execução da chamada de
método "p1.distancia( P2 )", cada ocorrência da expressão this
representa o objeto referido por p1; a expressão this.x representa a
variável x do objeto referido por p1 (objeto corrente), e p.x denota a
variável x do objeto referido por p (no caso, o objeto referido por p2).
O valor retornado por uma chamada a um método é especificado, no corpo
desse método, por meio do comando na forma "return e", onde e é uma expressão,
que denota o valor a ser retornado. O tipo dessa expressão deve ser compatível com
o tipo do valor retornado pelo método, especificado pela assinatura desse método.
Por exemplo, a expressão "Math.sqrt( dx * dx + dy * dy)", usada no
comando return do método distancia, é uma expressão de tipo double. Essa
expressão usa o método sqrt, definido na classe Math, para cálculo da raiz quadrada
do valor dado por dx * dx + dy * dy, passado como argumento. A classe math é
uma classe predefinida em Java, que oferece diversas funções matemáticas para uso
em programas escritos na linguagem.
Finalmente, o último comando do método main imprime a distância do ponto
p2 até o ponto p1 — retornada pela expressão "p1.distancia( p2 )". A impressão
desse valor é feita por meio de uma chamada ao método println, enviada ao objeto
System.out.
Atribuição de objetos
Como já foi dito, uma variável de um determinado tipo-classe contém uma
referência a um objeto dessa classe, e não esse próprio objeto. O efeito de um
5
comando de atribuição que tenha uma variável de tipo-classe como alvo é ilustrado
pelo exemplo a seguir, no qual as variáveis a e b são do tipo-classe Ponto.
Ponto a = new Ponto( 1, 2 );
Ponto b;
b = a;
b.y = b.x;
Após a atribuição b = a, essas duas variáveis, a e b, contêm referências para
um mesmo objeto. Desse modo, qualquer mudança no valor de um atributo do
objeto referido por b, tal como no comando "b.y = b.x", irá afetar o valor desse
atributo no objeto referenciado por a (e vice-versa).
O uso de uma variável de tipo-classe em uma expressão, tal como em "b.x",
envolve um acesso indireto (diz-se também que envolve uma "derreferenciação") ao
valor armazenado na variável x. O acesso a esse valor, nessa expressão, envolve
obter o endereço do objeto referenciado por b, armazenado na variável b, e usar
esse endereço para o acesso ao valor inteiro contido em x.
Note que uma atribuição a a ou b não modifica o estado do objeto. Considere
o exemplo:
Ponto a = new Ponto( 0, 0 );
Ponto b;
b = a;
b = null;
Nesse exemplo, a atribuição "b = null" não modifica o objeto anteriormente
referenciado por b (que no caso é o mesmo objeto referenciado por a), mas apenas
muda o objeto referenciado por b: depois dessa atribuição, b contém null,
significando que b não "aponta" para nenhum objeto, enquanto a continua a
"apontar para" o mesmo objeto criado pela avaliação da expressão "new Ponto( 0,
0 )", como ilustra a figura 2 abaixo:
Estado antes de "b = null;"
x
0
y
0
a
b
Estado depois de "b = null;"
x
0
y
0
a
b
null
Figura 2 : Atribuição de variáveis do tipo-classe
6
Variáveis e métodos de objeto e de classe
Variáveis e métodos podem ser estáticos ou não, isto é, podem ser variáveis
e métodos de classes ou de objetos. A declaração de uma variável ou método
estático é feita mediante o uso do modificador static na sua declaração. O exemplo
a seguir ilustra a declaração de variáveis e métodos estáticos e de objetos:
public class C {
static int x;
int y;
static void m1 ( int p ) {
x = p + 1;
}
void m2 ( int p ) {
x = p + 2;
y = p + 1;
}
}
Nessa classe, x é uma variável estática, ou uma variável de classe, ao
contrário de y, que é uma variável de instância, ou uma variável de objeto. De
maneira análoga, m1 é um método estático, ao passo que m2 não.
Uma variável estática, declarada em uma determinada classe, é
compartilhada por todos os objetos dessa classe, sendo criada uma única vez
durante a execução do programa. Uma variável de objeto, ao contrário, é criada a
cada vez que um novo objeto dessa classe é criado.
Uma variável estática x, definida em uma classe C, pode ser usada na forma
C.x, assim como na forma obj.x, onde obj é um objeto da classe C. Naturalmente,
uma variável que não é estática só pode ser usada nessa última forma.
Um método estático apenas pode usar variáveis estáticas. Por exemplo, y não
poderia ser usado no método estático m1, no exemplo acima. Ao contrário, um
método que não seja estático pode usar qualquer variável declarada na classe em
que ele é declarado.
Chamadas a métodos estáticos podem ser feitas segundo a mesma forma
usada para variáveis estáticas. Por exemplo, supondo que um método estático m,
sem parâmetros, é declarado em uma classe C, uma chamada a m pode ser feita na
forma "C.m()" ou "obj.m()", onde obj é um objeto da classe C. Como no caso de
acesso a variáveis não-estáticas, uma chamada a um método não estático só pode
ser feita nessa segunda forma.
O uso do modificador static em uma declaração de variável pode alterar o
comportamento de um programa, como ilustra o exemplo a seguir, em que o
comportamento do programa é diferente dependendo da presença ou não do
modificador static na declaração da variável x.
7
public class C {
static int x;
public static void main ( String[] args ) {
C c1 = new C();
C c2 = new C();
c1.x = 1;
System.out.println( c2.x );
}
}
O resultado impresso por esse programa é igual a 1, pois a atribuição "c1.x =
1;" modifica o valor de c2.x, uma vez que x é uma variável compartilhada por todos
os objetos da classe C.
Se for removido o modificador static na declaração de x, o resultado
impresso pelo programa será igual a 0, que é o valor inicial atribuído à variável x, do
objeto c2, quando esse objeto é criado.
Exercícios
1. Quais são os valores armazenados nas variáveis x e y ao final da execução
do seguinte trecho de programa?
int x;
int y =
x = y *
while (
x = x
y = y
}
10;
3;
x > y ) {
- 5;
+ 1;
2. Qual é o valor da variável s ao final da execução do seguinte trecho de
programa, nos dois casos seguintes:
(a) as variáveis a e b têm, inicialmente, valores 5 e 10, respectivamente;
(b) as variáveis a e b têm, inicialmente, valores 8 e 2, respectivamente.
int s = 0;
if ( a > b ) {
s = ( a + b ) / 2;
while ( a <= b ) {
s = s + a;
a = a + 1;
b = b - 2;
}
}
8
Exercício prático
A classe Calc apresentada abaixo, será utilizada em exercícios posteriores e
define métodos que resolvem alguns problemas como:
- dado um número inteiro, retorna o seu quadrado;
- dados dois números inteiros, retornar a soma dos seus quadrados;
- dados três números inteiros, determinar se são todos iguais ou não.
public class Calc {
/**
* Método que retorna o quadrado de um número
*/
public static int quadrado ( int x ) {
int iQuadrado = x * x;
return iQuadrado;
}
/**
* Método que retorna a soma dos quadrados de dois números
*/
public static int somaDosQuadrados ( int x, int y ) {
int iSomaQuadrados = quadrado( x ) + quadrado ( y );
return iSomaQuadrados;
}
/**
* Método que retorna se três números são iguais ou não
*/
public static boolean tresIguais ( int a, int b, int c ) {
boolean bolIguais = ( ( a == b ) && ( b == c ) );
return bolIguais;
}
/**
* Método usado para testar os métodos implementados nessa classe
*/
public static void main( String[] args ) {
System.out.println("Quadrado de 5: " + quadrado( 5 ) );
System.out.println("Soma dos quadrados de 3 e 4: " +
somaDosQuadrados( 3, 4 ) );
System.out.println("Os valores 3, 3 e 3 são iguais? " +
tresIguais( 3, 3, 3 ) );
}
}
Implemente nessa mesma classe, métodos para resolver os seguintes
problemas:
- dados dois números inteiros, retornar o máximo entre eles (max);
- dados três números inteiros, retornar o máximo entre eles (max3);
- dados três números inteiros, a, b e c, determinar se eles podem
representar os lados de um triângulo ou não (isto é, se existe um
triângulo com lados de comprimentos iguais a a, b e c). (ehTriang)
9
Alguns elementos úteis para a elaboração dos programas:
1. Uma chamada a um método estático pode ser feita da seguinte forma:
Calc.quadrado( 10 );
ou seja, o nome da classe, Calc, na qual o método é definido, seguido do
símbolo "." (ponto) e do nome do método e, finalmente, da lista dos argumentos
usados na chamada. Quando a chamada ocorre na mesma classe em que o método é
definido, o nome da classe (seguido de ".") não precisa ser especificado. Essa
chamada pode ser escrita, então, apenas como:
quadrado( 10 );
Java:
2. Tabela com os operadores de comparação (operadores relacionais) do
Operador
= =
!=
<
>
<=
>=
Significado
Igual a
Diferente de
Menor que
Maior que
Menor ou igual a
Maior ou igual a
Exemplo
1 = = 1
1 != 1
1 < 1
1 > 1
1 <= 1
1 >= 1
Resultado
true
false
false
false
true
true
3. Tabela com os operadores booleanos em Java:
Operador
!
& e &&
| e ||
^
Significado
Negação ("não")
Conjunção estrita e não-estrita ("e")
Disjunção estrita e não-estrita ("ou")
Disjunção exclusiva ("ou exclusivo"), estrita. É igual a true se
um dos argumentos, mas não ambos, for igual a true.
4. Tabela com os operadores aritméticos do Java:
Operador
+
*
/
Significado
Adição
Subtração
Multiplicação
Divisão
%
Resto
Exemplo
2 + 1
2 – 1
2 * 3
5 / 2
5 / 2.0
5 % 2
Resultado
3
1
6
2
2.5
1
10
5. Tabela com a precedência dos operadores comumente usados em Java (o
símbolo "≡" indica equivalência):
Operadores
*, /, %
+, >, <, >=, <=
==, !=
&
^
|
&&
||
? :
Precedência maior
Exemplos
i * j / k ≡ ( i * j ) / k
i+j*k≡ i + ( j * k )
i<j+k≡ i < ( j + k )
b == i < j ≡ b == ( i < j )
b & b1 == b2 ≡ b & ( b1 == b2 )
b1 ^ b2 & b ≡ b1 ^ ( b2 & b )
i < j | b1 & b2 ≡ ( i < j ) | ( b1 & b2 )
i + j != k && b1 ≡ ( ( i + j ) != k ) && b1
i1 < i2 || b && j < k ≡ (i1 < i2) || (b && (j < k))
i < j || b ? b1 : b2 ≡ ( ( i < j ) || b ) ? b1 : b2
Precedência menor
Mais Exercícios práticos
1. Implemente um método que determine se um dado valor inteiro positivo
representa um ano bissexto ou não. No calendário gregoriano, usado atualmente,
um ano é bissexto se for divisível por 4 e não for divisível por 100, ou se for divisível
por 400.
2. Defina um método somaD3 que, dado um número inteiro representado com
até três algarismos, fornece como resultado a soma dos números representados por
esses algarismos. Exemplo: somaD3(123) deve fornecer resultado 6.
3. Defina um método inverteD3 que, dado um número representado com até
três algarismos, fornece como resultado o número cuja representação é obtida
invertendo a ordem desses algarismos. Por exemplo: o resultado de inverteD3(123)
deve ser 321.
4. Implemente um método que retorne o comprimento aproximado de uma
circunferência dado o seu raio.
Dica: defina uma constante com o valor de pi. Em Java isso pode ser feito da
seguinte forma:
final float PI = 3.1416f;
5. Implemente um método que, dados cinco números inteiros, retorne true
se o conjunto formado pelos 2 últimos números é subconjunto daquele formado
pelos 3 primeiros, e false caso contrário.
11
6. Implemente um método que, dado um valor inteiro não-negativo que
representa a nota de um aluno em uma disciplina, retorna o caractere que
representa o conceito obtido por esse aluno nessa disciplina, de acordo com a
tabela:
Nota
0 a 59
60 a 74
75 a 89
90 a 100
Conceito
R
C
B
A
7. Implemente um método que, dados 4 valores inteiros a, b, c, e d, fornece
como resultado a soma das frações a/b e c/d.
12
Download