Sei sulla pagina 1di 61

Alexandre Michael Souza de Melo

Thiers Garretti Ramos Sousa

Programação de
Computadores II
 Jouberto Uchôa de Mendonça
Reitor 
Amélia Maria Cerqueira Uchôa
 Vice-Reitora
 Jouberto Uchôa de Mendonça Junior 
Junior 
Pró-Reitoria Administrativa - PROAD
Ihanmarck Damasceno dos Santos
Pró-Reitoria Acadêmica - PROAC
Domingos Sávio Alcântara Machado
Pró-Reitoria Adjunta de Graduação - PAGR
Temisson José dos Santos
Pró-Reitoria Adjunta de Pós-Graduação
e Pesquisa - PAPGP
Gilton Kennedy Sousa Fraga
Pró-Reitoria Adjunta de Assuntos
Comunitários e Extensão - PAACE
 Jane Luci Ornelas Freire
Freire
Gerente do Núcleo de Educação a Distância - Nead
Andrea Karla Ferreira Nunes
Coordenadora Pedagógica de Projetos - Nead
Lucas Cerqueira do
d o Vale
Coordenador de Tecnologias Educacionais - Nead
Equipe de Elaboração e
Produção de Conteúdos Midiáticos:
Alexandre Meneses Chagas - Supervisor
Ancéjo Santana Resende - Corretor 
Andira Maltas dos Santos – Diagramadora
Claudivan da Silva Santana - Diagramador 
Edilberto Marcelino da Gama Neto – Diagramador 
Edivan Santos Guimarães - Diagramador 
Fábio de Rezende Cardoso - Webdesigner 
Geová da Silva Borges Junior - Ilustrador 
Márcia Maria da Silva Santos - Corretora
Marina Santana Menezes - Webdesigner 
Matheus Oliveira dos Santos - Ilustrador 
Pedro Antonio Dantas P. Nou - Webdesigner 
Rebecca Wanderley N. Agra Silva - Designer 
Rodrigo Otávio Sales Pereira Guedes - Webdesigner 
Rodrigo Sangiovanni Lima - Assessor 
Walmir Oliveira Santos Júnior - Ilustrador 

Redação: M528p Melo, Alexandre Michael Souza de


Núcleo de Educação a Distância - Nead Programação de computadores II / Ale-
Av. Murilo Dantas, 300 - Farolândi  xandre Michael Souza de Melo,
Melo, Thiers Gar-
Gar-
Prédio da Reitoria - Sala 40 retti Ramos Sousa. –Aracaju : UNIT, 2011.
CEP: 49.032-490 - Aracaju / SE
Tel.: (79) 3218-2186 152 p.: il.
E-mail: infonead@unit.br 
Site: www
www.ead.unit.br 
.ead.unit.br  Inclui bibliografia
Impressão: 1. Informática 2.2. Programação
Programação de com-
Gráfica Gutemberg putadores. I. Sousa, Thiers Garretti Ramos
Telefone: (79) 3218-2154 Universidade. II. Tiradentes – Educação
E-mail: grafica@unit.br 
à Distância III. Titulo
Apresentação
Prezado(a) estudante,

A modernidade anda cada vez mais atrelada ao tempo,


e a educação não pode ficar para trás. Prova disso são as
nossas disciplinas on-line, que possibilitam a você estudar
com o maior conforto e comodidade possível, sem perder a
qualidade do conteúdo.

Por meio do nosso programa de disciplinas on-line


você pode ter acesso ao conhecimento de forma rápida,
prática e eficiente, como deve ser a sua forma de comunicação
e interação com o mundo na modernidade. Fóruns on-line,
chats, podcasts, livespace, vídeos, MSN, tudo é válido para
o seu aprendizado.

Mesmo com tantas opções, a Universidade Tiradentes


optou por criar a coleção de livros Série Bibliográfica Unit como
mais uma opção de acesso ao conhecimento. Escrita por nossos
professores, a obra contém todo o conteúdo da disciplina que
você está cursando na modalidade EAD e representa,
sobretudo, a nossa preocupação em garantir o seu acesso
ao conhecimento, onde quer que você esteja.

Desejo a você bom


aprendizado e muito sucesso!

Professor Jouberto Uchôa de Mendonça


Reitor da Universidade Tiradentes
Sumário
Parte 1: Princípios da Programação Orientada a
Objetos em Java. . . . . . . . . . . . . . . . . . . . . 11

Tema 1: Introdução à Orientação a Objetos . . . . . . . . . . . . . . . . . . . . 13


1.1 Revisando a Linguagem Java . . . . . . . . . . . . . . . . . . . . . . . . 14
1.2 Conceitos Básicos da OO . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
1.3 Classes e Objetos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
1.4 Atributos e Métodos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
Resumo. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45

Tema 2: Herança . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .47


2.1 Construtores e o Gerenciamento da Memória . . . . . . . . . 48
2.2 Membros de Instância X Membros de Classe . . . . . . . . . 54
2.3 Herança no Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .61
2.4 A classe Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
Resumo. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75

Parte 2: Aspectos Avançados da Programação


Orientada a Objetos em Java . . . . . . . . . . 77

Tema 3: Polimorfismo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .79


3.1 Do Conceito à Prática . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
3.2 Abstract X Final . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
3.3 Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
3.4 Interfaces Especiais . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
Resumo. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110

Tema 4: Exceções, Coleções e Fluxos . . . . . . . . . . . . . . . . . . . . . . . . 111


4.1 Tratamento de Exceções . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
4.2 Avançando nas Exceções . . . . . . . . . . . . . . . . . . . . . . . . . 120
4.3 Coleções da Java API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
4.4 Fluxo de Dados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .136
Resumo. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
1 Introdução à Orientação a
Objetos

Neste nosso primeiro tema, veremos uma introdução ao conceito


de Orientação a Objetos (OO). Inicialmente, comentaremos o novo
paradigma de desenvolvimento de aplicações e em seguida breves
comentários sobre os conceitos básicos. Haverá ainda a apresentação
dos conceitos de objetos e também de classes, com seus atributos e
métodos. Após este capítulo, estaremos preparados para aprofundar
os pilares da programação Orientada a Objetos.
Caso você já possua alguma informação a respeito da lingua-
gem orientada a objetos (como Java, por exemplo), este livro
pode ajudá-lo a aprofundar os seus conhecimentos, porém não fi-
que preocupado caso você não possua nenhuma experiência com a
programação OO. Como a linguagem Java será usada para ensinar
conceitos de OO, neste primeiro momento será feita uma breve
revisão da linguagem Java (sinta-se à vontade para rever o que achar
necessário sobre a linguagem no livro anterior “Programação de
Computadores I”).
14 Programação de Computadores II

1.1 Revisando a Linguagem Java

História
A tecnologia Java foi criada em 1991, a partir de um projeto
desenvolvido pela empresa Sun Microsystems (este projeto foi lide-
rado por James Gosling), e, poucos anos depois, em 1995, ganhou
notoriedade mundial. Isto aconteceu a partir do momento em que a
linguagem de programação passou a ser o foco principal da empresa.
A plataforma Java, desde o momento inicial, quando foi anunciada pela
Sun, é composta pela Máquina Virtual Java (JVM) e pela API JAVA; logo
em seguida, esta plataforma passou a ser incorporada ao navegador
Netscape Navigator (principal navegador da época). Esse fato impulsio-
nou a grande aceitação no uso da plataforma Java, fato verificado nos
anos seguintes.

Características
Vejamos a seguir algumas características que propiciaram o
crescimento e a grande aceitação da linguagem Java no cenário de
desenvolvimento web.

a) Simplicidade: a linguagem Java é fácil de aprender e de pro-


gramar (ainda mais fácil para desenvolvedores da linguagem
C++, já que tem muita semelhança com ela); para não se tor-
nar complexa, Java não incorporou mecanismos mais sofisti-
cados como ponteiros e herança múltipla; o gerenciamento
de memória ainda está presente na linguagem Java, mas
não é responsabilidade do desenvolvedor, pois o controle é
totalmente automático.

b) Orientada a objetos: Java é uma linguagem totalmente OO,


(quase) tudo é considerado um objeto (a exceção fica por
conta dos tipos primitivos, os quais não são objetos por uma
questão de desempenho); são oferecidos todos os mecanis-
mos presentes nas linguagens orientadas a objetos, como
Tema 1 | Introdução à orientação a objetos 15

c) Portabilidade: a Máquina Virtual Java (JVM – Java Virtual Ma-


chine) garante a portabilidade de todos os programas escri-
tos em Java; estes programas escritos no formato texto (com
a extensão .java) quando são compilados geram automatica-
mente um outro arquivo bytecode com a extensão .class. Este
bytecode pode ser executado em qualquer ambiente desde
que lá contenha a JVM, garantindo a veracidade da frase ”wri-
te once, run anywhere” (traduzindo: escreva uma vez, rode em
qualquer lugar).

Figura 01 - A portabilidade permite que o seu código seja executado em qualquer sistema operacional.

d) Alto desempenho: por executar um código que já foi ana-


lisado e convertido para um bytecode, obtém-se um bom
desempenho. O coletor de lixo é outro fator que melhora a
performance por liberar memória que não está mais sendo
usada. Além disso, o fato de compilar no próprio ambiente
que será posteriormente interpretado, faz com que a perfor-
mance seja melhor.

e) Interpretada: em um primeiro momento ela é compilada,


gerando um arquivo bytecode  intermediário que pode ser
executado em qualquer sistema operacional (desde que con-
tenha a máquina virtual Java).
16 Programação de Computadores II

Figura 02 - Após ser compilado, o arquivo fonte (*.java) se transforma em um arquivo binário (*.class) e em
seguida pode ser interpretado (podendo antes disso ainda ser verificado).

f) Robustez: entre algumas características que tornam lingua-


gem Java robusta, podemos citar: possuir no momento da
compilação uma varredura por código não alcançável; procu-
rar e identificar variáveis não inicializadas; ser uma lingua-
gem fortemente tipada; inicializar de maneira implícita todos
os tipos primitivos e possuir uma eficiente manipulação de
exceções.

g) Segurança: ser uma linguagem segura é muito importante, já


que foi projetada para ambientes distribuídos; a JVM garante
em um ambiente web que todos as aplicações Java fiquem
protegidas de programas maliciosos escritos em qualquer ou-
tra linguagem. Além disso, a coleta automática de lixo tam-
bém evita erros de gerenciamento de memória frequentes em
outras linguagens. Há também mecanismos de tratamento de
erros e o fato de todas as variáveis terem obrigatoriamente
os tipos informados no momento da declaração fortalecem
ainda mais o fator segurança.
Tema 1 | Introdução à orientação a objetos 25

Tomando este caso como referência, podemos fazer um pa-


ralelo e perceber que a OOP segue o mesmo princípio. Um sistema
desenvolvido seguindo o conceito OO é composto por vários objetos
(cada um deles com as suas respectivas propriedades e funcionalida-
des bem definidas). Desta forma o sistema espera apenas que cada
objeto faça a sua parte, sem se preocupar em como isto será feito,
ou seja, não importa a implementação interna de cada um deles, isto
é responsabilidade do desenvolvedor de cada objeto.

Objetivo
O principal objetivo da programação orientada a objeto é faci-
litar o desenvolvimento de software através da reutilização de obje-
tivos anteriormente desenvolvidos. Esta prática faz com que os sis-
temas sejam desenvolvidos mais rápido, de maneira mais confiável
e ainda com custos mais baixos. Este objetivo é conseguido através
diversas características de programação, sendo possível utilizar recur-
sos como instanciação, polimorfismo, herança, interface, entre outros.

Conceitos básicos
Assim como
c omo no paradigma procedural as funções fazem o papel
papel
principal, no paradigma OO, o conceito de objeto é o núcleo principal.
Um objeto pode ser entendido como uma representação codificada
de qualquer entidade do mundo real, onde este objeto é transferido
para linhas de código inclusive com suas características e seus com-
portamentos.
A programação orientada a objetos parte do princípio que os sis-
temas são construídos se espelhando ao máximo nos próprios objetos
da vida real. Antes de fazermos
fazermos um aprofundamento
aprofundamento em cada um dos
pilares da orientação a objetos, devemos ver de maneira simplificada
alguns dos conceitos principais.
Como acabamos de citar,
citar, fica evidente que o elemento conside-
rado ponto de partida para o entendimento do conceito OO é o próprio
objeto. Entendemos um objeto do mundo real como algo concreto (ex:
um livro) ou como algo conceitual (ex: uma viagem). Definimos que um
objeto possui duas características: o seu estado e o seu comportamen-
to. Portanto, podemos entender
entender o objeto como qualquer entidade real
26 Programação de Computadores II

Outro conceito importante é classe, que consiste em um mo-


delo para a “fabricação” dos objetos. Entenderemos a classe como
sendo uma abstração do objeto, algo que define todas as caracte-
rísticas comuns a todos os objetos. Estas características
características podem ser
divididas em estado (que chamamos no momento da codificação de
atributos) e em comportamentos (chamados nos nossos códigos de
métodos). Quando um objeto é criado, diz-se haver construído
uma instância de uma classe. Para que este processo aconteça, é
necessário fazer uso do conceito de construtor (maneira de criar a
instância), conceito que veremos mais adiante (no conteúdo 2.1).
Classes e objetos serão explorados de maneira mais aprofundada
no conteúdo seguinte.
Uma instanciação  consiste em utilizar classes servindo como
modelos para que os objetos possam ser criados. Um objeto pré-
definido passa a exisitr quando um programa é executado, passando
a se chamar então de instância de classe.
Objetos trocam informações entre eles através de mensagens,
esta é a maneira como um objeto invoca um outro objeto ou então
um método de um outro objeto.
objeto. Falaremos
Falaremos um pouco
pouco mais sobre o
conceito de mensagens no conteúdo 1.4 deste mesmo tema.
Há três tipos de comunicação entre os objetos, os quais são
brevemente comentados a seguir:

a) associação: são relacionamentos entre classes (ou entre ob-


jetos). Há dois tipos: agregação e composição;

b) generalização: também conhecida como herança (um dos


pilares da POO), será brevemente comentada ainda neste
mesmo conteúdo, porém o conceito de herança será bastan-
te comentado no tema seguinte (conteúdo 2.3);

c) dependência : uma alteração em um elemento altera


diretamente o seu dependente.

A utilização de uma estrutura bem definida, arrumada em pas-


tas ou pacotes (no inglês package ), facilita bastante a organização
organização de
Tema 1 | Introdução à orientação a objetos 27

uma organização lógica, o uso de pacotes faz com que um conjunto


de classes relacionadas entre si fiquem agrupadas no mesmo diretó-
rio, ou seja, também acaba propiciando uma organização
organização física. Fala-
remos mais sobre pacotes (packages) também no conteúdo seguinte.

Conceitos fundamentais
Iremos explorar mais profundamente nos conteúdos posteriores
os pilares da programação OO – encapsulamento, herança e polimor-
polimor-
fismo – faremos, neste momento, uma breve apresentação sobre cada
um deles:

a) encapsulamento: é a arte de ocultar uma informação ( infor-


infor-
mation hiding ), escondendo o que não precisa ou não devedeve
ser visto; em outras palavras, consiste em controlar o acesso
aos detalhes de implementação de uma classe através do
uso de palavras reservadas (modificadores); como o usuário
não tem acesso à implementação, esta pode ser alterada,
corrigida ou melhorada sem que ele tome conhecimento ou
mesmo tenha que fazer qualquer adaptação.

b) herança: é comum termos mais de uma classe compartilhan-


do estado e comportamentos de uma classe de referência
(estas classes serão chamadas de super-classes enquanto
que as que herdam serão chamadas de sub-classes). O con-
ceito de herança aproveita estas semelhanças para melhorar
o projeto (reduzindo a repetição de código), criando super-
classes que serão compostas justamente por estas simila-
ridades, enquanto que as sub-classes terão como objetivo
refinar e ampliar as características e comportamentos da
classe superior. Não há limites para níveis de herança, po-
rém, especificamente na linguagem Java, há apenas a he-
rança simples (uma sub-classe só pode herdar de uma úni-
ca super-classe), não havendo portanto a herança múltipla
(lembrando que este conceito existe em outras linguagens
orientadas a objetos). De maneira geral, pode ser dito que
a herança é o conceito no qual uma sub-classe herda auto-
maticamente todos os estados e comportamentos de uma
28 Programação de Computadores II

c) polimorfismo: como o nome sugere, significa várias formas


diferentes para a mesma funcionalidade. Uma classe pode
possuir um método com o mesmo nome (ex: ligar   ), mas
com diferentes assinaturas (quantidade e tipos de parâme-
tros distintos) para diversos fins (ex: ligar   o rádio; ligar  o
carro). Não será preciso tomar conhecimento de todas as
implementações deste mesmo método, apenas deve ser su-
ficiente escolher qual delas é a mais apropriada para a sua
necessidade; este processo contribui para a simplificação
do desenvolvimento do software e facilita a reutilização de
métodos com os mesmos nomes.

Outro conceito que merece destaque é o de interface . Um


grande motivo para a importância do uso de interface é o fato de
ela preencher a lacuna deixada pela ausência da herança múltipla,
que, como foi falado anteriormente, não existe na linguagem Java.
Uma interface define um conjunto de funcionalidades que precisam
ser fornecidas por qualquer classe que venha a implementar esta
interface. Há uma grande proximidade da interface com a classe
abstrata, sendo que uma interface abriga exclusivamente métodos
abstratos. Voltaremos a falar sobre este assunto no tema 3 (conte-
údos 3.2 e 3.3).
Não podemos deixar de falar sobre o conceito de abstração,
que consiste em impedir que o desenvolvedor conheça detalhes
da implementação de uma determinada funcionalidade. Com a uti-
lização da abstração, permanece explícita apenas a assinatura do
método, ficando oculto todo o código. Ou seja, não precisa tornar
público ‘como’ fazer, apenas ‘o que’ se faz. O importante é o que
um objeto ‘é’ e o que ele ‘faz’ sem se preocupar em ‘como’ é a sua
implementação.
Tema 1 | Introdução à orientação a objetos 29

INDICAÇÃO DE LEITURA COMPLEMENTAR

SINTES, A.  Aprenda Programação Orientada a Objetos. São Paulo:


Pearson Education do Brasil, 2010.

A leitura da página 1 até a 16 é muito interessante, pois faz uma im-


portante introdução à programação orientada a objetos.

CADENHEAD, R.; LEMAY, L.  Aprenda em 21 dias Java 2. 4. ed. São


Paulo: Elsevier Editora Ltda, 2005.

Para revisar e aprimorar seus estudos neste assunto, é sugerida uma


leitura nos capítulos 5 e 6 (da pág. 73 a 106).

PARA REFLETIR

Reflita junto com seus colegas sobre a importância dos conceitos


fundamentais da Orientação a Objetos, que são encapsulamento,
herança e polimorfismo.

1.3 Classes e Objetos

Classe
Uma classe pode ser entendida como um modelo, uma espécie
de forma (ou ainda de gabarito) para a definição de objetos. Quando
nos voltamos para o mundo dos softwares, usamos o conceito de
classes para agrupar estes objetos que possuem uma relação entre si
(características que os objetos têm em comum). A premissa da OO é
30 Programação de Computadores II

Desta maneira, entendo que, de acordo com o nosso mundo real,


aviões, navios, carros e motocicletas são classificados como meios de
transporte. Apesar de serem objetos de tamanhos, formas e valores
diferentes, eles possuem características comuns inerentes a todos os
meios de locomoção (quantidade de passageiros, tipo de combustível
etc). Assim sendo, a meta passa a ser codificar os objetos do mundo
real acima citados em uma linguagem de programação.
Uma classe constrói a especificação de todas as informações
comuns a objetos da mesma espécie. Estas informações são dividi-
das em duas categorias: atributos e comportamentos.
Os “atributos da classe” são as características, tais como marca,
cor, velocidade. Podemos chamar de “propriedades da classe” o con-
junto de todos os atributos. Vale ressaltar que cada atributo precisa
ser implementado com um nome e um tipo de dado correspondente.
Por sua vez, os comportamentos são ações que de maneira geral al-
teram os atributos. Por exemplo, os comportamentos acelerar e frear
que alteram o atributo velocidade; estes comportamentos são imple-
mentados através de métodos (que podem ser funções ou procedi-
mentos); cada método é implementado com uma assinatura (nome
do método, tipo de retorno e lista de parâmetros) e um conjunto de
instruções.

Objetos
Como não poderia deixar de ser, o conceito de objeto é de
grande importância para a Programação Orientada a Objetos. É uti-
lizado em praticamente todo processamento de uma aplicação. É o
elemento utilizado para representar qualquer coisa, seja ela real (ex:
carro) ou mesmo abstrata (ex: viagem). Objetos são simplesmente
instâncias de uma classe previamente definida.
A partir do momento que temos uma classe chamada Pessoa
com os atributos nome, idade e telefone, podemos criar uma ins-
tância desta classe que representa um objeto real, ou seja, teremos
um objeto  pessoa com o nome João, idade igual a 25 anos e com o
telefone 9876-1234.
Tema 1 | Introdução à orientação a objetos 37

BARNES, D., KOLLING M. Programação Orientada a Objetos com Java .


São Paulo: Makron Books, 2006.

No capítulo 1, há uma interessante apresentação ao estudo de objetos


(criação e interação) e de classes das páginas 3 a 15.

PARA REFLETIR
Se os tipos primitivos foram construídos para oferecerem uma melhor
performance, qual a vantagem no uso das classes Wrappers (que
emulam os tipos primitivos)?

Neste momento, é preciso que você faça uma reflexão sobre a preocu-
pação com o desempenho na tecnologia Java. Faça uma ponderação
sobre o fato dos tipos primitivos permanecerem presentes na espe-
cificação da linguagem Java e também sobre a existência das classes
Wrappers (que emulam os tipos primitivos).

1.4 Atributos e Métodos

Introdução
Antes de começarmos a falar sobre atributos e métodos, deve-
mos lembrar inicialmente do conceito de Classe. Conforme podemos
retirar da definição feita no conteúdo anterior, temos que uma classe é
composta por duas partes: atributos e comportamentos. Comentare-
mos cada uma destas partes separadamente nos próximos parágrafos.

 Atributos
Também chamados de membros de uma classe, os atributos
38 Programação de Computadores II

os responsáveis pelo armazenamento da informação, mantendo as


particularidades de cada um dos objetos. Cada classe pode possuir
vários atributos, cada um deles deve ser representado por um nome
(identificador). Além disso, para cada um dos atributos, faz-se necessá-
rio informar qual o tipo de dado desejado; podendo ser um tipo de dado
primitivo ou até mesmo algum outro objeto (a linguagem Java não per-
mite a criação de um atributo sem que seja informado o tipo de dado).
Tomando como exemplo uma classe chamada Pessoa, podemos
assumir a existência de vários atributos, cada um deles representado
pelo seu tipo e seu identificador.

public class Pessoa {


// atributos
String nome;
int idade;
}
Métodos
Lembrando que uma classe é composta por atributos e por
comportamentos. A partir de agora passaremos a chamar estes com-
portamentos de métodos. Um método é simplesmente um processo
que realiza uma ação, podendo em muitas vezes manipular o valor
de um ou mais atributos.
Além de iniciar sempre com letra minúscula, a recomendação
para a criação de um método é conter um verbo no infinitivo (termi-
nação ar, er, ir, or), imediatamente seguido de um substantivo. Por
exemplo: calcularIdade(), imprimirDados().
Sintaxe da assinatura de um método:
<modificador> <tipo de retorno> <nome> (<parâmetros>)
Sendo que:

<modificador>: palavra reservada que determina o nível de acesso


ao método;
<tipo de retorno>: indica se haverá ou não um tipo de retorno;
caso haja um retorno, será informado o tipo de dado deste retorno;
<nome>: identificador do método (seguir a recomendação acima);
Tema 1 | Introdução à orientação a objetos 39

Figura 07 - Objeto Pessoa.


Mensagens
Os objetos precisam se relacionar, precisam trocar informações
para realizar tarefas, não fazem nada sozinhos. Para que esta comuni-
cação ocorra, deve existir uma troca de mensagens. Estas mensagens
identificam as operações que precisam ser executadas através de três
componentes:

a) objeto para o qual a mensagem é acionada;

b) método dentro do objeto que será executado;

c) parâmetros necessários para a execução do método.

Considerando um objeto Veículo, é preciso que outro objeto


Motorista lhe envie uma mensagem, por exemplo, acionando o mé-
todo abrirPorta(), passando ainda um parâmetro: qual porta deseja
abrir. Há ainda uma grande vantagem na utilização da troca de men-
sagens: uma vez que as interações são feitas através destas men-
sagens, não é necessário que todos os objetos estejam dentro da
mesma aplicação, ou seja, é possível acionar métodos de objetos que
estão até mesmo em outro servidor de aplicações web.
Temos então o entendimento que um programa OO consiste
54 Programação de Computadores II

PARA REFLETIR

O gerenciamento de memória é algo muito importante dentro do con-


ceito de herança, já que podemos estar criando objetos sem a real
necessidade de utilização. Discuta com seus colegas de turma como
devem ser criados objetos que realmente sejam necessários, podendo
também discutir como é o funcionamento do coletor automático de
lixo (   ).
 garbage collector 

2.2 Membros de Instância X Membros de Classe

Os membros de instância de uma classe são totalmente diferen-


tes dos membros de uma classe.
Quando falamos de membros, estamos falando dos atributos e
métodos que esta classe possui.
Vamos começar falando sobre os membros de uma instância.
Os membros deste tipo só podem ser acessados através de uma ins-
tância de uma determinada classe, ou seja, precisamos primeiro criar
um novo objeto da classe que queremos acessar os membros.
Todos os atributos e métodos só poderão ser acessados quando
estivermos trabalhando com um objeto em memória. É importante ve-
rificar que quando a instância de um objeto deixa de existir, automati-
camente todos os seus métodos e atributos também deixam de existir.
Vamos a um exemplo:
Tema 2 | Herança 55

public class Computador{


private int qtdMemoria;
private int qtdDisco;

public Computador (int pQtdMemoria, int pDisco)


{
this.qtdMemoria = pQtdMemoria;
this.qtdDisco = pDisco;
}
public void AdicionarMemoria(int pQntMemoria)
{
this.qtdMemoria = this.qtdMemoria + pQnt-
Memoria;
}
}

Agora vamos criar um objeto do tipo Computador e vamos ma-


nipular os seus métodos e seus atributos.

Computador computador = new Computador(512,500);


computador. AdicionarMemoria(512);

No exemplo acima, criamos um objeto computador. No momen-


to de sua criação o computador foi criado com 512 de memória e 500
de disco rígido. Esses valores só serão válidos para o objeto compu-
tador que foi criado. Quando adicionamos um valor à memória do
computador, teremos o mesmo comportamento descrito. A adição de
memória ao computador só será válida para este objeto.
Quando criarmos um novo objeto, automaticamente os valores
que estão armazenados no objeto anterior deixam de existir e novos
valores são assumidos. Vamos a um exemplo:

Computador computadorSala = new Computador(1024,160);

Neste segundo caso, os valores que são atribuídos ao computa-


dor são de 1024 de memória e de 160 de disco. Ou seja, dois valores
56 Programação de Computadores II

Sendo assim, os membros de instância, sejam os atributos ou


os métodos, só vão existir quando houver um objeto instanciando
determinada classe, e automaticamente deixam de existir quando
estes objetos não estiverem mais em memória.
 Já os membros de classe possuem uma característica bem di-
ferente dos de instância. Todos os membros de classe são também
conhecidos como membros estáticos.
No Java os membros estáticos são representados pela palavra
reservada  static . É através desta palavra reservada que indicamos
para o compilador que estamos criando um membro estático.
Este tipo de membro nunca irá pertencer a uma instância de
uma classe. Ele sempre estará ligado diretamente a uma classe.
Toda vez que criarmos uma classe, mesmo que ela não seja
instanciada, todos os membros estáticos são os primeiros a serem
carregados. Vamos incluir, na classe de computador, um atributo es-
tático que vai representar um membro de classe para o computador.

public class Computador{


private int qtdMemoria;
private int qtdDisco;
private static int qtdComputadores;

public Computador (int pQtdMemoria, int pDisco)


{
this.qtdMemoria = pQtdMemoria;
this.qtdDisco = pDisco;
}
}

Podemos ver que agora existe um atributo estático dentro da


classe, o atributo qtdComputadores. Este atributo vai pertencer a
toda a classe, não mais a alguma instância específica. Quando preci-
samos utilizar a variável estática, não precisamos mais declarar uma
variável do tipo da classe nem criar um novo objeto. Basta apenas
chamar o atributo diretamente da classe. Por exemplo:
Tema 2 | Herança 57

No exemplo anterior, estamos apenas aumentando a quanti-


dade de computadores. Se analisarmos a situação acima, podemos
perceber que, apesar de não termos nenhum objeto criado, pode-
mos manipular a quantidade de computadores. Precisamos, então, de
muita atenção quando formos utilizar este tipo de atributo.
Como o atributo estará presente durante todo o ciclo de vida
da classe, as variáveis estáticas também estarão presentes e manten-
do o seu valor.
Assim como os atributos de uma classe, nós podemos ter os
métodos estáticos. Estes métodos irão funcionar da mesma maneira
que os atributos, ou seja, vão existir sem a dependência de uma
instância de uma classe. Porém, é importante lembrar que quando
temos um método estático não iremos sobrescrever este método na
subclasse, iremos ver o conceito de subclasse no tópico de hierar-
quia. Caso, na subclasse, formos usar o mesmo método estático,
teremos que redefinir este método.
Para utilizar um método como estático, precisamos colocar a
palavra reservada  static  logo após o modificador de visibilidade. Por
exemplo, iremos colocar o método estático na classe computador.

public class Computador{


 private int qtdMemoria;
 private int qtdDisco;
private static int qtdComputadores;

public Computador (int pQtdMemoria, int pDisco)


{
 this.qtdMemoria = pQtdMemoria;
 this.qtdDisco = pDisco;
}
public static void Imprimir()
{
System.out.Println(“Quantidade de com-
putadores cadastrados: ” + qtdComputadores);
}
}
72 Programação de Computadores II

public class Conta {

private int qtdTitulares;


private int codTitular;

public boolean equals(Object object)


{
Conta conta = (Conta) object;
if (conta.codTitular == this.codTitular)
{
return true;
}
  else
{
return false;
}
}
}

No exemplo, criamos uma lógica para o método equals em que,


para verificar se dois objetos são iguais, verificamos se o código do
titular é o mesmo. Algumas regras para a sobrecarga do método de-
vem ser respeitadas. Para referências não nulas:

• É reflexivo: a.equals(a) tem sempre que ser verdadeiro, ou


seja, um objeto é sempre igual a si mesmo.

• É simétrico: a.equals(b) retorna verdade se e somente se


b.equals(a).

• É transitivo: a, b e c, se a.equals(b) e b.equals(c) são ver-


dade então a.equals(c) tem que ser verdade.

• É consistente: para múltiplas invocações de a.equals(b)


terá sempre que retornar true ou sempre retornar false, en-
quanto informações usadas na comparação do equals não
Tema 2 | Herança 73

• O resultado de equals() entre um objeto e nulo deve re-


tornar sempre falso, ou seja, a.equals(null) é sempre falso.

 Já o método toString é utilizado para retornar a representação


string do objeto.
O toString() da classe Object retorna um string com o nome da
classe + ‘@’ + representação hexadecimal sem sinal do código hash
do objeto. Por exemplo:

Conta conta = new Conta();


System.out.println(conta.toString());
 //Valor retornado
aluno@457321245

Com isso é interessante que sempre que precisarmos utilizar o


método toString() que seja feita a reescrita deste método para que
ele retorne alguma informação que podemos mostrar como final.

public class Conta {

private int qtdTitulares;


private int codTitular;

public boolean toString(Object object)


{
System.out.println(“O titular da conta
possui o código: “ + this.codTitular;
}
}

Com isso, automaticamente quando utilizarmos o método


toString( ), teremos como resultado final o que ficou definido dentro
da reescrita do método.
74 Programação de Computadores II

INDICAÇÃO DE LEITURA COMPLEMENTAR

DEITEL, P.J.; DEITEL, H. M..  Java Como Programar . 8. ed. São Paulo:
Pearson Education do Brasil, 2010.

Uma maneira de aprofundar sobre a classe Object pode ser encontra-


da nas páginas 298 e 299 do capítulo 8.
Neste capítulo a definição da classe Object é demonstrada pelo autor
com alguns exemplos de como podemos utilizar esta classe. Também
são demonstrados os principais métodos da classe.

HORSTMANN, C.S.; CORNELL, G.. Core Java 2. Vol 1 - Fundamentos. 7.


ed. São Paulo: Alta Books, 2005.

Uma produtiva leitura pode ser feita nas páginas 95 a 101, onde po-
dem ser encontradas mais informações sobre os principais métodos
da Super Classe Object como equals (responsável por testes de igual-
dade), hasCode e toString.

PARA REFLETIR

Na orientação a objetos sempre utilizamos tipos criados por nós.


Porém, nunca tínhamos parado para pensar que o Java já possuía um
objeto genérico para todas as nossas classes. Com este tópico po-
demos perceber que podemos comparar objetos através de reescrita
de métodos.
ASPECTOS AVANÇADOS DA
PROGRAMAÇÃO ORIENTADA
A OBJETOS EM JAVA

Parte 2
3 Polimorfismo

 Já estamos no nosso terceiro tema


tema e o momento é de fazermos
um estudo
estudo sobre o conceito
conceito de polimorfi
polimorfismo.
smo. Inicialmente
Inicia lmente faremos
faremo s
um aprofundamento teórico sempre com exemplos comentados.
Veremos na sequência a importância das palavras abstract  e  e  final
 fin al
na linguagem Java. Também, neste tema, falaremos sobre o impor-
tante conceito de interfaces, com suas definições e implementações
e, no último conteúdo deste tema, ainda iremos comentar sobre
sobre algumas
interfaces especiais.
80 Programação de Computadores II

3.1 Do Conceito à Prática

Introdução
Polimorfismo é uma palavra de origem grega que quer dizer
várias formas. Isto é, mais de uma maneira de fazermos a mesma
coisa. Como estamos falando de uma linguagem de programação,
temos que, o que pode ser feito de diferentes maneiras, concentra-se
especificamente em um ponto, em como fazemos as chamadas aos
métodos. Podemos definir então o polimorfismo como um mecanis-
mo que permite que duas ou mais classes derivadas de uma mesma
classe ancestral possuam métodos com a mesma assinatura, porém
com comportamentos distintos, havendo uma especificidade para
cada classe derivada. Esta importante característica presente nas
linguagens OO permite que uma mesma mensagem enviada a um
objeto tenha comportamento distinto, variando de acordo com o tipo
de objeto instanciado.
Aperfeiçoando nossos conhecimentos de herança (que foi as-
sunto do conteúdo 2.3), percebemos que um objeto de qualquer
subclasse pode ser manipulado como sendo um objeto da sua super-
classe. Este comportamento faz com que objetos sejam tratados de
forma genérica, mas para isto acontecer, deve haver uma hierarquia
de classes. Existe também uma operação conhecida como typecast ,
que consiste em realizar uma conversão explicita de um objeto de
uma classe para outra. Para isto, basta colocar entre parênteses e
antes do objeto o tipo da classe a ser convertido, como no exemplo:

(ClasseDestino) meuObjeto;

Faz-se necessário que as classes origem e destino da conversão


tenham uma relação de herança. Devemos lembrar que o typecast 
de uma subclasse para uma superclasse é implícito e automático,
ficando transparente para o desenvolvedor, enquanto o inverso não é
garantido, sendo para isso necessário o typecast  explícito.

Funcionamento
Como vimos, o polimorfismo consiste em várias maneiras de
se fazer a mesma coisa, então podemos entender que temos a pos-
sibilidade de escrever um método de mesmo nome com diferentes
Tema 3 | Polimorfismo 81

mesmo método possa ter comportamentos diferentes. Quem decide


qual comportamento é o mais adequado para a situação é o objeto
que possui o método acionado. O que acabamos de falar é muito
importante para o entendimento do conceito de polimorfismo e, se
ainda não está seguro se entendeu bem a frase anterior, é recomen-
dável uma releitura acompanhada de uma reflexão.
Temos então que a decisão sobre a escolha de qual método
será selecionado ocorrerá de acordo com o tipo do objeto. Como esta
decisão acontecerá apenas em tempo de execução, ocasionando um
vínculo posterior, este procedimento passa a ser chamado de ligação
tardia ( l ate binding ou dynamic binding ).
Vejamos a seguinte situação: um determinado objeto “Pessoa”
aciona um método abastecer() de um outro objeto “Veiculo”; este
objeto é o responsável por decidir como será a implementação do
método abastecer(). Analisando mais profundamente, quem vai de-
cidir qual implementação será escolhida é o tipo do objeto “Veiculo”.
Em outras palavras, se o Veículo é um Carro, vai ser de uma maneira;
se for uma Lancha, vai ser de outra; se for uma Motocicleta, será de
uma terceira maneira e assim por diante. Podemos constatar esta
forma de tratamento polimórfico de maneira exemplificada. Vejamos
o trecho de código abaixo:

Veiculo meuVeiculo;
if (...) {
meuVeiculo = new Carro();
} else {
meuVeiculo = new Lancha();
}
  meuVeiculo.abastecer(); //método polimórfico

Neste momento, é válido lembrarmos a frase que mereceu aten-


ção poucos parágrafos atrás: quem decide qual comportamento é
o mais adequado para a situação é o objeto que possui o método
acionado. Para comprovar a veracidade desta afirmação, podemos
constatar que foi o objeto Veiculo quem decidiu qual método foi acio-
nado. Isto aconteceu de acordo com o seu tipo, que poderia ser Carro,
Lancha ou qualquer outra classe desde que herdasse de Veiculo
82 Programação de Computadores II

Apesar de entendermos que, dependendo da circunstância, o


objeto chamado vai agir de uma maneira ou de outra, vale ressaltar
que o objeto acionado não se modifica nem se transforma. O que
acontece é que, em tempo de execução, o compilador Java vai iden-
tificar qual classe de fato será chamada, mas todas as classes com
seus métodos já foram previamente criadas. Como no exemplo aci-
ma, apesar da variável meuVeiculo ser do tipo Veiculo, a instanciação
de verdade será de Carro ou de Lancha.
No momento que uma mensagem é enviada para um objeto de
uma subclasse, os seguintes passos são executados:

- a subclasse acionada verifica se possui um método com aquele


nome e com a mesma quantidade e tipos de parâmetros;

- caso realmente possua, este mesmo método é executado;

- caso contrário, a mensagem é enviada para a classe imediata-


mente superior no nível hierárquico;

- percorrendo toda a hierarquia, chegando até a classe Object,


e não sendo encontrado um método que atenda as condições
necessárias, acontece neste momento um erro de execução.

Vamos analisar detalhadamente um modelo simples contendo


as seguintes classes a seguir:

- uma classe Funcionario que contem um método calcularSalario( ),


como esta classe não é capaz de fato de calcular o salário, ela
retorna o valor zero;

public class Funcionario {


public double calcularSalario() {
return 0;
}
}
Tema 3 | Polimorfismo 83

- uma classe FuncionarioHorista herda de


Funcionario, apresenta os atributos salario-
Hora e horasTrabalhadas e ainda um método
calcularSalario( );

public class FuncionarioHorista


extends Funcionario {
private double salarioHora;
private int horasTrabalhadas;
public double calcularSalario() {
return (salarioHora *
horasTrabalhadas);
}
}

- uma classe FuncionarioMensalista que tam-


bém herda de Funcionario e contém o atribu-
to salarioMensal e o método calcularSalario( ).

public class FuncionarioMensalista


extends Funcionario {
private double salarioMensal;
public double calcularSalario()
{
return salarioMensal;
}
}

O modelo descrito acima ilustra como a em-


presa categoriza os seus funcionários. Independen-
temente do tipo, todo funcionário deve ser capaz
de calcular o seu próprio salário.
Caso seja um horista, o salário será a multi-
plicação do atributo salarioHora pelo atributo horas-
Trabalhadas, o que acontece no trecho de código na
Tema 3 | Polimorfismo 89

ab strata. Não há problema em conter métodos não-abstratos,


abstrata. não-abstratos, o que
não pode acontecer é que eles sejam a totalidade.
Vejamos a seguir um exemplo de uma classe abstrata:

public abstract class Funcionario {


 // atributos
 private int código;
 private String nome;
 private String cargo;
 private float salario;
 // métodos
 public int getCodigo() {
...
 }
 public void setCodigo(int codigo) {
...
 }
 … // demais métodos não abstratos
public abstract void alterarSalario
(float valor);
}

No exemplo acima, destacamos a presença do método abstrato


alterarSalario( ). Se ele não estivesse presente na classe Funcionario,
ou ainda se tal método não fosse abstrato, a classe não poderia ser
abstrata, pois não haveria outro método abstrato que garantisse a
classe como abstrata.
Quando uma subclasse herda de uma superclasse sendo esta
abstrata, todos os métodos não abstratos já estarão automatica-
mente presentes na subclasse e disponíveis para serem aciona-
dos. Entretanto, precisamos analisar o caso do método abstrato no
momento da herança. O que acontece é que o método abstrato
também será herdado (continua sendo abstrato). Neste momento,
precisamos tomar uma decisão: a primeira opção é não fazer nada
referente ao método, desta maneira a presença de um método abs-
trato implica em indicarmos a classe também como abstrata, o que
90 Programação de Computadores II

mesmo que aconteceu na superclasse abstrata); apenas as classes


que, por sua vez, herdassem desta poderiam ter instâncias criadas
(desde que não tomassem a mesma atitude que a primeira subclasse
tomou). A segunda opção opção consiste basicamente em fazermos uma
redefinição ( overriding
overriding ) do método abstrato
abstrato herdado, o objetivo des-
ta redefinição nada mais é que apenas repetir a assinatura completa
do método, mas desta vez retirando a palavra reservada abstract  e
também o ‘;’ no final da instrução e, além disso, agora sendo imple-
mentado de fato (com as suas devidas instruções) o mesmo método.
Abaixo segue um exemplo de como seria a implementação do
método abstrato alterarSalario()  em uma subclasse.

public void alterarSalario (float valor) {


  this.setSalario(valor);
}

Caso uma classe qualquer contenha exclusivamente métodos


abstratos, ela passa a ser chamada de classe abstrata pura (veremos
no conteúdo seguinte o conceito de interface e faremos
f aremos uma compa-
ração com o conceito de classe abstrata pura).

Modificador Final
A palavra reservada
reservada final é utilizada
utili zada tanto para modificar o com-
portamento de uma variável, de um método, de uma classe ou de
uma interface.

 Variável
 Variável final
Uma variável final só pode ter o seu valor atribuído uma única
vez. Uma vez recebido o seu valor inicial, tal valor não não pode mais
ser alterado.
alterado. Esta característica
característica acaba transformando funcionalmente
funcionalmente
tal variável em uma constante. Inclusive uma variável
variável com o modi-
ficador final, quando é do tipo primitivo ou ainda do tipo String, é
formalmente chamada de variável constante. Acontece um erro de
compilação ao tentar alterar o valor de uma variável  final.
Conforme exemplo abaixo, a palavra reservada final deve prece-
der o tipo e nome da variável.

final int limiteSuperior = 100;


Tema 3 | Polimorfismo 91

É possível não atribuir nenhum valor a uma variável  final no


momento da declaração, desta maneira, é possível a qualquer mo-
mento posterior do código
código realizar uma atribuição a tal variável.
variável. Neste
caso, não há problema, pois fica evidente que esta seria a primeira
operação de atribuição. Mas após este ponto, ponto, nenhuma
nenhuma alteração
pode ser realizada.
realizada. Resumindo: a primeira atribuição a uma variável fi-
nal pode acontecer a qualquer momento (não é obrigatório que seja no
instante da declaração), entretanto uma troca de valor não é permitida.
Quando se trata de um atributo de uma classe utilizando o mo-
dificador final, temos duas possibilidades para realizar a atribuição
(que, claro, só pode
pode ocorrer uma única
única vez). Ou a atribuição de valor
ocorre no próprio instante da declaração do atributo (ex: ”private
final int valor = 100;” ); ou, caso não seja escolhida a opção
anterior,
anterior, fica sendo estritamente obrigatório proceder à atribuição em
todos os construtores
construtores existentes na classe. Neste último caso, o atri-
buto é conhecido como blank final. Caso não seja utilizada nenhuma
das duas opções (nem atribuir o valor do atributo na inicialização
nem nos construtores), acontece um erro em tempo de compilação.

Método final
Um método que utiliza a palavra reservada final faz com que
este não possa ser redefinido nas subclasses que herdam da super-
classe a qual
qual este método pertence. O seu propósito
propósito é prevenir
prevenir com-
portamentos inesperados de uma subclasse alterando um método
que pode ser de uma importância fundamental para o funcionamento
e/ou consistência da classe.
classe. É gerado um erro
erro de compilação quando
quando
se tenta redefinir um método final.
No caso dos construtores de uma classe, como eles nunca são
herdados, não faz sentido pensar um utilizar o modificador final neles.
Na sequência, veremos um exemplo de um método com o mo-
dificador final.

public final void aumentarLimite(int valor) {


this.limite = this.limite + valor;
}
92 Programação de Computadores II

Classe final
Uma classe final não pode ser herdada por nenhuma outra
classe. Um motivo para isso é garantir uma maior segurança para a
classe e incrementar a sua eficiência. Quando se utiliza o modificador
final em uma classe, entende-se que ela está totalmente completa e
não é necessário (sequer possível) haver subclasses herdando de uma
classe final. É gerado um erro de compilação caso, em qualquer clas-
se, seja usada a palavra reservada extends seguida do nome de uma
classe que seja final.
Outra situação em que também acontece erro de compilação
é quando uma classe é declarada ao mesmo tempo como sendo
abstrata e final. Neste caso, haveria uma contradição, o modificador
abstract   estaria indicando que esta classe não está completa (que
existiria um método abstrato e que este método precisaria ser imple-
mentado pela sua subclasse); enquanto que a presença concomitante
do modificador final estaria informando exatamente o contrário, que
a classe agora estaria completa e não poderia haver nenhuma sub-
classe herdando dela. Por isso, acontece o erro em tempo de compi-
lação, para que seja evitada uma situação de conflito impossível de
ser resolvida.
Quando uma classe é definida como final, todos os métodos
contidos são implicitamente final. Isso implica que nenhum método
pertencente a esta classe final possa ser redefinido. O que parece
ser condizente, afinal de contas a própria classe não pode mesmo ser
herdada por nenhuma outra classe, então não haveria condições de
fato de nenhum método ser mesmo redefinido em subclasses.
Muitas classes da própria API Java são  final, como java.lang.
String e java.lang.System. A seguir, veremos um simples exemplo de
uma classe final.

public final class Jogo(String[] args) {


// atributos
...
// métodos
  ...
}
Tema 3 | Polimorfismo 93

INDICAÇÃO DE LEITURA COMPLEMENTAR

DEITEL, P.J.; DEITEL, H. M.  Java Como Programar . 8. ed., São Paulo:


Pearson Education do Brasil, 2010.

Para aprender um pouco mais sobre este conceito (abstract e final),


você pode ler as páginas 309 a 329 do capítulo 10. Neste capítulo,
é feita uma interessante abordagem sobre as aplicações das palavras
reservadas abstract e final nas variáveis, métodos e classes.

MENDES, D. R. Programação Java com Ênfase em Orientação a Objetos .


São Paulo: Novatec Editora Ltda, 2009.

O capítulo 5 deste livro faz uma interessante citação sobre os modi-


ficadores abstract e final. A leitura deve se concentrar no capítulo 5
nas páginas 196 a 213, onde o autor detalha classes e métodos abs-
tratos, além das classes, métodos e atributos com o uso da palavra
reservada final.

PARA REFLETIR

A partir do momento em que informarmos que uma determinada


classe é abstrata (precedendo com a palavra reservada abstract), faça
uma reflexão sobre o motivo de não podermos mais obter instâncias
a partir dela, até que façamos as devidas implementações dos méto-
dos abstratos.
94 Programação de Computadores II

3.3 Interfaces

Definição
Antes de começarmos a falar de Interfaces, devemos relembrar
algumas informações a respeito de classes abstratas. Já sabemos
que uma classe abstrata não pode ser instanciada, ou seja, não se
pode construir objetos a partir da sua definição. Devemos lembrar
também o conceito de método abstrato (aquele que não possui im-
plementação) e que uma classe abstrata deve possuir ao menos um
método abstrato.
Desta forma, estamos prontos para entender o conceito de Inter-
face. Inicialmente podemos fazer uma aproximação de interface com
uma classe abstrata pura. Lembrando que para ser pura, uma classe
abstrata precisa possuir exclusivamente métodos abstratos, ou seja,
não possui nenhum método implementado. Uma característica impor-
tante de uma interface é exatamente esta, nenhum método implemen-
tado, todos os métodos têm obrigatoriamente que ser abstratos.
O papel da interface é simplesmente descrever o que  outras
classes devem fazer, porém não especificam como elas devem fazer.
Temos então somente as assinaturas dos métodos (nome, parâme-
tros e tipo de retorno), sem nenhuma implementação. Como todos
os métodos são abstratos, podemos tornar implícitas as palavras
modificadoras  public  e abstract , simplesmente para evitar a repeti-
ção (a presença destas palavras não implica em erro, apenas geram
redundância).
Interfaces servem como especificações de padrões de compor-
tamentos para as classes. A sua utilização permite um projeto de
software mais bem elaborado, contendo funcionalidades mais orga-
nizadas.
Pode haver “atributos” na interface, porém estes seriam (tam-
bém implicitamente)  public ,  static  e  final (tornando-os constantes).
Repete-se a informação anterior, ou seja, a presença destas palavras
é perfeitamente dispensável, porém não ocasionam nenhum tipo de
erro. O que não pode acontecer é o uso da palavra-chave  final em
um método, pois isto impediria que ele fosse implementado pela
classe que a implementa. Entenderemos melhor esta situação após
98 Programação de Computadores II

Vejamos a seguir uma imagem de uma classe Quadrado que


herda de FiguraGeometrica e, ao mesmo tempo, implementa a interface
Imprímivel.

Figura 10 - Uma subclasse herdando da superclasse e implementando uma interface.

A aplicação ilustrada na imagem acima será codificada a seguir,


inicialmente veremos o código da classe abstrata pura:

 public abstract class FiguraGeometrica {


 public abstract int calcularArea();
 public abstract int calcularPerimetro();
}
E agora uma interface:
interface Imprimivel {
 void  imprimir();
}
Tema 3 | Polimorfismo 99

Por fim, veremos agora uma classe que herda da classe Figura
Geometrica e ao mesmo tempo implementa a interface Imprímivel
acima:

 public class Quadrado extends FiguraGeometrica


implements Imprimivel {
// atributo lado
 private int lado;
// construtor padrão
 public Quadrado() {
this.setLado(0);
}
// construtor específico
 public Quadrado(int lado) {
this.lado = lado;
}
// métodos get e set
 public int getLado() {
return this.lado;
}
 public void  setLado(int lado) {
this.lado = lado;
}
// método que calcula a área
 public int calcularArea() {
return this.getLado() * this.getLado();
}
// método que calcula o perímetro
 public int calcularPerimetro() {
return 4 * this.getLado();
}
// método que imprime o atributo lado,
// a área e o perímetro
 public void  imprimir() {
  System.out.println(“Quadrado”);
  System.out.println(“Lado:”+
this.getLado());
  System.out.println(“Área:” +
this.calcularArea());
  System.out.println(“Perímetro:” +
this.calcularPerimetro());
}
100 Programação de Computadores II

Neste modelo simples visto acima, percebemos a aplicação dos


conceitos aprendidos anteriormente:

- a classe abstrata pura FiguraGeometrica  (justamente por ser


pura!) não possui nenhum método implementado, ou seja,
possui apenas métodos abstratos;
- na interface Imprimivel  consta exclusivamente métodos abs-
tratos;
- a classe Quadrado herda de outra classe ( FiguraGeometrica ) e
simultaneamente implementa uma interface ( Imprimivel ), para
que ela possa ser instanciada, todos os métodos abstratos
herdados (tanto da classe abstrata como da interface) tiveram
que ser obrigatoriamente implementados, caso contrário, ela
teria que permanecer como sendo uma classe abstrata.

Esta situação (uso da interface) permitiu que fosse possível


contornar a ausência da herança múltipla na linguagem Java, uma vez
que se obtêm funcionalidades previamente determinadas em mais
de um local.
Após todas as explicações acima, podemos chegar a algumas
conclusões a respeito do uso de interfaces:

- evita um acoplamento quase sempre desnecessário, permitin-


do que classes distintas que implementem uma mesma inter-
face, não precisem compartilhar uma hierarquia;
- torna os testes unitários mais fáceis;
- facilita a reutilização de código;
- propiciar uma alta escalabilidade no projeto;
- melhora a qualidade do código e torna a aplicação mais versátil;
- faz com que a divisão das tarefas entre os desenvolvedores
de um projeto seja simplificada, havendo uma forte integra-
ção, desde que cada um respeite os ‘contratos’ especificados
pelas interfaces.
Tema 4 | Exceções, coleções e fluxos 117

/* A avaliação da disciplina é constituída por


duas provas. A primeira prova possui peso 4, enquanto a
segunda possui peso 6. Faça um programa que calcula a
média ponderada de um aluno na disciplina. */

 public class MediaPonderada {

 main(String[]
 public static void  args) {

// Objeto scanner para entrada de dados


Scanner ler = new Scanner(System. in);

try {
// ler a nota da primeira prova
  System.out.print(“Digite a nota1: ”)
  double nota1 = ler.nextDouble();

// ler a nota da segunda prova


  System.out.print(“Digite a nota2: ”)
  double nota2 = ler.nextDouble();

// Calcular a média ponderada


double media = (nota1 * 4 + nota2 * 6) / 10;

// Imprimir o resultado
  System.out.println(“Média Ponderada:” + media);

} catch (InputMismatchException ime) {
System.err 
.println(“ERRO: A nota deve ser do tipo
real”);
} finally {
System.out.println(“O finally foi executado.”);
}
}
}
118 Programação de Computadores II

Vamos executar o programa considerando que o usuário realizou


a entrada de dados corretamente e observar sua execução, por meio
da saída no console.

Figura 12 - Programa média ponderada executado sem lançar exceção.

Podemos observar, na Figura 12, que o código executou corre-


tamente, ou seja, todas as instruções presentes no escopo do bloco
try  foram executadas e, como não houve nenhuma exceção, o bloco
catch  foi pulado – não executado, então, o programa executou o
bloco  finally . Este é o fluxo de execução natural quando existe uma
sequência de blocos try-catch-finally  e nenhuma exceção é lançada.
Agora vamos observar o que ocorre quando um aluno descui-
dado atribui sua nota por extenso e o código está sendo tratado com
try-catch-finally .

Figura 13 - Exceção lançada e tratada no programa média ponderada.

Neste cenário, uma exceção foi lançada quando o algoritmo


estava executando a linha 22. Isto porque, o aluno descuidado digitou
sua nota por extenso e o programa está aguardando a entrada de um
dado do tipo real.
No momento que a exceção é lançada pela classe “Scanner”, o
programa desvia o fluxo para linha 30, onde encontra o bloco catch.
Este bloco é capaz de tratar exceções do tipo “InputMismatchExcep-
tion”   que é exatamente o tipo da exceção que ocorreu. Então, as
instruções internas a este bloco são executadas para tratar a exceção.
Tema 4 | Exceções, coleções e fluxos 119

É importante perceber que as instruções das linhas 23 até 29


não foram executadas, devido à ocorrência da exceção na linha 22.
Note ainda que, com o tratamento de exceções, foi exibida uma
mensagem amigável informando que a nota deve ser do tipo real e
não uma mensagem contendo o rastreamento da pilha de execução.
De modo que, para o usuário comum, esta mensagem é entendível
e possibilita que o mesmo identifique qual ação, realizada por ele,
gerou a falha no programa.
Enfim, espero que vocês tenham compreendido a importância
do tratamento de exceções na robustez do programa. Por hora, vocês
devem ter compreendido o conceito de exceção e como tratá-la utili-
zando os blocos try , catch e  finally . No próximo conteúdo, nós apro-
fundaremos o estudo sobre as exceções, conhecendo sua hierarquia
de classes e estudaremos como lançar uma exceção.

INDICAÇÃO DE LEITURA COMPLEMENTAR

Para saber mais sobre o tratamento de exceções leia: Exemplo de


tratamento de ArithmeticExceptions e InputMismatchException. No
capítulo 11 (p. 339-342) do livro de:

DEITEL, P.J. Java Como Programar . 8. ed. São Paulo: Editora Pearson 2010.

Neste trecho do livro, Deitel apresenta os conceitos introdutórios


de tratamento de exceção por meio de um método que calcula o
quociente entre dois números inteiros.

Para aprender um pouco mais sobre como lidar adequadamente com


exceções leia: Lidando com Exceptions.

KUNG, F. Lidando com Exceptions . Disponível em <http://blog.caelum.


120 Programação de Computadores II

Neste artigo, Fabio Kung esclarece dúvidas comuns sobre diferentes


situações reais no emprego de exceções. Para isso, ele descreve cinco
boas práticas sobre tratamento de exceções.

PARA REFLETIR
REFL ETIR

Vocês aprenderam que o a palavra-chave return é utilizada em um


método para retornar uma informação para o método que o invocou.
Discuta com seus colegas e com o tutor o que acontece se uma exceção
ocorrer antes do return ser executado.

4.2 Avançando nas Exceções

No conteúdo anterior, estudamos o conceito de exceção e


aprendemos como tratar exceções, por meio de blocos try-catch-fi-
nally , analisando o código da solução de um problema de média
ponderada.
Neste conteúdo, nós avançaremos no estudo das exceções, co-
nhecendo as diferentes classes de exceção, sua hierarquia e, por fim,
aprenderemos a criar uma nova classe de exceção para lançar uma
exceção específica ao nosso problema.

Hierarquia de classes de exceção


O Java define uma hierarquia especial para tratar de erros e
exceções. No topo desta hierarquia encontra-se a classe Throwable,
filha da classe Object.
Tema 4 | Exceções, coleções e fluxos 121

Figura 14 - Hierarquia das classes de exceção

Observe na Figura 14 que a classe Throwable é pai de todas as


classes de erros ( class  ) e exceç
class Error  exceções
ões ( class
class Exception ). Assim,
Assim, quan-
quan-
do tratamos uma exceção com um bloco catch, o argumento
argumento requerido
como parâmetro é a classe Throwable ou alguma subclasse dela.
A classe Error  descreve
 descreve situações anormais, inesperadas e intra-
táveis. Esta classe descreve uma situação tão grave que a aplicação
não pode fazer nada além de informar a causa do erro. Um exemplo
de erro é o OutOfMemoryError , que ocorre quando a aplicação neces-
sita de mais memória do que existe disponível.
A classe Exception é a superclasse das exceções em Java, ou
seja, toda exceção herda direta ou indiretamente desta classe. As
exceções em Java são classificadas em: verificadas ( checked 
checked   ) e não
verificadas ( unchecked 
unchecked  ).
As classes de exceções verificadas são assim denominadas por-
que o compilador Java exige que todas as exceções deste tipo sejam
tratadas, por meio de blocos try-catch-finally , ou relançadas, usando
a cláusula throws. Portanto, o compilador Java considera um erro de
compilação caso não o faça.
As classes de exceções não verificadas possuem este rótulo
porque o compilador Java não exige que elas sejam tratadas ou relan-
çadas. Porém,
Porém, julga-se uma boa prática de programação efetuar este
tratamento, a fim de maximizar a robustez do programa.
122 Programação de Computadores II

A classe RuntimeException , subclasse de Exception, representa


exceções em tempo de execução e é a superclasse de todas as
exceçõe
exce çõess não verificadas. As demais classes de exceção,
exceção, que herdam
direta ou indiretamente de Exception, mas não herdam de RuntimeException,
são do tipo verificadas.
Além da classe InputMismathException, que tratamos no con-
teúdo anterior, são exemplos de exceções não verificadas: a  Arith-
meticException , que representa uma condição aritmética excepcional
como, por exemplo, uma divisão por zero; a IllegalArgumentExcep-
tion, que representa um parâmetro ilegal ou inapropriado; a Inde-
 xOutOfBoundsException, que indica o acesso a uma posição fora dos
 xOutOfBoundsException
limites da estrutura; e a NullPointerException , quando manipulamos
um objeto ou uma posição de um vetor cuja referência é null.
As exceções do tipo verificadas mais comuns são: a IOExcep-
tion, que indica a ocorrência de falha durante a entrada e saída de
dados; a ClassNotFoundException, quando a classe carregada durante
a execução do programa não é encontrada; e a SQLException, que
representa uma falha relacionada ao banco de dados.
A hierarquia
hierarquia de classes de exceção é bastante extensa e o Java
permite que os programadores criem novas exceções.

Criando exceções
Para criar uma nova exceção é necessário definir uma classe que
herde, direta ou indiretamente, da classe Exception. Este processo de
criação segue os conceitos de herança que estudamos anteriormente.
A seguir, veja a sintaxe da criação de uma nova exceção.

 public class <Exceção> extends <SuperClasseDaExce


<SuperClasseDaExceção>
ção> {
// Corpo da exceção
}

Normalmente, as classes criadas são exceções verificadas (ou


seja, herdam da classe Exception, mas não da classe RuntimeExcep-
tion ), embora seja possível criar classes de exceções não
não verificadas.
Recomenda-se, como boa prática de programação, que toda classe de
exceção termine com a palavra Exception.
Tema 4 | Exceções, coleções e fluxos 123

Lançando exceções
Uma exceção é lançada por meio da cláusula throw  e uma ins-
tância da classe de exceção que se deseja lançar. Quando o Java
executa a cláusula throw, o fluxo de execução natural do método é
modificado, retornando ao método que o invocou. Logo, é importante
ter em mente que a cláusula throw  retorna à exceção que ocorreu e
não ao resultado do processamento do método.
Ao lançar uma exceção, nós devemos informar ao método que
ele pode lançar esta exceção. Para isso, utilizamos a palavra reser-
vada throws na assinatura do método, juntamente com a classe de
exceção. A cláusula throws é obrigatória para exceções verificadas,
mas é facultativa para exceções não verificadas.
É importante ficar atento à diferença entre as palavras reser-
vadas throw  e throws. A cláusula throw  retorna uma exceção do tipo
informado, ou seja, lança a exceção. Já a palavra reservada throws,
informa que o método pode lançar uma exceção do tipo definido na
sua assinatura. O código a seguir descreve um exemplo de sintaxe do
lançamento de exceções.

 public class <Nome da Classe> {


 <Método>() throws <Exceção> {
public void 
throw new <Exceção>();
}
}

Para entendermos melhor os conceitos vistos, suponha que


uma empresa bancária lhe contratou para codificar o método de sa-
que de uma conta.
Considerando que o usuário já tenha sido devidamente auten-
ticado no sistema bancário. Quais situações podem impedir o saque
do dinheiro?

1) O usuário digita um valor negativo;


2) O usuário digita um valor superior ao saldo da conta;

Na situação 1, o sistema deve lançar uma exceção para in-


124 Programação de Computadores II

valor do saque deve sempre ser maior do que zero. O Java possui,
na hierarquia de classes, uma classe com objetivo de lançar exceções
para informar que o valor do parâmetro informado é ilegal, a classe
IllegalArgumentException . Portanto, nós devemos lançar uma exceção
deste tipo e informar no seu construtor uma mensagem amigável
para o usuário: “O valor do saque deve ser maior que 0.”. O trecho
de código a seguir descreve o método sacar da classe Conta para
solucionar a situação 1.

 public class Conta {
. . . // Código omitido
 public void  sacar(double valor) {
if (valor < 0)
throw new IllegalArgumentException(“O valor
do saque deve ser maior que zero.”);
saldo = saldo - valor;
}
. . . // Código omitido
}

É importante observar que se o valor a ser sacado for menor


do que 0 (zero), ou seja, quando o usuário tentar sacar um valor ne-
gativo, a condição do “if ” é satisfeita e a cláusula throw  é executada.
Neste momento, uma exceção da classe IllegalArgumentException é
lançada e o fluxo de execução retorna ao método que invocou o mé-
todo sacar, antes mesmo de atualizar o saldo da conta.
Observe no código acima que não fomos obrigados a usar a
cláusula throws para declarar explicitamente que o método poderá
lançar uma exceção do tipo IllegalArgumentException , uma vez que
esta classe é do tipo não verificada.
 Já na situação 2 o sistema deve lançar uma exceção informando
que não existe saldo suficiente para realizar o saque. Esta exceção é
extremamente específica e, portanto, a biblioteca do Java não possui
uma classe de exceção com tal objetivo. Logo, nós criaremos a classe
SaldoInsuficienteException e, em seguida, lançaremos esta exceção
no método sacar da classe Conta.
Tema 4 | Exceções, coleções e fluxos 125

A classe SaldoInsuficienteException   representa uma exceção


que ocorre quando o valor a ser sacado é superior ao saldo da conta.
O código a seguir descreve a classe de exceção SaldoInsuficiente-
Exception que será criada para lançar uma exceção específica para
solucionar a situação 2.

 public class  SaldoInsuficienteException extends


Exception {
 public SaldoInsuficienteException() {
super(“Saldo insuficiente para realizar a
transação.”);
}
}

Observe no código acima que a classe SaldoInsuficienteException


herda da classe Exception. Portanto, esta classe de exceção é do tipo
verificada. Além disso, o construtor implementado na classe SaldoIn-
 suficienteException invoca o construtor do pai, por meio da palavra
reservada  super , e passa no parâmetro a mensagem “Saldo insuficien-
te para realizar a transação.” Esta mensagem poderá ser exibida ao
usuário quando a exceção for capturada no programa principal, usando
o bloco try-catch.
Agora que criamos nossa classe de exceção, nós precisamos
lançar uma exceção deste tipo quando o valor informado pelo usuário
for maior do que o saldo atual da conta. O trecho de código a seguir
descreve o método sacar da classe Conta para solucionar a situação 2.

 public class Conta {
// Código omitido
 sacar(double valor) throws SaldoInsufi-
 public void 
cienteException {
if (valor < 0)
throw new IllegalArgumentException (“O valor
do saque deve ser maior que zero.”);
if (valor > this.saldo)
throw new SaldoInsuficienteException();
saldo = saldo - valor;
}
// Código omitido
136 Programação de Computadores II

LAFORE, Robert. Estrutura de dados e algoritmos em java . São Paulo:


Ciência Moderna, 2004.

Neste trecho do livro, os autores descrevem as principais estruturas


de dados em Java.

PARA REFLETIR

Discuta com seus colegas de classe sobre as facilidades e as vanta-


gens de utilizar a estrutura de dados lista ao invés de vetores em
 Java.

4.4 Fluxo de Dados

No conteúdo anterior, estudamos a hierarquia de coleções e


aprendemos a manipular diferentes tipos de estrutura de dados, de-
finidos e implementados no Java API.
Neste conteúdo, nós aprendemos a lidar com fluxo de dados
por meio do Java API. Nosso objetivo é compreender o que é um fluxo
de dados e conhecer as principais interfaces e classes para lidar com
fluxos de entrada e saída de dados.

Fluxo de Dados
Um fluxo de dados ou  stream representa uma sequência orde-
nada de dados que podem ser transmitidos para ou de diferentes
dispositivos como, por exemplo, discos rígidos, discos ópticos, fitas
magnéticas, conexão remota via  socket  de rede, uma entrada ou sa-
ída padrão (normalmente teclado e mouse) ou, até mesmo, outros
Tema 4 | Exceções, coleções e fluxos 137

Através de um fluxo de dados é possível transmitir informações


representadas como uma sequência de bytes, de caracteres, de tipos
primitivos ou de objetos. O Java API define um conjunto de classes
abstratas e concretas para lidar com fluxos de entrada e saída de
dados, o pacote  java.oi .
Normalmente, os programas leem bytes através do fluxo de en-
trada, por meio da classe abstrata Input Stream (Figura 19, a seguir),
e transmitem bytes por um fluxo de saída, através da classe abstrata
OutputStream (Figura 20, a seguir).

Figura 19 - Fluxo de entrada de dados

Figura 20 - Fluxo de saída de dados

A grande vantagem das classes InputStream e OutputStream é que


elas empregam o conceito de polimorfismo e, portanto, são capazes de
manipular bytes de diferentes dispositivos.
As classes InputStream e OutputStream são abstratas. Logo, não
admitem instâncias. Para isto, o pacote Java API disponibiliza várias clas-
ses concretas que estendem estas classes básicas de fluxo de dados.
As classes ObjectInputStream ,  AudioInputStream, ByteAr-
rayInputStream e FileInputStream  são algumas das subclasses con-
138 Programação de Computadores II

ByteArrayOutputStream, FileOutputStream  são algumas das subclasses


concretas de OutputStream.
É importante ressaltar que, normalmente, os métodos das clas-
ses do pacote  java.oi  lançam exceções do tipo IOException ou alguma
subclasse desta. Logo, os conceitos aprendidos sobre tratamento de
exceção serão empregados.
Além disso, é de suma importância ressaltar que toda operação
envolvendo fluxo de dados deve-se garantir que o fluxo seja finali-
zado, independente de ocorrer uma exceção, a fim de evitar perda
ou inconsistência de informação. Portanto, nós devemos utilizar um
bloco try-finally  para garantir o fechado do fluxo de dados.
É comum encontrar programas que utilizam arquivos localiza-
dos em dispositivos de armazenamento secundário como origem ou
destino do fluxo de dados. Então, vamos focar nosso estudo em clas-
ses capazes de ler e escrever dados em arquivos.

Leitura e Escrita de dados de um arquivo


Para obter um fluxo de dados de bytes de um arquivo é neces-
sário instanciar um objeto do tipo FileInputStream . Por outro lado, se
o objetivo é obter um fluxo de saída de bytes, para gravação de um
arquivo, nós devemos instanciar um objeto do tipo FileOutputStream .
Os construtores destas classes necessitam de uma representação de
um arquivo, ou seja, uma instância da classe File.
Os objetos da classe FileInputStream  utilizam o método read 
para ler um byte do arquivo. O retorno deste método é um valor in-
teiro, representando o byte lido ou o valor -1, que indica fim do fluxo
de entrada.
A classe File é uma classe concreta que representa um arquivo
ou um diretório. Esta classe do pacote  java.io permite localização do
arquivo, juntamente com seu nome.
A File  define métodos para obter o tamanho do arquivo em
bytes( length ), para verificar se a referência é um arquivo ( isFile ) ou
um diretório ( isDirectory) , para verificar se o programa possui per-
missão para leitura ( canRead  ) ou escrita ( canWrite ). Uma instância da
classe File pode ainda retornar uma lista contendo o nome de todos
os arquivos presentes no diretório no qual está associado.
O objeto da classe File é imutável, portanto, uma vez instancia-
do, com um determinado caminho, sua localização nunca poderá ser
148 Programação de Computadores II

Anotações
Programação de Computadores II 149

Anotações
150 Programação de Computadores II

Anotações
Programação de Computadores II 151

Anotações
152 Programação de Computadores II

Anotações

Potrebbero piacerti anche