Prévia do material em texto
Program
ação Orientada a Objetos e Estrutura de Dados
Fábio da Silva Souza
Rolfi Cintas Gomes Luz
Organizador:
Adilson da Silva
Fábio da Silva Souza
Rolfi Cintas Gomes Luz
Organizador:
Adilson da Silva
GRUPO SER EDUCACIONAL
PROGRAMAÇÃO
ORIENTADA
A OBJETOS E
ESTRUTURA DE
DADOS
PROGRAMAÇÃO
ORIENTADA
A OBJETOS E
ESTRUTURA DE
DADOS
SER_Capa e Contra-Programação Orientada a Objetos e Estrutura de Dados.indd 1,3SER_Capa e Contra-Programação Orientada a Objetos e Estrutura de Dados.indd 1,3 27/01/2023 12:10:3327/01/2023 12:10:33
Programação
Orientada a Objetos
e Estrutura de Dados
© by Ser Educacional
Todos os direitos reservados. Nenhuma parte desta publicação poderá ser
reproduzida ou transmitida de qualquer modo ou por qualquer outro meio,
eletrônico ou mecânico, incluindo fotocópia, gravação ou qualquer outro
tipo de sistema de armazenamento e transmissão de informação, sem prévia
autorização, por escrito, do Grupo Ser Educacional.
Imagens e Ícones: ©Shutterstock, ©Freepik, ©Unsplash.
Diretor de EAD: Enzo Moreira.
Gerente de design instrucional: Paulo Kazuo Kato.
Coordenadora de projetos EAD: Jennifer dos Santos Sousa.
Equipe de Designers Instrucionais: Gabriela Falcão; José Carlos Mello; Lara
Salviano; Leide Rúbia; Márcia Gouveia; Mariana Fernandes; Mônica Oliveira
e Talita Bruto.
Equipe de Revisores: Camila Taís da Silva; Isis de Paula Oliveira; José Felipe
Soares; Nomager Fabiolo Nunes.
Equipe de Designers gráficos: Bruna Helena Ferreira; Danielle Almeida;
Jonas Fragoso; Lucas Amaral, Sabrina Guimarães, Sérgio Ramos e Rafael
Carvalho.
Ilustrador: João Henrique Martins.
SOUZA, Fábio da Silva. / LUZ, Rolfi Cintas Gomes.
Organizador: SILVA, Adilson.
Programação Orientada a Objetos e Estrutura de Dados:
Recife: Digital Pages e Grupo Ser Educacional - 2022.
187 p.: pdf
ISBN: 978-65-81507-81-7
1. Programação 2. Sistemas 3. Objetos.
Grupo Ser Educacional
Rua Treze de Maio, 254 - Santo Amaro
CEP: 50100-160, Recife - PE
PABX: (81) 3413-4611
E-mail: sereducacional@sereducacional.com
Iconografia
Estes ícones irão aparecer ao longo de sua leitura:
ACESSE
Links que
complementam o
contéudo.
OBJETIVO
Descrição do conteúdo
abordado.
IMPORTANTE
Informações importantes
que merecem atenção.
OBSERVAÇÃO
Nota sobre uma
informação.
PALAVRAS DO
PROFESSOR/AUTOR
Nota pessoal e particular
do autor.
PODCAST
Recomendação de
podcasts.
REFLITA
Convite a reflexão sobre
um determinado texto.
RESUMINDO
Um resumo sobre o que
foi visto no conteúdo.
SAIBA MAIS
Informações extras sobre
o conteúdo.
SINTETIZANDO
Uma síntese sobre o
conteúdo estudado.
VOCÊ SABIA?
Informações
complementares.
ASSISTA
Recomendação de vídeos
e videoaulas.
ATENÇÃO
Informações importantes
que merecem maior
atenção.
CURIOSIDADES
Informações
interessantes e
relevantes.
CONTEXTUALIZANDO
Contextualização sobre o
tema abordado.
DEFINIÇÃO
Definição sobre o tema
abordado.
DICA
Dicas interessantes sobre
o tema abordado.
EXEMPLIFICANDO
Exemplos e explicações
para melhor absorção do
tema.
EXEMPLO
Exemplos sobre o tema
abordado.
FIQUE DE OLHO
Informações que
merecem relevância.
SUMÁRIO
UNIDADE 1
Objetivos � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 11
Introdução � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 12
Introdução à orientação a objetos � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 13
Objetivos e vantagens da poo � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 14
Modelos � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � �14
Bibliotecas de operações � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � �16
Tipos de Dados Primitivos (Representação) � � � � � � � � � � � � � � � � � � � � � � � �16
A classe String � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 20
Variáveis � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 23
Operadores lógicos relacionais e comparadores em java � � � � � � � � � 23
Executando o java � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 30
Inserido Classes � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � �33
Operadores lógicos e negação em java � � � � � � � � � � � � � � � � � � � � � � � � � � 34
Estrutura de decisão em java � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 38
Estrutura de repetição em java � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � �42
Terminologia da programação orientada a objetos � � � � � � � � � � � � � 44
Instanciando um objeto � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � �47
UNIDADE 2
Objetivos � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 51
Introdução � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 52
Classes, pacotes, objetos, atributos, métodos, construtores,
palavra-chave this e sobrecarga � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 53
Classes � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 53
Pacotes � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 55
Objetos � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 57
Atributos � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 58
Métodos � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 60
Construtores � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 63
Palavra-chave This � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 67
Sobrecarga � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 68
Instanciação e referências de objetos � � � � � � � � � � � � � � � � � � � � � � � � � � �70
Envio de mensagens � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 72
Ciclo de vida de um objeto � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 73
Abstração e encapsulamento � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � �76
Abstração � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 76
Encapsulamento � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 78
Estruturas compostas homogêneas � � � � � � � � � � � � � � � � � � � � � � � � � � � � 83
Vetor � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 83
Vetor orientado a objetos � � � � � � � � � � � � � � � � � � � � � � � � � � � 89
Matriz � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 92
Estruturas comportamentais heterogêneas � � � � � � � � � � � � � � � � � � � 94
Instanciação � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 94
Atribuição � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 95
Tipos abstratos de dados � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 97
Tipos de armazenamento � � � � � � � � � � � � � � � � � �� � � � � � � � � 98
Coleções � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 99
UNIDADE 3
Objetivos � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 103
Introdução � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 104
Herança � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � �105
Utilização de herança � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 105
Palavra-chave super � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 109
Criação e uso de hierarquia de classes � � � � � � � � � � � � � � � � � � � � � � � � � � 111
Classes e subclasses � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 113
Classes abstratas e interfaces � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 113
Classes abstratas � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � �114
Interfaces � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 117
Relacionamento entre classes � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 121
Associação � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 121
Composição � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 127
Agregação � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � �133
Polimorfismo � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 134
Introdução ao polimorfismo � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 134
Polimorfismo na prática � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � �135
Ligação dinâmica (dynamic binding) � � � � � � � � � � � � � � � � � � � � � � � � � � � 138
Tratamento de exeções � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 140
Evitando e capturando exceções � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 140
UNIDADE 4
Objetivos � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 143
Introdução � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 144
Algoritmos recursivos � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � �145
Técnicas de ordenação � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � �146
Método Bubble Sort � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 147
Método Insertion Sort � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 149
Método Selection Sort � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 150
Estruturas Lineares � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 151
Listas � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 151
Lista Estática � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 152
Lista Ligada � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 152
Lista Duplamente Ligada � � � � � � � � � � � � � � � � � � � � � � � � � � � 156
Código Lista Duplamente Ligada � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 158
Lista Collection � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 158
Pilhas � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � �160
Código da Pilha de Alocação Estática � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 162
Filas – implementações � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 163
Collection Class Queue – Fila dinâmica � � � � � � � � � � � � � � � � � � � � � � � � � � � � 168
Árvores e suas generalizações � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � �170
Terminologia da estrutura Árvore � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 174
Nó � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 174
Node – Implementação dinâmica � � � � � � � � � � � � � � � � � � 176
Vetor – Implementação sequencial � � � � � � � � � � � � � � � � � 177
Árvores binárias � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 178
Tipos de Árvores binárias � � � � � � � � � � � � � � � � � � � � � � � � � � � 179
Árvores de busca � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 182
Árvore binária e de busca � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 183
Referências � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � 185
Apresentação
Olá, aluno(a). Seja bem-vindo(a) à disciplina!
Nessa disciplina será abordada a Programação Orientada a
Objetos (POO), utilizando a linguagem de programação JAVA. Tam-
bém serão apresentados conceitos de POO e conceitos e estruturas
de dados usadas para a manipulação e armazenamento de dados.
Dentre essas estruturas apresentadas, vamos encontrar os
vetores, as matrizes e as árvores. Para essas estruturas, serão apre-
sentadas formas de ordenação de dados.
Antes de começarmos, lembre-se que muitas dessas estru-
turas possuem formatos diferenciados que contribuem em diversas
áreas, como redes, hardware, banco de dados, Sistemas de Infor-
mação e negócios, entre outros.
No decorrer do conteúdo, todos os conceitos de orientação a
objetos serão abordados de forma simplificada para que você possa
absorver facilmente os temas abordados no material.
Dito isto, desejo a você boa leitura e bons estudos!
Autoria
Fábio da Silva Souza
O professor Fábio da Silva Souza é tecnólogo em Gestão da Tecno-
logia da Informação pela Faculdade Tecnológica de Guaratinguetá
(Fatec). Formou-se no ano de 2017 e, desde então, atua como pro-
gramador e analista de sistemas.
Currículo Lattes
http://lattes.cnpq.br/0316083306850058
Rolfi Cintas Gomes Luz
O professor Rolfi Cintas Gomes Luz é mestre (2012) em Ensino de
Ciências pela Universidade Cruzeiro do Sul. É graduado (2008) em
Ciência da Computação pela mesma instituição. Atua há duas déca-
das na área da computação como analista de sistemas, passando por
empresas nacionais e multinacionais, e possui uma década atuando
no ensino superior.
Currículo Lattes
http://lattes.cnpq.br/0778971556248045
Organizador
Adilson da Silva
O professor Adilson da Silva é formado em Ciência da Computação
pela Universidade Católica de Pernambuco (1995), possui especiali-
zação em Tecnologia da Informação pela UFPE (Universidade Fede-
ral de Pernambuco) e MBA em Gestão de Pessoas por Competências
pela faculdade Santa Maria. Também tem especialização em Meto-
dologias Ativas da Educação e Mestrado em Engenharia de Software
pelo C.E.R.S.A.R., além de possuir certificação Scrum Master.
Atua na área de informática há mais de trinta anos, como Analista de
Sistemas e como professor de disciplinas como Algoritmo, Banco de
dados, Gestão de projetos, Gestão ágil e Segurança da Informação.
Currículo Lattes
http://lattes.cnpq.br/7221002795193400
UN
ID
AD
E
1
Objetivos
◼ Apresentar as características de Programação Orientada a Objetos.
◼ Introduzir a linguagem Java para Programação Orientada a Objetos.
◼ Entender os conceitos da Programação Orientada a Objetos.◼ Compreender como surgiu o paradigma de Programação
Orientada a Objetos.
12
Introdução
Caro(a) aluno(a), como vai?
A partir de agora veremos a Programação Orientada a Obje-
tos. Dessa forma, falaremos sobre os objetivos esperados e quais as
vantagens de utilizá-la. Além disso, apresentaremos os principais
conceitos de orientação a objetos e a algumas das suas implemen-
tações em JAVA.
Veremos também como podemos executar os programas em
JAVA usando o Netbean. Assim, discutiremos a aplicação de alguns
conceitos de lógica de programação em Java, como o uso de opera-
dores (Lógicos e aritméticos). Trataremos também das estruturas
condicionais e das estruturas de repetição que podem ser usadas.
Por fim, apresentaremos algumas terminologias usadas no para-
digma de orientação a Objetos.
Pensando nisso, até o final desse material, discutiremos:
1. características da Programação Orientada a Objetos;
2. uso da Linguagem JAVA na ferramenta NetBeans;
3. uso de estruturas condicionais e de estruturas de repetição
em JAVA;
4. conceitos de Programação Orientada a Objetos.
Destaco que a Programação Orientada a Objetos é o paradig-
ma de programação mais utilizado atualmente no desenvolvimento
de sistemas, trazendo um aumento de produtividade com a utiliza-
ção de reuso de código. E, para que você possa se inserir no merca-
do, é necessário ter uma boa base nesse paradigma, e é o que essa
disciplina lhe dará.
Dito isto, vamos iniciar nossos estudos.
13
Introdução à orientação a objetos
Caro(a) aluno(a), é notório que computadores estão presentes no
cotidiano e servem para automatizar e agilizar a execução de tarefas
simples ou complexas. Mas, um computador não é capaz de efetuar
qualquer tarefa sem um software, pois é através dele que se proces-
sam as informações.
Nos primórdios da computação, um programa de computador
era escrito de maneira sequenciada, com suas funções uma abaixo da
outra e seguindo esta sequência até o final. Dentro da sequência, o pro-
grama trabalhava diante de seleções que executavam uma sequência
com base em condições (if ou else) verdadeiras (true) ou falsas (false)
ou diante de repetições que executassem tal sequência enquanto a
condição fosse verdadeira (iterações: for, foreach, do e while).
Em síntese, a Programação Orientada a Objetos (POO) con-
siste no desenvolvimento de sistemas computacionais utilizan-
do linguagens de programação que suportam orientação a objetos
por meio de classes e seus atributos e métodos, introduzindo uma
abordagem na qual o(a) programador(a) visualiza o programa em
execução como uma coleção de objetos cooperantes que se comuni-
cam por mensagens. Em outras palavras, podemos dizer que a POO
procura atender aos requisitos do(a) cliente através de funções
encontradas dentro dos objetos criados.
É importante destacarmos que cada um dos objetos é ins-
tância de uma classe e todas as classes formam uma hierarquia de
classes unidas via relacionamento de herança, como apontado na
página 4 do livro Introdução ao Paradigma de Orientação a Objetos,
publicado em 1996 e escrito por Carlos Kamienski.
Uma das características da Programação Orientada a Objetos,
como o próprio nome sugere, é o uso de objetos, em vez de funções
ou procedimentos, para a construção do programa. Eles se comuni-
cam mediante mensagens e cada objeto é uma instância de classe,
possibilitando que o programa seja escrito sem a necessidade de se
seguir uma sequência e que um método seja executado em qualquer
parte da classe, desde que no padrão aceito pela linguagem.
14
Objetivos e vantagens da poo
A Programação Orientada a Objetos tem por objetivo melhorar a
produtividade dos(as) programadores(as) no desenvolvimento de
sistemas, possibilitando a construção de sistemas robustos com
menos linhas de código, de modo que trechos de códigos sejam rea-
proveitados. A POO é vantajosa porque:
• possibilita a reutilização de código, sendo essa sua maior van-
tagem ao permitir que um(a) programador(a) escreva menos
linhas, já que um trecho de código pode ser reutilizado inú-
meras vezes em qualquer parte do sistema;
• possibilita que a aplicação seja escalável, fazendo com que ela
cresça de acordo com as necessidades que surgem no decorrer
do desenvolvimento ou mesmo diante da necessidade de im-
plantar novas funcionalidades;
• além disso, a manutenção do sistema é feita de forma simpli-
ficada, rápida e eficiente.
Modelos
Para programar utilizando a orientação a objetos, é necessário criar
classes que, em muitos casos, são só modelos de entidades e, para
que se possa representar dados a partir das classes, é necessária a
criação de objetos e/ou instanciação destas classes.
Além disso, a criação de um objeto, ou sua instanciação, é ne-
cessária para que se possa ter a representação de dados e a manipu-
lação das informações e executar operações contidas na classe.
No exemplo: “Livro livro = new Livro();”, a primeira parte antes do
igual indica que se está referenciando na memória do sistema opera-
cional o objeto denominado “livro”, que tem o tipo Livro, e a segun-
da parte é a criação da instância através da palavra-chave “new”.
EXEMPLO
15
Já Na página 14 do livro Introdução à Programação Orienta-
da a Objetos usando JAVA, editado em 2011, o autor Rafael Santos
afirma que:
as classes são estruturas das linguagens de
programação orientadas a objetos para con-
ter, para determinado modelo, os dados que
devem ser representados e as operações que
devem ser efetuadas com estes dados (SAN-
TOS, 2011).
Além disso, os dados contidos na classe são atributos do
objeto e as classes são abstrações do mundo real no mundo vir-
tual. Na programação, tais classes também são chamadas de
modelos (models), representações de objetos, pessoas, tarefas
e processos, usados no dia a dia, não necessariamente diante de
um computador.
Para melhor exemplificar isso, o Diagrama 1 representa um
modelo de abastecimento num posto de combustível.
Diagrama 1 - modelo de abastecimento em posto de combustível
Fonte: editorial Digital Pages (2020).
16
O modelo anterior traduz o processo num posto de combustí-
vel, onde o frentista ou caixa recolhe dados, como tipo de com-
bustível, valor total pago, quantidade de litros, preço por litro
e tipo de pagamento. Esses dados possuem comportamentos
usados, por exemplo, para fechamento de caixa, determinar
qual combustível é o mais vendido, se é necessária a compra de
mais combustível, dentre outros trâmites relativos a um posto
de combustível.
Bibliotecas de operações
Caro(a) aluno(a), é preciso que você saiba que nem todo mode-
lo possui dados e operações, visto que eles podem incluir ape-
nas dados ou operações. Lembre-se que modelos que contêm
só dados são pouco usados, pois não faz sentido ter um modelo
cujas informações não são manipuláveis. Já modelos que pos-
suem só operações são mais encontrados, sendo denominados
bibliotecas de operações, criadas para manipulação de dados
de outras classes.
Tipos de Dados Primitivos (Representação)
Em Sistemas de Informação, muito se fala sobre dados, informa-
ção e conhecimento. Nesse cenário, a computação, de forma geral,
refere-se à informação e ao conhecimento. Previsão ou tendências,
por sua vez, são tratadas na Inteligência de Negócios, ou BI (Busi-
ness Intelligence). Já os dados podem ser um conjunto de bits, uma
palavra, uma letra, um número, ou podem assumir algum valor sem
significado aparente.
DICA
17
EXEMPLO
EXEMPLO
EXEMPLO
Por exemplo, o abacate, R$ 5,00, 1 kg.
Além disso, a informação é a união de diversos dados que
possuem sentido ou significado.
Por exemplo: “hoje, 1 kg do abacate está custando R$ 5,00 reais”.
Já o conhecimento é a relação dos dados e das informações,
constituindo uma ação, aplicação ou saber em diversas áreas do co-
nhecimento, geralmente empregada quando se cruzam valores.
Por exemplo: “há dez anos, no mês de dezembro, 1 kg do abacate
custava R$ 5,00 reais”.
Por fim, os dados,em Estrutura de Dados, são trabalhados
como forma primordial de computar ou solucionar problemas do
mundo real, visando atender às regras de negócio, ou seja, às regras
do mundo real que devem ser incorporadas no mundo computacio-
nal ou atividades empresariais que utilizam esses dados como ca-
deia de valor do negócio.
É importante destacarmos ainda que a escolha do tipo de dado
em Estrutura de Dados leva em consideração alguns aspectos, como:
18
• identificador - nome pelo qual a estrutura será identificada.
Geralmente, possui ligação com sua aplicação no mundo real.
• tipo de armazenamento - define se serão armazenadas le-
tras, números, valores lógicos etc.
• velocidade em inserir dados - a velocidade é medida, geral-
mente em milissegundos, e pode variar dependendo do tipo de
estrutura escolhida.
• algoritmo de ordenação e localização de dados - existem
muitos algoritmos específicos para cada tipo de estrutura que
dependem muito da sua aplicação.
Às vezes, possuem estruturas que demoram mais para or-
denar, mas que, por outro lado, são mais velozes para localizar os
dados. Assim, através do tipo de aplicação de dados, verifica-se qual
o melhor formato de dados para tanto. Nesse contexto, surgem os
tipos de dados primitivos, que orientam ao programador a estrutura
mais adequada para aplicação na regra de negócio.
É importante destacarmos que, em Java, há poucos tipos de
dados primitivos, também conhecidos como nativos ou básicos. Eles
são parte da linguagem, portanto, suas nomenclaturas são pala-
vras-chaves e não são instâncias de outras classes. Assim, cada tipo
tem limitações, ocupando valores específicos em memória.
Para que você possa entender o que estamos tratando, abaixo, na
Tabela 1, constam as especificações dos tipos primitivos em JAVA. Veja:
Tabela 1.- Tipos Primitivos em JAVA
boolean
byte
true ou false
Entre -128 e 127
O valor
booleano
assume valores
true ou false
Números
inteiros de 8
bits de precisão
8
8
true
108
Tipo
primitivo Valores Observações
Bits em
memória Exemplo
19
Fonte: editorial Digital Pages (2020).
Caro(a) aluno(a), é necessário que você saiba que, para o
tipo char, cada número de 0 a 65535 é um dos caracteres na ta-
bela ASCII (American Standard Code for Information Interchange),
sigla em inglês para Código Padrão Americano para o Intercâm-
bio de Informação. Para que você entenda o que estamos deta-
lhando, o código a seguir dá um melhor entendimento sobre o
tipo char.
float
long
char
double
int
short
Entre 1.40239846e-46 e
3.40282347e+38
Entre
-9.223.372.036.854.775.808 e
+9.223.372.036.854.775.807
Entre 0 e 65535
Entre
4.94065645841246544e-324
e 1.7976931348623157e+308
Entre -2.147.483.648 e
+2.147.483.647
Entre -32768 e 32767
Pontos flutuantes
de precisão sim-
ples, usados para
representar valo-
res decimais
Números inteiros
de 64 bits de
precisão
Na teoria, char
armazena
números inteiros
de 16 bits de
precisão, mas,
na prática, é utili-
zado para
para qualquer
caractere
alfanumérico
(char = character)
Pontos
flutuantes de
precisão dupla,
usados para
representar
valores decimais
Números inteiro
de 32 bits de
precisão
Números inteiros
de 16 bits de
precisão
32
64
16
64
32
16
34897.75
7234829
s
34897.75
1000
1000
20
EXEMPLO
public class ExemploCharASCII {
public static void main(String args[]) {
char caractereJ = 74;
char caractereA = 65;
char caractereV = 86;
System.out.println(caractereJ + “” + caractereA + “” + caractereV +
“” + caractereA ) ;
}
}
Como você deve ter percebido, no código há três atri-
butos do tipo char e cada um representa uma letra. Assim,
para escrever a palavra JAVA utilizando char, é preciso usar
aspas vazias para dizer ao Java que se está concatenando e não
somando, pois, como char é inteiro, sem + “” + a linguagem
entenderia que os valores estão somados em vez de concate-
nados. Assim, executando o código anterior, a saída no con-
sole seria “JAVA”.
A classe String
Em Java, para atribuir qualquer texto no lugar de um array de char
(char []), usa-se a classe String. Isso porque, por ser uma classe,
String não é um tipo primitivo, apesar de não precisar ser instancia-
da. De acordo com Santos (2011), embora não seja preciso declarar
um array de char, o conceito de String é que a instância possui zero
ou mais caracteres do tipo char, enfileirados em ordem de leitura (da
esquerda para direita). Veja:
21
EXEMPLO
public class testePalavra {
public static void main(String args[]) {
//Usando array de char
char [] tipoChar = {‘p’,’a’,’l’,’a’,’v’,’r’,’a’};
//usando classe String sem instanciá-la String classeString =
“Palavra”;
//usando a classe String, instanciando-a
String classeString2 = new String (“Palavra”);
}
}
No exemplo a cima, usando char, é preciso criar um array e
cada letra precisa ser passada por aspas simples, enquanto na classe
String é possível passar diretamente entre aspas duplas ou por parâ-
metro entre aspas duplas ao instanciar a classe String.
Perceba que, no código anterior, o mais comum é fazer
“String nomeVariavel = “palavra”;” em vez de “String nomeVaria-
vel = new String(“palavra”);”.
É importante destacarmos que o tamanho de uma String depende
da quantidade de memória disponível no sistema, ou seja, ela consome
poucos ou muitos bits de memória conforme o tamanho do texto.
Além do que detalhamos, é preciso que você saiba que a classe
String tem métodos essenciais que auxiliam no desenvolvimento de
trabalho com textos. Alguns dos métodos, e para que eles servem, são:
• length - regressa à quantidade exata de caracteres con-
tidos em uma String. Este método, além de caracteres
visíveis, também retorna espaços, quebras de linhas,
22
tabulações etc. É importante destacarmos que o método
lengh não aceita parâmetros.
• charAt - volta ao caractere que está numa posição do texto.
Para execução do método, é necessário que se passe um valor
inteiro como argumento.
• getChars - retrocede a caracteres partindo de uma posi-
ção inicial até outra. Exige que se passe por quatro parâ-
metros, posição inicial (srcBegin), posição final (srcEnd),
destino (dest) e posição do início do destino (dstBegin).
Para tanto, é preciso criar um array de char como destino
passado como parâmetro.
• replace - substitui caractere ou texto e possui como parâme-
tro dois argumentos. O primeiro é o caractere ou texto a ser
substituído e o segundo, o caractere ou texto substituto.
• toUpperCase - deixa todo o texto maiúsculo e não
aceita parâmetros.
• toLowerCase - deixa todo o texto minúsculo e não
aceita parâmetros.
• trim: remove espaços contidos no início e/ou no final
de um texto, exceto entre duas palavras. Também não
aceita parâmetros.
A classe String é imutável, não sendo possível alterá-la após cria-
da. Por outro lado, uma variável da classe String também admite a
junção de textos, denominada como concatenação. A concatenação
é feita utilizando o operador de adição (+) que junta dois ou mais
textos em um. Ela só é possível porque, na concatenação, é criado
um novo objeto temporário da classe String, posteriormente asso-
ciado à referência da variável criada.
DICA
23
Variáveis
No algoritmo e na computação, temos as variáveis, que são definidas
como espaço em memória que possui um identificador e pode alo-
car dados. Como nos tipos de dados primitivos que compõem a cadeia
(inteiro, real e lógico), nas linguagens de programação, temos tipos
primitivos mais específicos, em que cada linguagem possui seu tipo
primitivo, porém, algumas linguagens possuem um padrão.
Para que você não tenha dúvidas, já lhe adianto que, nesta disci-
plina, será utilizada a linguagem Java nos exemplos e, nessa linguagem,
para definir uma variável, usa-se o conceito de declaração de variável.
É importantedestacarmos que Java é uma linguagem de pro-
gramação simples, portável, gratuita, robusta e com bibliotecas para
aplicações, além de ser possível executá-la em um terminal Unix ou
MS-DOS. Por ser multiplataforma, é utilizada para desenvolvimen-
to de sistemas web, mobile ou desktop e possui frameworks fáceis de
utilizar e também há integração fácil com o banco de dados.
Esta linguagem também é fortemente tipada, ou seja, ao de-
clarar atributos ou variáveis, declara-se também o tipo de dado (nu-
mérico, texto, booleano, entre outros). Grosso modo, diz-se que,
para um campo do tipo primitivo int (numérico), não é possível
atribuir um caractere não inteiro entre 0 e 9. Isso porque os tipos de
dados primitivos são palavras-chaves reservadas pela linguagem de
programação e não se pode utilizá-las como nome para uma variável.
Operadores lógicos relacionais
e comparadores em java
Os fluxos de um programa são baseados em operadores lógicos e ló-
gicos relacionais. Eles servem para que, durante a execução de um
trecho do código e segundo a condição, o programa faça algo ou, em
uma iteração, ele execute aquilo até que a condição seja cumprida ou
enquanto ela for válida. Dessa maneira, os operadores lógicos rela-
cionais em Java retornam o valor booleano true ou false e são listados
a seguir, junto com seu uso para verificação.
24
◼ == (igual): se dois valores são iguais. Se o valor à esquerda e
o valor à direita foram iguais, o resultado é true, do contrário
é false.
◼ != ( ): se dois valores são diferentes. Se o valor à esquerda
for diferente do valor à direita, o resultado é true, do con-
trário é false.
◼ > (maior): se um valor é maior que o outro. Se o valor à es-
querda for maior que o valor à direita, o resultado é true, do
contrário é false.
◼ < (menor): se um valor é menor que o outro. Se o valor à es-
querda for menor que o valor à direita, o resultado é true, do
contrário é false.
◼ >= (maior ou igual): se um valor é maior ou igual ao outro.
Se o valor à esquerda for maior ou igual ao valor à direita, o
resultado é true, do contrário é false.
◼ <= (menor ou igual): se um valor é menor ou igual ao outro.
Se o valor à esquerda for menor ou igual ao valor à direita, o
resultado é true, do contrário é false.
É importante destacarmos que os operadores anteriores são uti-
lizados para comparar valores nativos numéricos, inclusive o tipo char:
public class TesteOperadoresLogicos {
public static void main(String args[]) {
int numeroComparado = 18;
if (15 == numeroComparado) {
System.out.println(“os números são iguais”);
} else {
EXEMPLO
25
System.out.println(“os números são diferentes”);
}
If (21 != numeroComparado) {
System.out.println(“os números são diferentes”);
} else {
System.out.println(“os números são iguais”);
}
If (33 > numeroComparado) {
System.out.println(“o número é maior que o número comparado”);
} else {
System.out.println(“o número é menor que o número comparado”);
}
if (19 < numeroComparado) {
System.out.println(“o número é menor que o número comprado”);
} else {
System.out.println(“o número é menor que o número comprado”);
}
if ( 18 >= numeroComparado) {
System.out.println(“o número é maior ou igual ao número comparado”);
} else {
System.out.println(“o número é menor que o número comparado”);
}
26
EXEMPLO
if (18 <= numeroComparado) {
System.out.println(“o número é menor ou igual ao número comparado”);
} else {
System.out.println(“o número é maior que o número comparado”);
}
}
}
E, caso seja necessário comparar Strings ou objetos, se usa o
método equals, como neste exemplo da classe String:
public class ComparaPalavra {
public static void main(String args[]) { String palavra1 = “Palavra”;
String palavra2 = “Palavra”;
If (palavra1.equals(palavra2)) {
System.out.println(“As palavras são iguais”);
} else {
System.out.println(“As palavras são diferentes”);
}
}
}
27
EXEMPLO
Perceba que, no exemplo anterior, a palavra1 é igual à pala-
vra2, portanto, ao executar o código, é exibido no console o resulta-
do “As palavras são iguais”.
Ao utilizar o método equals da classe String, todos os caracteres da
palavra precisam estar na mesma sequência, inclusive os espaça-
mentos. Isso porque o método também diferencia se o caractere é
maiúsculo ou minúsculo. Então, “Arroz com Feijão” não é a mesma
coisa que “Arroz coM feijão” ou “Arroz comFeijão”. Assim, para sa-
ber se uma String é igual a outra, sem distinguir caracteres maiús-
culos e minúsculos, é utilizado o equalsIgnoreCase.
É importante você saber que todas as classes possuem o mé-
todo equals (herdado da classe Object) e, para customizá-lo, é pre-
ciso sobrescrevê-lo utilizando a annotation @Override acima do
método, veja:
public class Livro {
private long id;
private String nomeAutor;
private int anoLancamento;
private String titulo;
public String getNomeAutor() {
return nomeAutor;
}
DICA
28
public void setNomeAutor(String nomeAutor) {
this.nomeAutor = nomeAutor;
}
public int getAnoLancamento() {
return anoLancamento;
}
public void setAnoLancamento(int anoLancamento) {
this.anoLancamento = anoLancamento;
}
public String getTitulo() {
return titulo;
}
public void setTitulo(String titulo) {
this.titulo = titulo;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Livro other = (Livro) obj;
29
if (anoLancamento != other.anoLancamento)
return false;
if (nomeAutor == null) {
if (other.nomeAutor != null)
return false;
} else if (!nomeAutor.equals(other.nomeAutor))
return false;
if (titulo == null) {
if (other.titulo != null)
return false;
} else if (!titulo.equals(other.titulo))
return false;
return true;
}
}
No exemplo acima, o método equals foi sobrescrito com a
annotation @Override. No código, o objeto passado pelo parâmetro
“(Object obj)” é do tipo Object e não do tipo Livro, isso acontece por-
que todas as classes herdam direta ou indiretamente a classe Object,
sendo ela a base para todas as demais. Em seguida, como no código
do exemplo, é feita a conversão explícita do objeto genérico para o
tipo Livro (Livro other = (Livro) obj;).
Ele também confere cada atributo da classe Livro, com ex-
ceção do id, algo que foi feito de forma proposital, pois, se ele
fosse verificado também, conforme a circunstância, haveria dois
objetos Livros com o mesmo autor, mesmo título e mesmo ano de
lançamento, mas com o id diferente, fazendo que o método equals
retorne false.
30
Lembre-se que o id é utilizado para aplicações em banco de
dados, logo, ao averiguar se um objeto já existe no banco, não se
considera o id, pois assim é evitada a duplicidade de dados, já que os
demais atributos seriam iguais. Não obstante, isso depende da regra
de negócio.
Executando o java
Após a instalação do JDK e do Netbeans, abra o Netbeans. Clique em
Arquivo e, logo em seguida, clique em Novo Projeto, e aparecerá a
tela representada na Figura 1:
Figura 1 - Ambiente Netbeans: tela inicial de Novo Projeto
Fonte: editorial Digital Pages (2019).
Logo depois, na tela do Novo Projeto, clique em Java, Aplica-
ção Java e Próximo, e aparecerá a tela conforme a Figura 2:
31
Figura 2 - Ambiente Netbeans: criando Nova Aplicação do Projeto
Fonte: editorial Digital Pages (2019).
Nessa etapa, escreva o Nome do Projeto (todo exemplo e
programa que serão desenvolvidos na disciplina será um Projeto
Novo). A localização física padrão do projeto é na pasta Documentos\
NetBeansProjects do Usuário local. Deixe selecionadoCriar Classe
Principal, assim:
Figura 3 - Ambiente Netbeans: tela inicial de Desenvolvimento
Fonte: editorial Digital Pages (2019).
32
EXEMPLO
Na área de desenvolvimento, temos o botão Play, que é o bo-
tão que inicia o comando de execução do projeto e o faz na parte de
baixo da área de desenvolvimento. Fique tranquilo(a), pois, na dis-
ciplina, será fornecido o código para execução das estruturas.
Atente-se ao fato de que todo nome do programa deve-
rá (neste primeiro momento) ser o nome do projeto. Logo, veja o
exemplo a seguir.
public class prjOla {
public static void main(String []args)
{
System.out.println(“Olá”);
}
}
O nome do projeto, ao criar um novo, é prjOla. O Java é case
sensitive, isto é, ele diferencia letras maiúsculas e minúsculas. En-
tão, prjOLA é diferente de PRJOLA e diferente de prjola. Por isso, no-
mes de programas, projetos e comandos devem seguir exatamente
como estão escritos no material.
No comando anterior, para poder executar, cole o código e di-
gite play. Lembre-se que os comandos em Java funcionam via blo-
cos, que são as aberturas e fechamentos de chaves.
O principal método a ser executado no projeto é o public static
void main que, ao ser iniciado, irá apresentar na tela a palavra “Olá”
através do comando System.out.println().
A seguir, estão exemplos do uso de tipos primitivos em ope-
ração matemática e demonstração em tela.
33
EXEMPLO
public class prjSoma {
public static void main(String []args)
{
int numA = 5; int numB = 6;
int result = numA + numB;
System.out.println(“O resultado de A + B :” + result);
}
}
Inserido Classes
Caro(a) aluno(a), para inserir mais uma classe no projeto, clique
com o botão direito do mouse em cima do pacote, como na Figura 4.
Figura 4 - Ambiente Netbeans: criando uma nova Classe no Projeto (passo 1)
Fonte: editorial Digital Pages (2019).
34
Logo em seguida, aparecerá a janela da Figura 5. Neste mo-
mento, somente escreva o nome da classe e clique em Finalizar.
Figura 5 - Ambiente Netbeans: criando uma nova Classe no Projeto (passo 2)
Fonte: editorial Digital Pages (2019).
Operadores lógicos e negação em java
Os operadores lógicos seguem a mesma ideia que os lógicos relacio-
nais e auxiliam na execução de processos diante de circunstâncias
para tomada de decisões, em que duas ou mais condições são consi-
deradas e retornam um valor booleano true ou false. Há apenas dois
operadores lógicos:
• && (dois “e comercial”): a pronúncia correta é and e a tradu-
ção para o português é “e”. É utilizado para comparar duas ou
mais condições e todas precisam ser verdadeiras.
• || (duas vezes o símbolo pipe, localizado no mesmo botão que
a contra-barra “\”, próximo à letra Z no teclado): a pronúncia
correta é or e a tradução para o português é “ou”. É utiliza-
do para comparar duas ou mais condições em que pelo menos
uma delas precisa ser verdadeira.
35
EXEMPLO
public class OperadorLogico {
public static void main(String args[]) {
int resultado1 = 15 + 12;
int resultado2 = 22 + 33;
if (resultado1 == 27 && resultado2 == 55) {
System.out.println(“As condições foram atendidas”);
} else {
System.out.println(“As condições não foram atendidas”);
}
if (resultado1 > 27 || resultado2 == 55) {
System.out.println(“Uma ou outra condição foi atendida”);
} else {
System.out.println(“Nenhuma das condições foi atendida”);
}
}
}
No exemplo acima, a primeira condição (primeiro if) é
que o resultado1 precisa ser igual a 27 e o resultado2, igual a 55.
Como ambas as comparações são verdadeiras, ao executar o có-
digo, para a primeira condição é exibido o texto “As condições
foram atendidas”.
Já na segunda condição, o resultado1 precisa ser maior que 27
e o resultado2, igual a 55. Assim, a primeira condição não é atendi-
da, pois resultado1 é igual a 27 e não maior. Já a segunda condição
é atendida, pois o resultado2 é igual a 55. Como o operador lógico é
36
EXEMPLO
“ou” e só uma condição precisa ser verdadeira, ao executar a segun-
da condição, o texto exibido é “Uma ou outra condição foi atendida”.
A negação é utilizada para negar (inverter) um valor boolea-
no, seja ele verdadeiro (true) ou falso (false). A negação utiliza o sinal
! (exclamação) e sua denominação é not (não). Dessa maneira, toda
comparação retorna um valor booleano, assim, a negação impacta
diretamente no valor booleano, em que o verdadeiro vira falso e o
falso vira verdadeiro.
public class ComparaPalavra {
public static void main(String args[]) {
int resultado1 = 15 + 12;
int resultado2 = 22 + 33;
if (!(resultado1 == 28) && resultado2 == 55) {
System.out.println(“As condições foram atendidas”);
} else {
System.out.println(“As condições não foram atendidas”);
}
if (!(resultado1 > 27 || resultado2 == 55)) {
System.out.println(“Uma ou outra condição foi atendida”);
} else {
System.out.println(“Nenhuma das condições foi atendida”);
}
}
}
37
Como você já deve ter percebido, um programa de compu-
tador segue a mesma regra da matemática, processando primeiro
o conteúdo interno dentro dos parênteses e, depois, o que está fora
dos parênteses. Pensando nisso, o raciocínio para o operador de ne-
gação parece um pouco complicado em um primeiro momento e,
por isso, ele foi separado em tópicos para melhor entendimento.
Observando o código anterior, na primeira condição, se tem:
!(resultado1 == 28) && resultado2 == 55
• Dentro dos parênteses resultado1 == 28. O resultado dá o valor
booleano false, já que o resultado1 é igual a 27, e não 28.
• O sinal de negação está negando o resultado dentro dos parênteses.
Desse modo, o resultado vira true já que é false (inverso de false);
• Depois, se tem a comparação “resultado2 == 55”, o que resul-
ta em true.
• Então, aparecem dois valores booleanos –true: o “!(resultado 1 ==
28)” é verdadeiro e o “resultado2 == 55” também. Como o compa-
rador lógico neste caso é “&&” (e), todas as condições precisam ser
verdadeiras, o que realmente acontece. Assim sendo, para a primeira
condição, é exibido no terminal “As condições foram atendidas”.
Já para a segunda condição, se tem o seguinte cenário:
!(resultado1 > 27 || resultado2 == 55)
• Dentro dos parênteses, há duas comparações de operadores
lógicos de comparação e o operador lógico “||” (ou).
• O resultado1 é igual a 27, e não maior, logo, o resultado é false.
• O resultado2 é igual a 55, logo, o resultado é true.
• A condição interna dentro dos parênteses volta true, porque
apenas uma das comparações precisa retornar true.
• O sinal de negação está negando esse resultado. Como ele é
true, o resultado final é false.
• Portanto, o resultado exibido no terminal é “Nenhuma das
condições foi atendida”.
38
Estrutura de decisão em java
Um programa de computador trabalha constantemente em cima de
decisões que são executadas a partir dos valores booleanos true e false.
Além disso, os comandos if-else permitem que um comando ou bloco
seja executado diante de uma comparação lógica. Assim, a instrução
if-else possui uma estrutura básica que pode ser observada na Figura 6.
Figura 6 - Exemplo de condição IF-ELSE *
Fonte: editorial Digital Pages (2020).
Como você deve ter observado, na figura 6 se tem a declaração
do tipo primitivo boolean, com referência na memória chamada de
“verdadeiro”, ou seja, o valor booleano possui valor true. Além disso,
na estrutura if-else, a primeira condição, destacada em vermelho e na
qual está o if, só é executada caso a condição dentro dos parênteses for
verdadeira (true). Na hipótese da primeira condição ser false, é execu-
tada a condição dentro de else, destacada em cinza. Assim, em uma es-
trutura if-else, se não houver uma condição a ser executada dentro da
condição else, ela não é obrigatória e nem é declarada,contendo apenas
o if. Em alguns casos, há N condições em uma estrutura if-else, que é,
então, denominada de if-else aninhada, como na figura 7.
* Caro(a) aluno(a), para uma melhor visualização da figura, sugerimos que
acesse o material disponível no seu Ambiente Virtual de Aprendizagem (AVA)�
39
Figura 7 - Exemplo de decisão IF, ELSE E IF-ELSE *
Fonte: editorial Digital Pages (2020).
Como você pôde perceber, na figura 7, há uma instrução
if-else aninhada, composta por vários blocos de instruções if-else.
Se a primeira condição, destacada em vermelho, for true, as demais
não são executadas. Mas, caso seja false, é apurada a segunda condi-
ção, destacada em verde e, se ela for true, as demais abaixo não são
examinadas. E, caso seja false, é verificada a terceira condição, des-
tacada em bordô. Isso acontece até a condição em laranja e a última
condição só é executada se a condição em laranja não for true. Para
comparação de casos mais simples, é possível fazer uso do operador
ternário. A sintaxe do operador ternário é: “condiçãoBooleana? se-
Verdade : seFalso;”.
* Caro(a) aluno(a), para uma melhor visualização da figura, sugerimos que
acesse o material disponível no seu Ambiente Virtual de Aprendizagem (AVA)�
40
Figura 8 - Exemplo de SWITCH-CASE *
Fonte: editorial Digital Pages (2020).
Na figura 8, em azul, está o condicionador ternário e, em ver-
de, a expressão que resulta em um valor booleano true ou false. Se a
expressão booleana for true, é executado o trecho entre ? (interro-
gação) e : (dois-pontos), destacado em vermelho.
Mas, caso a expressão booleana seja false, é executado o tre-
cho após os: (dois-pontos), destacado em laranja. O operador ter-
nário é similar a uma expressão if-else simples.
Similar à um if-else aninhado, há o switch-case, utilizado para
uma expressão que possui diversas possibilidades, como na figura 9.
Figura 9 - Exemplo de SWITCH-CASE *
Fonte: editorial Digital Pages (2020).
* Caro(a) aluno(a), para uma melhor visualização da figura, sugerimos que
acesse o material disponível no seu Ambiente Virtual de Aprendizagem (AVA)�
41
Diferentemente do if-else aninhado, o switch-case não para de
ser executado até que a última condição não seja validada, exceto se
usada a palavra-chave “break”. Na figura 9, há um switch-case e cada
bloco possui a palavra-chave “break” para que, quando a condição for
correspondida, o bloco de código switch-case seja interrompido.
Destacada em amarelo, a palavra-chave “default” é executa-
da quando nenhuma das condições é correspondida. Não obstante,
há outras observações importantes quanto ao switch-case:
• a palavra-chave “default” não é obrigatória e não precisa ter um
break, afinal, não há mais condições a serem comparadas e, por con-
sequência, não há necessidade de interromper o bloco switch-case.
• caso o bloco do switch-case não possua default, o último case
não têm necessidade de ter break.
• caso duas ou mais condições executem o mesmo processo,
elas são alinhadas abaixo uma da outra sem expressão. Veja
na Figura 10 que, na parte destacada em cinza, caso a variável
número1 seja 8, 9 ou 10, é exibido no console o texto “O núme-
ro é 8, 9 ou 10”; e se não for nenhuma destas opções, é exibido
no console “O número não é 8, 9 ou 10”;
Figura 10 - Exemplo de SWITCH-CASE ANINHADO *
Fonte: editorial Digital Pages (2020).
* Caro(a) aluno(a), para uma melhor visualização da figura, sugerimos que
acesse o material disponível no seu Ambiente Virtual de Aprendizagem (AVA)�
42
• O switch-case também é designado para comparar valo-
res numéricos de, no máximo, tipo int, que variam entre
-2147483648 e 2147483647. Somente a partir do Java 7, é
possível trabalhar com Strings, portanto, tipos boolean, long,
float e double não funcionam.
Estrutura de repetição em java
Além da estrutura de decisão, o sistema conta com estruturas de repetição
que só param por uma condição programada, como se nota na Figura 11.
Figura 11 - Exemplo de Estrutura de repetição *
Fonte: editorial Digital Pages (2020).
* Caro(a) aluno(a), para uma melhor visualização da figura, sugerimos que
acesse o material disponível no seu Ambiente Virtual de Aprendizagem (AVA)�
43
Como você deve ter percebido, na Figura 11 há quatro tipos de
estruturas de repetição:
• o for, destacado em cinza, realiza uma tarefa enquanto a
condição for verdadeira. Na figura 10, a variável i é decla-
rada no próprio escopo da condição, mas poderia ser de-
clarada fora. A estrutura for é lida da seguinte maneira:
“enquanto a condição for verdadeira, execute a tarefa”.
Cada trecho é separado por ponto e vírgula. Assim, no pri-
meiro ponto e vírgula consta a declaração da variável e a
atribuição de um valor a ela. No segundo, há a condição e,
no terceiro, a incrementação.
• o do-while, destacado em bordô, executa uma tarefa enquanto
a condição for verdadeira. A estrutura é lida da seguinte ma-
neira: “execute a tarefa, enquanto a condição for verdadeira”.
A variável foi declarada fora da iteração, pois, se fosse decla-
rada dentro, ela seria criada novamente a cada iteração. Den-
tro do bloco do, é concretizada a incrementação e, dentro do
while, a condição é verificada.
• o while, destacado em vermelho, faz uma tarefa enquanto a
condição for verdadeira. A estrutura é lida da seguinte ma-
neira: “enquanto a condição for verdadeira, execute a ta-
refa”. A variável é declarada fora do escopo do while, pois,
se fosse declarada dentro, ela seria criada a cada iteração.
Assim, a verificação e o incremento se dão dentro do escopo
do while.
• o último, embora seja similar ao for, é conhecido como forEa-
ch. Ele é utilizado para percorrer arrays e, a cada valor contido
no array, ele promove uma tarefa. A estrutura forEach é inter-
pretada da seguinte maneira: “a cada dado, execute a tarefa”.
O forEach não possui contador porque um array possui chave e
valor, em que a chave é o índice e o valor é o dado em si. Logo,
ele percorre o índice do array.
Embora while e do-while sejam similares, o while só executa
se a condição for true, enquanto o do-while executa ao menos uma
vez, mesmo que a condição seja false.
44
EXEMPLO
public class ExemploRepeticao {
public static void main(String args[]) {
int j = 0;
do {
System.out.println( “ O valor de J é “ + j );
} while (j != 0);
int k = 0;
while (k != 0) {
System.out.println( “ O valor de k é “ + k );
}
}
}
Observe que, nesse exemplo, o do-while é executado uma vez,
enquanto o while não é executado.
Terminologia da programação orientada
a objetos
No mundo de orientação a objetos, assim como em outras áreas,
existem terminologias utilizadas nas linguagens orientadas a ob-
jetos, tais como:
• objetos - são instâncias de classes que possuem estado e
comportamento. O estado é representado pelos campos (atri-
butos) daquele objeto, que podem ser modificados ao longo
do tempo. Já os comportamentos são os métodos, compostos
45
por um conjunto de instruções, com a característica de alterar
o estado do objeto.
• classes - são estruturas de um objeto (model) que contêm
atributos e métodos. Todo objeto é uma instância pertencente
à classe e toda classe precisa de um nome único no pacote em
que se encontra. Seguindo a convenção de código, uma classe
é declarada usando UpperCamelCase.
• atributos - são as propriedades (variáveis) que descrevem o
objeto. Em uma linguagem fortemente tipada, precisam ser
de um tipo, seja ele primitivo ou de outra classe. Em uma lin-
guagem fracamente tipada, o tipo não importa, pois a lin-
guagem consegue se arranjar com o tipo de dado presente
naquele atributo.
• variáveis - são nomes atribuídos ao endereço de memória do
computador utilizado para armazenamento de tipos de dados do
programa executado. Possuem esse nome porque variam duran-
te a execução, não sendo um valor fixo. Seguindo a convenção de
código, são declaradas usando LowerCamelCase e devem iniciar
por uma letra (de a-z),underline (_) ou cifrão ($). É importante
que você nunca inicie o nome de uma variável com números.
Uma variável global pode ser acessada por qualquer método na
classe e é declarada no cabeçalho da classe. Já uma variável local
é visível apenas pelo método que está executando.
• constantes - possuem as mesmas características de uma va-
riável, mas são imutáveis durante a execução do programa.
Seguindo a convenção de códigos, uma constante é declarada
usando UpperCase.
• escopo - trata-se do local em que uma variável é acessada. Há
dois tipos de escopo, o global e o local. Uma variável declarada
no cabeçalho de uma classe possui o escopo global, ou seja,
pode ser acessada por qualquer método de uma classe. Já uma
variável local, declarada dentro de um método, tem o escopo
local e só é acessada dentro daquele método, não sendo possí-
vel utilizá-la em outro método.
46
• métodos e mensagens - os métodos são processos exe-
cutados a fim de realizar alguma operação relacionada ao
objeto a que ela pertence. Eles se comunicam por meio de
mensagens que podem ou não conter dados. Já as mensa-
gens são as informações contidas dentro dos parênteses
dos métodos, batizados como parâmetros. A declaração de
um método, seguindo a convenção de códigos, é escrita em
LowerCamelCase.
• herança - admite que um objeto herde as mesmas carac-
terísticas de outro objeto, facilitando o desenvolvimento
e reaproveitando o código já existente. Uma classe Equi-
pamento Eletronico, por exemplo, possui característi-
cas (como marca, modelo, altura, largura, profundidade
e preço), comportamentos (como liga e desliga) e outras
duas classes, TV e Rádio, que apresentam as mesmas ca-
racterísticas que o equipamento, fazendo com que herdem
esses atributos.
• ocultação de informação (privacidade) - termo manifesto
como Information Hiding, que provê a ocultação de informa-
ções desnecessárias, permitindo a visibilidade de itens es-
senciais da classe. Assim, a privacidade é controlada pelos
três tipos de modificadores para atributos e métodos: pu-
blic, protected e private ou, respectivamente, visível a todas
as classes de qualquer pacote; visível a todas as classes do
mesmo pacote exceto em caso de herança visível por classes
que a herdam em pacotes diferentes; e visível apenas pela
própria classe.
• encapsulamento de dados - muitos autores afirmam que o
encapsulamento é utilizado para a proteção de dados, o que de
fato acontece. Porém, a ideia é que esses dados sejam acessí-
veis diante dos métodos disponíveis naquela classe, denomi-
nados de getters e setters. Contudo, há outras formas de acesso
a estes dados.
• polimorfismo - em POO, possibilita que uma classe herde
outra de maneira que seus comportamentos sejam similares,
47
mas distintos. Um exemplo é a classe animal, na qual quase
todos os animais têm o comportamento de emitir som, mas
cada espécie emite um som particular.
• identação - a identação (ou endentação) é o termo atribuído
ao código-fonte de um programa, que auxilia na identificação
de hierarquia de blocos de instrução, tornando o código mais
“limpo” e de fácil entendimento. A identação não é obrigató-
ria na programação, mas sua prática é essencial.
Um sistema computacional é gerado com objetivo de solucionar
problemas cotidianos e a orientação a objetos é destinada a geren-
ciar a complexidade na solução dos problemas com um conjunto de
objetos. Assim, baseados em objetos reais, os objetos criados são
compostos por características e comportamentos, além de serem
únicos na memória do sistema.
Instanciando um objeto
Para criar um objeto na memória do sistema, é preciso declarar o
tipo do objeto seguido de um nome. Isso faz com que o programa
reserve um espaço na memória do computador e, mediante o nome
do objeto, se obtém acesso aos dados armazenados no objeto. No
entanto, isso não é suficiente para que se trabalhe com o objeto, já
que ele possui somente uma referência na memória.
Para que as informações do objeto sejam acessíveis, é ne-
cessário que ele seja instanciado e, para isso, emprega-se a pa-
lavra-chave “new” seguida do construtor da classe. Lembre-se
que toda classe tem seu construtor, que não precisa ser declara-
do, mas, conforme a necessidade, é criado da maneira que me-
lhor convém.
DICA
48
DICA
Figura 12 - Exemplo de Instanciação *
Fonte: editorial Digital Pages (2020).
Devem ser utilizados ao longo da unidade, sejam gráficos, mapas
mentais e/ou conceituais, fluxogramas, organogramas, tabelas e
quadros, além de imagens que ilustrem graficamente o texto. Para
esse fim se utilize de programas como canva.com, Lucidchart.com,
pixabay e freepic. Ao inseri-lo, não esqueça de referenciar a fonte e
colocar a legenda.
* Caro(a) aluno(a), para uma melhor visualização da figura, sugerimos que
acesse o material disponível no seu Ambiente Virtual de Aprendizagem (AVA)�
Construtor padrão
Construtor da classe Car
Nova instância do objeto Car
Construtor customizado
com parâmetros
Referência do
objeto em
memória
49
Caro(a) aluno(a), neste material, discutimos o surgimento da pro-
gramação orientada a objetos, sua referência com modelos de en-
tidades e explicamos que, para programar, é preciso representar
objetos por meio de classes que contêm estados e comportamentos
de um objeto.
Além disso, explicamos que a POO melhora a produtividade dos(as)
programadores(as), permitindo a reutilização de códigos e é de fácil
manutenibilidade, além de ser escalável.
Sobre a linguagem de programação Java, ressaltamos que ela é
100% orientada a objetos e que funciona em diversos dispositivos
e sistemas operacionais diferentes, sendo fortemente tipada, pos-
suindo tipos primitivos e uma classe diferenciada chamada String
para trabalhar com caracteres alfanuméricos, mas que não precisa
ser instanciada, além de haver vários métodos internos essenciais
para um(a) programador(a) trabalhar com os caracteres.
Também foram abordados operadores lógicos, operadores lógicos
relacionais, negação, comparadores de classes, estruturas de deci-
são e repetição. E, ao final, foram abordados conceitos da orientação
a objetos, incluindo objetos, classes, atributos, variáveis, constan-
tes, escopos, métodos e mensagens, herança e encapsulamento.
Lembre-se de estudar antes e depois da sua aula, aproveite o mo-
mento com o(a) docente para tirar todas as suas dúvidas.
Até breve!
SINTETIZANDO
UN
ID
AD
E
2
Objetivos
◼ Apresentar as características de Programação Orientada a Objetos.
◼ Compreender como surgiu o paradigma de Programação
Orientada a Objetos.
◼ Introduzir a linguagem Java para Programação Orientada a Objetos.
◼ Entender os conceitos da Programação Orientada a Objetos.
◼ Apresentar as estruturas de dados compostas homogêneas
e heterogêneas.
◼ Apresentar os tipos abstratos de dados.
52
Introdução
Olá, aluno(a), como vai?
Vamos iniciar mais uma etapa de estudos e, a partir de agora,
veremos os principais conceitos de atributos e métodos, entenden-
do a ocultação das informações através do encapsulamento. Além
disso, serão apresentados os conceitos de instâncias, referências e
como as classes enviam mensagens.
Destaco que, neste material, será explicado qual o ciclo de
vida de um objeto. Também apresentaremos o conceito de estrutura
compostas homogêneas e heterogêneas.
Dito isto, desejo a você boa leitura e bons estudos!
53
Classes, pacotes, objetos, atributos,
métodos, construtores, palavra-chave
this e sobrecarga
Classes
A programação orientada a objetos demanda que, ao desenvolver
um programa de computador, constantemente, sejam utilizadas
classes, com as quais fazemos a representação de uma entidade,
seja ela real ou abstrata. A declaração de uma classe é simples, con-
forme a sintaxe expressa na figura 1:
Figura 1 - Sintaxe básica de uma classe*
Fonte: Editorial Digital Pages, 2020.
* Caro(a) aluno(a), para uma melhor visualização da figura, sugerimos que
acesse o material disponívelno seu Ambiente Virtual de Aprendizagem (AVA)�
54
Como você deve ter percebido, na figura 1 temos o modifica-
dor de acesso da classe que determina a visibilidade da classe, e que
pode ser public, protected, private ou default.
Os modificadores de acesso são palavras-chaves reservadas pela
linguagem, e devem ser escritos com caracteres minúsculos.
Os modificadores protected e private são raramente usados, e
só são possíveis através de classes aninhadas.
No contexto da programação em Java, o modificador de acesso de-
fault não deve ser declarado, sendo oculto.
Além dos modificadores de acesso, temos o nome da clas-
se, que deverá ser único no pacote e seguir a convenção de códigos.
Além disso, ela deve ser declarada em CammelCase, seguindo padrão
UpperCamelCase, seu nome deverá ser um substantivo e ter relação
com o que ela realmente propõe, sendo simples e objetivo.
Fique atento(a), pois as declarações de atributos e métodos
devem ser realizadas dentro do escopo da classe. Importante res-
saltar que:
• toda classe deve possuir a palavra-chave class declarada entre
o modificador de acesso e o nome da classe;
• toda classe deve estar contida em um pacote. Isso porque,
mesmo que ele não seja definido, por padrão, a classe estará
em um pacote default.
DEFINIÇÃO
VOCÊ SABIA?
55
Pacotes
Caro(a) aluno(a), ao programar fazendo uso da orientação a objetos,
utilizamos pacotes (packages). Pensando nisso, mesmo que eles não
sejam definidos, as classes devem estar dentro de um pacote default
do próprio Java.
Lembre-se: pacotes são utilizados a fim de estruturarmos melhor
o sistema desenvolvido, facilitando a localização de tipos, evitando
conflitos de nomes e controlando o acesso.
Ao trabalharmos com pacotes diferentes do default, temos
que declarar, na estrutura da classe, a palavra-chave packages, se-
guida do nome do pacote. Carvalho e Teixeira (2012, p. 36) definem
pacotes como sendo um envoltório de classes, um guarda classes e
outros pacotes. Dessa maneira, podemos visualizar os pacotes como
diretórios, ou pastas, nos quais podemos guardar arquivos (classes)
e outros diretórios (pacotes).
Veja o exemplo da declaração de uma classe com pacote:
package modelos;
public class Pessoa {
private String nome;
}
Se declarado, o pacote deve estar na primeira linha de código.
DICA
DICA
56
Mendes (2009, p. 79) diz que os nomes de pacotes, por reco-
mendação, mas não obrigatoriamente, devem ser declarados utili-
zando o nome do domínio reverso da empresa.
Imagine a seguinte situação: uma empresa com domínio www.mi-
nhaempresa.com.br está desenvolvendo o projeto farmacêutica,
portanto, durante o desenvolvimento do projeto, ao criar o pacote
interfaces, deve ser utilizado o nome br.com.minhaempresa.far-
maceutica.interfaces.
É importante destacarmos que o uso de pacotes é útil para re-
solver problemas com a importação de classes que possuem nomes
iguais. E, por convenção, os pacotes devem possuir nomenclaturas
que usam letras minúsculas e sem caracteres especiais. Veja um
exemplo de estrutura de pacotes de um sistema na figura 2:
Figura 2 - Exemplo de estrutura de pacotes
Fonte: Editorial Digital Pages, 2020.
EXEMPLO
57
Como você deve ter percebido, na figura 2 temos vários pa-
cotes do projeto fictício denominado gtc. e, em cada pacote, há um
tipo de classe que corresponde ao que ela faz. Além disso, todos os
pacotes após o .gtc. estão dentro do primeiro pacote gtc.
Objetos
Inicialmente, é necessário que você saiba que os objetos são todas
as entidades que podem ser modeladas (podendo ser concretas
ou não) e que possuem estado e comportamento. Além disso, o
estado de um objeto é o conjunto das características que ele pos-
sui e que podem ser modificadas. Já o comportamento é definido
pelas ações que modificam o estado do objeto. Veja um exemplo
na figura 3:
Figura 3 - Exemplo de classe com estado, comportamento e outro objeto*
Fonte: Editorial Digital Pages, 2020.
Para que você entenda, na figura 3 temos duas classes que são
representações do objeto carro e fabricante. Destacado em cinza,
temos o estado da classe, que são os seus atributos e, em vermelho,
temos o comportamento que são os métodos que alteram o estado
da classe. Observe que o objeto carro, possui como atributo o objeto
fabricante, que também possui estado e comportamento.
* Caro(a) aluno(a), para uma melhor visualização da figura, sugerimos que
acesse o material disponível no seu Ambiente Virtual de Aprendizagem (AVA)�
58
Atributos
Os atributos são as características que um objeto possui. Dessa manei-
ra, podemos entender que um atributo é a representação de um dado
ou informação de estado do objeto e cada objeto de uma classe pos-
sui seu valor próprio. Existem dois tipos de atributos na programação
orientada a objetos: os atributos de objetos e os atributos de classes.
Os atributos de objetos são os que descrevem os valores daque-
le objeto, em que cada instância possui a sua particularidade. Já os
atributos de classe são os valores compartilhados entre todas as
instâncias do objeto.
Em Java, por se tratar de uma linguagem fortemente tipada,
ao declarar um atributo, precisamos dizer de qual tipo é aquele atri-
buto. Veja a sintaxe básica para declaração de atributos na figura 4:
Figura 4 - Exemplo de declaração de atributos em classe*
Fonte: Editorial Digital Pages, 2020.
* Caro(a) aluno(a), para uma melhor visualização da figura, sugerimos que
acesse o material disponível no seu Ambiente Virtual de Aprendizagem (AVA)�
Indica que o atributo é da classe
Indica que o atributo é da classe
Indica que o atributo é
da classe
Indica que o atributo é da classe
Tipo de dado: primitivo ou outro projeto
Nome do atributo
Indica que o atributo é da classe
DEFINIÇÃO
59
Observando a figura 4, podemos fazer algumas observações:
• os atributos possuem modificadores de acesso, que podem
ser private, protected, public ou default, e que determinam
a visibilidade do atributo. Lembre-se que os modificadores
são palavras-chave da linguagem e são declarados em le-
tras minúsculas.
• a palavra-chave static é reservada da linguagem e deve ser
declarada em letras minúsculas. É utilizada para informar
que aquele atributo é de classe, portanto, todas as instân-
cias da classe possuem o mesmo valor e, se em algum mo-
mento ele for alterado, todas as instâncias passarão a ter o
novo valor.
• o tipo do dado é uma característica das linguagens forte-
mente tipadas como o Java, em que é necessário declarar
qual é o tipo do atributo, que pode ser primitivo ou um outro
objeto (classe).
• por último, temos o nome do atributo que deve ser único na
classe. Seguindo a convenção de códigos, ele deve ser declara-
do utilizando LowerCamelCase.
Uma observação importante a se fazer sobre os atributos de
classe é que, para acessá-los, não é necessário instanciar o obje-
to, porém a classe precisa estar visível a outra classe e o atributo
precisa ter o modificador public, protected ou default. Caso contrário,
eles devem ser acessados por métodos também declarados como
estáticos, por exemplo, os métodos getters e setters. Para acessar o
atributo estático da classe, precisamos fazer referência ao objeto,
seguido do atributo.
Carro.quantidadeVendida; ou Carro.getQuantidadeVendida();.
EXEMPLO
60
Métodos
Os métodos são estruturas contidas dentro de classes que são utili-
zadas para realizar operações. Eles, geralmente, alteram o estado de
um objeto, porém podem ser utilizados para realizar qualquer fun-
ção (não demandando, necessariamente, a alteração de um objeto, e
podendo apenas retornar um dado qualquer).
Dependendo do que o método faz, se ele é utilizado por vá-
rias classes e não realiza operações específicas com atributos de
uma classe (como calcular dois valores), ele pode se conter apenas
dentro de classes que não possuem atributos, sendo essas classes
denominadas bibliotecas de operações. Veja como é a sintaxe da de-
claraçãode métodos, na figura 5:
Figura 5 - Sintaxe básica de método*
Fonte: Editorial Digital Pages, 2020.
Dentro da classe mostrada na figura 5, temos:
• em azul escuro, os modificadores de acesso aos métodos, que
podem ser public, protected, private ou default.
• em rosa, o modificador static que possibilita utilizar o método
da classe sem a necessidade de instanciá-la.
* Caro(a) aluno(a), para uma melhor visualização da figura, sugerimos que
acesse o material disponível no seu Ambiente Virtual de Aprendizagem (AVA)�
61
• em vermelho, duas informações. A primeira está na parte de cima,
onde se indica a palavra int. Isso quer dizer que o método deve-
rá retornar algum dado que represente o tipo int (qualquer valor
dentro da faixa de -2147483648 a 2147483647). Depois, temos a
palavra-chave return, que retorna um resultado entre os números
inteiros. Um método pode retornar qualquer tipo de dado, seja ele
primitivo ou não, além de poder não retornar nada, sendo que o
tipo de retorno deve ser void e não possuir a palavra-chave return.
• em verde, os nomes dos métodos. Diferente dos atributos,
podemos possuir vários métodos com o mesmo nome, porém
eles precisam possuir uma assinatura diferente. Lembre-se
que a assinatura de um método é composta por nomes e parâ-
metros. Assim, a possibilidade de criar dois ou mais métodos
com mesmo nome e assinaturas diferentes é chamada de so-
brecarga de métodos. Seguindo a convenção de códigos Java,
os nomes de métodos devem ser declarados usando LowerCa-
melCase e representar um verbo.
• em azul, os parâmetros. Eles precisam estar dentro dos parên-
teses do método. A quantidade de argumentos passados por pa-
râmetros podem ser nenhuma ou várias, e de diferentes tipos de
dados ou objetos. Os parâmetros, se declarados, devem possuir
tipo e nome, assim como fazemos para declarar atributos na
classe. Esses parâmetros devem ser informações que são utiliza-
das dentro do método, e não estão acessíveis fora do seu escopo.
• em laranja, o escopo do método que está entre as chaves. Nele
são realizadas as operações, declaradas variáveis e, se neces-
sário, enviado o retorno de que o método necessita. Lembre-
-se que toda variável declarada ou objeto instanciado dentro
do escopo do método é acessível somente dentro do método e
não pode ser reutilizado em outro método.
A invocação de um método é feita, geralmente, de forma ex-
plícita a partir de outros trechos de códigos. Esses trechos podem
estar dentro de outros métodos (um método chamando o outro) que
está dentro da própria classe ou a partir de outras classes. Veja o
exemplo da figura 6:
62
Figura 6 - Sintaxe para invocação de métodos*
Fonte: Editorial Digital Pages, 2020.
Como você pode observar na figura 6, temos o método main da
classe Start declarado em cinza. Ele é um método padrão da linguagem
Java, que é utilizado para dar o pontapé inicial na aplicação. Esse método
sempre será public static void e deve possuir como parâmetro um array de
argumentos do tipo String, que pode ser ou não passado durante a inicia-
lização da aplicação. Para esse caso, os parâmetros não são obrigatórios
e nome args pode ser qualquer outro, porém deve ser um array, ou seja,
possuir os colchetes declarados após o nome do parâmetro. O método
main não possui retorno, já que o retorno esperado é void.
Assim, com base na figura 5, a invocação do método realizada
na figura 6 é feita de duas maneiras diferentes, conforme explica-
remos a seguir.
• A primeira maneira está destacada em amarelo, e é feita sem a ne-
cessidade de criar uma instância do objeto ExemploMetodo. Isso
só é possível graças ao método ter sido declarado com modificador
static, o que não funcionaria para o método subtrair. Para fins didá-
ticos e uma melhor visualização da declaração de um método, foram
separados cada trecho, porém o correto é declarar da seguinte ma-
neira: NomeClasse.nomeMetodo(tipoAtributo nomeAtributo);.
* Caro(a) aluno(a), para uma melhor visualização da figura, sugerimos que
acesse o material disponível no seu Ambiente Virtual de Aprendizagem (AVA)�
63
DICA
• A segunda maneira está destacada em laranja e só é possível
após a instanciação da classe ExemploMetodo, destacada em
roxo. Isso permite que os métodos estáticos e não estáticos do
objeto exemplo fiquem acessíveis. Assim como foi feito para a
primeira maneira, a declaração foi separada em trechos para
melhor visualização e o correto é instanciaObjeto.nomeMe-
todo(tipoAtributo nomeATributo);.
Os parâmetros passados ao invocar cada método são núme-
ros inteiros, assim como é esperado pelos métodos na classe Exem-
ploMetodo. Eles devem ser passados na mesma ordem em que a
assinatura do método se encontra. Ou seja, se um método aguarda
argumentos do tipo int e boolean em sua respectiva ordem, não se
pode passar os argumentos boolean e int (de forma invertida), ou
deixar de passar um dos parâmetros.
Construtores
Construtores são métodos especiais chamados através do uso da
palavra-chave new. São usados quando desejamos criar uma ins-
tância de classe.
Um construtor não precisa ser declarado, mas, dependendo do con-
texto, pode ser essencial que ele seja, pois é por meio dele que pode-
mos, por exemplo, inicializar uma variável ou executar um método
antes de fazer uso do objeto.
Por meio da criação de construtores podemos garantir que
o código que eles contêm será executado antes de qualquer outro
código em outros métodos, já que uma instância de uma classe só
pode ser usada depois de ter sido criada com new, o que causará a
execução do construtor (SANTOS, 2013).
64
Quando os atributos (primitivos ou de classe) são inicializa-
dos, mas não receberam uma programação durante sua estruturação,
iniciam-se automaticamente com valores default listados a seguir.
• Tipo boolean: inicializados com valor false, por padrão.
• Tipo byte, char, int, long e short: inicializados com valor 0 (zero).
• Tipo float e double: inicializados com valor 0.0 (zero ponto zero).
• Instâncias de classes: inicializadas com valor null.
Para que você entenda o que estamos tratando, sugiro que veja o
exemplo de uma classe sem um construtor definido:
public class BrinquedoAPilha {
private int quantidadePilha;
}
Uma referência com valor null não estará acessível a menos
que ela seja inicializada através do uso da palavra-chave new.
No exemplo do código anterior, ao instanciar o objeto, o
construtor default será executado, porém nada de mais acontecerá.
Mas, supondo que a quantidade mínima de pilhas sejam duas, po-
demos declarar o construtor para atribuir o valor dois sempre que o
objeto for instanciado. Veja o exemplo do que estamos falando:
public class BrinquedoAPilha {
public Brinquedo () {
this.quantidadePilha = 2;
}
private int quantidadePilha;
}
EXEMPLO
65
No exemplo do código anterior, foi declarado o construtor
padrão que atribui o valor dois ao atributo quantidadePilha sempre
que o objeto seja instanciado. Isso porque, se isso não fosse feito, o
valor sempre se iniciaria como sendo zero.
O construtor, embora seja um método, possui suas particula-
ridades o que o diferencia dos demais métodos. São elas:
• o construtor não precisa ser declarado. Se não declarado, o
compilador usa o construtor default da classe. Assim, o cons-
trutor default é assinado pelo nome da classe e sem argumen-
tos como parâmetros;
• o construtor sempre possui o mesmo nome da classe à qual
ele pertence;
• o construtor não tem nenhum tipo de retorno e não deve ser
declarado como void;
• o construtor pode receber todos os modificadores de aces-
so, porém não é muito comum encontrar construtores com
modificadores de acesso private. Isso ocorre porque acaba
não sendo usual uma classe conter um construtor com essa
característica, a menos que ele seja chamado a partir de ou-
tros construtores com modificador de acesso public, protec-
ted ou default;
• o construtor só pode ser chamado a partir da declaraçãoda
palavra-chave new, que é utilizada somente quando dese-
jamos instanciar um novo objeto. Porém, dentro da própria
classe, se não desejamos criar uma instância nova do mesmo
objeto, mas sim trabalhar com o construtor, devemos fazer o
uso da palavra-chave this.
Uma classe pode possuir vários construtores com assina-
turas diferentes, assim como os métodos comuns. Porém, ao de-
clarar um construtor com assinaturas diferentes do default, e para
garantir que o código funcionará corretamente, é importante a
66
EXEMPLO
criação explícita do construtor default, pois alguns frameworks
podem fazer uso deles.
public class BrinquedoAPilha {
public Brinquedo () { } //Construtor default
public Brinquedo (int quantidadePilha) { //Construtor com assinatura
diferente
this.quantidadePilha = quantidadePilha;
}
private int quantidadePilha;
}
O modificador default é similar ao protected, porém a diferença está
no contexto de herança. Dessa maneira, o protected permite que
classes em diferentes pacotes herdem atributos protected, já o de-
fault só permite herança por classes que estejam no mesmo pacote.
Perceba que, no exemplo do código anterior, o construtor default foi
desenvolvido simplesmente por ter sido criado um outro construtor
com assinatura diferente.
SAIBA MAIS
67
Palavra-chave This
A palavra-chave this (traduzida do inglês como “este”) só pode ser
utilizada para referenciar a própria instância do objeto criado, seus
atributos e métodos. Além disso, não funciona com atributos e mé-
todos com modificador static, já que eles pertencem à classe e não
ao objeto. Portanto, quando estamos nos referindo a um atributo ou
método correspondente ao objeto, devemos fazer uso do this. Veja o
exemplo na figura 7:
Figura 7 - Exemplo do uso da palavra-chave this
Fonte: Editorial Digital Pages, 2020.
* Caro(a) aluno(a), para uma melhor visualização da figura, sugerimos que
acesse o material disponível no seu Ambiente Virtual de Aprendizagem (AVA)�
68
Observe que, na figura 7, temos o Método Main que inicia-
liza a aplicação e, ao executá-lo, estamos instanciando um objeto
da mesma classe. Ao criar o objeto na linha sete (destacada em cin-
za), estamos chamando o construtor declarado na linha 11 (também
destacado em cinza) que, posteriormente, chama o construtor da
linha 17 (destacado em vermelho) por meio da palavra-chave this,
apresentada na linha 13.
Perceba que o construtor, que possui o parâmetro int,
possui a variável declarada com nome de contador, nome se-
melhante ao atributo do objeto. Para distingui-los, usamos a
palavra-chave this, em que this.contador pertence ao atributo
do objeto, e o que não faz uso da palavra-chave this pertence à
variável do método.
Importante salientar que o fato de chamar o construtor den-
tro da própria classe, por meio da palavra-chave this, não significa
que estamos instanciando um novo objeto e nem é possível fazer
uso da sintaxe new this();.
Sobrecarga
A sobrecarga é a denominação da possibilidade de se ter dois ou mais
métodos com o mesmo nome. Isso só é possível graças aos métodos
possuírem assinaturas que são consideradas pelo nome e argumen-
tos passados em seus parâmetros.
Alguns escritores defendem que o tipo de retorno faz parte da
assinatura, mas, segundo Santos (2013), o tipo de retorno do mé-
todo não é considerado parte da assinatura. Essa afirmação é com-
provada quando se tenta criar métodos com retornos diferentes,
porém com nome e tipos de parâmetros iguais, na mesma ordem.
Ela pode ser útil e evita que o método seja escrito com numeradores,
por exemplo: metodo1, metodo2 etc.
69
EXEMPLO
Um exemplo detalhado pode ser visto na figura 8:
Figura 8 - Sobrecarga de métodos
Fonte: Editorial Digital Pages, 2020.
Conforme pode ser visto na figura 8, possuímos dois métodos com
o mesmo nome, porém com assinaturas diferentes. Observe que, na
chamada dos métodos, os parâmetros correspondem à quantidade
e tipos de atributos contidos dentro dos parâmetros em respectivas
ordens. O primeiro método pagarPrestacao tem somente dois pa-
râmetros, dos tipos string e double. Já o segundo possui três parâ-
metros, um do tipo int, outro do tipo string e outro do tipo double. É
importante destacarmos que é indispensável respeitar a ordem dos
parâmetros já é suficiente para que o compilador entenda a assina-
tura do método e saiba qual deles deverá ser executado. Isso porque,
se durante a chamada de um dos métodos essa ordem de atributos
fosse invertida, passando tipos de dados diferentes do esperado, o
código não funcionaria.
70
Instanciação e referências de objetos
A possibilidade de criar um programa de computador com base em
objetos, concretos ou não, facilita o cotidiano do(a) programador(a).
Isso porque é mais fácil criar programas com estruturas que façam
algo com um objeto, do que programas que sigam uma estrutura
(programação estrutural).
Cada objeto é uma instância de classes e, para serem aces-
síveis, devem possuir referências na memória do sistema e, além
disso, as referências de um objeto são, basicamente, variáveis de
sistema que possuem o tipo da classe. Veja o exemplo da figura 9,
sobre referência e instanciação:
Figura 9 - Exemplo de referência e instanciação*
Fonte: Editorial Digital Pages, 2020.
Para instanciar um objeto, precisamos fazer a declaração utilizan-
do a palavra-chave new, seguido do construtor da classe. Isso é suficien-
te para que criemos uma instância, porém, para que possamos trabalhar
com essa instância, é necessário atribuí-la a uma referência do tipo da
classe, na memória do sistema. Então poderemos fazer uso do objeto.
No exemplo da figura 9, temos duas classes. Na classe Exem-
ploReferencia, instanciamos o objeto da classe Carro na referência
carro, para que só então tenhamos acesso a seus atributos e métodos.
* Caro(a) aluno(a), para uma melhor visualização da figura, sugerimos que
acesse o material disponível no seu Ambiente Virtual de Aprendizagem (AVA)�
71
Pensando nisso, destacado em cinza na linha quatro, fizemos
referência da classe Carro à memória do sistema com o nome carro.
Ou seja, nesse ponto estamos dizendo ao compilador que reserve na
memória um espaço para um objeto do tipo Carro e que essa refe-
rência terá nome de carro.
O nome da referência pode ser qualquer um, porém cabe ao(à)
programador(a) seguir a convenção de códigos, que diz que de-
vemos declarar um nome que realmente corresponda ao que a
referência possui em si, a fim de deixar o código mais legível
aos humanos.
No caso da figura 9, a referência carro está nula, já que ainda
não temos um objeto criado para ela. Depois, na linha sete estamos
atribuindo, nessa referência (em cinza), um novo objeto da classe
Carro (em laranja) e, por isso, fazemos uso da palavra-chave new
seguido do construtor default da classe Carro.
Somente após termos atribuído o objeto na referência criada
em memória é que podemos inserir informações, como é feito nas
linhas 10 e 11 por meio dos métodos set, ou obter informações, como
é feito na linha 17 por meio dos métodos get.
Essa forma de inserir informações, ou recuperar informações,
por meio dos métodos get e set, é chamada de encapsulamento de
dados, pois seus atributos estão declarados com modificador de
acesso private.
DICA
DEFINIÇÃO
72
Envio de mensagens
A programação orientada a objetos promove a comunicação de um
objeto com outro. Esta comunicação provém da troca de mensagens
que poderá ter ou não um retorno.
Segundo Batista e Moraes (2013, p. 59), as mensagens são
formadas por três elementos:
• emissor - objeto que envia a mensagem;
• receptor - objeto que recebe a mensagem;
• especificação de serviço - o método deverá ser executado e,
se contiver parâmetros, eles devem ser enviados juntos.
Veja um exemplo da troca de mensagens na figura 10:
Figura 10 - Exemplo de troca de mensagens entre objetos
Fonte: Editorial Digital Pages, 2020.
No exemplo da figura 10, o emissorPessoa especifica qual
serviço deseja que o receptor Lâmpada execute. Portanto, nesse
exemplo, a mensagem enviada por Pessoa seria: Lampada.acen-
der(); ou Lampada.apagar();.
73
Importante observar que a mensagem enviada deverá sempre ser
algo que o outro objeto sabe executar e, se possuir parâmetros, eles
devem ser enviados junto com a mensagem.
Ciclo de vida de um objeto
Todo objeto criado em Java possui um ciclo de vida, que está subdi-
vidido em três fases distintas.
• Criação de objetos: o fato de referenciar o objeto não significa
que estamos criando o objeto. Isso porque todo objeto é criado
a partir do momento em que se utiliza a palavra-chave new
seguida do construtor da classe.
• Acessibilidade do objeto: o objeto só estará acessível a partir
do momento em que ele é atribuído a uma referência de uma
variável. Ele pode perder a referência e ficar inacessível nos
seguintes casos:
• caso o objeto seja substituído por outro objeto: o novo obje-
to ganha a sua referência e o anterior a perde;
• caso a referência a qual o objeto pertence seja atribuída à
palavra-chave null.
• Descarte de objeto: Java conta com o Garbage Collector (co-
letor de lixo), que remove da memória do sistema todo obje-
to inacessível. Ele é executado em segundo plano juntamente
com a aplicação Java, portanto, não há como prever quando o
Garbage Collector entrará em cena e nem mesmo saber quais
objetos foram coletados, sendo possível apenas saber quan-
tos objetos podem ser coletados.
Veja um exemplo do ciclo de vida do objeto na figura 11:
DICA
74
Figura 11 - Exemplo de ciclo de vida de objetos*
Fonte: Editorial Digital Pages, 2020.
Conforme você deve ter observado, no exemplo da figura 11,
temos as seguintes informações:
• em cinza, temos a criação da referência do objeto na memória;
• em laranja, a criação de um novo objeto;
• em marrom, a atribuição de um objeto em uma referência já criada.
Por outro lado, destrinchando o código da figura 11, temos as
seguintes anotações:
• na linha quatro, estamos criando a referência em memória
denominada carro para algum objeto do tipo Carro, mas, até
esse momento, não possuímos nenhum objeto;
* Caro(a) aluno(a), para uma melhor visualização da figura, sugerimos que
acesse o material disponível no seu Ambiente Virtual de Aprendizagem (AVA)�
75
• já na linha sete, estamos criando um objeto do tipo Carro, mas
não o estamos atribuindo a nenhuma referência, portanto ele
está inacessível, podendo ser removido pelo Garbage Collector;
• na linha dez, estamos criando um novo objeto do tipo Carro
e o atribuindo à referência criada na linha quatro, portanto,
a partir deste momento a referência carro possui um objeto
do tipo Carro. Perceba que, até este momento, dois objetos
foram criados, um acessível e outro inacessível;
• na linha 13, estamos criando a referência em memória, cha-
mada carro2, para o objeto do tipo Carro. Nessa linha, a re-
ferência e a criação do objeto são feitas juntas, portanto, até
essa linha temos dois objetos acessíveis e um objeto inaces-
sível, totalizando três objetos do tipo Carro;
• na linha 16, é atribuído um novo objeto à referência carro2,
portanto, o objeto que estava vinculado a ela agora já não está
mais acessível, estando disponível para o Garbage Collector.
Até o momento criamos quatro objetos, dois estão acessí-
veis e dois não estão acessíveis;
• na linha 19, é atribuída a palavra-chave null à referência carro,
tornando o objeto que estava atribuído a ela inacessível e dis-
ponível ao Garbage Collector. Dessa maneira, até o momento
foram criados quatro objetos, um está acessível pela refe-
rência carro2 e três inacessíveis;
• da linha 22 a linha 30, estamos fazendo uma iteração que vai de zero
a nove (i começa com zero e vai até nove, que é menor que dez),
portanto, o código dentro de sua estrutura é realizado dez vezes;
• na linha 25, há a criação da referência carro3 e a atribuição de
um novo objeto a essa referência, logo são criados dez obje-
tos, mas apenas um está acessível (o último);
• na linha 28, acontece algo parecido com o caso anterior, po-
rém a referência em memória já está criada. Até a sua primeira
iteração, a referência está nula e, a partir da segunda iteração,
o objeto é substituído e fica disponível apenas o último objeto
criado. No total, também são dez objetos criados.
76
Podemos dizer que, nas condições anteriores, até a linha 19, pos-
suíamos apenas quatro objetos, em que apenas um estava acessível.
Mas, durante a iteração, foram criados 20 objetos do tipo Carro, mas
apenas dois deles estão acessíveis, um atribuído à referência carro3
e outro à referência carro. Então, após a conclusão da iteração, fo-
ram criados 24 objetos, mas apenas três estavam acessíveis, sendo
que 21 deles podem ser coletados, a qualquer momento, pelo Gar-
bage Collector.
Abstração e encapsulamento
Programar orientado a objetos faz com que o(a) programador(a)
desenvolva um sistema com base em objetos, abstraindo as infor-
mações relevantes para o desenvolvimento. Essas informações, de-
pendendo das circunstâncias, necessitam de uma certa proteção,
deixando acessível apenas aquilo que realmente interessa ou evi-
tando que os atributos sejam acessados de forma direta, seja para
alterar ou obter as informações. Assim, vamos discutir as particula-
ridades de cada um dos temas.
Abstração
De modo geral, segundo Moreira Neto (2009, p. 9), a abstração con-
siste em compreender um sistema, seja ele concreto ou não, criando
um modelo documentado e padronizado que reflita seu comporta-
mento, ou para utilizar em uma definição mais específica no desen-
volvimento de software.
Este conceito se dá ao fato de poder representar um objeto real
utilizando uma classe. Ele possui essa denominação devido à pos-
sibilidade de abstrair propriedades e funcionalidades em métodos.
DICA
77
REFLITA
Para entender o que estamos tratando, observe a figura abaixo:
Figura 12 - Exemplo para abstração de propriedades e funcionalidades
Fonte: Adobe Stock. Acesso em: 08 jun. 2020.
Analisando a figura 12, podemos observar as propriedades e
funcionalidades, sendo:
• propriedades: marca, modelo, ano de fabricação, ano do mo-
delo, quantidade de lugares, quantidade de portas, cor etc.;
• funcionalidades: acelerar, trocar de marcha, abastecer, trocar
pneus, calibrar pneus, substituir estepe, inclinar bancos, ligar e des-
ligar a lanterna, o farol, ligar/desligar o limpador de para-brisa etc.
Como você deve ter percebido, são inúmeras possibilidades de abs-
trações de dados e funcionalidades de um objeto, mas será que de-
vemos incluir todas as possibilidades em nosso sistema?
Bom, a resposta para esta pergunta é: depende. A abstração acon-
tece para definirmos o modelo de um objeto no sistema. Dessa ma-
neira, se uma informação ou funcionalidade for irrelevante para o
sistema, ela não precisa estar presente, pois não terá nenhuma uti-
lidade. Além disso, ter essas funcionalidades inúteis pode consumir
memória ou processamento, deixando o sistema mais pesado. Dessa
maneira, tudo depende do modelo de negócio aplicado ao sistema.
78
Encapsulamento
O termo encapsulamento, para a linguagem de programação orienta-
da a objetos, determina a disposição para acesso de métodos ou atri-
butos em uma classe pública. Isso possibilita a ocultação de atributos,
tornando acessíveis via métodos apenas aqueles que realmente pre-
cisam ser acessados. Moreira Neto (2009, p. 73) diz que os atributos
de um objeto podem ser escondidos de outros objetos por uma inter-
face pública de métodos, de modo a impedir acessos indevidos.
O encapsulamento de dados só é possível graças aos modificadores
de acesso para membros de classes, conforme pode ser visto no quadro 1:
Quadro 1 - Modificadores de acesso para membros de classe
Fonte: Adaptado de Moreira Neto, 2009, p. 73.
Perceba que, no quadro 1, temos as declarações sobre os mo-
dificadores de acesso que determinam a visibilidadede um atributo
ou método. Para o cenário private, apenas a classe à qual o atribu-
to/método pertence é visível e acessível. Um atributo/método- sem
declaração de modificador (<none>) é denominado default e a sua
visibilidade é permitida apenas pela própria classe ou classes que
estão no mesmo pacote.
Atributos/métodos declarados como protected são similares
aos que não possuem declaração, porém são acessíveis também por
classes que os herdam, mesmo que em diferentes pacotes. Assim,
atributo/método com modificadorpublic têm sua visibilidade a par-
tir de qualquer classe e independe do pacote em que ele se encontra.
private
<none>
protected
public
*
*
*
*
*
*
*
*
* *
Modificador Própria classe Classes do mesmo pacote
Subclasses
(herança)
Universo
(qualquer classe)
79
Com essa visão sobre a visibilidade dos atributos e métodos,
podemos enxergar a importância de encapsular os atributos. Vamos
a um exemplo:
Maria possui R$ 300,00 em sua conta bancária, logo o atributo saldo
é igual a R$ 300,00. Mas, supondo que Maria vá fazer um saque e
que, chegando no caixa eletrônico, ela solicite o valor de R$ 400,00.
Dessa maneira, sem uma proteção adequada ao atributo saldo, ela
poderia realizar o saque e ficar com saldo de R$ -100,00. Então, em
um acesso de maneira direta a esse atributo, além da possibilidade
de sacar um valor indisponível, também há a possibilidade de que o
saldo seja alterado diante de qualquer lugar, por qualquer circuns-
tância, seja atribuindo um valor a mais ao que já se tem, ou dimi-
nuindo o valor existente. Veja o exemplo em código na figura 13:
Figura 13 - Exemplo de classes com atributos sem encapsulamento
Fonte: Editorial Digital Pages, 2020.
EXEMPLO
80
Observando a figura 13, temos três classes. As classes Pessoa
e ContaCorrente estão sem encapsulamento, ou seja, seus atribu-
tos estão todos públicos e acessíveis de qualquer lugar. Já na classe
ExemploEncapsulamento, temos o método main que é executado
para inicializar a aplicação Java e, ao executá-la, criamos, na me-
mória, a referência contaCorrente com a instância do objeto do tipo
ContaCorrente, que por sua vez instancia o objeto Pessoa em seu
atributo pessoa.
Para acessar os atributos de pessoa, precisamos apenas utilizar a
referência contaCorrente, depois a referência pessoa e depois seu
atributo. Com isso, conseguimos acesso direto a esse dado, podendo
alterá-lo a qualquer momento.
Ainda considerando o exemplo de Maria, vamos ao atri-
buto saldo. Veja que foi atribuído o valor de R$ 300,00 e, pos-
teriormente, foi feita a retirada de R$ 400,00, deixando o valor
do saldo negativo (R$ -100,00). Poderíamos validar se há sal-
do suficiente na conta para realizar o saque, mas isso deve-
rá ser um serviço da classe ContaCorrente e não da classe
ExemploEncapsulamento.
DICA
81
Agora, para a segurança da aplicação, vamos a um exem-
plo de uma estrutura com os dados encapsulados, apresentado
na figura 14:
Figura 14 - Exemplo de classes com atributos encapsulados
Fonte: Editorial Digital Pages, 2020.
Observe que nesse novo caso nas classes Pessoa e ContaCor-
rente os atributos possuem encapsulamento, ou seja, seus atributos
só estão acessíveis via métodos disponíveis na própria classe.
Dessa maneira, ao executar a aplicação, o método main irá
instanciar o objeto da classe ContaCorrente na referência de me-
mória contaCorrente, igual na figura 13, porém os acessos aos dados
só são possíveis por meio dos métodos contidos na própria classe.
Lembre-se que, para atribuirmos/acessarmos a variável
nome, da classe Pessoa, precisamos utilizar o método getPes-
soa da classe ContaCorrente, que irá obter o objeto pessoa e então
fazer uso do método setNome da classe Pessoa, passando como
parâmetro o texto desejado. Nesse momento, quem é responsá-
vel pela atribuição do nome é a classe Pessoa, não mais a classe
ExemploEncapsulamento.
82
Observe que temos, na classe ContaCorrente, os métodos
realizarSaque, consultarSaldo e depositar. Eles serão responsáveis
pelos serviços. Inclusive, o primeiro método deverá validar se há
saldo suficiente para saque. Perceba, ainda, que não é mais possível
alterar o valor do saldo diretamente, a menos que utilizemos um dos
métodos responsáveis pelo serviço.
É muito comum declarar métodos dos tipos getters e setters
para retornar o valor de um atributo, ou modificá-los (até porque,
geralmente as IDEs fornecem essa funcionalidade). Por padrão, para
obter informações de atributos, com exceção do tipo boolean, deve-
-se prefixar o nome do atributo com a palavra get (obter), seguindo
o padrão LowerCamelCase. Assim, quando o tipo for boolean o pre-
fixo é is. Veja alguns exemplos:
• no atributo name do tipo String, seu método recebe
getName();
• no atributo birthday do tipo Date, seu método recebe
getBirthday();
• no atributo active do tipo boolean, seu método recebe
isActive();
• no atributo alive do tipo boolean, seu método recebe isAlive().
Para alterar atributos, sem exceções, devemos prefixar os
atributos com a palavra set (pôr), seguindo o padrão LowerCamel-
Case. Veja alguns exemplos:
• no atributo name do tipo String, seu método recebe setName();
• no atributo birthday do tipo Date, seu método recebe
setBirthday();
• no atributo active do tipo boolean, seu método recebe
setActive();
• no atributo alive do tipo boolean, seu método recebe
setAlive().
83
DICA
Observe que os nomes dos métodos e os atributos foram escritos em
inglês, isso não é uma obrigatoriedade, mas possibilita uma leitura
mais fácil do código, justamente pela utilização dos padrões da con-
venção de códigos e em virtude das palavras-chaves serem sempre
em inglês.
Estruturas compostas homogêneas
As estruturas compostas homogêneas são aquelas que podem
receber diversos valores do mesmo tipo. Os principais repre-
sentantes dessa estrutura são os vetores e as matrizes. Vamos
conhecê-los.
Vetor
O vetor é um tipo de estrutura de dados composto por várias instân-
cias do mesmo tipo e o identificador somente diferencia seu índice,
como endereçamento. Ou seja, nada mais é do que um tipo primitivo
com profundidade. Veja um exemplo:
String nome[] = new String[3]; nome[0]=“José”;
nome[1]=“Paulo”;
nome[2]=“João”;
EXEMPLO
84
EXEMPLO
Observe que iniciamos a estrutura do vetor com os colchetes,
após o nome da variável. Após isso, usamos o comando new, que
é um comando que instancia o objeto string. Temos o valor 3 entre
colchetes que significa 3 “variáveis” do tipo string, com o identifi-
cador nome[ ]. Os números entre os colchetes significam sua iden-
tificação numérica, que vai até dois. Além disso, temos três variáveis
com os ids 0, 1 e 2, e essa identificação numérica, ou id, são as pro-
fundidades do vetor. No caso, o id 0 conta como variável, por isso,
vai até o id 2.
É importante que você saiba o que significa:
Instância: é uma convenção da programação orientada a ob-
jetos, que cria instâncias ou cópias de objetos ou programas dentro
do programa atual.
Atribuição: é uma característica das variáveis. Elas recebem
valores predeterminados pelo(a) programador(a) ou por intermé-
dio de funções que captam valores dos usuários do programa. Abai-
xo, seguem alguns exemplos de atribuição em vetor.
• Atribuição por valores predeterminados pelo programador:
nome[0]=“Rolfi”;
• Atribuição por valores que o usuário irá inserir:
nome[1]=javax.swing.JOptionPane.showMessageDialog(null,
“Digite seu nome”);
• Atribuição por variáveis ou por outro vetor:
nome[0]=outronome;
nome[0]=nome[1];
85
EXEMPLO
Além disso:
Operação aritmética: é um conjunto de instruções que pro-
movem operações aritméticas em variáveis. Por exemplo:
float nota[]=new float[4];
float media;
nota[0]=7.5f;
nota[1]=10.0f;
nota[2]=8.0f;
nota[3]=10.0f;
media=(nota[0] + nota[1] + nota[2] + nota[3])/4;
Navegação: é utilizado para navegar no vetor, pode-se cha-
mar pelo id ou fazer um loop navegando por todos os ids. Nos co-
mandosapresentados a seguir, o vetor nome está sendo impresso,
um a um, na tela. Veja:
• Navegando pelo id:
System.out.println(nome[0]);
• Navegando em todo o vetor:
for(int contador=0; contador < nome.length; contador++)
{
System.out.println(nome[contador]);
}
86
O comando abaixo, que é o for-each, é um dos principais co-
mandos de navegação de objetos e estruturas de dados:
for(int aux: vetor)
{
System.out.println(aux);
}
Esse loop navega nas posições do vetor e passa seus valores, um a
um, na variável aux.
Ordenação: é um conjunto de instruções que visa ordenar
uma estrutura de dados, nesse caso, o vetor. Abaixo, segue um dos
formatos mais tradicionais de ordenação de vetor.
1 for(int contadorL=0; contadorL< vetor.length ;
contadorL++)
2 {
3 for(int contadorC=contadorL+1; contadorC< vetor.length;
conta- dorC++)
4 {
5 if(vetor[contadorL]> vetor[contadorC])
6 {
int aux= vetor[contadorL];
vetor[contadorL]=vetor[contadorC];
vetor[contadorC]=aux;
10 }
11 }
12 }
DICA
87
Esse algoritmo é o Método Bolha, ou Bubble Sort, um algorit-
mo de ordenação tradicional que busca o menor elemento da lista,
posicionando ao topo de forma ordenada. Primeiramente, busca-se
o menor valor, navegando no vetor através dos índices contadorL e
contador. Ao encontrar um valor maior, faz-se a troca, “descendo”
o valor maior e “subindo” o valor menor.
Por exemplo: valores 9, 0, -1 para os endereços 0, 1 e 2 do vetor.
Tabela 1 - Exemplo dos valores iniciais vetor em memória
Fonte: Editorial Digital Pages, 2019.
Primeiramente, o algoritmo verifica se o vetor na posição 0 é maior
que o vetor na posição 1, ou seja, se 9 > 0. Nesse caso, é maior, então, troca-
-se o menor pelo maior através das navegações dos índices. Faz-se a pri-
meira troca de 9 por 0, usando a variável auxiliar, como nas linhas abaixo:
7 int aux=vetor[contadorL];
8 vetor[contadorL]=vetor[contadorC];
9 vetor[contadorC]=aux;
O aux recebe o maior valor, que é o valor 9, pois o contadorL
nesse primeiro momento é 0, e o vetor na posição 0 recebe o valor
de vetor na posição 1, que é o valor de contadorC, recebendo, assim,
o valor 0. O vetor na posição 1 recebe o valor de aux.
Tabela 2 - Exemplo da primeira troca no vetor
Fonte: Editorial Digital Pages, 2019.
Continuando o loop, o contadorL na posição 0 verifica se exis-
te um outro valor menor dentro do vetor, encontra na posição 2 o
valor -1, e faz a troca pelo valor menor. Logo, todos os valores do
vetor são percorridos, como na tabela 3.
Valor 9 0 -1
Índice vetor[0] vetor[1] vetor[2]
Valor 0 9 -1
Índice vetor[0] vetor[1] vetor[2]
88
Tabela 3 - Exemplo da Segunda Troca no Vetor
Fonte: Editorial Digital Pages, 2019.
Após o primeiro item (o índice 0) possuir o menor valor do
vetor, a segunda etapa é passar para verificar se o índice 1 possui o
menor valor. Na primeira varredura entre índice 1 com o valor de 9,
verifica-se se o próximo índice, o 2, é maior com o valor de 0. Nesse
caso, faz-se a troca, obtendo-se o resultado exibido na tabela 4.
Tabela 4 - Exemplo da segunda troca no vetor
Fonte: Editorial Digital Pages, 2019.
Como não existem valores para serem verificados, finaliza-se
a ordenação do vetor.
Aplicação: um vetor pode ser usado como lista ou estruturas que
envolvam listas, como lista de compras, lista de clientes, lista de CEPs etc.
Para que você veja, na prática, o que estamos tratando, abaixo
temos um exemplo do código do vetor estruturado.
No código a seguir, temos um exemplo de vetor em formato estru-
turado, ou seja, digitado diretamente da função main. Esse comando
cria um vetor na linha 9 com dados predeterminados e na linha 11 ele
ordena esses valores de forma crescente. Após a ordenação, tem-se
a impressão na linha 20.
Valor -1 9 0
Índice vetor[0] vetor[1] vetor[2]
Valor -1 0 9
Índice vetor[0] vetor[1] vetor[2]
EXEMPLO
89
Figura 15 - Exemplo de vetor em formato estruturado
Fonte: Editorial Digital Pages, 2019.
Vetor orientado a objetos
Os vetores orientados a objetos são vetores que, tradicionalmen-
te, podem ser criados em qualquer local da classe. Porém, quando
abordamos o paradigma de orientação a objetos dentro do conceito
de vetor, temos que criar duas classes, uma será o tads propriamen-
te dito, a classe vetorOO, que é chamado como estrutura de dados
dentro da classe Prj_VetorOO.
Já empregando os conceitos de orientação a objetos dentro da
classe VetorOO, observamos o tratamento de dados através do uso
de métodos ou funções, diferente do exemplo acima, em que todo o
tratamento está dentro da função main.
90
Figura 16 - Exemplo do tratamento de dados através do uso de métodos ou funções
Fonte: Editorial Digital Pages, 2019.
Como você pode perceber, a classe Prj_VetorOO possui a instan-
ciação da classe VetorOO em vet e, dentro da própria vet, temos a função
set, valor que insere valores nas posições determinadas da função.
Também para inserir valores usamos o inserir que, através de
uma sequência estipulada na linha 39, apenas recebe os dados e ve-
rifica, de acordo com o número de inserções, se poderá ser inserido.
Além disso, o construtor da linha 26 inicia-se recebendo um
valor para aprofundar o vetor no valor desejado.
As funções getValor e setValor são funções de captura e envio
de dados em qualquer estrutura. Assim, o getSize retorna a quan-
tidade de itens inseridos, enquanto a getMax retorna a capacidade
máxima de itens que podem ser inseridos. O remover retira itens do
vetor diminuindo o getSize e o método sort ordena os itens do vetor.
Dessa maneira, quando tratamos de orientação a objetos,
nesse caso, temos todos os comportamentos dos dados dentro de
um vetor, que, no caso, seria a linha 5 private int vetor, que cria o ve-
tor como private para que nenhuma classe externa possa acessá-la
diretamente, somente a partir dos métodos citados anteriormente.
91
Figura 17 - Exemplo de comportamentos dos dados dentro de um vetor
Fonte: Editorial Digital Pages, 2019.
92
Matriz
Matriz é uma estrutura homogênea de dados que permite organizar
os dados em formato de linhas e colunas, e sua indexação é pelo en-
dereço de linhas e colunas, semelhante à estrutura de um vetor, mas
com formato bidimensional, podendo assumir mais de duas dimen-
sões. Dependendo da linguagem de programação podem assumir
até 17 dimensões, por exemplo:
String nomes[][]=new String[2][3]; nomes[0][0]=”Rolfi”;
nomes[0][1]=”Ana”;
nomes[0][2]=”Maria”;
nomes[1][0]=”João”;
nomes[1][1]=”Pedro”;
nomes[1][2]=”Paula”;
Em estrutura de memória, ficaria ilustrado da seguinte forma:
Tabela 5 - Exemplo em Memória de Matriz
Fonte: Editorial Digital Pages, 2019.
Observe que as colunas são de, no máximo, três itens. Conta-
-se o 0 e temos 0, 1, 2, totalizando três colunas. Nas linhas, temos
dois de tamanho. Conta-se o 0 e temos 0 e 1, como no vetor.
Dito isto, vamos entender alguns temas relacionados às matrizes.
Atribuição: as atribuições em matrizes funcionam da seguin-
te forma:
nomes[índice da linha][índice da coluna]=valor do tipo
da coluna.
Linhas
Colunas
0
"Rolfi"
"João"
1
"Ana"
"Pedro"
2
"Maria"
"Paula"
93
Por exemplo:
nomes[0][0]=”Rolfi”;
Operação aritmética com matriz: as operações aritméticas,
assim como nas variáveis e vetores, também funcionam da mesma
forma na matriz. Deve-se primeiramente fazer referência da posi-
ção que se deseja trabalhar e submetê-la às operações matemáticas.
Por exemplo:
float notas[][]=new float[2][2]; notas[0][0]=10f;
notas[0][1]=5.5f;
notas[1][0]=6.0f;
notas[0][1]=8.0f;
float medias =(notas[0][0] + notas[0][1] + notas[1][0] + no-
tas[1][1])/notas.length;
Navegação: para navegar na matriz, pode-se chamar pelo id da
linha e da coluna ou fazer um loop, navegando por todos os ids. Neste
comando, a matriz nomes está sendo impressa, um a um, na tela.
• Navegando pelo id:
System.out.println(nomes[0][0]);
• Navegando em toda a matriz, porém, deve ser feito um laço
para a linha e coluna:
for(int contadorL=0;contadorL < 2; contadorL++) {
for(int contadorC=0; contadorC < 3; contadorC++) {
System.out.println( nomes[ contadorL ] [ contadorC ] );
}
}
Ou pelo comando abaixo, que é o for-each (um dos principais
comandos de navegação de objetos e estruturas de dados):
for(String linha[]: nomes) {
for(String coluna: linha) {
System.out.println(coluna);
}
}
94
Esse loop navega nas posições da matriz, passando seus va-
lores, um a um, na variável coluna, que recebe, da variável linha,
a linha com os valores da matriz nomes. Assim, para cada dimen-
são, deve-se ter um loop. Nesse caso, temos duas dimensões, então,
usa-se dois loops.
Estruturas comportamentais heterogêneas
As estruturas heterogêneas consistem em estruturas que podem re-
ceber diversos tipos primitivos com um objetivo em comum. Nesse
aspecto, temos os conceitos de registro e campo.
Por um lado, o campo é um tipo primitivo com identificador
que possui uma característica única que, quando linkada a outros
campos, produzem um significado ou conjunto de informações. Já o
registro é uma informação de um determinado objeto ou entidade
que possui diversos campos.
Na maioria das literaturas, usa-se o C para explicar a es-
trutura heterogênea, pois, por não ser orientado a objetos, conse-
gue-se distinguir nitidamente a estrutura heterogênea. Temos o
seguinte código:
struct Estudante { int ID;
char nome[50]; int idade;
float media;
};
Em que cada estudante adicionado seria um registro
em memória.
Instanciação
Tendo-se o C, uma struct ou estrutura, a instanciação corresponde às
estruturas de dados heterogêneos, que possuem diversos atributos
95
(variáveis primitivas) com identificador e tipo primitivo. Logo, sua
instanciação ou cópia, é como no exemplo apresentado abaixo.
struct Estudante est1;
Atribuição
Em structs, temos dois tipos de atribuição, são eles:
• struct Estudante est1 = {1,”João”,23,10};
ou
• struct Estudante est2;
est2.ID =2;
strcpy(est2.nome , “Pedro”);
est2.idade=30;
est2.media=7.5;
As operações, impressões e acessos são através do ponto,
como foi apresentado no exemplo anterior.
Em Java, temos diversas formas de trabalhar uma estrutura
heterogênea que difere do C. Lembre-se que uma estrutura consiste
em uma classe ou objeto. E, além disso, que um objeto é a principal
entidade de uma estrutura de programação, orientada a objetos que
possuem propriedades e ações,como também podem assumir ca-
racterísticas de uma situação real, uma regra de negócio ou entidade
em geral. Veja o exemplo:
public class Estudante {
int ID;
String nome;
int idade;
float media;
}
96
public class Exemplo {
public static void main(String []args)
{
Estudante est1=new Estudante();
est1.ID = 1;
}
}
Observe que, no exemplo acima, o código está muito próximo
da struct. No caso do Java, também funciona, porém, nos conceitos
de orientação a objetos, está fora de padrão e convenção.
No exemplo abaixo, segue o código no formato ideal para
orientação a objeto, pois usa-se o conceito de encapsulamento, que
é o uso das funções sets e gets para gerenciar as variáveis ou atri-
butos da estrutura. Para alterar valores, usa-se o set e o nome da
variável. Veja:
private int idade;
public void setIdade(int aux) {
this.idade = aux;
}
Toda variável, ou tipo primitivo, dentro de um objeto que
será aproveitado em outras classes deve ser private. Mas, caso não
tenha uma palavra reservada como public, o compilador entende
como public por default.
Observe que, nesse momento, o nome da função assumiu o
nome da variável, acrescentando a palavra set como convenção de
alteração. Assim, para captar valores, usa-se o get, como na função
a seguir:
public int getIdade(){
return this.idade;
}
97
Abaixo, a classe exemplo seria o formato em que a instan-
ciação está correta dentro dos padrões OO. Na linha 6, atribui-se o
valor 1 para a variável ID, através do método setID; na linha 8, veri-
fica-se o valor atribuído no ID através do método getID. Veja:
Figura 18 - Classe Exemplo
Fonte: Editorial Digital Pages, 2019.
Instanciação: diferentemente do C, o Java é orientado a obje-
tos. Por isso, tudo que é desenvolvido em Java e que tenha interação
com dados primitivos, geralmente, é um objeto.
No código abaixo, tem-se a classe estudante, que é instan-
ciada através da palavra new:
Estudante est=new Estudante();
Tipos abstratos de dados
Os tipos abstratos de dados (TADs) são estruturas que correspon-
dem ao funcionamento de estruturas do mundo real, como pilha,
fila, lista, árvore, grafos ou qualquer estrutura que organize, de for-
ma singular, uma coleção de dados. De acordo com Lafore (2004):
o significado de Tipo Abstrato de Dados é mais
estendido quando aplicado a estruturas de
dados como pilhas e filas. Como em qualquer
classe, significa que os dados e as operações
podem ser executados neles, mas, neste con-
texto, até os fundamentos de como os dados
são armazenados tornam-se invisíveis para o
usuário (LAFORE, 2004, p. 187).
98
Em algumas situações, os TADs podem ser confundidos com
estruturas heterogêneas. Porém, o objetivo deles é possuir em si
uma coleção de dados ou objetos, além de se responsabilizar com a
sua organização e tratamento, como: inserção, deleção, ordenação,
busca e endereçamento.
Lembre-se: um TADs em si gerencia sua própria coleção.
Em termos de códigos, em Java ou em qualquer outra
linguagem de programação, o TADs não envolve comandos
complexos e, sim, uma lógica bem definida que faz com que os
dados sejam organizados dependendo de sua estrutura. Veja
os comandos:
Lista: comporta-se como uma lista do mundo real, que rece-
be valores e remove em sequência ou por posições.
Pilha: comporta-se como uma organização de pilha, como
em pilha de livros, em que o primeiro valor que se insere é o último
a ser removido.
Fila: comporta-se como uma organização de fila, como uma
fila de banco, em que o primeiro valor que entra é o primeiro valor
que será removido.
Tipos de armazenamento
Os tipos de armazenamento em TADs podem variar entre di-
nâmico e estático. O estático possui capacidade limitada, ou
estática, de posições. Já o armazenamento dinâmico possui
capacidade de alocação por demanda, ou seja, quando existe a
necessidade de inserir algum valor, ele se adapta a uma nova
quantidade de valores.
DICA
99
Na capacidade estática, tem-se a vantagem de saber a quantidade
de valores. Porém, não podendo ser alterada sua capacidade, faz
com que seja um formato de armazenamento muito específico, de-
pendendo da regra.
Coleções
Os TADs são muito utilizados em diversas linguagens, porém, em
Java, existem algumas bibliotecas prontas para os principais TADs,
dentro de orientação a objetos, chamadas de pacotes.
O pacote que contém alguns dos principais TADs é o java.util,
que possui o collection frameworks, classes e interfaces que pos-
suem, prontos, os TADs que não necessariamente precisariam ser
reescritos, apenas instanciados para uso. As collections são divididas
em conjuntos, listas e mapas.
Figura 19- As interfaces da collection
Fonte: Adaptada de Oracle, 2019. Acesso em: 20 maio 2022.
CURIOSIDADE
100
As listas trabalham em formatos sequenciais de inserção e
remoção. Por serem sequenciais, a busca de dados torna-se mais
lenta. Além disso, os conjuntos trabalham em formatos algorít-
micos para inserção, tornando mais lenta a inserção. Assim, a
busca, a navegação e a remoção, porém, tornam-se mais velozes.
Por fim, os mapas trabalham organizando os dados juntamente
com seus índices, tornando mais complexa a programação. Po-
rém, sua busca é veloz.
Na figura 20, observa-se as classes que podem ser instancia-
das para estrutura de dados das collections, dos grandes eixos list e
queue (“sequência”) e set (“conjunto”). Veja.
Figura 20 - Classes collections
Fonte: Editorial Digital Pages, 2020.
101
Caro(a) aluno(a), chegamos ao final desta etapade estudos. Nesse
material estudamos sobre a estrutura de classes. Além disso, apren-
demos que atributos e métodos devem estar dentro das classes e que
atributos e métodos, com o modificador static, podem ser acessados
sem a instanciação de um objeto. Isso porque eles são atributos e
métodos da classe e não dos objetos. Além disso, se o atributo static
for alterado, isso é refletido em todos os objetos instanciados.
Aprendemos também que podemos criar métodos com o mesmo
nome, mas que possuam assinaturas distintas. Essas assinaturas são
compostas pelo nome e atributos do método e devemos sempre res-
peitar a ordem dos atributos, passando os tipos de dados corretamente.
Também discutimos o fato de que os construtores são métodos es-
peciais que não precisam ser declarados, não possuem tipo de re-
torno e devem conter o mesmo nome que a classe. Além disso, você
pôde perceber que não é muito viável utilizar métodos construtores
com modificadores private, protected ou default, já que o primeiro
não é acessível por nenhuma outra classe, e o segundo é apenas por
classes do mesmo pacote, ou através de herança.
Aprendemos que referência e instanciação são coisas distintas.
Enquanto a referência é apenas a declaração que poderá conter um
objeto referenciado à memória, a instanciação é a criação do ob-
jeto. Para podermos fazer uso do objeto, ele deverá ser atribuído a
uma referência.
Além disso, vimos a abstração das informações contidas em um
objeto e a importância da omissão dessas informações a quaisquer
classes, tornando o acesso a elas possíveis apenas através de méto-
dos específicos na própria classe que os contém.
Até aqui estudamos as estruturas compostas homogêneas, co-
nhecidas como vetores e matrizes, além das estruturas compostas
heterogêneas, conhecidas como structs ou objetos dependendo da
linguagem de programação.
SINTETIZANDO
102
Antes de finalizarmos o nosso material, analisamos os conceitos
de TADs, que são tipos abstratos de dados, ou seja, uma estrutura
que se comporta com características e padrões do mundo real para
tratamento de dados, como lista, pilha e fila. Vimos esses conceitos
dentro da linguagem Java, em que, na própria linguagem, foi pos-
sível visualizar uma API que contém uma collection framework (que
possui diversas estruturas prontas para uso), fazendo com que o
desenvolvedor foque na regra de negócio.
Lembre-se de estudar antes e depois da sua aula, aproveite o mo-
mento com o(a) docente para tirar todas as suas dúvidas.
Até breve!
UN
ID
AD
E
3
Objetivos
◼ Aprender conceitos de herança e sua utilização.
◼ Entender como se dá a criação e o uso da hierarquia de classes.
◼ Aprender a criar e utilizar classes abstratas e interfaces.
◼ Aprender sobre os conceitos dos relacionamentos entre as
classes e os tipos existentes.
104
Introdução
Olá, tudo bem? A partir de agora veremos o conceito de he-
rança e, além disso, vamos entender como são criadas as hierarquias
de classes. Você também aprenderá sobre como utilizar as classes
abstratas e as interfaces. E, antes de finalizarmos esse material, ve-
remos os relacionamentos entre as classes e os tipos existentes.
Dito isto, convido você, aluno(a) EAD, a conhecer os temas
citados de forma descomplicada e muito intuitiva conforme abor-
daremos neste material. Boa leitura e bons estudos!
105
Herança
Caro(a) aluno(a), inicialmente é preciso que você entenda que a reu-
tilização de classes por meio da herança é uma das características da
programação orientada a objetos. O mecanismo de herança permite a
reutilização dos atributos e métodos de uma classe em outra, assim,
mesmo que não seja declarado explicitamente, todas as classes em
Java são dispostas de forma hierárquica. Dessa maneira, classes her-
dadas são chamadas de superclasse e possuem atributos e métodos
mais generalizados, já classes herdeiras são chamadas de subclasse e
possuem atributos e métodos mais específicos. Além disso, podemos
encontrar outras nomenclaturas para esses termos, assim, super-
classes podem ser referidas como classes pai ou classes mãe e, ana-
logamente, subclasses também podem ser chamadas de classes filha.
Utilização de herança
Para utilizar a herança, a subclasse deve conter a palavra-chave
“extends”, depois da declaração de seu nome, seguido do título da
superclasse, por exemplo:
public class ClasseHerdeira extends ClasseHerdada {}.
Dessa maneira, a empregabilidade da herança se dá ao fato de po-
der dizer “é um”, na qual um objeto “é um” tipo de outro objeto. Dito isso,
analisaremos o diagrama de classes para entendermos melhor, veja.
Diagrama 1: Diagrama de Classes sem Uso de Herança
Fonte: Editoria Digital Pages (2020).
106
Observando o diagrama, verificamos que as classes Alu-
no e Professor são duas entidades distintas, embora ambas
possuam alguns atributos em comum, como nome, dataNas-
cimento, cpf, endereco e telefone. Dessa forma, de acordo com
o diagrama anterior, precisamos criar duas classes. Observe o
exemplo a seguir.
Figura 1 - Declaração de classes sem herança
Fonte: Editoria Digital Pages (2020).
Note que, na implementação das classes, ambas compar-
tilham atributos em comum. Diante desse cenário, a aplicação de
herança para as duas classes é possível, uma vez que todo profes-
sor e aluno são pessoas. Nesse caso, precisamos criar uma terceira
classe para utilizarmos o conceito de herança; uma classe que seja
genérica e contenha atributos e métodos úteis às outras duas classes
filhas. Assim, criaremos uma repartição denominada Pessoa e ela
conterá os atributos que são comuns entre as subclasses menciona-
das, veja abaixo.
import java.util.Date;
import java.util.List;
public class: Aluno {
private String nome;
private Date datanascimento;
private String cpg;
private String endereco;
private String telefone;
private List(string) cursos;
private double notas:[];
public( double : obternota(){
int qtdnotas = this.notas.length;
double totalnotas = 0;
for( int i=o; |< qtdnotas| i++) {
totalnotas == this.notas[]i];
}
return totalNotas();
}
//
}
import java.util.Date;
import java.util.List;
public class: Aluno {
private String nome;
private Date datanascimento;
private String cpg;
private String endereco;
private String telefone;
private List(string) cursos;
private double notas:[];
public( double : obterSalario(){
return this.salario;
}
//
}
107
Diagrama 2 - Diagrama de Classe com Uso de Herança
Fonte: Editorial Digital Pages (2022).
No diagrama apresentado, podemos observar a super-
classe denominada Pessoa e suas subclasses Aluno e Professor.
Observe que Aluno e Professor possuem uma ligação com a su-
perclasse Pessoa por meio de uma seta. No diagrama, isso indi-
ca que as subclasses Aluno e Professor herdarão os atributos da
superclasse Pessoa.
Nesse sentido, precisamos criar três classes para simbolizar
corretamente o diagrama apresentado. Assim, Aluno e Professor se-
rão subclasses que se estenderão à Pessoa, veja.
108
Figura 2 - Declaração de classes com herança
Fonte: Editorial Digital Pages (2020).
Note que a superclasse Pessoa é declarada normalmente,
apesar de poder ser apresentada como classe abstrata. Já as classes
filhas (Aluno e Professor) possuem a palavra-chave extends, segui-
da do nome da superclasse. Por meio da utilização da palavra-cha-
ve, comunicamos que os atributos e os métodos contidos em Pessoa
também estão contidos em Aluno e Professor e poderão ser aces-
sados normalmente, desde que declarados como public. Já o termo
protected permite acesso por meio da herança, ou default, se estive-
rem no mesmo pacote.
109
DICA
Um detalhe importante é que os atributos das três classes
estão declarados com o modificador de acesso private. Isso indica
que eles só poderão ser acessados pela própria classe e que, mesmo
diante da herança, os atributos dePessoa não estarão acessíveis di-
retamente por Aluno e Professor. Nesse sentido, os objetos possui-
rão os atributos que herdaram e poderão acessá-los por meio dos
métodos getters e setters, ou outros meios que possibilitem a ma-
nipulação dos dados. Caso sua intenção, seja de que as subclasses
possuam acesso direto aos atributos e métodos da classe mãe, eles
devem ser declarados como protected ou public.
Os métodos getters e setters não foram declarados para esse exemplo
como forma de simplificação, mas saiba que eles precisam ser exter-
nados, caso deseje realizar o encapsulamento de dados. Lembre-se
que os métodos getters e setters são utilizados para essa finalidade.
Outro ponto importante é que a programação Java só permite
herança simples entre as classes, ou seja, subclasses só podem her-
dar uma única superclasse. Já uma superclasse pode ser herdada por
inúmeras outras subclasses.
Por fim, quando não é definido uma herança por meio da pala-
vra-chave extends, indiretamente a classe herdará a classe Object. Isso
porque a classe Object é uma classe genérica que contém o construtor
default “public Object()” e onze métodos, cujos mais comumente utili-
zados são os getClass(), equals(), hash-Code() e toString().
Palavra-chave super
Para acessarmos os atributos e métodos do próprio objeto, fazemos
uso da palavra-chave this. Mas, se quisermos acessar os atributos
do objeto herdado, podemos fazer uso da palavra-chave super. Pen-
sando nisso, a palavra-chave super deve ser utilizada para acessar
110
DICA
os atributos e métodos da superclasse, desde que eles não tenham
sido declarados como modificadores de acesso private.
Assim, o uso da palavra-chave super segue algumas regras de
utilização diante da subclasse. Veja:
• para chamar o construtor da superclasse, basta declarar a pala-
vra-chave super, seguido dos argumentos dentro dos parênte-
ses, caso o construtor da superclasse possua. Exemplo: super();
• a chamada do construtor da superclasse só poderá ser feita dentro de
construtores da subclasse e deverá ser a primeira linha do construtor;
• caso haja hierarquia de classes, por exemplo, A herda B que
herda C, e a chamada de construtor deverá ser em cascata, ou
seja, A chama construtor da classe B que chama o construtor
da classe C;
• o construtor padrão da superclasse será chamado automati-
camente caso não seja feito uma declaração explícita, ou seja,
mesmo que não declarado, o construtor da subclasse chamará
o construtor da superclasse;
• desde que o modificador de acesso permita, métodos exis-
tentes na superclasse podem ser executados fazendo uso
da palavra-chave super, seguido do ponto e o nome do
método com seus parâmetros caso existam. Exemplo: su-
per.somar(int numero1, int numero2);. O mesmo acontece
com os atributos.
O uso da palavra-chave super não é obrigatório, desde que ambas
as classes não possuam métodos ou atributos com o mesmo nome.
Mas, caso essa duplicidade ocorra e o método não seja sobrescrito, a
não utilização do super desencadeará a execução do método da sub-
classe, enquanto o método da superclasse será ignorado.
111
DICA
Como essa condição não é usual, é difícil encontrar có-
digos que façam uso da palavra-chave super. Dessa forma,
quando não há “duplicidade” na subclasse, a palavra-cha-
ve this pode ser utilizada para chamar métodos e atributos
da superclasse (exceto para o construtor da superclasse, que
sempre será super). Isso ocorre devido ao sistema Java, que
“procura” métodos e atributos automaticamente, de forma
hierárquica, começando pela subclasse até a última super-
classe. Assim, caso o atributo ou método não seja encontra-
do em nenhuma das classes da hierarquia, haverá um erro de
compilação do projeto.
Criação e uso de hierarquia de classes
Exceto a classe Object, todas as classes em Java são subclasses
e, mesmo quando não há uma declaração explícita de heran-
ça, a classe herda a classe Object. Por esse motivo, Object é a
classe mais genérica em Java, ou seja, é superclasse de todas
as demais.
Nesse seguimento, a hierarquia permite que uma subclas-
se se estenda à apenas uma superclasse, enquanto uma classe
mãe pode se estender a inúmeras outras classes filha. Dessa ma-
neira, se estruturarmos um sistema de empresa, podemos ates-
tar essa estrutura.
Lembre-se que a hierarquia de classes possibilita a criação de ca-
tegorias com funções mais comuns, assim como permite a criação
de classes mais especializadas que herdem essas características e
funcionalidades específicas.
112
Diagrama 3 - Diagrama de Classe: Exemplo de Hierarquia
Fonte: Editorial Digital Pages (2020).
O diagrama apresentado é um exemplo de representação de
herança e associação, que demonstra as hierarquias das classes, des-
de a mais especializada até a mais genérica. Observe que as classes
interligadas pela seta indicam que o objeto herda atributos e méto-
dos de forma hierárquica. E, como sabemos, as subclasses mais espe-
cializadas possuem atributos e métodos mais específicos do objeto,
enquanto as superclasses são genéricas e possuem atributos e mé-
todos gerais, que servem às subclasses. Por fim, as classes que estão
interligadas pela linha indicam que o objeto está associado ao outro
113
e apontam que um objeto contém outro. Essa associação é um rela-
cionamento, mas não indica hierarquia. Um exemplo é que a classe
Colaborador possui todos os atributos e métodos contidos em Pessoa
Fisica, Pessoa e Object.
À vista disso, é preciso apontar que a classe Object contém ape-
nas métodos sem atributos. Esses métodos são genéricos e podem ser
chamados ou sobrescritos por qualquer classe. Assim, com base no
diagrama, a classe Fornecedor pode chamar o método clone da clas-
se Object, já que a classe Fornecedor herda PessoaJuridica, que herda
Pessoa que, por sua vez, herda Object. Por esse motivo, como mencio-
nado, todas as classes possuem os métodos contidos em Object.
Classes e subclasses
Não há como fugir da hierarquia de classes, já que todas elas her-
dam de Object. Contudo, podemos limitar que uma classe não seja
superclasse de uma classe filha. Essa limitação é feita pelo uso da
palavra-chave final após a palavra-chave class.
Isso é feito geralmente por razões de segurança
e eficiência. Muitas das classes da biblioteca pa-
drão do Java são finais, por exemplo java.lang.
System e java.lang.String. Todos os métodos [...]
são implicitamente finais.” (BEDER, 2014, p. 66).
Portanto, classes declaradas como final não podem ser her-
dadas por outras.
Classes abstratas e interfaces
Planejar um sistema é uma tarefa árdua e deve ser feita de maneira
minuciosa por um engenheiro de software, já que determinar quais
classes serão utilizadas e identificar suas relações de herança é uma
das partes mais trabalhosas do processo. Lembre-se que a determi-
nação das classes que serão exclusivamente superclasses e que não
serão utilizadas para serem instâncias pode influenciar em como o
software será desenvolvido.
114
Por esse motivo, classes determinadas exclusivamente como
superclasses podem e devem ser declaradas como abstratas. Essas
servem como um guia para a programação, contendo apenas atribu-
tos e métodos que devem ser implementados nas subclasses. Assim,
além das classes abstratas, Java permite a criação de interfaces que
contenham métodos sem corpo. Dessa maneira, diferentemente das
hierarquias, uma classe Java pode implementar várias interfaces.
Classes abstratas
Classes abstratas, além de não poderem ser instanciadas, possibi-
litam a criação de assinaturas de métodos, declarados como abs-
tract. Elas não possuirão chaves com escopo, devem ser finalizadas
com ponto e vírgula e não podem ser declaradas como private. Além
disso, elas obrigarão as subclasses a implementarem o método, de-
vendo ou não o sobrescrever, fazendo uso da annotation @Override.
A declaração de classes e métodos abstratos também deve possuir
modificadores abstract após o modificador de acesso daclasse ou
método. Para que você entenda o que estamos explicando, a seguir
veremos as diferenças em suas declarações.
Primeiro, vamos aos exemplos das declarações de classes
normais, sem uso do abstract:
public class Combustivel {
private int litrosDisponiveis;
public void abastecerReservatorio(int litros) {
this.litrosDisponiveis += litros;
}
public int getLitrosDisponiveis(){
return this.litrosDisponiveis;
}
}
public class Etanol extends Combustivel {
115
public void printLitrosDisponiveis() {
System.out.println(getLitrosDisponiveis() + “ Litros de
Etanol”);
}
}
public class Gasolina extends Combustivel {
public void printLitrosDisponiveis() {
System.out.println(getLitrosDisponiveis() + “ Litros de
Gasolina”);
}
}
Note que as classes Etanol e Gasolina se estendem à classe
Combustível, e que ela não possui nenhum método abstrato. Assim,
se executarmos o código com as três classes declaradas dessa ma-
neira, ambas poderão ser instanciadas, todavia, como a classe Com-
bustivel não deve ser um objeto e sim uma superclasse, ela poderá
ser declarada como abstract, além de conter uma assinatura abstrata
para o método printLitrosDisponiveis, presente nas duas subclas-
ses. Veja a seguir.
public abstract class Combustivel {
private int litrosDisponiveis;
public void abastecerReservatorio(int litros) {
this.litrosDisponiveis += litros;
}
public int getLitrosDisponiveis(){
return this.litrosDisponiveis;
}
public abstract void printLitrosDisponiveis();
}
116
Observe, ainda, que a classe Combustivel possui o modifi-
cador abstract, impossibilitando que a classe seja instanciada, mas
permitindo-a conter métodos abstratos, como o caso do método
printLitrosDisponiveis, finalizado com ponto e vírgula e sem esco-
po, diferentemente de um método comum. Dessa maneira, teremos:
public class Etanol extends Combustivel {
@Override
public void printLitrosDisponiveis() { System.out.println(-
getLitrosDisponiveis() + “ Litros de Etanol”);
}
}
public class Gasolina extends Combustivel { @Override
public void printLitrosDisponiveis() { System.out.println(-
getLitrosDisponiveis() + “ Litros de Gasolina”);
}
}
Observe também que as classes Etanol e Gasolina possuem,
obrigatoriamente, o método printLitrosDisponiveis, que está decla-
rado como abstract na classe Combustível.
Nesse sentido, uma informação importante sobre as classes
abstratas é que seus construtores e atributos não podem ser decla-
rados como abstract. Mesmo que a classe abstrata não possa ser ins-
tanciada, a subclasse pode chamar o construtor da classe mãe para
inicializar atributos, ou executar métodos normalmente, como é
feito para casos comuns.
Por último, a diferença da declaração de um método abstrato
ou não é que o método declarado como abstrato deve ser implemen-
tado (escrito) pela subclasse, enquanto os métodos normais devem
ser herdados pela subclasse. Ainda assim, pode haver hierarquia de
classes abstratas, assim como em classes normais. Todavia, apenas
a classe que não for abstrata deverá implementar os métodos.
117
Interfaces
Uma classe em Java não pode estender (herdar) duas ou mais clas-
ses, o que limita as implementações funcionais de alguns objetos.
Contudo, é possível executar em uma classe, seja abstrata ou não,
diversas interfaces que contenham assinaturas de métodos.
Assim como uma classe abstrata, uma interface não pode
ser instanciada, e todos os seus métodos devem ser implicitamente
abstract e public. Além disso, se houverem campos, eles serão con-
siderados static e final, devendo ser inicializados na sua declaração
(SANTOS, 2011).
Se uma classe for declarada como abstrata, sem variáveis e
com apenas métodos abstratos e/ou constantes, o ideal é transfor-
má-la em interface. Lembre-se que interfaces não podem ser ins-
tanciadas, assim como as classes abstratas, e possuem seus métodos
com modificadores de acesso public e modificadores abstract, de for-
ma implícita, dispensando declarações. Embora não seja possível de-
clarar atributos nas interfaces, é possível declaração de constantes.
Embora Java não permita herança múltipla entre as classes,
isso não ocorre com as interfaces, ou seja, é possível uma interfa-
ce estender diversas outras. Da mesma forma, uma classe abstrata
pode implementar interfaces, porém apenas a próxima subclasse
mais especializada e não abstrata da hierarquia implementará os
métodos dela.
Assim, veremos como é a usabilidade de interfaces:
public interface AtivarDesativar {
void ativar();
void desativar();
}
Primeiro, criamos uma interface que ativará ou desativará
alguma coisa em nosso sistema. Essa “coisa” pode ser um objeto
que nos faça sentido utilizar ou eliminar. Assim, geralmente, a ati-
vação ou desativação de um objeto é utilizada em banco de dados
para a exclusão lógica de um registro.
118
DICA
public interface VivoMorto {
void vivo();
void morto();
}
Depois, criamos uma outra interface como exemplo, que ser-
ve para “definir” se um ser está vivo ou morto.
public interface CalcularIdade {
int calcularIdade();
}
Por último, uma interface será utilizada para calcular a idade
de algo ou de alguém.
Note que, em todas as interfaces, sua composição é iniciada pelo
modificador de acesso public, seguido da palavra-chave interface e
nome. Assim, no escopo da interface temos métodos com assinatura
e tipo de retorno, embora pudéssemos ter declarado constantes no
escopo da interface. Lembre-se que todo método declarado sempre
será public e abstract, e as constantes sempre serão static e final (fa-
tor que determina que o atributo é uma constante).
Agora, veremos como fazer a implementação de interfaces
em classes:
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
public class Pessoa implements AtivarDesativar, VivoMorto,
CalcularIdade {
119
private String nome;
private boolean ativo;
private boolean vivo;
private Date nascimento;
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
public Date getNascimento() {
return nascimento;
}
public void setNascimento(Date nascimento) {
this.nascimento = nascimento;
}
public boolean isAtivo() {
return ativo;
}
public boolean isVivo() {
return vivo;
}
@Override
public int calcularIdade() {
GregorianCalendar hoje = new GregorianCalendar();
GregorianCalendar nascimento = new GregorianCalendar();
nascimento.setTime(this.nascimento);
120
int anoAtual = hoje.get(Calendar.YEAR);
int anoNascimento = nascimento.get(Calendar.YEAR);
return new Integer(anoAtual-anoNascimento);
}
@Override
public void vivo() {
this.vivo = true;
}
@Override
public void morto() {
this.vivo = false;
}
@Override
public void ativar() {
this.ativo = true;
}
@Override
public void desativar() {
this.vivo = false;
}
}
Perceba que, na declaração da classe possuímos a palavra-
-chave implements, seguida dos nomes das interfaces. E, no de-
correr do escopo da classe, possuímos os métodos existentes nas
interfaces e, acima de cada método, a annotation @Override, à qual
se refere ao método sobrescrito nessa classe.
Agora, veja a diferença entre classes abstratas e interfaces.
121
Quadro 1 - Diferenças entre classe abstrata e interface
Fonte: Editorial Digital Pages (2020).
Relacionamento entre classes
Além da ligação entre heranças, há mais três tipos de rela-
cionamento entre classes, classificados como: associação,
composição e agregação. Esses relacionamentos não indicam
hierarquia, uma vez que, nesse caso, podemos dizer que um
objeto “tem um” outro objeto, mas não que um objeto “é um”
outro objeto.
Associação
A associação é um relacionamento estrutural entre duas classes que
especifica quais são os objetos conectados entre si. Isso possibilita
que, por meiodos vínculos do relacionamento, um objeto invoque
estados e comportamentos de outros objetos.
Veja o exemplo a seguir.
InterfacesClasses abstratas
Não permite herança múltipla
Deve possuir a palavra-chave class
Permite a declaração de atributos
Métodos podem ser public e abstract
Pode conter métodos abstratos
Possui construtores
Métodos não abstratos podem
ser estáticos
Permite herança múltipla
Deve possuir a palavra-chave interface
Não permite a declaração de atributos
Métodos são public e abstract
Só possui métodos abstratos
Não possui construtores
Por conter apenas métodos abstra-
tos, não é possível declarar métodos
estáticos
122
Diagrama 4 - Diagrama de Classe – Associação entre Classes
Fonte: Editorial Digital Pages (2020).
No diagrama representado, a classe Pessoa poderá possuir
uma lista de objetos do tipo Automóvel, por isso está representa-
da pelo 0..*, que indica que uma pessoa poderá ter nenhum ou vá-
rios automóveis. Em contrapartida, todo automóvel deve pertencer
à apenas uma pessoa, por isso do lado da entidade Pessoa temos o
valor 1. Nesse caso, o relacionamento entre as classes será visível
apenas na classe Pessoa, que conterá a lista de carros. Assim, para
utilizarmos esse tipo de relacionamento na programação, devemos
trabalhar com arrays, que são listas de objetos do mesmo tipo.
Assim, veja na prática a declaração das classes do Diagrama 4.
Nesse sentido, iniciaremos pela classe Carro, já que a classe Pessoa
terá um array dela:
public class Carro {
private String placa;
private String marca;
private String modelo;
private String classi;
private short anoFabricacao;
private short anoModelo;
public String getPlaca() {
return placa;
}
123
public void setPlaca(String placa) {
this.placa = placa;
}
public String getMarca() {
return marca;
}
public void setMarca(String marca) {
this.marca = marca;
}
public String getModelo() {
return modelo;
}
public void setModelo(String modelo) {
this.modelo = modelo;
}
public String getClassi() {
return classi;
}
public void setClassi(String classi) {
this.classi = classi;
}
public short getAnoFabricacao() {
return anoFabricacao;
}
public void setAnoFabricacao(short anoFabricacao) {
this.anoFabricacao = anoFabricacao;
}
124
public short getAnoModelo() {
return anoModelo;
}
public void setAnoModelo(short anoModelo) {
this.anoModelo = anoModelo;
}
}
Agora, veremos a declaração da classe Pessoa: public class
Pessoa {
public Pessoa() {
if(this.carros == null ) {
this.carros = new Carro[0];
}
}
private String nome;
private long cpf;
private Carro [] carros;
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
public long getCpf() {
return cpf;
}
public void setCpf(long cpf) {
125
this.cpf = cpf;
}
public Carro[] getCarros() {
return carros;
}
public void setCarros(Carro[] carros) {
this.carros = carros;
}
}
Perceba que, na classe Pessoa, possuímos um array de obje-
tos da classe Carro (Carro [] carros;). Isso indica que a classe Pessoa
“tem um” objeto do tipo array de Carro, mas não necessariamente
significa que seja obrigatório ter um objeto carro dentro do array do
objeto da classe Pessoa. Assim, para evitarmos NullPointerExcep-
tion, devemos inicializar o array, conforme feito no construtor da
classe Pessoa (note que todo array também é um tipo de objeto, ou
seja, é uma lista).
NullPointerException é uma exceção gerada pelo sistema quando
se tenta acessar um objeto em memória que ainda não foi instan-
ciado (ou inicializado). O significado é que o objeto está nulo e não
possui um valor definido naquele momento. A classe de exceção é a
java.lang.NullPointerException. Geralmente ocorre exceção quan-
do um objeto não é inicializado, todavia, isso não se aplica aos ti-
pos primitivos.
Dessa forma, para entendermos como funciona o relaciona-
mento das categorias, nesse caso, vamos à um exemplo de imple-
mentação de um código que manipule ambas as classes:
DEFINIÇÃO
126
public class Start {
public static void main(String[] args) {
Carro carro1 = new Carro();
carro1.setAnoFabricacao((short) 2010);
carro1.setAnoModelo((short) 2011);
carro1.setMarca(“Marca 1”);
carro1.setModelo(“Modelo 1”);
carro1.setClassi(“77784646846HHjJ46468764”);
carro1.setPlaca(“AAA-1234”);
Carro carro2 = new Carro();
carro2.setAnoFabricacao((short) 2010);
carro2.setAnoModelo((short) 2011);
carro2.setMarca(“Marca 2”);
carro2.setModelo(“Modelo 2”);
carro2.setClassi(“748XJS684633jJ46AA3889”);
carro2.setPlaca(“BBB-4321”);
Pessoa pessoa = new Pessoa();
pessoa.setNome(“Fulano da Silva Beltrano”);
pessoa.setCpf(00011122233);
Carro [] carros = {carro1, carro2};
pessoa.setCarros(carros);
for(Carro carro : pessoa.getCarros()) {
System.out.println(“O veículo “ + carro.getModelo()
+ “ pertence ao “ + pessoa.getNome());
}
System.out.printf( “%s tem %d %s”,
pessoa.getNome(), pessoa.getCarros().length,
pessoa.getCarros().length > 1 ? “carros” : “carro”
);
}
}
127
Observe que são instanciados dois objetos do tipo Carro e um
objeto do tipo Pessoa. Posteriormente, foi declarado um array de
objetos Carro, adicionando os dois objetos Carro ao array, em se-
guida fazendo set do array de Carros, relacionando-os à Pessoa.
Por último, iteramos todos os carros contidos dentro do ar-
ray, escrevendo no terminal pelo comando System.out.println o
modelo do carro e o nome do dono. Finalizando, escrevemos no ter-
minal o nome da pessoa e quantos carros ela possui, fazendo uso do
operador condicional ternário, que é composto pela condição ? ex-
pressão1 : expressão2, bastante similar à uma função if else simples.
Composição
A composição, segundo (BEDER, 2014), é um relacionamento com
características “todo-parte”; assim, uma classe só tem sentido em
sua existência (parte), se outra classe existir (todo). Veja o exemplo.
Diagrama 5 - Diagrama de classes – Associação e Composição entre Classes
Fonte: Editorial Digital Pages (2020).
128
No diagrama de classes apresentado, as classes Cliente e Pe-
dido estão associadas de forma simples. Nesse sentido, um cliente
pode ter nenhum ou vários pedidos, assim como os pedidos só per-
tencem a um cliente. Depois, temos a composição entre Pedido, que
é o “todo”, e o ItemPedido, considerado a “parte”, isto é, que não
pode existir sem a presença de um Pedido. Note, ainda, que a ligação
entre as classes Pedido e ItemPedido é feita por meio da linha com
o “diamante” escuro e que, obrigatoriamente, o pedido deverá ter
pelo menos um itemPedido. Seguindo a mesma lógica, portanto, o
itemPedido pertence a apenas um requerimento.
Veja, na prática, a declaração das classes do Diagrama 5. Des-
taco que iniciaremos pelo ItemPedido, necessário para a construção
do Pedido, veja:
public class ItemPedido {
public ItemPedido () {
this.quantidade = 1;
}
public ItemPedido(String descricao, int quantidade, double
valorUnitario)}
{
this.descricao = descricao;
this.setQuantidade(quantidade);
this.setValorUnitario(valorUnitario);
}
private String descricao;
private int quantidade;
private double valorUnitario;
public String getDescricao() {
return descricao;
}
129
public void setDescricao(String descricao) {
this.descricao = descricao;
}
public int getQuantidade() {
return quantidade;
}
Public void setQuantidade(int quantidade) {
If(quantidade > 01) {
this.quantidade = quantidade;
} else {
This.quantidade = 1;
}
}
public double getValorUnitario() {
return valorUnitario;
}
public void setValorUnitario(double valorUnitario) {
if(valorUnitario > 0) {
this.valorUnitario = valorUnitario;
} else {
This.valorUitario = 0;
}
}
public void addQuantidade(int quantidade) {
this.quantidade += quantidade;
}
@Override
130
public int hashCode() {
final int prime = 31; int result = 1;
result = prime * result + ((descricao == null) ? 0 : descri-
cao. hashCode());
result = prime * result + quantidade;long temp;
temp = Double.doubleToLongBits(valorUnitario);
result = prime * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
ItemPedido other = (ItemPedido) obj;
if (descricao == null) {
if (other.descricao != null)
return false;
} else if (!descricao.equals(other.descricao))
return false;
if (quantidade != other.quantidade)
return false;
if (Double.doubleToLongBits(valorUnitario) != Double.
double- ToLongBits(other.valorUnitario))
return false;
131
return true;
}
}
Observe que nessa classe possuímos dois construtores, um
que já inicializa a quantidade igual a 1 e outro que permite a inserção
das informações sem precisar fazer os setters. Além disso, no encap-
sulamento dos dados, verificamos se a quantidade inserida é maior
que zero, já que não faz sentido um item de pedido zerado ou com
quantidade negativa. Isso é feito, também, para o valor unitário. Re-
pare que sobrescrevemos os métodos equals e hashCode existentes
na classe Object. Esses métodos serão essenciais para a funcionali-
dade de adição do novo item na classe Pedido. Todavia, não se preo-
cupe quanto à sobrescrita destes métodos, pois, possivelmente sua
IDE o fará automaticamente.
Dito isto, agora, veremos a classe Pedido:
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
public class Pedido {
public Pedido () {
this.itemPedidos = new HashSet<ItemPedido>();
}
private Date dataPedido;
private String formaPagamento;
private Set<ItemPedido> itemPedidos;
public Date getDataPedido() {
return dataPedido;
}
public void setDataPedido(Date dataPedido) {
this.dataPedido = dataPedido;
132
}
public String getFormaPagamento() {
return formaPagamento;
}
public void setFormaPagamento(String formaPagamento) {
this.formaPagamento = formaPagamento;
}
public Set<ItemPedido> getItemPedidos() {
return itemPedidos;
}
public void addItemPedido(ItemPedido item) {
if(this.itemPedidos.contains(item)) {
this.itemPedidos.stream()
.filter(oneItem -> oneItem.equals(item))
.forEach(oneItem -> oneItem.addQuantidade(item.
getQuantidade()));
} else {
this.itemPedidos.add(item);
}
}
public float getTotalPedido() {
float total = 0;
for(ItemPedido item : itemPedidos) {
total += item.getValorUnitario() * item.
getQuantidade();
}
return total;
}
}
133
Nessa classe, para o ItemPedido, fizemos uso da interface
Set, que é uma collection que não permite duplicidade de dados. Nes-
se sentido, por ser uma interface, também fizemos a implementação
da classe HashSet. Em seguida, como não é permitida a duplicidade
de dados no método addItemPedido, verificamos se o item já existe
na lista. Caso exista, fazendo uso de uma expressão lambda, procu-
ramos o item em questão e adicionamos à quantidade existente à
nova, por meio do método addQuantidade.
Perceba que isso só é possível graças aos métodos equals e hash-
Code existentes no item do pedido. Lembre-se que o hashCode utiliza
um cálculo matemático para determinar um valor àquele atributo e,
caso um dos atributos possua valores diferentes, ele não será conside-
rado igual e executará a condição else, adicionando o item à lista.
Agregação
A agregação é similar à associação e sua diferença é apenas conceitual. Nesse
sentido, a agregação não é perceptível e, caso existam dúvidas entre agre-
gação e associação, opte sempre pela associação (KNOERNSCHILD, 2001).
Assim, Beder afirma que:
a agregação é um relacionamento com carac-
terísticas ‘todo-parte’ [...] menos intenso, po-
dendo haver um certo grau de independência
entre eles. Ou seja, a existência do objeto ‘par-
te’ faz sentido mesmo sem a existência do ob-
jeto ‘todo’ (BEDER, 2014, p. 39).
Para entendermos o conceito em maior profundidade, obser-
ve o diagrama a seguir.
Diagrama 6 - Diagrama de Classes – Agregação entre Classes
Fonte: Editorial Digital Pages (2020).
134
Observando o diagrama, note que um time pode ter um ou vários
atletas, e o atleta pode ou não ter um time (o que significa que o
atleta pode existir sem a necessidade da presença de um time). Nes-
se contexto, há exclusividade, ou seja, embora o atleta só possa es-
tar associado a um único time, ele pode existir sem a existência de
um time propriamente dito.
Polimorfismo
Introdução ao polimorfismo
A palavra polimorfismo vem do grego poli morfos, cujo significado
pode ser traduzido como “o que possui várias formas”. Em infor-
mática, na programação, esse significado não é muito diferente:
polimorfismo (“muitas formas”) permite a ma-
nipulação de instâncias de classes que herdam de
uma mesma classe ancestral de forma unificada:
podemos escrever métodos que recebam instân-
cias de uma classe C, e os mesmos métodos serão
capazes de processar instâncias de qualquer classe
que herde da classe C, já que qualquer classe que
herde de C é-um-tipo-de C. (SANTOS, 2011, p. 140).
Isso significa que, por exemplo, as subclasses Professor e
Aluno, por serem tipos de Pessoa, estendem a superclasse Pes-
soa e podem executar métodos que sejam declarados na classe
Pessoa ou em sua superclasse. Nesse caso, o objeto mais es-
pecializado transforma-se em um objeto genérico de qualquer
nível acima da sua hierarquia. Em outras palavras, em Java, to-
dos os objetos podem ser tratados como se fossem objetos de
uma superclasse.
DICA
135
Polimorfismo na prática
O Diagrama 7 apresenta um exemplo da situação supracitada.
Diagrama 7 - Diagrama de Classes: exemplo de polimorfismo
Fonte: Editorial Digital Pages (2020).
A classe Pessoa tem atributos comuns para qualquer pessoa
e a declaração de seu método obterNomeCompleto sugere que este
também é comum a qualquer pessoa. Como as classes Professor e
Aluno são tipos de pessoa, elas estendem a classe Pessoa e, conse-
quentemente, herdam seus atributos e método. Agora, em relação à
implementação dessas classes, observe as Figuras 3 e 4.
Figura 3 - Implementação da classe Pessoa
Fonte: Editorial Digital Pages (2020).
136
Figura 4 -Implementação das classes Aluno e Professor.
.Fonte: Editorial Digital Pages (2020).
De modo geral, não há novidade alguma até o momento: sim-
plesmente, aplicou-se o conceito de herança. Agora, é necessário
explorar a manipulação dessas classes, criando uma classe que con-
tenha o método main para que seja possível executar o conteúdo e
entender o conceito de polimorfismo, veja um exemplo.
public class Start {
public static void main(String[] args) {
Pessoa pessoa = new Pessoa();
Aluno aluno = new Aluno();
Professor professor = new Professor();
pessoa.setNome(“João”);
pessoa.setSobrenome(“da Silva”);
aluno.setNome(“Ana”);
aluno.setSobrenome(“de Oliveira Lima”);
aluno.setTurma(“6º B”);
professor.setNome(“Guilherme”);
professor.setSobrenome(“Nascimento”);
professor.setSalario(4389.33);
imprimeNomeCompleto(pessoa);
imprimeNomeCompleto(aluno);
imprimeNomeCompleto(professor);
137
}
public static void imprimeNomeCompleto(Pessoa pessoa) {
System.out.println(pessoa.obterNomeCompleto());
}
}
No código apresentado, ocorreu a criação de três objetos:
• objeto pessoa do tipo Pessoa;
• objeto aluno do tipo Aluno;
• objeto professor do tipo Professor.
Posteriormente, houve a inserção dos valores dos atributos
para os respectivos objetos. Ao final, chamou-se o método impri-
meNomeCompleto, que espera receber um objeto do tipo Pessoa.
Por três vezes este método foi chamado e um objeto diferente foi
passado em cada chamada. Como o parâmetro do método espera um
objeto do tipo Pessoa, ao passar os objetos aluno e professor, não há
erro de compilação, uma vez que estes são subclasses da superclasse
Pessoa: dessa maneira, de acordo com o conceito de herança, ambos
não deixam de ser objetos da superclasse.
Caso não houvesse polimorfismo, ao passar os objetos do tipo
Aluno e Professor por parâmetro ao método imprimeNomeCompleto,haveria um erro de compilação. Com o polimorfismo, porém, os obje-
tos aluno e professor, “transformam-se” em um objeto do tipo Pes-
soa: ao executar-se a aplicação, o resultado é apresentado conforme
o esperado, posto que ambos os objetos têm os mesmos atributos e
método herdados da classe Pessoa. Observe o resultado a seguir.
Figura 5 - Resultado da execução da classe Start- exemplo do polimorfismo
Fonte: Editorial Digital Pages (2020).
138
Santos, em seu livro “Introdução à programação orientada a
objetos usando Java”, de 2011, ressalta que “as regras de cast entre
instâncias de classes não permitem a conversão de classes mais ge-
néricas para classes mais específicas, nem de classes que não este-
jam em uma hierarquia de herança” (p. 145).
Em outras palavras, se, por exemplo, o parâmetro do método
imprimeNomeCompleto fosse do tipo Professor, não seria possível
passar os objetos do tipo Aluno e Pessoa por parâmetro, visto que
Pessoa é mais genérico que Professor e a conversão do objeto mais
genérico para o mais específico não é possível.
Por sua vez, o tipo Aluno está no mesmo nível hierárquico que
Professor, visto que ambos herdam de Pessoa. Como são dois objetos
diferentes por estarem no mesmo nível hierárquico, a classe Aluno não
é um tipo de Professor, embora ambos sejam um tipo de Pessoa.
Ligação dinâmica (dynamic binding)
A ligação dinâmica está muito relacionada aos conceitos do po-
limorfismo e herança. Nesse caso, uma subclasse tem métodos
herdados da superclasse e os sobrescreve da melhor maneira para
realizar suas operações. E, somente durante a execução e utilizando
conceitos do polimorfismo, o Java toma a decisão de qual método
deve ser usado: se o da subclasse ou de uma das superclasses de ní-
veis acima. Para entender o que estamos tratando, veja:
a implementação do polimorfismo pode exi-
gir facilidades proporcionadas pela ligação
dinâmica. De um modo geral, uma ligação
(binding) é uma associação, possivelmente,
entre um atributo e uma entidade ou entre
uma operação e um símbolo. Uma ligação é
dita estática se ocorre em tempo de compila-
ção [...] e não é mais modificada durante toda
a execução do programa. A ligação dinâmica
é feita em tempo de execução ou pode ser al-
terada no decorrer da execução do programa
(KAMIENSKI, 1996, p. 15).
139
Pode-se afirmar, portanto, que a ligação dinâmica aconte-
ce apenas durante a execução do programa, quando o próprio Java
toma a decisão de qual método de qual classe deve ser usado. Já a li-
gação estática é aquela executada diretamente, quando não há apli-
cação do polimorfismo. Assim, observe o Diagrama 8 que representa
uma hierarquia entre as classes.
Diagrama 8 - Diagrama de Classe: Hierarquia de Classes.
Fonte: Editorial Digital Pages (2020).
Como você deve ter percebido, o diagrama apresenta uma es-
trutura longa de hierarquia, na qual as classes Gato, Tigre e Leão
são as mais especializadas, e as demais, acima, são sucessivamen-
te mais genéricas – até a primeira do topo, que é a classe Object do
próprio Java.
140
DICA
Tratamento de exeções
No desenvolvimento de software, uma exceção não é algo que ocor-
re de forma rara. Exceções, na verdade, são eventos que, por algum
motivo – neste caso, uma condição excepcional –, geram erros no
sistema, os quais podem ou não ser esperados.
Em muitos dos casos, exceções ocorrem quando o progra-
mador se esquece de inicializar um objeto ou encontra erros du-
rante a conversão, ou casting, de objetos.
Se essas exceções ocorrerem e o devido tratamento para o caso não
for realizado, o sistema provavelmente será interrompido naquele
ponto e não executará o processamento restante.
Ricarte (2001, p. 29) diz que “para cada exceção que pode
ocorrer durante a execução do código, um bloco de ações de trata-
mento (um exception handler) deve ser especificado. O compilador
Java verifica e reforça que toda exceção ‘não-trivial’ tenha um blo-
co de tratamento associado”. Em muitos casos, as exceções podem
ser evitadas de forma simples, apenas utilizando condicionadores
como if-else ou operador ternário, por exemplo. Em alguns contex-
tos, porém, o ideal é capturar e tratar a exceção utilizando o bloco
try-catch; e, em determinados casos, até mesmo lançar a exceção
para o método acima da pilha de chamadas.
Evitando e capturando exceções
A forma mais comum para evitar uma exceção é realizar verifica-
ções no objeto antes de utilizá-lo para seu propósito. Um exemplo
muito comum é checar se o objeto não está nulo, o que evita erros
de NullPointerException. Assim, a captura de exceção é feita com os
141
blocos try-catch, que também podem possuir o finally. Além disso, é
possível ter na estrutura vários catches que capturam um tipo espe-
cífico de exceção. Sua estrutura pode ser da seguinte maneira:
try {
// Código que implementa alguma função no sistema, que
pode gerar
alguma exceção
} catch (Exception e) {
// Trecho no qual é feita a captura da exceção
} finally {
// Se declarado, o finally será sempre executado, indepen-
dentemente se gerou ou não uma exceção
}
O bloco try-catch-finally precisa estar dentro do escopo de um mé-
todo e não há necessidade de implantá-lo em locais nos quais pos-
sivelmente não haverá erros.
Caro(a) estudante. Chegamos ao final de mais uma etapa de estudos.
Nesse módulo, aprendemos sobre a usabilidade da herança e qual é
a melhor forma de implementá-la entre as classes. Além disso, dis-
cutimos a utilização da palavra-chave super, que permite chamar
diretamente métodos e atributos das superclasses (desde que não
sejam privates).
VOCÊ SABIA?
SINTETIZANDO
142
Também vimos como funciona a hierarquia entre as classes e como
o Java trabalha para tratar sobre esse assunto, além de observarmos
como é a forma correta de implementação de hierarquia.
Um outro ponto estudado, referiu-se à utilização das classes abs-
tratas e interfaces, além das principais diferenças entre elas. Vimos
também que a herança múltipla entre classes em Java não é pos-
sível, mas que essa regra não ocorre entre interfaces. Além disso,
embora não seja possível que uma subclasse se estenda à mais de
uma classe mãe, aprendemos que uma classe pode implementar
inúmeras interfaces.
Por fim, estudamos que, além da herança, as classes possuem rela-
cionamentos classificados em três tipos: agregação, composição e
associação. Lembre-se que esses relacionamentos só são possíveis
quando podemos dizer que um objeto “tem um”, mas não “é um”.
Esperamos ter contribuído com o seu aprendizado.
Até a próxima!
UN
ID
AD
E
4
Objetivos
◼ Entender o conceito de recursividade.
◼ Entender os algoritmos de ordenação e conhecer o código dos
algoritmos mais usados.
◼ Conhecer estruturas de dados: listas, filas, pilhas e árvores.
◼ Conhecer algumas implementações em Java dessas estruturas.
144
Introdução
Olá, aluno(a). Como vai? Chegamos a mais uma etapa de estudos e,
nesse momento, serão apresentados a você os conceitos de algorit-
mos recursivos e algoritmos de ordenação. E, para implementar es-
ses conceitos, apresentaremos códigos Java usando vetores.
Veremos também o conceito de recursividade e a implemen-
tação de algoritmos recursivos. Discutiremos sobre os algoritmos
mais conhecidos de ordenação de vetores, mostrando o código de
alguns desses algoritmos.
Antes de finalizarmos este material, veremos os conceitos de es-
trutura de dados conhecidos como Lista, Filas, Pilhas e Árvores, apre-
sentando parte dos códigos desenvolvidos para o uso desses conceitos.
Dito isto, convido você a conhecer os temas citados de forma des-
complicada e muito intuitiva, conforme abordaremos neste material.
Boa leitura e bons estudos!
145
Algoritmos recursivos
Alguns problemas exigem que sejam trabalhados laços de repeti-
ções para executar as mesmas instruções em quantidades estabe-
lecidas ou esperadas. Dessa maneira, os algoritmos recursivos são
uma espécie de loop que possuem um critério de parada. Porém o
mesmo algoritmo“chama a si mesmo com argumentos tratados”.
Figura 1 - Exemplo de algoritmo recursivo
Fonte: Comitê Editorial Digital Pages (2019).
Caro(a) aluno(a), para que você entenda o que abordamos até agora,
o programa a seguir imprime do 10 ao 1 na tela. Veja!
O método da linha 4, chamado recursão, é iniciado pelo método
main, que é o primeiro método a ser executado da classe da linha 16.
Dessa maneira, ao ser iniciado, passa-se o parâmetro 10. Esse algo-
ritmo faz a contagem regressiva de 10 até 1, chamando a si mesmo
class Recursao {
public static void repercusao(int contador){
System.out.print(contador);
contador = contador--1
if (contador == 0) {
return;
}
//chamando novamente
repercusao(contador);
}
public static void main(string[] args) {
repercusao(10);
}
}
EXEMPLO
146
decrementando -1 da variável contador. Mas, caso não tivesse um
critério de parada, esta recursão na linha 9 com a condição conta-
dor==0, a execução se estenderia até o travamento do computador,
pois ele chamaria a si mesmo perpetuamente, como se fosse um
loop. Porém, dentro dele, deve ter um critério de parada geralmente
com o comando return, como na linha 10.
Resumindo, a recursão nada mais é do que uma função que chama
ela mesma, recebendo um argumento e repassando o mesmo argu-
mento tratado, com um critério de parada dentro da própria função.
Figura 2 - Exemplo de recursão
Fonte: Comitê Editorial Digital Pages (2019).
É importante destacarmos que o fatorial é um exemplo clás-
sico de recursão, onde seu critério de parada, geralmente, vem no
início da função e a chamada de si mesmo, ou recursão, acontece na
mesma linha do return, como na linha 7. O resultado da nova cha-
mada é multiplicado ao argumento inicial decrementando 1.
Técnicas de ordenação
A ordenação é um dos métodos de Estrutura de Dados que visa or-
denar dados desordenados. E, para isso, existem muitas formas:
class Fatorial {
public static int fatorial(int n){
if (n== 1) {
return n;
}
Return (fatorial(n-1)*n);
}
public static void main(Sstring[] args) {
System.out.println(factorial(6));
}
}
147
• selection sort;
• insertion sort;
• quick sort;
• merge sort;
• heap sort;
• radix sort (LSD);
• radix sort (MSD);
• std::sort (intro sort);
• std::stable_sort (adaptive merge sort);
• shell sort;
• bubble sort;
• cocktail shaker sort:
• gnome sort;
• bitonic sort;
• bogo sort.
O que diferencia esses métodos é o tempo para ordenação e
a forma de troca dos dados. Alguns usam recursão, outros somente
loops ou a mescla entre eles. Como você pode perceber, não existe
uma forma única de resolver um problema, porém sempre deve ser
observado se o algoritmo se encaixa nas regras de negócio. Pensan-
do nisso, abaixo você irá conhecer os mais tradicionais: bubble sort,
insertion sort, e quick sort. Vamos lá!
Método Bubble Sort
O método Bubble Sort, ou “método bolha”, tem por objetivo a ordenação
dos dados a partir de 2 em 2, trocando sempre o maior valor pelo menor
148
valor e percorrendo o vetor por diversas vezes. O nome deste algoritmo
remete à ideia de que os dados se comportam como “bolhas” dentro de
um tanque e, embora seja simples, não é tão eficiente quanto outros algo-
ritmos de ordenação quando os dados estão totalmente desorganizados.
Na classe Bubble Sort, por exemplo, temos a função main na
linha 24, que inicia um vetor com valores desordenados e envia esse
vetor para o método Bubble Sort na linha 25.
Assim, ao chamar o método na linha 25, inicia-se a ordena-
ção a partir da linha 2 onde este método possui dois loops: um que
percorre todos os índices e o segundo loop que percorre até o pe-
núltimo. Perceba que a troca dos dados é feita após a verificação da
linha 5 no if que verifica se o valor do índice -1 é maior que o valor
do índice do atual. Ou seja, a troca é feita de 2 em 2.
Após a ordenação, o programa volta para a função main, execu-
tando a linha 26, chamando outra função que é a imprimir, e execu-
ta-se o bloco da linha 16, fazendo uma impressão for-each em tela do
vetor ordenado, diferente de uma variável que precisa ser uma variável
global. Observe que, ao executar o código, não precisamos receber o ve-
tor ordenado, ele se ordena pelo endereço de memória, fazendo apenas
a referência dele, conseguimos fazer a impressão do vetor ordenado.
Figura 3 - Exemplo de Método Bubble Sort
Fonte: Comitê Editorial Digital Pages (2019).
package bubblesort;
public class BubbleSort {
public static void main(String args[]) {
int[] v = {5, 2, 4, 3, 0, 9, 7, 8, 1, 6};
BubbleSort bs = new BubbleSort();
bs.ordenar(v);
for(int num : v) {
System.out.print(num + " ");
}
}
public void ordenar(int[] v) {
// for utilizado para controlar a quantidade de
vezes que o vetor será ordenado.
for(int i = 0; i < v.length - 1; i++) {
// for utilizado para ordenar o vetor.
for(int j = 0; j < v.length - 1 - i; j++) {
/* Se o valor da posição atual do vetor for
maior que o proximo valor,
então troca os valores de lugar no vetor. */
if(v[j] > v[j + 1]) {
int aux = v[j];
v[j] = v[j + 1];
v[j + 1] = aux;
}
}
}
}
}
149
EXEMPLO
Método Insertion Sort
O método Insertion Sort é um algoritmo de ordenação que per-
corre o vetor da esquerda para a direita, trazendo os menores
valores para a esquerda e, assim, inicia as trocas a partir do se-
gundo elemento.
Considere um vetor de 5 posições vetor[]=new [] {7,6,4,2,9};
1º - 7-6-4-2-9 – desordenado;
2º - 6-7-4-2-9 – a primeira troca do segundo elemento pelo
primeiro;
3º - 4-6-7-2-9 – a terceira troca, empurrando os elementos maio-
res para a direita e os menores para a esquerda;
4º - 2-4-6-7-9 – a quarta troca, empurrando os elementos maio-
res para a direita e os menores para a esquerda, fazendo o 2 ser o
primeiro item do vetor;
5º - 2-4-6-7-9, no caso do 9, como é um elemento maior que o 7,
ele permanece no mesmo local, retornando o vetor ordenado.
Observe no código a seguir que, na linha 23, o método main
inicia um vetor desordenado e passa, via parâmetro, para a função
insertion sort na linha 2. Assim, o primeiro loop percorre o vetor e o
segundo percorre da esquerda para a direita, ordenando os valores.
Após a ordenação, chama-se o método imprimir para mostrar o ve-
tor ordenado.
150
Figura 4 - Exemplo de Método Insertion Section
Fonte: Comitê Editorial Digital Pages (2019).
Método Selection Sort
O método Selection Sort busca no vetor o menor valor entre todos
os elementos e o insere na primeira posição após a verificação.
Esse tipo de método é diferente do Bubble Sort, que faz a troca pelo
maior ou menor por meio do insert de forma ordenada para todos
os elementos.
Figura 5 - Exemplo do método Selection Sort
Fonte: Comitê Editorial Digital Pages (2019).
1 class BubblesortExemplo {
2 public static void bubblesort(int[] array) {
3 for(int i=0; i < array.length; i++){
4 for(int j=1; j < (array.length-1); j++){
5 if(array[j-i] > array[j]){
6 int aux = array[j-1];
7 array[j-1] = array[j];
8 array[j] = aux;
9 }
10
11 }
12 }
13
14 }
15
16 private static void imprimir(int vetor[])
17 {
18 for(int aux: vetor)
19 {
20 System.out.println(aux);
21 }
22 }
23 public static void main(String[] aux){
24 int array[] = new int[200,0,-20,-11];
25 bubblesort(array);
26 imprimir(array);
27 }
28 }
public static void selectionSort(int[] vetor) {
for (int i = 0; i < vetor.length; i++) {
int i_menor = i;
for (int j = i + 1; j < vetor.length; j++)
if (vetor[j] < vetor[i_menor])
i_menor = j;
int aux = v[i];
vetor[i] = v[i_menor];
vvetor[i_menor]= aux;
}
}
151
Estruturas Lineares
As estruturas lineares são estruturas que agrupam dados em se-
quência ou em uma ordem pré-determinada. Por exemplo, em se
tratando da ordenação em Lista sequencial FIFO (First in First Out – o
primeiro que entra é o primeiro que sai) e LIFO (Last in First Out – o
último que entra é o primeiro que sai), porém sua retirada pode ser
tanto em sequência como de forma desordenada, por prioridade etc.
Dessa maneira, as estruturas lineares mais utilizadas são:
lista, pilha e fila que implementam o vetor dentro de suas estruturas
de acordo com seu comportamento. Perceba que o armazenamento
tem sua implementação diferenciada no código.
É importante destacarmos que, quando se fala em Listas,
no geral, implementa-se a estrutura primitiva do vetor, que é uma
estrutura sequencial indexada com o mesmo tipo. E quando se im-
plementa uma estrutura em vetor por herança, absorve a maior ca-
racterística do vetor à sua limitação quanto ao tamanho.
Assim, a limitação, quanto ao tamanho do vetor, faz com que
as estruturas possam ser divididas em duas formas de alocação: a es-
tática e a dinâmica. A alocação estática faz com que qualquer estrutu-
ra tenha um tamanho máximo para inserção de elementos, forçando
o programador a usar soluções paliativas para manter a estrutura em
vetor. Já a alocação dinâmica é uma estrutura que não implementa o
vetor, porém implementa o conceito de No, onde é possível alterar
sua estrutura de acordo com a necessidade do(a) programador(a).
Dito isto, a partir de agora, vamos detalhar as estruturas li-
neares mais utilizadas: a lista, a pilha e a fila. Vamos lá!
Listas
Inicialmente, é necessário destacarmos que as listas são estruturas
que alocam, de forma sequencial, os dados. Temos as listas estáti-
cas, que implementam o vetor, e a lista dinâmica, que são as listas
ligadas e duplamente ligadas, tratando-se do Java Collections da
interface List.
152
EXEMPLO
Lista Estática
A Lista Estática implementa o vetor, podendo ser de forma estrutu-
rada ou objeto. Tanto uma quanto a outra são fixas a seus tamanhos
e requerem soluções paliativas após o tamanho máximo ser utiliza-
do. A grande vantagem da lista estática ou do uso do vetor na estru-
tura é a capacidade de indexação dos elementos, fazendo com que a
individualização de cada um seja mais efetiva. Diferente de outras
estruturas em que é necessário navegar na estrutura para acessar
os valores.
Observe o código a seguir:
System.out.println( vetor[11]); //
Neste código a partir do índice 11, busca-se o valor do vetor na po-
sição 11.
Lista Ligada
É um tipo de dado abstrato que se comporta como o conceito de
Lista, mas implementa o Objeto No ou Node.
◼ No ou Node
O No ou Node é um objeto que faz autorreferência, tendo um for-
mato em que pode ter de 0 a n itens lincados.
Em relação ao conceito de No, entende-se que os itens podem
ser inseridos e removidos em qualquer local da estrutura, podendo
aumentá-los e diminuí-los conforme a necessidade. Sua navegação
ocorre por meio da referência do objeto denominado próximo, aces-
sado por meio do método getProximo().
153
O construtor na linha 6 é uma função que recebe como parâmetro o valor
a ser alocado e a referência do objeto que será próximo do objeto atual.
Na linha 3, armazena-se o próximo item que, para acessá-lo,
é preciso usar o método getProximo() e, para setar a referência do
próximo No, usa-se o método setProximo(). Além disso, as funções
setValor() e getValor() armazenam os valores a serem alocados.
Dessa maneira, ao tratar-se da Lista ou qualquer outra estru-
tura de dados, deve-se, obrigatoriamente, implementar o código do
No para ser referenciado na estrutura.
Figura 6 - Implementação do código No
Fonte: Comitê Editorial Digital Pages (2019).
É importante destacarmos que a Lista Ligada é uma estrutura
que implementa o Node e cada elemento dela consiste em um Node,
que possui em si mesmo a referência de seu próximo, possibilitando
uma capacidade N de armazenar e diminuir itens conforme a neces-
sidade. Possui como principal objeto o próximo que armazena o en-
dereço do próximo objeto e o valor que armazena os valores alocados.
CURIOSIDADE
class No {
private No proximo;
private int valor;
No(int v, No n) {
valor = v;
proximo = n;
}
public int setValor() {
return valor;
}
public int getValor() {
return valor;
}
public void setProximo(No aux) {
proximo = aux;
}
public No getProximo() {
return proximo;
}
}
154
Figura 7 - Implementação do Node na estrutura de Lista ligada
Fonte: Comitê Editorial Digital Pages (2019).
Para que você entenda o que estamos abordando, sugiro
que observe a seguinte estrutura de Lista Ligada apresentada na
figura 8.
Figura 8 - Estrutura de Lista Ligada
Fonte: Comitê Editorial Digital Pages (2019).
O primeiro No é o início do No, trazendo em si a referência
do próximo No, que seria o que armazena o -1. Para percorrer os
nos, usa-se o método getProximo(), e, no seu retorno, usa-se o ge-
tProximo() do outro No. Assim, chega-se ao ultimoNo que, ao usar o
getProximo() do ultimoNo, ele retornará null, caracterizado, assim,
por ser o último No da estrutura.
◼ Inserção
Inserção dos elementos 5 e 7 nas extremidades ocorre após, primei-
ramente, os Nos com os valores serem criados e depois inseridos nas
extremidades. No caso do 5 que está alocado no início, apenas irá
setar como seu próximo, o primeiroNo, fazendo, desse modo, o se-
gundo No da estrutura. No exemplo da inserção no último No, temos
ele setando o seu objeto próximo para o novo No, tornando o novo
No o último, e ele o penúltimo No da lista.
valor próximo
nullprimeiroNo
0 -1
primeiroNo
10 20
null
ultimoNo
155
Figura 9 - Inserção dos elementos 5 e 7 nas extremidades
Fonte: Comitê Editorial Digital Pages (2019).
◼ Busca de um elemento
A busca em uma estrutura de Lista Ligada se dá pela navegação da
função getProximo(), que sempre irá fornecer o endereço do próxi-
mo No e, ao chegar no final, ele irá retornar null. Assim, o algoritmo
faz um loop entre os getProximo() e confere o valor do No aux com
o valor pesquisado, retornando true, independente se localizou ou
não o item na lista.
Figura 10 - Busca de um elemento em uma estrutura de Lista Ligada
Fonte: Comitê Editorial Digital Pages (2019).
◼ Remoção de um elemento
Para remover um elemento no No, usa-se o mesmo conceito da
busca, ou seja, um loop para achar o No que contém o valor a ser
removido. Assim, ao achar o No e criar um objeto temporário cha-
mado atual, é necessário fazer uma busca pelo seu No anterior. E, ao
localizar, armazená-lo em aux e fazer a referência de aux na função
setProximo() no valor do próximo de atual. Perceba que o No que ar-
mazena o 0 é lincado ao No que armazena o 10 e, automaticamente,
o No que armazena o -1 é retirado da Lista.
05 -1 10
null
primeiroNo
20
ultimoNo
null
0 -1 10 75
primeiroNo
20
ultimoNo
0 -1 10 20
null
primeiroNo ultimoNo
aux
156
Figura 11 - Remoção de um elemento no No
Fonte: Comitê Editorial Digital Pages (2019).
Lista Duplamente Ligada
A Lista Duplamente Ligada é uma evolução da Lista Ligada, pois a
mesma apenas armazena o endereço do seu próximo e, caso ultra-
passe para o próximo na navegação, não consegue fazer o caminho
de volta entre os Nodes.
O armazenamento do endereço se dá nos objetos próximo e
anterior do Node, como nas figuras a seguir.
Figura 12 - Armazenamento do endereço
Fonte: Comitê Editorial Digital Pages (2019).
Figura 13 - Exemplo de referenciação
Fonte: Comitê Editorial Digital Pages (2019).
-15 100 20 7
null
atual
primeiroNo ultimoNo
-15 100 20 7
null
atual
aux
primeiroNo ultimoNo
0 105 20 7
null
aux
ultimoNoprimeiroNo
próximovaloranterior
null
null
Node
nullnull
Primeiro nó Útimo nó
0 -1 10 20
157
◼ Inserção de um elemento
A inserção dos valores 5 e 7 nas extremidades se dá, primeiramente,
pela criação do Node comos valores e, depois, são apontadas suas
referências aos elementos da estrutura.
Figura 14 - Inserção de um elemento
Fonte: Comitê Editorial Digital Pages (2019).
◼ Remoção de um elemento
Na primeira estrutura do algoritmo, é percorrido o elemento que se
busca apagar e se armazena o endereço do No no objeto atual. Após
localizar o objeto a ser removido, faz-se uma nova varredura, per-
correndo o objeto anterior ou atual, e armazena a referência em aux.
Dessa maneira, ao localizar o aux, coloca-se o endereço do próximo
No do atual em aux, liga-se o aux com o próximo do atual e, auto-
maticamente, o atual sai de memória.
Figura 15 - Remoção de um elemento
Fonte: Comitê Editorial Digital Pages (2019).
null null
nullnull
Primeiro nó
Primeiro nó
Útimo nó
Útimo nó
5
5
-1
0
10
-1 20
20 7
10 7
null
null
null
null
null
null
Primeiro nó
Primeiro nó
Atual
Aux
Aux
Atual
Excluído -1
Primeiro nó
Útimo nó
Útimo nó
Útimo nó
5
5
5
-1
0
0
10
-1
-1
20
20
20 7
10
10
7
7
158
Código Lista Duplamente Ligada
A Lista Duplamente Ligada segue alguns padrões da Lista Ligada.
Primeiramente, usa-se a classe Node, onde contém as referências
anteriores e posteriores do No atual.
Na linha 3, o objeto autorreferenciável próximo aloca o en-
dereço do próximo No e o objeto autorreferenciável anterior aloca o
endereço do No anterior para o primeiro No de uma estrutura. Dessa
maneira, o item anterior será null enquanto o último No e o endere-
ço do próximo será null.
Figura 16 - Código da Lista Duplamente Ligada
Fonte: Comitê Editorial Digital Pages (2019).
Lista Collection
A lista desenvolvida no formato estático implementa a interface
Collection, no código a seguir (linha 3), e importa o pacote util.Col-
lection que contém as classes que podem ser implementadas da Col-
lection como: Vector, ArrayList e LindekList, os quais fazem parte do
formato sequencial ou linear de alocação dinâmica.
class Node {
private Node proximo, anterior;
private int valor;
Node(int auxvalor, Node auxprox, Node auxant) {
valor = auxvalor;
proximo = auxprox;
anterior = auxant;
}
public void setValor(int aux) { valor = aux; }
public int getValor() { return valor; }
public void setProximo(Node aux) { proximo = aux; }
public Node getProximo() { return proximo; }
public void setAnterior(Node aux) { anterior = aux; }
public Node getAnterior() { return anterior; }
}
159
Figura 17 - Lista Collection
Fonte: Comitê Editorial Digital Pages (2019).
160
DICA
Dessa maneira, na linha 11, utiliza-se o LinkedList e na ins-
tanciação poderia ser usado qualquer Collection sequencial, como
Vector ou ArrayList, seguindo o código, usa-se o <Integer>. Com esse
formato, coloca-se o tipo do primitivo ou objeto que irá ser utilizado
na estrutura e, neste caso, o objeto col recebe somente valores do
tipo inteiro.
Quando se usa a interface Collection, as funções são padroni-
zadas independente da estrutura utilizada, como:
• Add – insere elementos;
• Remove – remove elementos;
• Constains – busca elementos na estrutura.
No código visto nas linhas 9 e 11, faz-se a instanciação. Já nas linhas
13 e 14 são inseridos 100 valores na estrutura dentro do loop e a im-
pressão da estrutura é feita nas linhas 16 e 20, com a impressão via
for-each na função “imprimir”.
Pilhas
A TAD (Tipo Abstrato de Dados) Pilha é uma variação da Lista Linear
que possui características semelhantes a uma “pilha” do mundo
real. Ela armazena os valores inseridos no formato de LIFO - Last in
First Out (O último que entra é o primeiro a sair).
Como uma “Pilha de livros”, onde o primeiro a ser empi-
lhado é o último a ser acessado, segue um exemplo de empilha-
mento de dados.
161
Diagrama 1 - Exemplo de Empilhamento de Dados
Fonte: Comitê Editorial Digital Pages (2019).
A Pilha possui as seguintes operações:
• push( X) – aloca ou adiciona o valor na estrutura Pilha;
• pop() – desempilha ou remove o valor do topo da Pilha, retor-
nando o valor pela função;
• peek() – acessa o topo da Pilha sem removê-lo;
• isEmpty() – retorna o estado atual da Pilha se possuir itens
alocados false, mas, se não possuir, retorna true;
• isFull() – retorna true se a Pilha está com todos os espaços
alocados, porém, para Pilhas que são de alocação dinâmica,
não possui a função; isFull(), – somente as Pilhas que imple-
mentam o vetor.
162
Código da Pilha de Alocação Estática
Figura 18 - Código da Pilha com Alocação Estática
Fonte: Comitê Editorial Digital Pages (2019).
Observa-se na linha 5 o Construtor que recebe o tamanho máxi-
mo da Pilha para poder colocar os itens e, a partir daí, ele implementa
um vetor com esse tamanho máximo e, a partir do momento que ele
tem tamanho máximo, ele tem aquela quantidade de itens para poder
colocar dentro do nosso vetor que está sendo implementado na Pilha.
Já na linha 10 temos a função isEmpty que verifica se o topo =
-1. Isso significa que se o topo for -1 ela está vazia, então vai retornar
true. Na linha 14 temos os isFull, ele verifica se a Pilha está cheia, ou
seja, se o topo é equivalente ao número da quantidade de elementos do
vetorPilha.length (o atributo que retorna o tamanho máximo do vetor).
O peek nada mais é do que o retorno do valor que está no índice topo
da pilha na linha 18. Por outro lado, o método push tem a função de
adicionar itens dentro da pilha, incrementando o topo e atribuindo
valor na Pilha no índice topo. Já o método pop é a função para remo-
ver itens da Pilha, fazendo decremento no topo.
1 public class Pilha {
2 private int vetorPilha[];
3 private int topo;
4 // Inicia a pilha com Tamanho max
5 public Pilha(int max) {
6 vetorPilha = new int[max];
7 topo = -1;
8 }
9 // Verificação se existem Valores na Pilha
10 public boolean isEmpty() {
11 return (topo == -1);
12 }
13 // Retorna se a pilha está cheia ou não
14 public boolean isFull() {
15 return (topo == vetorPilha.length -1);
16 }
17 // Retorna o topo da pilha
18 public int pick() {
19 return vetorPilha[topo];
20 }
21 //Adicionar valor na pilha
22 public void push(int j) {
23 if (isFull()) {
24 topo++;
25 vetorPilha[topo] = j;
26 }
27 }
28 //Excluir valor da pilha
29 public int pop() {
30 return vetorPilha[topo--];
31 }
32 }
DEFINIÇÃO
163
Figura 19 - Código que usa a classe Pilha como estrutura de dados para ser implementada
Fonte: Comitê Editorial Digital Pages (2019).
Filas – implementações
As Filas são estruturas de dados ou TADs que armazenam no for-
mato FIFO. First-in-first-out, ou simplesmente FIFO, significa que o
elemento que é inserido é o primeiro a ser removido, analogamente
a uma fila de banco ou qualquer fila do mundo real.
Tabela 1 - A estrutura Fila
Fonte: Comitê Editorial Digital Pages (2019).
class PrjPilha {
public static void main(String[] args){
Pilha pilha = new Pilha(5);
pilha.push(10);
pilha.push(20);
pilha.push(30);
pilha.push(40);
System.out.println("valor do jogo"+pilha.pick());
System.out.println("Está Vazia:"+pilha.isEmpty());
System.out.println("Está cheia:"+pilha.isFull());
while (!pilha.isEmpty()) {
System.out.print(pilha.pop());
}
}
}
164
Os comportamentos padronizados da Fila são:
• Queue(objeto) – insere o elemento objeto ou valor na Fila;
• deQueue() – remove os elementos da Fila conforme ordem
de entrada;
• Peek() – assim como a Pilha, a Fila trabalha o conceito de
topo, que nada mais é do que o elemento em evidência na es-
trutura (no caso da Fila, é o primeiro elemento que foi inseri-
do, enquanto, na Pilha, é o último elemento que foi inserido);
• isEmpty() – é a função que retorna true para uma estrutura
vazia e false para uma estrutura que já contém pelo menoso
primeiro elemento inserido;
• isFull() – é uma função que retorna booleano, ou seja, true
para uma estrutura completa ou cheia e false caso possa inse-
rir mais elementos, considerando uma Fila de alocação está-
tica que possui tamanho fixo.
O código a seguir demonstra a aplicação do vetor[] no com-
portamento de uma Fila que implementa o formato FIFO. Além
disso, a classe FilaEstática é uma estrutura que dá permissão para
outros programas acessarem como objeto uma fila estática (estru-
tura com tamanho fixo) com tamanho passado via construtor. Per-
ceba que, na linha 7, tem-se o construtor que inicia a estrutura Fila
com o tamanho passado pelo(a) programador(a).
As funções isEmpty e isFull retornam resultados booleanos, respec-
tivamente, se a estrutura está vazia e se a estrutura está cheia.
A função peek retorna a posição do elemento corrente da es-
trutura, que é sempre a posição 0 do vetor, pois, tratando-se da
aplicação da FIFO, a posição mais importante da estrutura é o pri-
meiro elemento, como se vê na figura 20.
DICA
165
Figura 20 - Aplicação do Vetor
Fonte: Comitê Editorial Digital Pages (2019).
A função enQueue, ou enfileirar, recebe por parâmetro o
argumento valor que insere no vetor privado o elemento na po-
sição qtdElementos, variável que controla a posição de quan-
tidades de elementos na fila. Ele se inicia com -1 e, conforme
aumenta o número de elementos, incrementa e, quando remove
elementos, decrementa, controlando a quantidade de elementos
da Fila, o que o torna o principal parâmetro para saber se a Fila
está cheia ou não.
1 public class FilaEstatica {
2 private int[] vFila;
3 private int qtdElementos;
4
5 // Construção da fila Estática som tamanho fixo
6 public FilaEstatica(int tamanho) {
7 vFila = new int[tamanho];
8 qtdElementos = -1;
9 }
10 //Verificação se está vazia
11 public boolean isEmpty() {
12 return (qtdElementos == -1);
13 }
14 //Verificação se está cheia
15 public boolean isFull() {
16 return (qtdElementos == vFila.length - 1) ? true : false;
17 }
18 // Topo da fila
19 public int peak() {
20 return (!isEmpty()) ? vFila[0] : -1;
21 }
22 //Adicionar elementos FIFO
23 public void enQueue(int valor) {
24 if (!isFull()) {
25 qtdElementos++;
26 vFila[qtdElementos] = valor;
27 }
28 }
166
Figura 21 - Função enQueue
Fonte: Comitê Editorial Digital Pages (2019).
A função deQueue, ou desenfileirar, tem por responsabilidade
o comportamento de retirar o elemento do vetor Fila e fazer com
que todos os elementos avancem uma posição por meio do loop da
linha 35. Já na linha 33, retorna o valor do primeiro elemento e in-
crementa a quantidade de elementos da Fila na linha 34.
A impressão da Fila se dá na função imprimir, que se refere aos
valores da linha 47 no loop for-each. A classe Prj_FilaEstatica aplica a
estrutura FilaEstatica e utiliza como estrutura de dados, assim, na linha
5 se faz a instanciação, das linhas 6 a 9 se faz a inserção de elementos na
Fila e na linha 11 se faz uma remoção e a impressão do valor da Fila.
Na inserção, são adicionados os elementos -1, 25, 5 e 7, res-
pectivamente. No caso da organização FIFO, o primeiro elemento
se torna o -1 e, ao verificar seu peek ou removê-lo, será trabalhada
sempre a primeira posição do vetor que no caso é 0 e está armaze-
nando nesse primeiro momento o -1.
Para a remoção, chama-se a função deQueue, na linha 11. Pri-
meiro, remove-se o elemento dentro da estrutura, juntamente com
o incremento da quantidade de elementos da Fila e, após isso, retor-
na no System.out.println() o número -1. Assim, automaticamente, a
estrutura dos dados muda dentro da Fila, mudando de -1, 25, 5 e 7
para 25, 5 e 7, tornando agora o 25 o valor da primeira posição.
30 //Remover elementoi FIFO
31 public int deQueue() {
32 if (!isEmpty()){
33 int removido = vFila[0];
34 qtdElementos--;
35 for (int i = 0; i < qtdElementos; i++)
36 vFila[i] = vFila[i + 1];
37 return removido;
38 } else {
39 return -1;
40 }
41 }
42 //Impressão dos valores da Fila
43 public void imprimir()
44 {
45 for(int aux: vFila)
46 System.out.println(aux);
47 }
48 }
167
DICA
Figura 22 - Função deQueue
Fonte: Comitê Editorial Digital Pages (2019).
De maneira geral, nessa classe que implementa a alocação
estática existem algumas desvantagens: uma delas é a questão de
inserção de elementos ultrapassarem o tamanho máximo do estipu-
lado na criação. Nesse caso, uma solução paliativa seria armazenar
os itens em um objeto auxiliar e reinstanciar o objeto atual com um
valor máximo maior em tempo de execução e, assim, inserir nova-
mente na estrutura os valores antigos e novos. No entanto, torna-se
ineficiente, pois se trata de mais processamento para a aplicação e,
dependendo do tamanho da nova estrutura, demora demais.
Para aplicações de pequeno porte, que não exijam volumes altos de
informação, pode ser trabalhada a Fila estática. Uma grande vanta-
gem de trabalhar com o formato estático, tanto Pilha quanto Fila, é
a própria aplicação do vetor que torna o código mais simples de ser
implementado e reconhecido.
168
Collection Class Queue – Fila dinâmica
A Fila dinâmica é uma estrutura de dados que implementa os concei-
tos da Fila estática. A diferença se dá, conceitualmente, no tamanho
máximo: como na estática, temos o tamanho fixo da estrutura na
dinâmica. Nela os elementos podem ser inseridos e removidos sem
a estrutura ter um tamanho fixo, tornando a Fila dinâmica em um
formato melhor para trabalhar em tempo de execução, isso quando
não se sabe a quantidade de elementos que deverão ser inseridos.
Além disso, é importante saber que a interface Queue está
dentro da API Collection, implementando a alocação dinâmica com
Node; possui diversas funções já embutidas nela, por exemplo, add,
remove e peek – funções que poderiam ser implementadas na mão,
mas, nesse formato, já estão prontas na interface, otimizando a
produtividade ao programar. Nos imports desse projeto temos, nas
linhas 2 e 3, a primeira referência à Fila dinâmica do projeto, onde
se instancia, na linha 8, a interface Queue com o LinkedList e com o
tipo Integer, que armazena números inteiros.
Das linhas 11 a 13, adicionam-se os valores 1, 2 e 3 por meio da
própria função add da LinkedList. Assim, a linha 15 remove um item
da Fila, a linha 16 retorna a quantidade de elementos dentro da Fila
e a linha 17 retorna o valor booleano para a busca do valor 1 dentro da
Fila com a função contains. É importante destacarmos que, no uso da
Fila, são demonstradas, no programa, duas formas de navegar nos
elementos dela nas linhas 19 a 22 e 24 a 29.
Figura 23 - Interface Queue
Fonte: Comitê Editorial Digital Pages (2019).
169
Na primeira solução, que está nas linhas 19 a 22, utili-
za-se a função toAr- ray(), que retorna um vetor da fila e o ar-
mazena no vaux[] e, no loop, faz a varredura pelo vaux. Nessa
solução ocorre a transformação de um objeto da API em vetor e
pode-se manipulá-lo.
Já a segunda solução, a mais ideal, é usar o Padrão de Proje-
to Iterator. Os Designers Patterns (Padrões de Projeto) são soluções
padronizadas para problemas recorrentes da programação. Em al-
gumas linguagens, deve-se implementar do zero e seguir apenas o
conceito, mas, no caso do Java e de algumas outras linguagens, há o
código pronto, sendo um deles o Iterator.
O Iterator é um padrão de projeto que, por meio do conceito de
orientação a objetos, encapsula a estrutura de dados como uma co-
leção de elementos e permite percorrer todos da estrutura, ou seja,
um padrão de solução para navegação em estruturas que não per-
mitem indexação, como o vetor, que, ao informar a posição, retorna
o elemento vetor[posição]. O for-each é uma das variações de Itera-
tor, porém possui algumas funções queprecisam ser invocadas para
uso, como na linha 1 se importa o Iterator.
Observe que, na linha 24, o Iterator deve ser instanciado com
o nome do objeto que será percorrido com o “.iterator()”, onde as
classes da API Collection implementam essa solução. Após ser ins-
tanciado, cria-se um objeto Iterator da estrutura de dados com o
nome filaIterator.
No loop da linha 27, a filaIterator.hasNext() retorna true, en-
quanto houver elementos para navegação na estrutura, e seu acesso
é por meio da função. next(), que retorna o elemento atual do objeto
e aponta para o próximo da estrutura, caso houver; e retorna false
caso não tenha, encerrando assim o laço de repetição, como se ob-
serva na figura 24.
DEFINIÇÃO
170
Figura 24 - O padrão Iterator
Fonte: Comitê Editorial Digital Pages (2019).
Árvores e suas generalizações
A Árvore é um tipo de estrutura abstrata de dados que comporta-se de
forma semelhante a uma árvore, empregando conceitos de raiz e folhas.
Também emprega em si fortemente o conceito de Node, ou Nó, como
elemento em nível hierárquico. Lembre-se que, na hierarquia, comu-
mente os nós podem assumir representações como pai, filhos e irmãos.
O principal Nó da estrutura da Árvore é a raiz, o Nó inicial que
dá acesso aos demais elementos da estrutura, ou seja, é o ponto ini-
cial em uma navegação e, a partir dele, pode-se navegar nos filhos
e conjuntos de pais e filhos, chamados de conjuntos não vazios ou
subárvores, como Node 1, Node 2 e Node n.
Diagrama 2 – A estrutura Node
Fonte: Comitê Editorial Digital Pages (2019).
NODE 1 NODE 2 NODE N
NODE RAIZ
171
De acordo com critérios de armazenamento, as árvores po-
dem formar ordenação de valores e elementos que façam com que
alguns Nós sejam armazenados mais à direita ou à esquerda. No
diagrama a seguir, tem-se a árvore Exemplo e inicia-se da Raiz de
árvore que chama-se Node Raiz. Observe!
Diagrama 3 – Node Raiz
Fonte: comitê editorial Digital Pages (2019).
Como você pode perceber, os filhos de Node Raiz são Node 1 e Node
2; os filhos de Node 1 são Node 3 e 4 e, obviamente, o Node 1 é pai desses
Nodes e o filho de Node 2 é o Node 5, tornando o Node 2 pai de Node 5.
A leitura dessa Árvore sempre indica o nome do pai e o nome dos
filhos e vice-versa.
Observe que, após os filhos dos filhos, não há mais Nodes.
Nessa situação, ocorre o conceito de folha, nomenclatura para as
extremidades da estrutura da Árvore, onde muitos algoritmos utili-
zam o Node folha para fazer aplicações dentro da Árvore.
Filho de Node 2
Node
RaizRAIZ DA ÁRVORE
Fo
lh
as
FILHOS DE NODE RAIZ
FILHOS DE NODE 1
Node 1
Node 3 Node 4 Node 5
Node 2
DICA
172
As Árvores podem ser classificadas em hierárquica, diagra-
ma de inclusão, parênteses aninhados e representação encadeada,
como se observa nos diagramas 4, 5, 6, 7 e figura 25.
Diagrama 4 - Árvore Hierárquica
Fonte: Comitê Editorial Digital Pages (2019).
Diagrama 5 – Exemplo de Inclusão
Fonte: Comitê Editorial Digital Pages (2019).
Figura 25 – Prênteses aninhados
Fonte: Comitê Editorial Digital Pages (2019).
A
B C D
E F G H
I
A
B
HD I
E
G
C
F
(A (B (E)) (C (F) (G)) (D (H (I))))
173
Diagrama 6 - Adaptação do formato Árvore pra representação aninhada
Fonte: Comitê Editorial Digital Pages (2019).
Diagrama 7 - Adaptação do formato Árvore para representação encadeada
Fonte: Comitê Editorial Digital Pages (2019).
Pode-se observar que a representação do diagrama 5, passando para
o formato da representação encadeada, ficaria como o diagrama 6.
12
10 15
1613
12
10
13 16
15
CURIOSIDADE
174
Terminologia da estrutura Árvore
As Árvores possuem várias propriedades e características que mui-
tos algoritmos utilizam para poder efetuar operações como bus-
ca, inserção, remoção, ordenação e classificação. Dito isto, abaixo
abordaremos suas principais terminologias.
Nó
Estrutura referenciável que armazena valores dentro de uma árvo-
re, a qual possui apenas um pai, podendo possuir vários “irmãos”.
Ela nunca apresenta dois pais ou ligação com seu irmão. Pensando
nisso, abaixo listamos suas principais terminologias.
• Arco – também chamado de vértice ou ligação, é o caminho
entre dois Nós pais até os filhos.
• Raiz – é o primeiro elemento Node ou Nó, que dá acesso a to-
dos os elementos da árvore.
• Altura ou profundidade – é o nível do Nó folha ou terminal,
somando +1.
• Chave – é denominado chave o elemento que propõe valor ao
elemento Nó. Em diversas situações a própria chave do Nó é
seu valor.
• Floresta – conjunto de 0 ou mais árvores.
• Sub árvore – são conjuntos de Nós pais após a Raiz, poden-
do ser consideradas quaisquer Nós contendo sequência de
outros Nós.
• Pai – é o Nó que possui filhos ou irmãos. Assim, não sendo
Raiz, não poderá ser folha, mas, sendo Raiz e não tendo filhos,
não poderá ser pai, pois ele mesmo é a folha.
• Filho – é o Nó que possui pai, podendo possuir irmãos ou ser
um Nó folha.
175
• Rótulo – nome do Nó, geralmente seu dado, valor ou elemento.
• Grau de saída – número de filhos linkados a ele.
• Grau de uma árvore – é o maior número de filhos de um Nó
em toda a árvore.
• Folha – são Nós que não possuem filhos, ou seja, possuem
grau de saída 0, também chamados de Node terminal.
• Nó interno – todos os Nós que possuem filhos ou grau de saí-
da maior ou igual a 1, ou seja, que não são um Nó terminal.
• Caminho na árvore – é a interligação de Nós consecutivos,
que traz a relação de hierarquia, por exemplo: “é pai de” ou “é
filho de”. É o caminho do Nó Raiz até determinado Nó dentro
da Árvore.
• Comprimento do caminho – é o número de arco/vértice ou de
ligações entre um Nó e outro.
• Nível de um Nó – é a quantidade de arcos da Raiz até o Nó
específico; no caso, a Raiz tem 0 arcos, seu nível é 0; sendo
que um filho da Raiz possui um arco, então teria nível 1, e
assim sucessivamente.
Veja a seguir exemplos de valores para cada propriedade da Árvore.
Nó: são todos os elementos Nós da estrutura.
Raiz: A.
Chave: do Nó A o valor é A, do Nó B o valor é B e, assim, sucessiva-
mente, pois o exemplo não deu uma identificação específica para
cada Nó.
Grau de saída: A = 3, B = 1, C = 2, H = 1, E = 0 e I = 0.
EXEMPLO
176
Grau de uma Árvore: 3, pois A possui 3 filhos e os demais possuem 2
e 1, que são inferiores.
Folhas ou Nó terminal: E, F, G e I.
Nó interno: A, B, C, D e H, ou seja, Nós que não são folhas.
Comprimento do caminho: A até G é 2 e A até I é 3.
Nível de um Nó: A = 0, B = 1, G = 2 e I = 3.
Altura ou profundidade: Último nível, que é 3 + 1 = 4.
Node – Implementação dinâmica
Nos comandos que implementam a Árvore, tem-se a classe Node
como classe principal, por ser referenciável e ser moldada ao for-
mato de qualquer topologia, estrutura ou formas de organização.
Essa classe tem como regra armazenar o endereço do Node da
esquerda e da direita, formando, assim, uma ligação entre os filhos
para, a partir da ligação com eles, navegar por toda a estrutura.
No código a seguir tem-se o atributo-chave, que armazena a
informação ou dado do elemento Node como Object, fazendo com que
possa receber diversos valores. Porém, de forma geral, utiliza-se o
Integer ao invés de Object na linha 2, como nos exemplos anteriores.
Figura 26 - Atributo-chave
Fonte: Comitê Editorial Digital Pages (2019).
177
A seguir, há uma referência visual da classe Node, em que se
observa onde estão sendo feitas as referências à classe da direita, da
esquerda e à chave no meio.
Figura 27 - Classe Node
Fonte: Comitê Editorial Digital Pages (2019).
Vetor – Implementação sequencial
Nem todas as Árvores trabalham com conceito de Node. Em alguns
casos, pode-se implementar conceitos de Árvore de forma sequen-
cial, em que as chaves das Árvores são armazenadas em vetor e os
seus filhos representam sempre um múltiplo do seu índice dentro
do vetor. A fórmula para acesso aos filhos é utilizada por meio de
seus índices: para acessar os filhos à direita, 2 * índice, e para osfilhos à esquerda, 2 * índice + 1. Veja um exemplo!
Diagrama 8 - Implementação Sequencial
Fonte: Comitê Editorial Digital Pages (2019).
CHAVEESQUERDA DIREITA
1
3
65
2
4 null
vet[0]
1
vet[3]
4
vet[3]
4
vet[1]
2
vet[5]
6
vet[5]
6
vet[4]
5
vet[4]
5
vet[2]
3
vet[2]
3
vet[6]
null
vet[6]
null
178
Árvores binárias
Uma Árvore binária, ou binary tree, é uma aplicação de estrutura de
Árvore com regras específicas, a saber:
• o Nó só pode ter de 0 até 2 filhos;
• os dois filhos de uma Árvore binária são chamados de filho à
esquerda e filho à direita;
• as subárvores de uma árvore binária são subárvores da direita
ou da esquerda;
• o filho à esquerda de um Nó tem o valor da chave menor que
seu pai, já o filho à direita possui o valor da chave maior ou
igual a seu pai;
• uma Árvore vazia não contém elementos;
• todos os elementos da Árvore são Nós e arcos.
Diagrama 9 - Árvore Binária
Fonte: Comitê Editorial Digital Pages (2019).
5
7
64
3
2 8
Chave ou informação
Filhos à esquerda Filhos à direita
179
Tipos de Árvores binárias
◼ Árvore estritamente binária
Quando todos os Nós da Árvore possuem dois filhos ou zero sen-
do terminais.
Diagrama 10 – Exemplo de Árvore Estritamente Binária
Fonte: Comitê Editorial Digital Pages (2019).
◼ Árvore binária completa ou cheia
São Árvores nas quais o último nível é uma subárvore vazia e, por
esse motivo, possui a menor altura e todo o Nó termina no mesmo
nível. A altura dessa Árvore pode ser calculada por fórmula.
Diagrama 11 - Exemplo de Árvore Binária Completa
Fonte: Comitê Editorial Digital Pages (2019).
1
3
5
7
2
4
6
180
◼ Árvore binária balanceada
Esses tipos de Árvores são estruturas que têm como base a altura
das subárvores em 1 e, a cada operação na árvore, tem-se seu balan-
ceamento, tornando a altura da Árvore sempre em 1.
Diagrama 12 - Exemplo de Árvore Binária Balanceada
Fonte: Comitê Editorial Digital Pages (2019).
◼ Percurso na Árvore
Uma Árvore possui diversos tipos de elementos e organizações e,
dependendo da forma que se armazenam os valores, ela pode pos-
suir formatos distintos. Porém não há uma indexação para que sai-
bamos os elementos ou estruturas que ela possui, ou seja, apenas
por meio de uma visita ou percurso na estrutura pode-se saber
quais elementos e chaves (dados) ela apresenta.
Existem algumas formas de percorrer os elementos de uma
Árvore, que são:
• pré-ordem;
• in-ordem;
• pós-ordem.
A travessia em pré-ordem, ou pré-fixa, inicia-se pela Raiz e
percorre os Nós e subárvores à esquerda para depois fazer o mesmo
percurso. Observe!
1
3
65
2
4
181
Figura 28 - A travessia em pré-ordem
Fonte: Comitê Editorial Digital Pages (2019).
Já a travessia em in-ordem, ou intra-ordem, percorre a su-
bárvore à esquerda e, ao chegar na folha, volta para a Raiz e, em
seguida, visita a subárvore à direita, assim como o respectivo código
em Java para o percurso.
Figura 29 – A travessia in-ondem
Fonte: Comitê Editorial Digital Pages (2019).
B
C
E
D
F G
I
H
J
3º
6º
5º
4º
7º
8º
9º
2º
1º
B
C
E
D
F G
I
H
J
3º
6º
5º
4º
7º
8º
9º
2º
1º
182
Por fim, a travessia em pós-ordem, ou pós-fixa, percorre a
subárvore à esquerda, depois a subárvore à direita e a Raiz. Por últi-
mo, o respectivo código em Java para o percurso.
Figura 29a - A travessia em pós-ordem
Fonte: Comitê Editorial Digital Pages (2019).
Figura 29b - A travessia em pós-ordem
Fonte: Comitê Editorial Digital Pages (2019).
Árvores de busca
Uma das maiores aplicações e usos da estrutura Árvore é a bus-
ca. Há diversos algoritmos que organizam a estrutura de Árvores
para maximizar a varredura e melhorar a eficiência do percurso
na Árvore ao localizar um valor ou chave. A principal árvore para
essa função é a árvore binária de busca, a qual, a partir de seu
formato de organização, também possui variações. Veja a seguir
cada uma delas.
B
C
E
D
F G
I
H
J
3º
6º
5º
4º
7º
8º
9º
2º
1º
183
• Árvore 2-3: implementa a árvore binária, porém, na sua inser-
ção, em vez de armazenar apenas uma chave, pode armazenar
um intervalo de chaves ou duas chaves, fazendo com que o Node
possua dois atributos-chave para armazenar valores de um va-
lor inferior até um superior. Também nos links dos próximos
filhos não se tem apenas o direito e esquerdo, mas também o
filho do “meio”, tornando a árvore ternária, em vez de binária.
Lembre-se que, a cada inserção, um Nó filho pode possuir mais
de um valor na chave e, nesse intervalo, possuir filhos à direita,
esquerda e meio, tornando a busca mais veloz por não precisar
entrar em novos Nós pais para se aprofundar na Árvore.
• Árvore AVL: é uma árvore de busca balanceada, ou seja, bus-
ca minimizar as comparações entre seus Nós rotacionando ou
balanceando a árvore a cada inserção ou remoção, trazendo
sempre o melhor formato entre os Nós da estrutura e tornando
as alturas das subárvores à direita e à esquerda bem próximas.
• Árvore rubro-negra: possui diversas funções já embutidas
nela, como add, remove, contains, entre outras. Essas fun-
ções, que poderiam ser implementadas à mão, mas que nesse
formato já estão prontas na classe Tree, fazem com que se ga-
nhe produtividade ao programar.
• Árvore splay: é um tipo de árvore binária que transforma os
Nós acessados em Raízes por meio de rotações, fazendo com
que os elementos mais acessados tornem-se mais rapida-
mente localizáveis na busca.
Árvore binária e de busca
A Árvore de busca binária (abb) é uma aplicação da estrutura Ár-
vore que armazena suas chaves de forma que, a partir da Raiz,
todos os elementos menores que sejam alocados na subárvore es-
querda e todos os elementos maiores sejam alocados na subárvore
da direita, fazendo com que as buscas não dependam de algorit-
mos complexos, mas apenas de comparações entre maior ou me-
nor até o Nó terminal.
184
Caro(a) aluno(a), caso queira conhecer um pouco mais sobre Árvore
binária, sugerimos que acesse: https://joaoarthurbm.github.io/eda/
posts/bst/.
Caro(a) aluno(a), chegamos ao final da nossa jornada de estudos.
Neste material, aprendemos sobre o uso de algoritmos recursivos
e a sua implementação. Além disso, apresentamos o código de al-
guns desses algoritmos. Vimos também os algoritmos de ordenação
e como são implementados em vetores. Em seguida, entramos na
área de Estrutura de Dados, apresentando várias estruturas dife-
rentes e os conceitos usados na criação delas.
Ao longo do nosso material, você deve ter aprendido que os códigos
apresentados e as dicas de páginas que apresentam a implemen-
tação de várias dessas estruturas podem ajudar a entender melhor
como elas funcionam.
Dito isto, sugerimos que tente implementar esses algoritmos usan-
do a linguagem Java. Lembre-se que apresentamos a Classe Collec-
tion para algumas dessas estruturas.
Esperamos ter contribuído com o seu aprendizado. Sucesso!
DICA
SINTETIZANDO
185
Referências
UNIDADE 1
FUGERY, Sergio. Programação Orientada a Objetos: Conceitos e
técnicas. Editora Érica. 2014.
KAMIENSKI, C. A. . Introdução ao Paradigma de Orientação a Obje-
tos. Centro Federal de Educação Tecnológica da Paraíba - Diretoria
de Ensino - Coordenação de Informática, 1996.
LAFORE, R. Estrutura de dados e algoritmos em Java. Rio de Janei-
ro: Ciência Moderna, 2004.
PROGRAMAÇÃO Orientada a Objetos (Parte 12): a classe Object e
seus métodos. Postado por Carlos Emilio Padilla Severo. (10min.
11s.). son. color. port. Disponível em: <https://www.youtube.com/
watch?v=2ob3rYxpUlE>. Acesso em: 03 jun. 2020.
SANTOS, Rafael. Introdução à programação orientada a objetos
usando JAVA. 4. reimp. Rio de Janeiro: Elsevier, 2011.
UNIDADE 2
BATISTA, R. S.; MORAES, R. A. Introdução à programação orientada
a objetos. Teresina: Instituto Federal de Educação, Ciência e Tecno-
logia do Piauí - IFPI, 2013.
CARVALHO, V. A.; TEIXEIRA, G. F. Programação orientada a objetos:
curso técnico em informática. Colatina: Instituto Federal de Educa-ção, Ciência e Tecnologia do Espírito Santo – IFES, 2012. Disponível
em: <http://redeetec.mec.gov.br/images/stories/pdf/eixo_infor_
comun/tec_inf/081112_progr_obj.pdf>. Acesso em: 08 jun. 2020.
MENDES, D. R. Programação Java com ênfase em orientação a ob-
jetos. -. São Paulo: Novatec, 2009.
MOREIRA NETO, O. Entendendo e dominando o Java. 3. ed. São
Paulo: Digerati, 2009.
SANTOS, R. Introdução à programação orientada a objetos usando
Java. 2. ed. São Paulo: Elsevier, 2013.
186
LAFORE, R. Estrutura de dados e algoritmos em Java. Rio de Janei-
ro: Ciência Moderna, 2004.
ORACLE. Tecnologias Java. 2019. Disponível em: <https://www.
oracle.com/br/java/technologies/>. Acesso em: 25 mar. 2019.
TIOBE. TIOBE index for march 2019. Disponível em: <https://www.
tiobe.com/tiobeindex/>. Acesso em: 25 mar. 2019.
UNIDADE 3
BEDER, D. M. Introdução à programação orientada a objetos em Java.
1. ed. São Paulo: EDUFSCar. 2014. Disponível em: <http://livresaber.
sead.ufscar.br:8080/ jspui/bitstream/123456789/2659/1/SI_Ednal-
do_ProgramacaoOrientadaObj_2. pdf> Acesso em 14 abr. 2020.
CURSO de Java 42: orientação a objetos: palavra-chave final. Pos-
tado por: Loia- ne Groner. (12min. 0s.). son. color. port. Disponível
em: <https://youtu.be/858FJ6D- QRVg>. Acesso em: 16 jun. 2020.
JAVA8 - aula 2 -expressão lambdas. Postado por eXcript. (5min.
44s.). son. color. port. Disponível em: <https://www.youtube.com/
watch?v=4EyhqB-JIXs&list=PLes- CEcYj003SbwnNhQ9eyF7I-
jPlEK0uVg&index=2>. Acesso em: 16 jun. 2020.
KAMIENSKI, C. A. . Introdução ao Paradigma de Orientação a Obje-
tos. Centro Federal de Educação Tecnológica da Paraíba - Diretoria
de Ensino - Coordenação de Informática, 1996.
KNOERNSCHILD, K. Java design: objects, UML, and process. 1. ed.
Indianópolis: Pearson Education Corporate, 2001.
RICARTE, I. L. M. Programação orientada a objetos: uma aborda-
gem com Java. Campinas: Unicamp, 2001. Disponível em: <http://
www.dca.fee.unicamp.br/cur- sos/PooJava/Aulas/poojava.pdf>.
Acesso em: 10 jun. 2020.
SANTOS, R. Introdução à programação orientada a objetos usando
Java. Rio de Janeiro: Elsevier, 2011.
UNIDADE 4
BEDER, D. M. Introdução à programação orientada a objetos em Java.
1. ed. São Paulo: EDUFSCar. 2014. Disponível em: <http://livresaber.
187
sead.ufscar.br:8080/jspui/bitstream/123456789/2659/1/SI_Ednal-
do_ProgramacaoOrientadaObj_2.pdf> Acesso em 14 abr. 2020.
CURSO de Java 42: orientação a objetos - palavra-chave final. Pos-
tado por: Loiane Groner. (12min. 0s.). son. color. port. Disponível
em: <https://www.youtube.com/watch?v=858FJ6DQRVg>. Acesso
em: 16 jun. 2020.
JAVA8 - aula 2 - expressão lambdas. Postado por eXcript. (5min. 44s.).
son. color. port. Disponível em: <https://www.youtube.com/watch?-
v=4EyhqB-JIXs&list=PLes-CEcYj003SbwnNhQ9eyF7IjPlEK0uVg&in-
dex=2>. Acesso em: 16 jun. 2020.
KNOERNSCHILD, K. Java design: objects, UML, and process. 1. ed. In-
dianópolis: Pearson Education Corporate, 2001.
SANTOS, R. Introdução à programação orientada a objetos usando
Java. Rio de Janeiro: Elsevier, 2011.