Buscar

Aula 04 - LPG

Prévia do material em texto

Aula 04: Princípios da Orientação à Objetos 
 
Objetivos 
 
 
 Recordando... 
Na aula passada criamos uma classe Carro, na qual havia alguns atributos e 
métodos. Na classe que usamos para instanciar e testar um objeto da classe 
Carro, que chamamos de classe Principal, iniciamos diretamente os atributos do 
objeto meuCarro, logo após instanciá-lo. Entretanto, esta NÃO é a forma mais 
adequada de fazer isso. 
De fato, ao projetarmos uma classe, devemos ter em mente que o conceito de 
encapsulamento é muito importante na POO. Isto significa que os métodos da classe 
devem, geralmente, serem os únicos responsáveis pelo acesso aos atributos da classe. 
Isso ―esconde‖ o modo de funcionamento da classe, privilegiando o uso da interface da 
classe como forma única de acesso, o que facilita o teste, a manutenção e a 
reusabilidade das mesmas. 
 Uma forma mais adequada 
Para evitarmos iniciar os atributos da classe fora da mesma, devemos criar 
métodos na própria classe que façam esse acesso, tanto de escrita quando de 
leitura, sempre que necessário. 
Podemos alterar a classe Carro, introduzindo um método configurarCarro, que 
seja responsável pela inicialização dos atributos. Entretanto, como passar os 
valores que desejamos para os atributos do carro? A resposta é: através de 
argumentos no método. Ao enviarmos uma mensagem para um objeto da 
classe Carro através do método configurarCarro, devemos especificar os 
valores desejados para o carro (que chamamos de parâmetros correspondentes 
aos argumentos). Ou seja, o método configurarCarro deve estar apto a receber 
esses parâmetros como argumentos e atribuí-los aos atributos do carro (por 
vezes os termos parâmetros e argumentos são usados indiferentemente, mas 
argumentos são tipos formais e parâmetros são valores reais). 
O código a seguir prepara o método configurarCarro para receber os 
argumentos desejados para a classe Carro: 
 Reforçar o conceito de encapsulamento 
 Apresentar métodos parametrizados 
 Introduzir os níveis de acessibilidade de atributos e métodos 
 Esclarecer o uso dos construtores 
 
 
 
void configurarCarro(String m, String c, 
boolean e) { 
 marca = m; 
 cor = c; 
 motorEstado = e; 
} 
Na classe Principal, para realizar a chamada do método, agora devemos passar 
os parâmetros adequados aos argumentos, da seguinte forma: 
meuCarro.configurarCarro("Palio","azul",fals
e); 
 
Na parte de Desafios da aula 1 foram criados o atributo 
rotacaoMotor e os métodos acelerar e desacelerar. Estes 
métodos também podem ser reescritos para receber como 
parâmetros os valores a serem somados ou subtraídos do 
atributo rotacaoMotor. A inclusão do atributo fica assim: 
 
int rotacaoMotor; 
 O código dos métodos acelerar e desacelerar fica assim: 
void acelerar(int parcela) { 
 rotacaoMotor += parcela; 
 System.out.println("O motor foi acelerado 
para " 
 + rotacaoMotor + " 
RPM."); 
} 
void desacelerar(int parcela) { 
 rotacaoMotor -= parcela; 
 System.out.println("O motor foi 
desacelerado para " 
 + rotacaoMotor + " 
RPM."); 
} 
 
E as chamadas dos métodos na classe principal ficam assim: 
 System.out.println("---------"); 
 System.out.println("Acelerando o 
motor..."); 
 meuCarro.acelerar(80); 
 
 System.out.println("---------"); 
 System.out.println("Desacelerando o 
motor..."); 
 meuCarro.desacelerar(120); 
Edite, compile e teste o funcionamento da alteração do 
código. 
 
 
 Definindo a acessibilidade 
De fato, a correção feita no código da classe Carro foi positiva, mas não impede 
o uso indevido dos atributos por uma classe externa. Para tanto, devemos 
utilizar o recurso de definir a acessibilidade do elemento (classe, atributo ou 
método). Fazemos isso, em Java, usando as palavras reservadas public, 
private, package e protected na declaração. 
Os atributos e métodos public constituem a interface da classe e são acessíveis 
a qualquer método de qualquer classe externa. Os atributos e métodos private 
são de acesso exclusivo dos métodos da classe. Os de acessibilidade protected 
são passíveis de acesso por métodos de classes que herdem da classe que os 
definiu (trabalharemos esse conceito mais adiante). O nível package, que é 
assumido como padrão quando nenhum nível de acessibilidade é explicitamente 
definido (o que é o caso do nosso exemplo da aula passada), define o atributo 
ou método como acessível para qualquer método de classe dentro do mesmo 
pacote (este conceito também será trabalhado mais adiante). 
No nosso caso, desejamos restringir o acesso aos atributos da classe Carro. 
Para tanto, vamos defini-los como private. Altere assim o código: 
 private String marca; 
 private String cor; 
 private boolean motorEstado; 
 private int rotacaoMotor; 
 
Agora tente acessar os atributos como feito na aula passada...Pois é, a 
linguagem impede o acesso dessa forma, pois ela passou a ser errada. 
 
 Mais sobre parametrização – set e get 
Além de passarmos parâmetros para os métodos de uma classe durante uma 
mensagem, podemos também receber dados dos métodos chamados. Uma 
forma de fazê-lo é definir um valor de retorno para um método chamado. Os 
campos private de uma classe só podem ser manipulados por métodos dessa 
classe. Qualquer objeto de outra classe (um cliente, portanto), que deseje 
acessar um campo private da classe servidora, deve chamar os métodos public 
que a esta ofereça para manipular seus campos private. As classes costumam 
oferecer métodos public para permitir aos clientes da classe configurar (set - 
atribuir) ou obter (get - obter) valores das variáveis private. Os nomes desses 
métodos não precisam começar com set ou get, mas essa convenção de 
atribuição de nomes é altamente recomendada em Java. Por exemplo, 
podemos criar um método getRotacaoMotor que retorne o valor do atributo 
rotacaoMotor. 
Assim: 
 
 int getRotacaoMotor () { 
 return rotacaoMotor; 
 } 
 
Para chamarmos o método, poderíamos usar este código: 
 
System.out.println("Rotacao do motor = " + 
 meuCarro. getRotacaoMotor () + " 
RPM."); 
 
Da mesma forma, caso quiséssemos criar um método setRotacaoMotor que 
configurasse o valor do atributo rotacaoMotor, ele seria assim: 
 
void setRotacaoMotor (int r) { 
 rotacaoMotor = r; 
 } 
 
e a atribuição de uma valor à rotação teria, portanto, a seguinte sintaxe: 
 
 meuCarro.setRotacaoMotor (1000) 
 
Criando objetos com o uso de construtores 
A criação do método configurarCarro foi uma boa idéia para melhorar o 
encapsulamento da classe. Entretanto, ainda não é a melhor forma de iniciar os 
valores dos atributos. Melhor seria utilizarmos um método especial, que 
chamamos Construtor. O construtor de uma classe é um método 
automaticamente chamado quando instanciamos um objeto dessa classe. Em 
Java, o método construtor tem o mesmo nome da classe e é chamado quando 
usamos o comando new. Reparando bem, no exemplo da classe carro temos: 
new Carro(); o que sugere que estamos chamando um método chamado 
Carro (o uso dos parêntesis é um indicativo disso). Entretanto onde está este 
método que não foi declarado? A resposta é que a linguagem pressupõe que 
toda classe tem um construtor padrão (default), que não possui argumentos e 
inicia os atributos numéricos com zero e os atributos lógicos com false. 
Entretanto, podemos definir explicitamente o construtor da classe, fazendo com 
que ele seja responsável pela iniciação de atributos da classe com valores 
distintos dos valores padrão. Assim, podemos ter: 
 
 public Carro () { 
 marca = "Palio"; 
 cor = "azul"; 
 motorEstado = true; 
 rotacaoMotor = 1000; 
 }Bem, o construtor é um método como os demais e, portanto, também pode 
receber argumentos. Assim, poderíamos melhorar o nosso construtor permitindo 
que recebesse como parâmetros os valores a serem postos inicialmente em 
alguns atributos. Um código possível seria: 
 
 public Carro (String m, String c) { 
 marca = m; 
 cor = c; 
 motorEstado = true; 
 rotacaoMotor = 1000; 
 } 
 
Uma classe pode possuir mais de um construtor. Eles devem ter o mesmo 
nome e diferenciar-se apenas pela quantidade e tipo de argumentos que 
recebem. Isto é chamado de sobrecarga de construtor (veremos o assunto 
sobrecarga de métodos, em detalhes, em uma aula mais à frente). 
Desta forma, podemos ter diferentes construtores para a mesma classe, 
permitindo flexibilizar a criação e a iniciação dos atributos dos objetos em 
diferentes situações que se façam necessárias. 
 
Por exemplo, além dos construtores anteriormente citados, poderíamos ter 
ainda: 
 public Carro (String m, String c, float r) 
{ 
 marca = m; 
 cor = c; 
 motorEstado = true; 
 rotacaoMotor = r; 
 } 
 
 
 
 
 
Pacotes 
No desenvolvimento de pequenas atividades ou aplicações, é viável manter o 
código da aplicação e suas classes associadas em um mesmo diretório de 
trabalho — em geral, o diretório corrente. No entanto, para grandes aplicações é 
preciso organizar as classes de maneira a evitar problemas com nomes 
duplicados de classes e permitir a localização do código da classe de forma 
eficiente. Em Java, a solução para esse problema está na organização de 
classes e interfaces em pacotes. 
Um pacote é uma unidade de organização de código que congrega classes, 
interfaces e exceções relacionadas. O código-base de Java está todo 
estruturado em pacotes e as aplicações desenvolvidas em Java também devem 
ser assim organizadas. 
Essencialmente, uma classe Xyz que pertence a um pacote nome.do.pacote 
tem um ―nome completo‖ que é nome.do.pacote.Xyz. Assim, se outra 
aplicação tiver uma classe de mesmo nome não haverá conflitos de resolução, 
pois classes em pacotes diferentes têm nomes completos distintos. 
A organização das classes em pacotes também serve como indicação para o 
compilador Java para encontrar o arquivo que contém o código da classe. O 
ambiente Java normalmente utiliza a especificação de uma variável de ambiente 
CLASSPATH, a qual define uma lista de diretórios que contém os arquivos de 
classes Java. No entanto, para não ter listas demasiadamente longas, os nomes 
dos pacotes definem subdiretórios de busca a partir dos diretórios em 
CLASSPATH. 
No mesmo exemplo, ao encontrar no código uma referência para a classe Xyz, 
o compilador deverá procurar o arquivo com o nome Xyz.class; como essa 
classe faz parte do pacote nome.do.pacote, ele irá procurar em algum 
subdiretório nome/do/pacote. 
Se o arquivo Xyz.class estiver no diretório /home/java/nome/do/pacote, então o 
diretório /home/java deve estar incluído no caminho de busca de classes 
definido por CLASSPATH. 
Para indicar que as definições de um arquivo fonte Java fazem parte de um 
determinado pacote, a primeira linha de código deve ser a declaração de 
pacote: 
package nome.do.pacote; 
Caso tal declaração não esteja presente, as classes farão parte do ―pacote 
default‖, que está mapeado para o diretório corrente. 
Para referenciar uma classe de um pacote no código fonte, é possível sempre 
usar o ―nome completo‖ da classe; no entanto, é possível também usar a 
declaração import. Por exemplo, se no início do código estiver presente a 
declaração: 
import nome.do.pacote.Xyz; 
 
Então a classe Xyz pode ser referenciada sem o prefixo nome.do.pacote no 
restante do código. Alternativamente, a declaração import nome.do.pacote.*; 
indica que quaisquer classes do pacote especificado podem ser referenciadas 
apenas pelo nome no restante do código fonte. A única exceção para essa 
regra refere-se às classes do pacote java.lang — essas classes são 
consideradas essenciais para a interpretação de qualquer programa Java e, por 
este motivo, o correspondente import é implícito na definição de qualquer 
classe Java. 
Veja no exemplo uma maneira de receber dados a partir do teclado: 
import java.util.Scanner; 
 
public class SomaInteiros { 
public static void main(String[] args) { 
// cria Scanner para obter entrada a partir do teclado 
Scanner objEntrada = new Scanner(System.in); 
int num1; 
int num2; 
int soma; 
System.out.print("Entre o primeiro inteiro: "); 
num1 = objEntrada.nextInt(); 
System.out.print("Entre o segundo inteiro: "); 
num2 = objEntrada.nextInt(); 
soma = num1 + num2; 
System.out.printf("A somoa é %d\n",soma); 
} 
} 
 
COMENTARIOS: 
A declaração import java.util.Scanner; ajuda o compilador a localizar uma classe utilizada nesse 
programa. As bibliotecas de classes Java ou Java Application Programming Interface (API do Java), 
necessitam que se utilize a diretiva import para identificar as classes pré-definidas, utilizadas num 
programa em Java. Neste exemplo foi utilizada a classe Scanner pré-definida no pacote Java.util. 
Um Scanner permite a um programa ler dados, que podem ser provenientes de várias origens, 
como de um arquivo no disco ou digitado pelo usuário a partir do teclado. No exemplo em questão, 
a expressão Scanner objEntrada = new Scanner(System.in); cria um objeto Scanner que lê dados do 
teclado, pois o objeto de entrada padrão que é o teclado é o System.in. Assim, após de obtido um 
objeto da classe Scanner, pode-se utilizar os métodos disponíveis desta classe, neste exemplo a 
expressão num1 = objEntrada.nextInt(); chama o método que inicializa a variável inteira num1 com 
um valor inteiro digitado antes de se pressionar a tecla enter do teclado. o método nextInt() espera 
receber sempre um valor inteiro caso contrário será gerada uma situação de exceção (erro) e o 
programa terminará. 
Note que neste exemplo quando são escritas as mensagens na tela, através do objeto padrão de 
saída, System.out, são usados dois métodos distintos o print() e o printf(). 
 
 
 Definindo Variáveis 
Para manipular dados, eles devem estar associados a variáveis, as quais 
devem ser definidas dentro de tipos( int, double, , etc.) . 
Na programação orientada a objetos, existem quatro tipos de variáveis, no que 
se refere à maneira como são armazenadas pelo interpretador Java: 
 De instâncias (atributos de objetos); 
 De classe; 
 Locais (variáveis e parâmetros de métodos); 
 Constantes. 
 
Para manipular esses tipos de variáveis, é necessário seguir os procedimentos 
de encapsulamento, utilizando o tipo adequado de método dentre os três 
disponíveis: 
De instância (métodos do objeto); 
De classe (métodos da classe); 
Construtores (método especial). 
 
Para definir uma variável, devemos escolher seu tipo ( de acordo com sua 
abrangência de tamanho e valor), um modificador(quando necessário), um 
nome de fácil entendimento e um valor inicial ( que pode ser do tipo primitivo ou 
referência). A sintaxe para definição de variável é: 
[modoficador] tipo identificador = [valor inicial]; 
Exemplos: 
public int idade=18; 
private double salário=1456.3; 
byte base =2; 
 
Entendendo a diferença entre “de classe’ e “de instância” 
Uma variável será considerada de instância quando estiver definida no corpo 
da classe ( ou seja, fora dos métodos) e não possuir o modificador static. 
Uma variável definida como parâmetro de método, ou dentro de um 
método, é chamada local. 
Uma variável definida fora dos métodos, e com modificador static, é 
considerada como atributo de classe. 
 
Uma variável definida fora dos métodos, e com o modificador final, é 
considerada constante. 
Métodos que não apresentam modificador staticsão considerados como de 
instância. Seu efeito se restringe ao objeto chamado pela sua referência. 
Métodos com modificador static são considerados como de classe. Seu efeito 
se estende a todos os objetos oriundos da classe em questão. 
Atributos static 
Até o momento só havíamos aprendido como definir atributos de instância. 
Cada objeto tinha seus próprios atributos e uma modificação nos atributos de 
um objeto não afetava os atributos de outros objetos. 
Neste tópico iremos apreender como definir atributos de classe. Esses atributos 
são os mesmos para todos os objetos de uma classe. Eles são, portanto, 
compartilhados pelos objetos. Uma mudança em um destes atributos é visível 
por todos os objetos instanciados dessa classe. Atributos de classe também são 
chamados de atributos static. 
Para exemplificar, definiremos uma classe Robot que usa um atributo static 
como se fosse um contador, para saber quantos objetos robots foram criados 
(instanciados). 
 //Classe Robot 
class Robot { 
 public int x, y; // posição do robot 
 public static int contador; //contador de instancias 
 public Robot(int x,int y){ 
 this.x = x; this.y = y; 
 contador++; 
 } 
} 
A seguir a classe de teste Principal: 
class Principal { 
 public static void main(String args[]) { 
 Robot.contador=0; //inicializando variavel static 
 Robot r1,r2; 
 System.out.println(Robot.contador); 
 r1 = new Robot(10,12); 
 System.out.println(Robot.contador); 
 r2 = new Robot(11,13); 
 System.out.println(Robot.contador); 
 } //main method 
} //class Principal 
Apesar de termos exemplificado com um inteiro, você poderia ter usado uma 
classe no lugar desse atributo, naturalmente tomando o cuidado de chamar new 
antes de usá-lo. 
 
Métodos static 
Métodos static também são chamados de métodos de classes. Estes métodos 
só podem operar sobre atributos que também sejam static. Assim: 
 public static void metodoA() {... 
Exemplificando, vamos criar um método static para incrementar o contador: 
 public static void incrementa(){ 
 contador++; 
 } 
 
Atributos de instância 
Para armazenar dados dentro de um objeto de modo que, a partir de então, 
possamos acessá-los por meio da referência desse objeto, devemos definir os 
dados em questão como variáveis de instância, fazendo deles atributos de 
instância. Assim, torna-se possível alterar ou consultar o valor desses dados por 
meio da referência do objeto. 
Um atributo de instância é visível a todos os métodos dessa instância. À 
instância de um objeto corresponde uma área de memória, criada para 
armazenar a estrutura desse objeto. Essa área pode conter variáveis de tipo 
primitivo e de tipo de referência. 
Analisemos o exemplo: 
public class Conta{ 
private int numero; 
private double saldo; 
private double juros; 
private java.util.Date vencimento; 
} 
No código anterior é possível notar que a classe possui quatro atributos de 
instância – três do tipo primitivo(numero, saldo e juros) e um do tipo referência 
Java.util.Date(vencimento). A partir da classe Conta, é possível criar vários 
objetos com conteúdos diferentes e que podem ser manipulados por meio de 
referências. 
Veja o código a seguir: 
public class TesteConta { 
public static void main(String[] args){ 
Conta conta1=new Conta(); 
System.out.println(“Ref. conta1: ” + conta1); 
Conta conta2=new Conta(); 
System.out.println(“Ref. conta2: ” + conta2); 
} 
 
 
No código anterior temos duas instâncias de objetos criadas a partir da classe 
Conta, chamadas conta1 e conta2. Veja que elas têm valores diferentes, ou 
seja, são objetos diferentes, podendo conter dados diferentes. 
 
Constantes 
Para declarar uma constante, use a palavra chave final antes da declaração da 
variável e inclua um valor inicial para esta variável. 
Exemplo: 
final float pi=4.141592; 
final boolean debug=false; 
final int maxsize = 40000; 
 
 
 Desafios 
1. Crie métodos set e get para todos os atributos private da classe Carro. 
2. Crie um novo construtor que receba também o estado em que o motor deve ser 
iniciado e inicie a rotação de acordo com o estado passado como argumento: 0 
(para motor desligado) ou 1000 (para motor ligado). 
3. Para a classe Quadrado, criada no desafio da aula passada, crie também 
métodos get e set para o atributo lado. Não permita que o cliente da classe 
passe um valor negativo para o atributo lado. 
4. Crie uma classe Livro, que possua atributos para nome, autor, edição, editora e 
ano de publicação. Os atributos devem ser privados e devem possuir métodos 
de acesso. Deve haver um método que imprima os atributos correntes do livro. 
Deve haver um construtor padrão que inicie os atributos com o valor 
‗desconhecido‘ e a seguir imprima uma mensagem de saudação com os 
atributos correntes. Deve haver construtores alternativos para instanciar os 
objetos da classe Livro com os respectivos parâmetros correspondentes aos 
atributos. Adicionalmente, crie um programa principal que instancie diferentes 
livros chamando os diferentes construtores e os métodos get e set dos atributos 
que se fizerem necessários. 
5. Crie uma classe ContaPoupanca que possui os atributos taxaJurosAnual (que 
guarda a taxa de juros dessa conta) e saldo (que guarda quanto o poupador 
tem em depósito). O construtor da classe deve receber e iniciar os valores dos 
atributos. Forneça o método calculaRemuneracaoMensal que calcula a 
remuneração mensal da conta multiplicando o saldo pela taxa de juros anual e 
dividindo o resultado por 12 e adiciona esse montante ao saldo da conta. 
Forneça também os métodos para modificar a taxa de juros anual e o saldo da 
conta com novos valores. 
Escreva um programa para testar a sua classe ContaPoupanca. Instancie dois 
objetos conta1 e conta2 com saldos $2.000 e $3.000 respectivamente. 
Configure inicialmente taxaJurosAnual como 4% e então calcule a remuneração 
mensal e imprima os novos saldos para os novos poupadores. Em seguida 
configure taxaJurosAnual para 5%, calcule a remuneração do próximo mês e 
imprima os novos saldos para os dois poupadores.

Continue navegando