Logo Passei Direto
Buscar
Material
páginas com resultados encontrados.
páginas com resultados encontrados.

Prévia do material em texto

Programação 
Orientada a Objetos
Programação 
Orientada a Objetos
1ª edição
2018
Autoria
Parecerista Validador
Samira Santos da Silva
Natália Gonçalves Machado
Adriano Donizete Pila
*Todos os gráficos, tabelas e esquemas são creditados à autoria, salvo quando indicada a referência.
Informamos que é de inteira responsabilidade da autoria a emissão de conceitos. Nenhuma parte
desta publicação poderá ser reproduzida por qualquer meio ou forma sem autorização. A violação dos
direitos autorais é crime estabelecido pela Lei n.º 9.610/98 e punido pelo artigo 184 do Código Penal.
4
Sumário
Sumário
Unidade 1
1. Classes e Objetos .......................................................8
Unidade 2
2. Aprofundamento em Classes e Objetos ................22
Unidade 3
3. Herança e Polimorfismo ...........................................36
Unidade 4
4. Composição e Agregação ........................................50
Unidade 5
5. Boas Práticas I ...........................................................63
Unidade 6
6. Armazenamento de Dados ......................................79
5
Sumário
Unidade 7
7. Interface Gráfica .......................................................94
Unidade 8
8. Boas Práticas II ....................................................... 111
6
Palavras do professor
Seja bem-vindo à disciplina Programação Orientada a Objetos. A pro-
posta principal deste estudo é abordar os diversos conceitos relaciona-
dos à orientação a objetos, exemplificados com o desenvolvimento na 
linguagem Java, de forma a levá-lo à plena compreensão e utilização 
desse paradigma em possíveis projetos durante sua carreira acadêmica 
e profissional. 
Ao longo das unidades, apresentaremos as definições necessárias para a 
compreensão de orientação a objetos, como este conceito é utilizado na 
modelagem de sistemas por meio de diagramas UML e como aplicá-los 
no desenvolvimento em linguagem Java. Assim, ao se deparar com um 
problema em seus projetos futuros, você poderá também seguir os mes-
mos passos aqui apresentados. 
Apresentaremos também práticas que serão bastante úteis para o 
aumento da produtividade e eficiência em projetos mais complexos e/ou 
que envolvam muitas pessoas. A manipulação dos dados também será 
tratada, de forma a fazer uma alusão a softwares presentes em nosso coti-
diano, em que dados são requeridos do usuário e armazenados em algum 
banco de dados. 
Além disso, noções sobre a criação de interfaces gráficas serão abordadas 
a fim de aproximar a codificação de softwares na disciplina dos sistemas do 
mundo real, em que a comunicação/interação do sistema com o usuário 
se dá através de recursos gráficos. Em muitas aplicações do dia a dia, usuá-
rios leigos necessitam de interfaces gráficas amigáveis, seja por questão 
de tempo, seja por praticidade. A interação por linhas de comando, nesse 
caso, seria um problema.
Por fim, esta disciplina visa dar uma visão geral das diversas possibilidades 
que a programação orientada a objetos traz para o desenvolvimento de 
sistemas mais complexos, em que a programação estruturada se torna 
difícil. Ao final desta disciplina, você será capaz de decidir em quais sis-
temas esse tipo de programação se aplica, sendo capaz de utilizá-lo caso 
seja a melhor opção. 
Bons estudos!
7
Objetivos da disciplina
• Definir conceitos de programação orientada a objetos (classe, 
herança, encapsulamento, polimorfismo, agregação e 
composição).
• Aplicar os conceitos de programação orientada a objetos em uma 
linguagem de programação.
• Analisar os problemas que se aplicam à programação orientada a 
objetos.
• Apontar boas práticas de programação.
• Descrever conceitos básicos para armazenamento de dados.
• Dominar e aplicar os principais aspectos no desenvolvimento de 
interfaces gráficas.
8
 1Unidade 11. Classes e Objetos 
Para iniciar seus estudos
Nesta unidade, você compreenderá os conceitos mais básicos de orien-
tação a objetos: os objetos e as classes. Além disso, você aprenderá como 
representá-los em um dos diagramas mais utilizados nos dias de hoje – o 
diagrama de classes UML – a fim de gerar uma modelagem que possa dar 
uma noção de como realizar a implementação. Por fim, você dominará a 
implementação de um código utilizando a linguagem Java, uma lingua-
gem moderna e de fácil compreensão. Vamos lá? 
Objetivos de Aprendizagem
• Contextualizar o paradigma de programação orientado a objetos, 
dominando os conceitos mais básicos associados, as formas de 
representação em diagrama de classes UML e sua implementação 
na linguagem Java. 
9
Programação Orientada a Objetos | Unidade 1 - Classes e Objetos 
Introdução da unidade
No contexto de programação orientada a objetos estão os conceitos de objetos e classes. Sua compreensão é 
de extrema importância para o aprofundamento e domínio de linguagens de programação orientadas a objetos. 
Nesta unidade, elucidamos esses conceitos e seus conteúdos relacionados, fazendo associações com objetos do 
dia a dia. Além disso, apresentaremos as diferenças entre a programação estruturada convencional e a progra-
mação orientada a objetos, permitindo ao aluno optar por cada uma das abordagens, dependendo da aplicação. 
Por fim, mostraremos como o resultado da análise e projeto de um software pode ser representado por meio de 
diagramas de classes UML, uma das formas de modelagem mais utilizadas nos dias atuais. Após apresentarmos 
essa modelagem, introduziremos a codificação em Java, uma linguagem moderna, porém, bem definida e que 
possui diversas facilidades, associadas principalmente à produtividade e reuso de código.
1.1 Classes e objetos
Nesta unidade, apresentaremos alguns conceitos de orientação a objetos que são necessários para o pleno 
entendimento deste paradigma de programação. Em seguida, a representação UML é utilizada para consolidar 
os conceitos definidos. Por fim, mostramos na prática como os conceitos de orientação a objetos se aplicam na 
programação em linguagem Java.
1.1.1 Conceitos de orientação a objetos
Nesta seção, serão apresentados os principais conceitos necessários para a iniciação dos estudos em orientação 
a objetos. Primeiramente, vamos abordar as principais diferenças entre as técnicas de programação estruturada 
e programação orientada a objetos. A seguir, aclaramos as definições de classes e objetos, que são a essência 
para a compreensão de orientação a objetos. Em seguida, apresentaremos a definição de atributos e métodos 
de classes. Por fim, os conceitos da unidade serão consolidados por meio da representação UML e codificação 
em Java.
1.1.2 Programação estruturada versus programação orientada a objetos
Programação estruturada e programação orientada a objetos são o que chamamos de paradigmas. Paradigmas 
são modelos ou padrões que foram adotados após anos de experiência. Na programação estruturada, o foco 
principal da programação está nas ações. Ações em programação são implementadas por meio de procedimen-
tos e funções; além disso, há um maior controle em relação ao fluxo de execução de programas por meio de 
estruturas de sequência, estruturas de decisão e estruturas de repetição.
Devido à facilidade ao entendimento, as linguagens estruturadas são utilizadas geralmente em cursos intro-
dutórios de programação. Seu foco está principalmente em como as tarefas devem ser feitas ao invés de o que 
deve ser feito. Nesse paradigma, não há uma separação clara entre o tratamento de dados e o comportamento 
do programa. Sua aplicação é frequentemente dada em aplicações mais simples e mais diretas. 
10
Programação Orientada a Objetos | Unidade 1 - Classes e Objetos 
A programação orientada a objetos surgiu entre as décadas de 1960 e 1970 com o objetivo de modelar proble-
mas mais complexos. A linguagem Simula 67, por exemplo, foi introduzida nesse período e já apresentava alguns 
dos conceitos de linguagens orientadas a objetos. Em 1970, surgia a linguagem Smalltalk, a primeira linguagem 
totalmente orientada a objetos.A linguagem C++, uma versão da linguagem C, porém orientada a objetos, se 
deu no início da década de 80 e é utilizada amplamente até os dias atuais. Mais tarde, no ano de 1991, a Sun 
Microsystems deu início ao desenvolvimento da linguagem Java, que também se utiliza do paradigma de orien-
tação a objetos e se faz presente no desenvolvimento dos softwares mais modernos.
A programação orientada a objetos não visa substituir a programação estruturada tradicional, mas, sim, evoluir 
as diversas práticas adotadas por ela. Ambos os paradigmas abordam o mesmo problema de formas diferentes. 
Enquanto o paradigma estruturado foca nas ações, representado por funções e procedimentos, o paradigma 
orientado a objetos foca nos objetos e seus relacionamentos. Outros conceitos introduzidos nesse paradigma 
são os conceitos de encapsulamento, classe, herança e polimorfismo. 
Existem diversas vantagens na utilização de linguagens de programação orientadas a objetos no desenvolvi-
mento de softwares. Suas práticas resultam em baixa complexidade do código para problemas complexos, o 
que facilita o processo de manutenção enquanto provê um aumento na produtividade. Com a possibilidade de 
criação de bibliotecas de classes, permite-se o compartilhamento e, até mesmo, reuso do código nesse tipo de 
linguagem.
1.1.3 Definindo classes e objetos
Os conceitos de classes e, principalmente, objetos são a chave para entender o paradigma de orientação a obje-
tos. Além disso, a explicação de um está inteiramente ligada à explicação do outro. Objetos podem ser compara-
dos a objetos do mundo real, como, por exemplo, mochila, mesa, computador, cadeira etc. Entretanto, os objetos 
no mundo real possuem duas características básicas: seu estado (atributo) e seu comportamento. A figura abaixo 
ilustra um exemplo do objeto “cachorro”.
Figura 1 – Imagem do objeto cachorro
Fonte: SHUTTERSTOCK, 2018.
11
Programação Orientada a Objetos | Unidade 1 - Classes e Objetos 
O objeto “cachorro” pode possuir, por exemplo, os seguintes atributos: nome, cor e raça. No caso da figura 
apresentada, o nome poderia ser “Rex”, sua cor poderia ser “marrom” e sua raça “Golden Retriever”, ou seja, 
esses seriam os valores dos atributos específicos do cachorro da Figura 1. Os possíveis comportamentos de um 
cachorro poderiam ser: “latir”, “dormir”, “comer”, “abanar o rabo”, etc. Os comportamentos são hábitos comuns 
a todos os cachorros. 
Outro exemplo de objeto seria a “bicicleta” ilustrada na Figura 2, que pode ter, por exemplo, os seguintes atribu-
tos: cor, preço, marca e quantidade de marchas. Neste caso, o valor do atributo cor poderia ser “amarelo”; o preço, 
“200 reais”; a marca, “BikeNew”; e a quantidade de marchas, “21”. Alguns dos comportamentos de uma bicicleta 
poderiam ser: “mudar a marcha”, “frear”, “acender a lanterna”, etc. 
Figura 2 – Imagem de uma bicicleta
Fonte: SHUTTERSTOCK, 2018.
Se considerarmos os exemplos dados acima, podemos concluir que objetos são instâncias específicas de deter-
minada classe, ou seja, determinando os valores dos atributos para a classe “Cachorro”, construímos a descrição 
de um objeto que pertence a essa classe. Assim, classe pode ser definida como um agrupamento de objetos que 
possuem atributos e comportamentos semelhantes. Na Figura 3, definida abaixo, podemos verificar quatro tipos 
de bicicleta, e todas pertencem à classe “Bicicleta”. Entretanto, como cada uma possui valores diferentes para os 
atributos da classe “Bicicleta”, podemos dizer que cada uma delas é uma instância ou um objeto dessa classe.
12
Programação Orientada a Objetos | Unidade 1 - Classes e Objetos 
Figura 3 – Quatro objetos que pertencem à classe “Bicicleta”
Fonte: SHUTTERSTOCK, 2018.
1.1.4 Atributos e métodos
Objetos em softwares são abstrações do mundo real. Sendo assim, possuem atributos que revelam seu estado, 
bem como métodos que descrevem seu comportamento. Fazendo uma comparação com o paradigma de pro-
gramação estruturada, os atributos correspondem às variáveis, e métodos correspondem às funções. Para definir 
os atributos da classe “Bicicleta”, por exemplo, precisamos pensar quais são as características comuns a todas as 
bicicletas, como cor, marca, quantidade de marchas, etc. Os atributos, além de possuírem um nome – exemplos: 
cor, marca, etc. –, também devem possuir um tipo. Esse tipo pode ser algum já predefinido pela própria lingua-
gem, ou seja, nativo à linguagem, ou pode ser um tipo definido pelo programador. O tipo do atributo nomeado 
“quantidade de marchas” pode ser, por exemplo, inteiro, representado pela palavra reservada “int”. 
Em relação aos métodos, é necessário o questionamento sobre quais comportamentos são apresentados por 
todos os objetos da classe “Bicicleta”. Por exemplo, “frear” é um comportamento comum a todas as bicicletas, 
independentemente de cor, marca ou quantidade de marchas. Por isso, na classe “Bicicleta” deve existir o método 
“frear”. Assim como as funções no paradigma estruturado, métodos também podem receber argumentos de 
diversos tipos, que são declarados na sua assinatura. Um método também pode possuir um valor de retorno, que 
pode ser tanto valores escalares quanto instâncias de classes (objetos). Métodos podem não retornar nenhum 
valor ou retornar um único valor, mas não podem retornar múltiplos valores. 
13
Programação Orientada a Objetos | Unidade 1 - Classes e Objetos 
A visibilidade de um membro de uma classe, seja ele um atributo ou um método, pode ser definida de três for-
mas. Membros que são públicos podem ser acessados por todas as classes. Membros que são privados podem 
ser acessados somente pela própria classe a qual pertencem, ou seja, uma classe externa não pode fazer referên-
cia a esses membros. Membros que são protegidos podem ser acessados pela classe a qual pertencem, por suas 
subclasses e também por outras classes que pertencem ao mesmo pacote de classes.
Classes possuem métodos especiais denominados construtores. Esses métodos são utilizados na instanciação 
de um objeto de determinada classe. Se alguém deseja criar um objeto de determinada classe, é necessária a 
chamada ao método construtor dessa classe. O método construtor de uma classe, quando definido, possui o 
mesmo nome que a classe. Quando não definimos um método construtor para uma classe, um método constru-
tor default é criado automaticamente, de forma a inicializar os atributos com valores nulos.
1.2 Representação UML
Em orientação a objetos, são necessários alguns passos que antecedem o início da codificação de fato, prin-
cipalmente em softwares mais complexos, nos quais o número de pessoas envolvidas geralmente é grande. O 
primeiro passo seria a análise ou levantamento dos requisitos do software a ser desenvolvido. Nesta etapa, é 
determinado o que será feito. O segundo passo consiste em projetar uma solução satisfatória, ou seja, deter-
minar como será feita a implementação do software. Quando essas etapas são realizadas utilizando um ponto de 
vista orientado a objetos, chamamos de análise e projeto orientados a objetos. 
Essas etapas iniciais, se realizadas de forma bem feita, podem poupar muito esforço e dinheiro nas fases poste-
riores, visto que erros encontrados no princípio do projeto são mais fáceis de corrigir do que se encontrados na 
etapa de codificação, por exemplo. Assim, pessoas envolvidas no desenvolvimento de um sistema devem estabe-
lecer um meio de comunicar o modo como eles resolveram determinado problema. Um exemplo de linguagem 
gráfica frequentemente utilizada para esse propósito de modelar o resultado do processo de análise de projeto 
orientado a objetos é a unified modeling language (UML).
Tendo seu início dado em 1994, desde 1997 a UML tem sido adotada como padrão internacional utilizado para 
representar graficamente a modelagem de softwares orientados a objetos. Essa linguagem consiste em padrões 
de notações gráficas que acabam gerando uma representação em forma de diagramas. Na versão 2.0 da UML, 
existem 13tipos diferentes de diagramas, que podem ser de dois tipos: diagramas estruturais e diagramas 
comportamentais. Nos diagramas estruturais, é possível descrever a estrutura estática do sistema como um 
todo, ou seja, como as classes se organizam, seus relacionamentos, etc. Os diagramas comportamentais descre-
vem os aspectos dinâmicos de um sistema, muitas vezes importantes e difíceis de serem capturados, utilizando 
um diagrama comportamental, como as partes do sistema que passam por alterações conforme a interação do 
usuário vai acontecendo. 
Neste material, vamos manter o interesse em uma instância dos diagramas estruturais específica: os diagramas 
de classe. Pode-se dizer que esse tipo de diagrama é o mais utilizado e um dos mais importantes dentre os dia-
gramas da UML. Por meio desse diagrama, é possível definir a estrutura do sistema, em termos de suas classes, 
os atributos e métodos que ela possui, e como as classes se relacionam e trocam mensagens entre si. Nesse dia-
grama, uma classe é a representação mais básica, sendo definida por um ícone contendo um retângulo dividido 
em três compartimentos verticais: o primeiro compartimento (de cima para baixo) consiste no nome da classe; 
o segundo consiste nos atributos que esta classe possui; o último compartimento apresenta a declaração dos 
métodos que essa classe possui. 
14
Programação Orientada a Objetos | Unidade 1 - Classes e Objetos 
Figura 4 – Representação da classe “Lampada” no diagrama de classes UML
Lampada
- estadoDaLampada: boolean
- acende ( )
- apaga ( ) 
- retornaEstado( ): boolean 
Fonte: Elaborada pelo autor.
A Figura 4 ilustra a representação da classe “Lampada” no diagrama de classes UML. Nela, podemos identificar 
que a classe “Lampada” possui somente um atributo, o “estadoDaLampada”. Além disso, essa classe apresenta 
três métodos: “acende( )”, “apaga( )” e “retornaEstado( )”.
Em alguns diagramas, os dois últimos compartimentos são omitidos. Em outros casos, 
somente os atributos ou métodos mais importantes para a finalidade são apresentados. 
Fique atento!
Para representar a visibilidade de um membro de uma classe em diagramas UML, utilizamos os seguintes símbo-
los: “+” para visibilidade pública, “-” para visibilidade privada e “#” para visibilidade protegida. No exemplo da 
Figura 4, podemos notar, por meio do símbolo “-” posicionado antes do nome de cada membro, que todos são 
privados. 
Cada atributo possui um tipo, que é representado por “:” após seu nome, seguido da palavra que representa o 
tipo. Na classe “Lampada”, o atributo “estadoDaLampada” é definido como booleano (ou seja, pode possuir valor 
true ou false). Caso o método receba argumentos, seus tipos são definidos da mesma forma, e os argumentos 
são separados por vírgulas dentro dos parênteses em “nomedoMetodo( )”, por exemplo. Além disso, os argumen-
tos de um método podem ser definidos somente por seus tipos, sem mencionar seus nomes. Se a função tem 
retorno, podemos colocar na frente da sua declaração o símbolo “:”, seguido do tipo do retorno, como mostrado 
no método “retornaEstado( ): boolean”, que retorna um booleano. Se um método não retorna nada, pode ser 
indicado colocando “:void” após sua declaração ou simplesmente omitindo seu tipo de retorno. 
Por fim, além de descrever classes, diagramas de classe UML também podem ilustrar o relacionamento entre elas.
1.3 Codificação em Java
Apesar de o paradigma de orientação a objetos ser utilizado em diversas linguagens, nesta apostila conduzire-
mos o leitor à prática de programação orientada a objetos utilizando a linguagem Java. Nesta seção, os conceitos 
mais básicos de Java serão previamente definidos a fim de consolidar uma base para a aplicação dos conceitos de 
orientação a objetos, que será apresentada em sequência.
15
Programação Orientada a Objetos | Unidade 1 - Classes e Objetos 
1.3.1 Primeiro programa em Java
A criação de um primeiro programa simples em Java para impressão de uma mensagem na tela é ilustrada na 
Figura 5. Em Java, sempre temos uma classe principal, que deve conter o método main, cuja assinatura é invariá-
vel. Esta é a classe na qual a execução do programa se inicia, sendo, portanto, uma classe que não é instanciada. 
Determinamos sua visibilidade como pública por meio do termo “public class” precedendo seu nome e definimos 
seus métodos entre colchetes. O main é precedido pelas palavras reservadas “public static void”, que significam 
que o método é público (pode ser chamado fora da classe) e estático (static), ou seja, permite-se que o método 
seja chamado sem a criação de uma instância da classe e não tem valor de retorno (void). 
Argumentos podem ser passados por meio da linha de comando durante a chamada do interpretador e são rece-
bidos por um vetor de Strings, classe definida em Java para representar cadeias de caracteres, denominadas args. 
O comando System.out é conhecido como objeto de saída padrão. E o método println( ) dentro desse objeto faz a 
impressão do seu argumento, colocando ao final uma quebra de linha. 
Figura 5 – Exemplo de um primeiro programa em Java
public class MeuPrimeiroProg{
 public static void main ( String args [ ] ) {
 System.out.println ( “Seja bem-vindo ao Java”);
 }
}
Fonte: Elaborada pelo autor.
1.3.2 Tipos de dados e variáveis
A linguagem Java, em especial, oferece dois tipos de dados com os quais podemos trabalhar: tipos primitivos e 
tipos de referência. Tipos primitivos correspondem a dados simples ou escalares e são detalhados no Quadro 1. 
Tipos de referência consistem em arrays, classes e interfaces, e serão definidos posteriormente.
Quadro 1 – Tipos primitivos na linguagem Java
Tipo Descrição
boolean Assume o valor true or false.
char Caractere em notação unicode de 16 bits. Serve para 
armazenar dados alfanuméricos.
byte Inteiro de 8 bits em notação de complemento de dois.
short Inteiro de 16 bits com notação em complemento de dois.
int Inteiro de 32 bits em notação de complemento de dois.
16
Programação Orientada a Objetos | Unidade 1 - Classes e Objetos 
Tipo Descrição
long Inteiro de 64 bits em notação de complemento de dois.
float Números em notação de ponto flutuante normalizada na 
precisão simples de 32 bits.
double Números em notação de ponto flutuante normalizada na 
precisão dupla de 64 bits.
Fonte: Elaborado pelo autor.
Os tipos do Quadro 1 descrevem não somente variáveis e atributos, mas também tipos de argumentos de méto-
dos e tipos de retorno de funções. A definição de uma variável ou de um atributo pode se dar da seguinte forma:
<tipoDeDado> <nomevariavel/atributo>;
Ou com atribuição de valor:
<tipoDeDado> <nomevariavel/atributo> = <valor>;
Na declaração de um método, especificamos os tipos dos argumentos e o tipo do retorno da seguinte forma:
<tipoRetorno> nomeMetodo (<tipoarg1> <nomearg1>,<tipoarg2> <nomearg2>)
Além disso, em Java existem três especificadores de acesso aos membros de uma classe: public para membros 
públicos, private para membros privados e protected para membros protegidos. Utilizando a sintaxe anterior, a 
declaração de um método usando especificadores seria feita da seguinte forma:
<especificador> <tipoRetorno> nomeMetodo (<tipoarg1> <nomearg1>,<tipoarg2> <nomearg2>)
1.3.3 Importando classes
A instrução import em Java é utilizada dentro de uma classe para carregar classes externas para serem referencia-
das. No Java, existe um conjunto de classes já definidas e que podem ser úteis no desenvolvimento de programas. 
Essas classes são agrupadas em pacotes. Podemos importar uma classe específica definindo, por exemplo, o seu 
caminho completo:
import java.util.Scanner
Nesse caminho, a classe Scanner, que pertence ao pacote java.util do Java, seria importada. Também é possível 
importar todas as classes contidas em um pacote por meio do uso do asterisco:
import java.util.*
1.3.4 Lendo dados da entrada
Em muitos programas, faz-se necessária a entrada de dados pelo usuário durante sua execução. Em Java, conse-
guimosisso por meio da importação e instanciação da classe java.util.Scanner. A Figura 6 ilustra a leitura de dois 
dados inteiros do teclado, a realização da subtração entre eles e a impressão do resultado. Um objeto da classe 
17
Programação Orientada a Objetos | Unidade 1 - Classes e Objetos 
Scanner permite que o programa faça a leitura de dados, que podem vir do teclado ou de um arquivo, por exem-
plo. Assim, na sua instanciação, devemos passar como argumento no seu construtor Scanner( ) de onde os dados 
vêm. Nesse caso, como queremos especificar que os dados vêm do teclado, passamos como parâmetro o System.
in, que significa entrada-padrão. Assim, criamos o objeto “entrada”. Sempre que chamamos o método nextInt( ) 
do objeto “entrada”, fazemos a leitura do próximo inteiro da entrada. 
Figura 6 – Exemplo de entrada do teclado na linguagem Java
import java.util.Scanner;
public class Subtracao{
 public static void main(String args [ ]) {
 Scanner estrada = new Scanner (System. in);w
 int numero1, numero2, subtracao;
 System.out.printf(“Informe o primeiro número\n”);
 numero1 = entrada.next( ); //lê o primeiro número
 System.out.printf(“Informe o segundo número\n”);
 numero2 = entrada.nextInt( ); //lê o segundo número
 subtracao = numero1-numero2;
 System.out.printf (“A subtração é %d\n”, subtracao);
 }
}
Fonte: Elaborada pelo autor.
Caso o dado a ser lido não seja um inteiro, outros métodos podem ser necessários. Outra forma de chamarmos o 
construtor Scanner( ) para criar um objeto é colocar o nome do pacote onde a classe está situada:
Scanner entrada = new java.util.Scanner (System.in);
Assim, não seria necessária a importação da classe Scanner.
1.3.5 Vetores e matrizes
Os vetores em Java são definidos de forma muito semelhante a linguagens estruturadas convencionais. Podemos 
criar vetores de inteiros das seguintes formas:
int c[ ]; 
c = new int [6];
Ou já definindo seus valores:
int vet[ ] = {1,5,10,4,9,11}
Ou sendo seu tamanho uma variável:
int tamanho = 6;
int[ ] c = new int [tamanho];
18
Programação Orientada a Objetos | Unidade 1 - Classes e Objetos 
Além de vetores, também se pode criar matrizes em Java. Matrizes podem ser definidas como vetores multidi-
mensionais e são declaradas de forma semelhante aos vetores:
int mat[ ] [ ] = { { 1,2,3},{4,5,6 } };
Ou de forma alternativa:
int b[ ] [ ] = new int [4][5];
Ou, até mesmo, uma matriz com quantidade de colunas diferentes para cada linha:
int c[ ][ ] = new int [2][ ]; //cria duas linhas
c[0] = new int[5]; //cria 5 colunas para a linha 0
c[1] = new int[3]; //cria 3 colunas para a linha 1
1.3.6 Definindo classes e objetos
As classes em Java são definidas por meio da palavra reservada class, precedida pelo especificador da classe e 
seguida por seu nome. Dentro da classe, com seu escopo definido por colchetes, primeiro apresentamos os atri-
butos que pertencem a esta classe. Além do tipo, o especificador também deve ser definido para cada atributo. 
Após definir todos os atributos, definimos os métodos que a classe possui. Por padrão, primeiro declaramos e 
definimos o método Construtor da classe com nome idêntico ao da mesma. Esse método recebe como parâ-
metro os valores que devem ser atribuídos aos atributos no momento da instanciação, ou seja, no momento da 
criação do objeto. O método construtor não tem valor de retorno.
No exemplo da Figura 7, fazemos referência aos atributos da classe “Conta” em seu construtor, utilizando a pala-
vra reservada this, seguida de um ponto e do nome do atributo. Assim, conseguimos distinguir entre os atributos 
da classe e os parâmetros do método construtor que possuem os mesmos nomes. Caso o método construtor 
não seja definido para uma classe, no ato da instanciação, nenhum parâmetro é passado como argumento, e o 
construtor default da classe é chamado. Este construtor define o valor de cada atributo como sendo nulo.
Podemos definir mais de um construtor para uma classe alterando os argumentos que são 
recebidos pelo método, o que define o conceito de sobrecarga. A escolha do construtor a ser 
chamado será feita em tempo de execução, dependendo dos parâmetros passados.
Após a definição do construtor, definimos os métodos que a classe possui. Os métodos são 
declarados conforme as funções em linguagens estruturadas, com a exceção de que deve-
mos determinar o especificador de cada método, incluindo o construtor. O especificador, 
como mencionado anteriormente, vai dizer quem pode acessar aquele método. 
Saiba mais
19
Programação Orientada a Objetos | Unidade 1 - Classes e Objetos 
Figura 7 – Codificação da classe “Conta” na linguagem Java
public class Conta {
 private String titular;
 private double saldo;
 private int numero;
 private int agencia;
public Conta(String titular. double saldo, int numero, int agencia ){
 this.titular=titular;
 this.saldo=saldo;
 this.numero=numero;
 this.agencia=agencia;
}
public void deposita(double quantidade){
 saldo+=quantidade;
}
public void saca(double quantidade){
 saldo+=quantidade;
}
public void saca( ){
 system.out.printf(“Seu saldo é %f.”,saldo);
}
/* . . . Outros métodos . . . */}
}
Fonte: Elaborada pelo autor.
A Figura 7 define a classe “Conta”, que possui quatro atributos, sendo o titular uma String; o saldo, um double; e 
o número e agência inteiros. O construtor dessa classe recebe valores para todos os seus atributos. Além disso, 
a classe possui outros três métodos: um método para fazer o depósito de determinada quantidade (recebida 
por parâmetro); outro método para fazer um saque de determinada quantidade (recebida por parâmetro); e um 
último método sem valor de retorno ou argumento para verificar o saldo da conta.
Destrutores são métodos especiais de uma classe que liberam a memória ocupada por um 
objeto. Isso impede que a memória se esgote, fazendo com que uma tarefa não possa ser 
finalizada. Em Java, o método destrutor, definido como finalize( ), é chamado de forma auto-
mática, mas pode ser redefinido para uma classe.
Saiba mais
É importante notar que acionar o método destrutor finalize( ) não faz o objeto ser destruído 
de imediato, mas o marca para destruição. 
Fique atento!
20
Programação Orientada a Objetos | Unidade 1 - Classes e Objetos 
Definir objetos, no contexto do Java, significa instanciar classes. No exemplo da Figura 8, instanciamos a classe 
denominada “Conta” dentro da função main, criando um objeto chamado “minhaConta”. Nesse sentido, a sin-
taxe da instanciação consiste em determinar o tipo do objeto a ser criado seguido do seu nome, o sinal de atri-
buição “=”, seguido pela palavra reservada new e a chamada do construtor conforme definida no interior da 
classe “Conta”. Caso o construtor da classe “Conta” não estivesse definido, poderíamos chamar seu construtor 
utilizando Conta( ), ou seja, sem passar nada como parâmetro.
Figura 8 – Instanciação e utilização do objeto “minhaConta” da classe “Conta”
public class Banco{
 public static void main ( String arg [ ] ){
 Conta minhaconta = new Conta ( “Bob”, 300.50, 02345, 1234 );
 minhaConta.deposita (100);
 minhaConta.saca (50);
 minhaConta.Saldo( );
 }
}
Fonte: Elaborada pelo autor.
Após a instanciação do objeto, podemos fazer chamadas a seus métodos utilizando a seguinte sintaxe:
nomeObjeto.nomeMetodo(parametros);
No exemplo da Figura 8, fazemos um depósito de 100 reais; em seguida, um saque de 50 reais e, por fim, pedimos 
para exibir o saldo na tela. Métodos podem ter parâmetros ou não, e podem retornar valores ou não. Nesse exem-
plo, temos exemplos de todas essas situações, com exceção de métodos que retornam valores.
Síntese da unidade
Nesta unidade, definimos os principais conceitos necessários paracompreensão e utilização de classes e objetos. 
Foram apresentadas as principais diferenças entre uma linguagem de programação estruturada convencional 
e linguagens orientadas a objetos, bem como a definição de classes, objetos, atributos e métodos. Além disso, 
possibilitamos ao aluno o entendimento de como representar os conceitos de orientação a objetos utilizando o 
diagrama de classes UML. 
Por fim, introduzimos a codificação na linguagem Java partindo de exemplos de códigos mais simples e abor-
dando impressões de caracteres na tela, definição de tipos de dados, importação de classes, leitura de dados do 
teclado, definição de vetores e matrizes, até chegar à definição de classes, seus métodos e atributos e instancia-
ção de objetos.
21
Considerações finais
Os conceitos apresentados nesta unidade servem como base para a com-
preensão da programação orientada a objetos. Apesar de o aprofunda-
mento desses conceitos ainda não ter sido realizado, os conceitos até 
então apresentados já permitem a codificação de softwares mais simples 
na linguagem de programação Java, utilizando o paradigma de progra-
mação orientada a objetos.
22
 2Unidade 22. Aprofundamento em Classes 
e Objetos
Para iniciar seus estudos
Olá! Nesta unidade, você vai fazer um aprofundamento nos conceitos 
relacionados a objetos e classes. Serão apresentados e estudados dois 
dos pilares da programação orientada a objetos: a abstração e o encap-
sulamento. Compreender suas definições e como aplicá-los possibilita a 
você se beneficiar de diversas características de linguagens orientadas a 
objetos. Vamos nessa?
Objetivos de Aprendizagem
• Adquirir conhecimento e aplicar os conceitos de abstração e 
encapsulamento na representação de um sistema utilizando 
diagrama de classes UML e, na codificação, utilizando a lingua-
gem Java. 
23
Programação Orientada a Objetos | Unidade 2 - Aprofundamento em Classes e Objetos
Introdução da unidade
Nesta unidade, faremos um aprofundamento nos conceitos de classe e objetos definidos na unidade anterior. 
Ademais, introduziremos o conceito de abstração e encapsulamento, dois dos grandes pilares da programação 
orientada a objetos, e apresentaremos sua aplicação no diagrama de classes UML. A implementação desses con-
ceitos em qualquer aplicação faz do código mais conciso e seguro, gerando um grau a mais de segurança para o 
software. Neste mesmo diagrama, mostraremos como se dá a representação de coleções de valores em UML, os 
vetores e matrizes, que são úteis em qualquer linguagem de programação. 
Além disso, apresentaremos as estruturas de decisão e de repetição em Java. Elas são úteis na programação de 
sistemas de quaisquer espécies. As estruturas de decisão permitem que realizemos saltos no código. As estrutu-
ras de repetição permitirão que repitamos um determinado trecho de código, o que resulta em códigos mais lim-
pos, possibilitando o reuso e facilitando a manutenção do software. Por fim, mostraremos como os conceitos de 
abstração e encapsulamento são incorporados à programação orientada a objetos utilizando a linguagem Java. 
2.1 Aprofundamento em classes e objetos
Abstração e encapsulamento são dois entre os quatro pilares da programação orientada a objetos. Nesta unidade, 
definiremos seus conceitos e realizaremos um aprofundamento no conhecimento de classes e objetos. Apresen-
taremos como esses conceitos podem ser utilizados na representação por diagrama de classes UML, além de 
como representar vetores e matrizes nesse diagrama. Por fim, apresentaremos a codificação em Java utilizando 
estruturas de decisão e repetição, bem como a utilização da abstração e encapsulamento nessa linguagem.
2.1.1 Conceito de abstração
Abstração é um dos pontos mais importantes da orientação a objetos. Segundo o Novo Dicionário da Língua 
Portuguesa escrito por Cândido de Figueiredo em 1913, a palavra “abstrair” tem o seguinte significado:
Abstrair (abs-tra-ir): Verbo transitivo cujo significado é: Separar. Verbo intransitivo cujo sig-
nificado é: Considerar separadamente. Verbo pronominal cujo significado é: Concentrar-se. 
Alhear-se (FIGUEIREDO, 1913).
Glossário
Sendo assim, a palavra “abstração” conota a captação da essência de determinado problema, considerando ape-
nas o que é mais importante. Considere, por exemplo, modelos de sistema solar. No exemplo da figura a seguir, 
temos um exemplo de um modelo de sistema solar onde utilizamos esferas que representam os planetas e pali-
tos que fazem com que suas órbitas sejam circulares.
24
Programação Orientada a Objetos | Unidade 2 - Aprofundamento em Classes e Objetos
Figura 9 – Sistema solar
Fonte: SHUTTERSTOCK, 2018.
Ao analisar o modelo de sistema solar dessa figura, precisamos ignorar diversos detalhes. Os planetas, na ver-
dade, não são esferas perfeitas, mas sim achatadas nos polos. As órbitas, por outro lado, também não são circula-
res, mas sim elípticas. Entretanto, ainda assim utilizamos esse tipo de modelo para representar um sistema solar, 
pois descrever os detalhes dos planetas seria mais trabalhoso e resultaria em um modelo bem mais complexo. 
Assim, fizemos uma abstração de um sistema solar. O modelo descrito nessa figura para a maioria dos casos é 
bom o suficiente.
Ao trabalharmos com programação, exercermos a abstração a todo momento, mesmo sem saber que o esta-
mos fazendo. Ela se faz necessária na escrita de algoritmos, na especificação formal de linguagens e mesmo no 
desenvolvimento de softwares. Com ela, conseguimos ver a representação por inteiro e, ao mesmo tempo, temos 
a possibilidade de decompor um problema complexo e grande em subproblemas mais simples e menores. 
De acordo com a definição de classe abordada na unidade anterior, podemos dizer que uma classe deve abstrair 
um conjunto de objetos do mundo real que possuam características semelhantes. Para os que estão começando 
a se envolver no mundo da programação, pode parecer ser algo complexo. Entretanto, se pararmos para analisar, 
existe uma lógica por trás da abstração que não é tão complicada. O exemplo a seguir demonstra isso.
Imagine o desenvolvimento de um software que fará o controle de estoques de produtos em uma loja. Os produ-
tos possuem características em comum, independentemente do tipo, como podemos observar na figura a seguir. 
Sendo assim, podemos considerar essas características como propriedades da classe Produto, que é a classe que 
conseguimos abstrair dessa aplicação. Logo, ela então será transformada em uma classe no projeto do software.
25
Programação Orientada a Objetos | Unidade 2 - Aprofundamento em Classes e Objetos
Figura 10 – Plataforma de produtos à venda
Fonte: Elaborada por UOL EDTECH, 2018.
Observando a figura anterior, identificamos características comuns a todos os produtos, independentemente 
do tipo. Dentre elas, destacamos: descrição, foto, tempo de expiração da promoção e preço. Desse modo, essas 
características seriam atributos da classe Produto. Note que a abstração precisa ser mais abrangente possível, 
aproveitando ao máximo as vantagens da programação orientada a objetos.
Classes não são objetos, mas a estrutura de um objeto. Uma classe Bicicleta só “vira” objeto 
quando é instanciada e um objeto do tipo da classe Bicicleta é criado. Ao encerrar a instância 
da classe, o objeto do tipo Bicicleta deixa de existir. Entretanto, a classe Bicicleta continua 
existindo.
Fique atento!
A programação orientada a objetos não é algo novo (já tem mais de 30 anos), mas ainda 
assim é muito mal utilizada por programadores que muitas vezes fazem o uso das linguagens 
(C++, Java, etc…) sem utilizar os conceitos de orientação a objetos de forma correta.
Saiba mais
26
Programação Orientada a Objetos | Unidade 2 - Aprofundamento em Classes e Objetos
2.1.2 Conceito de encapsulamento
Assim como a abstração, o encapsulamento é também essencial na programação orientada a objetos. Ele dá 
segurança à aplicação, escondendo algumas propriedades, dando a impressão de uma caixa preta. O encap-sulamento pode ser visto, de forma simplificada, como um meio de restringir o acesso aos membros (atributos, 
métodos, etc…) de um determinado objeto. Em alguns casos, o acesso a estes membros só pode ser dado através 
de métodos com essa especialidade, evitando também que os dados sejam alterados por um descuido.
O nível de encapsulamento de um membro é definido nas classes através dos especificadores aprendidos na 
unidade anterior (público, privado e protegido). A maioria das linguagens implementam o encapsulamento colo-
cando suas propriedades (atributos) como privadas e utilizando-se de métodos especiais, chamados getters e 
setters (apresentados na videoaula, lembram?), que servem para retornar e atribuir o valor de uma propriedade, 
respectivamente. Assim, métodos externos à classe não conseguem acessar as propriedades de forma direta, 
adicionando mais um grau de segurança ao sistema. 
Fazendo uma analogia com o mundo real, percebemos o conceito de encapsulamento em todo lugar. 
Quando apertamos o botão para ligar o aparelho de som, não sabemos o que está acontecendo internamente. 
Assim, podemos dizer que os métodos que ligam o aparelho de som estão encapsulados. 
A figura a seguir exemplifica o encapsulamento em uma classe “Produto” que possui três atributos: preço, descri-
ção (desc) e foto. Nela, podemos perceber que a única forma de uma classe externa acessar seus atributos é atra-
vés dos métodos getters e setters. Assim, temos um método get e um método set para cada atributo. O método get 
irá retornar o conteúdo do atributo para uma classe externa e o método set irá receber como parâmetro um valor 
que será atribuído ao atributo. Assim, podemos dizer que os métodos getters e setters são públicos e os atributos 
são privados, resultando em um encapsulamento dos dados.
Figura 11 – Encapsulamento dos atributos: preço, descrição e foto
Fonte: Elaborada pelo autor.
27
Programação Orientada a Objetos | Unidade 2 - Aprofundamento em Classes e Objetos
De certa forma, abstração e encapsulamento estão ligados, pois é na abstração que o programador decide quais 
informações irão ser ocultadas.
Quando há uma falha devido à ausência de proteção de um membro do objeto, diz-se que 
houve uma violação no encapsulamento.
Saiba mais
Encapsulamento deve ser um dos principais objetivos de um programador que utilize de lin-
guagens orientadas a objetos.
2.1.3 Representação UML
No diagrama de classes UML, representamos uma abstração de objetos do desenvolvimento de uma classe, 
utilizando a representação gráfica descrita na unidade anterior. Assim, esse conjunto de objetos pode ser, por 
exemplo, representado pela classe “Produto” da figura a seguir. Ao invés de focarmos nos detalhes de cada tipo 
de produto, criamos uma classe que agrupa as características que todos os produtos têm em comum, como des-
crição, foto e preço. Assim, estes são os atributos da classe. 
No terceiro compartimento (de cima para baixo), vamos declarar a assinatura dos métodos que a classe “Produto” 
possui. O primeiro dentre eles é o método construtor, identificado pelo estereótipo <<constructor>> posicionado 
antes da assinatura do método. O método construtor, como explicado na unidade anterior, é o método que ins-
tância a classe, ou seja, ele é chamado sempre que desejarmos criar um objeto que pertence a esta classe. Este 
método sempre tem o nome da classe que ele instancia, pode possuir parâmetros ou não e não tem valor de 
retorno.
28
Programação Orientada a Objetos | Unidade 2 - Aprofundamento em Classes e Objetos
Figura 12 – Representação no diagrama de classes UML da classe “Produto”
Produto
- desc : String
- foto : String
- preco : double
<<constructor>> Produto ( ) 
+ getDesc ( ) : String
+ setDesc (texto: String)
+ getFoto ( ) : String
+ setFoto (texto: String)
+ getPreco ( ) : double
+ setPreco ( valor: double)
Fonte: Elaborada pelo autor.
Neste mesmo compartimento, verificamos uma sequência de métodos com nomes que começam pelas palavras 
“get” e “set”. O uso destes métodos nas linguagens orientadas a objetos é uma forma de encapsulamento. Geral-
mente definimos os dois métodos para cada atributo da classe. O que o método “get” faz é retornar o atributo 
referente a ele. Já o método “set” recebe como parâmetro um valor e atribui este valor a seu atributo de referência. 
Note que todos os atributos são definidos como privados, através da utilização do símbolo “-” antes do seu nome. 
Isso faz com que classes externas não consigam acessar os valores desses atributos de forma direta. Já os méto-
dos da classe “Produto” são definidos como públicos, através do símbolo “+” antes de sua assinatura. Isso signi-
fica que estes métodos podem ser chamados a partir de qualquer classe externa. É interessante perceber que o 
fato de que os atributos são privados e os métodos “set” e “get” são públicos não é uma mera coincidência. Isso 
acontece para que o encapsulamento seja permitido. Permitir que os atributos sejam acessados apenas através 
destes métodos é uma boa prática de programação orientada a objetos.
2.1.3.1 Representando vetores e matrizes em UML
Na unidade anterior, verificamos que podemos definir vetores e matrizes em Java, assim como na maioria das lin-
guagens. Dessa forma, também devemos poder representá-los no diagrama de classes UML. Conseguimos isso 
através da multiplicidade. A multiplicidade vem acompanhando o tipo de um atributo. Considere, por exemplo, 
o atributo “tipoPneu” da classe definida na figura a seguir. Esse atributo vem acompanhado do seu tipo (String) 
seguido do número 4 entre colchetes. O número 4 é o que chamamos de multiplicidade do atributo “tipoPneu”. 
Ele indica que “tipoPneu” é um vetor de inteiros de tamanho igual a 4. Já no atributo “portas”, temos um vetor do 
tipo int[2..4] que indica que é o atributo é um vetor de inteiros de no mínimo 2 posições e no máximo 4 posições, 
representando que um carro pode ter 2 ou 4 portas.
29
Programação Orientada a Objetos | Unidade 2 - Aprofundamento em Classes e Objetos
Figura 13 – Representação no diagrama de classes UML da classe “Carro”
Carro
- qtdRodas : int
- tipoPneu : String [4]
- cor : String
- portas : int[2..4]
<<constructor>> Carro ( ) 
+ getQtdRodas ( ) : int
+ setQtdRodas (valor: int)
+ getTipoPneu ( ) : String[4]
+ setTipoPneu (texto:String[4])
+ getCor ( ) : String
+ setCor ( texto: String)
+ getPortas ( ) : int[2..4]
+ setPortas (valor:int[2..4])
Fonte: Elaborada pelo autor.
Para definir um vetor ou uma matriz, basta colocar, após seu tipo, um par de colchetes e, dentro dos colchetes, 
o valor da multiplicidade. A Tabela 1 exemplifica os valores que podem ser colocados para a multiplicidade e seu 
significado.
Tabela 1 – Valores de multiplicidade e seu significado
Valor Descrição
1 (default) Atributo com um único valor.
0..1 Atributo pode ter valor nulo.
* Atributo é uma coleção de valores.
1..* Atributo é uma coleção de valores com pelo menos 1 
item.
n..m Atributo é uma coleção com no mínimo n elementos 
e no máximo m. 
n,m Atributo é uma matriz m x n. 
Fonte: Elaborada pelo autor.
30
Programação Orientada a Objetos | Unidade 2 - Aprofundamento em Classes e Objetos
2.1.4 Codificação em Java
Nesta seção, apresentaremos algumas estruturas importantes na codificação em Java. Primeiramente, apresen-
taremos estruturas de desvio condicional essenciais na programação. Em seguida, ilustraremos a utilização de 
laços de repetição em Java. Por fim, exemplificaremos a utilização da abstração e encapsulamento programando 
na linguagem Java. 
2.1.4.1 Estruturas de desvio
As estruturas de desvio em Java podem ser divididas em dois tipos: condicionais e incondicionais. As estruturas 
de desvio condicional fazem um “salto” no código mediante a alguma condição que deve ser satisfeita. Por outro 
lado, as estruturas de desvio incondicional realizam um “salto” no código sem a necessidade de satisfazer uma 
condição, ou seja, o salto é sempre tomado. 
Na maior parte daslinguagens, e também em Java, o salto condicional pode ser dado através de dois tipos de 
instruções: if ou switch. A figura a seguir exemplifica o uso do If.
Figura 14 – Exemplo de codificação do comando If
if ( <condicao> ) {
 <comando1>
 } else {
 <comando2>
 }
Fonte: Elaborada pelo autor.
Nesta figura, podemos perceber que caso determinada condição definida por <condicao> seja satisfeita, 
o <comando1> será executado, caso contrário (else), <comando2> será executado. Ambos os comandos 
(<comando1> ou <comando2>) podem ser substituídos por qualquer trecho de código. Já a <condicao> pode ser 
uma comparação qualquer, por exemplo, entre a variável “preco” e um número: “preco==100” ou “preco<200”. 
O comando switch é utilizado quando queremos comparar uma variável com diversos valores e tomar decisões 
diferentes caso a variável seja igual a cada um desses valores. Na figura a seguir, observamos a sintaxe para a 
utilização desta instrução. A tag <expressao> pode ser substituída por uma expressão qualquer de código, geral-
mente é uma variável. As tags <valor1>,<valor2>, …, etc simbolizam os valores com os quais desejamos comparar 
a expressão. Caso a expressão possua valor igual a <valor1>, o <comando1> é executado. Caso possua valor igual 
a <valor2>, o <comando2> é executado, e assim por diante. Após a definição de cada “case”, colocamos a palavra 
reservada “break” com o intuito de parar de fazer as comparações no momento que uma das comparações for 
satisfeita. Já a palavra “default” define qual comando será executado se nenhuma das comparações anteriores for 
satisfeita.
31
Programação Orientada a Objetos | Unidade 2 - Aprofundamento em Classes e Objetos
Figura 15 – Exemplo de codificação do comando switch
switch ( <expressao> ) {
 case <valor1>:<comandos 1>
 break;
 case <valor2>:<comandos 2>
 break;
 case <valor3>:<comandos 3>
 break;
 ...
Fonte: Elaborada pelo autor.
2.1.4.2 Estruturas de repetição
As estruturas de repetição são bastante úteis em qualquer linguagem de programação. Elas proporcionam uma 
melhor qualidade do código, fazendo com que partes do código que se repitam não apareçam duplicadas. Isso 
evita a complexidade do código, facilitando por exemplo a manutenção. 
Em Java e em grande parte das linguagens de programação atuais, repetições são implementadas com a uti-
lização de for, while e do/while. A instrução for funciona como mostrado na figura a seguir. A <expressaoinicial> 
seria por exemplo uma atribuição que é feita antes das iterações começarem, por exemplo, “int i=0”. O segundo 
termo seria a <condição>. Essa condição é verificada a cada iteração do laço for, para verificar se a sequência de 
<comandos> vai ser executada a seguir. Um exemplo seria “i<100”. O <incremento> é um valor que vai sendo alte-
rado e que em algum momento faz a <condicao> não ser satisfeita e o laço de repetição ser finalizado. Exemplo 
disso seria “i=i+1”. No caso do exemplo descrito aqui, a variável i começa com o valor zero. A cada iteração ela é 
incrementada em um e a verificação se seu valor é menor que 100 é realizada. No momento que i atingir o valor 
100, o loop é finalizado e os <comandos> não serão mais executados.
Figura 16 – Exemplo de codificação do comando for
for (<expressaoinicial>; <condição>; <incremento>) {
 <comandos>
}
Fonte: Elaborada pelo autor
32
Programação Orientada a Objetos | Unidade 2 - Aprofundamento em Classes e Objetos
Na figura a seguir, exemplificamos o uso da instrução while. Os <comandos> definidos entre chaves são repetidos 
enquanto a <condicao> definida por satisfeita. Esta condição é equivalente à mostrada nos exemplos anteriores.
Figura 17 – Exemplo de codificação do comando while
while (<condição>) {
 <comandos>
}
Fonte: Elaborada pelo autor.
Por fim, a figura a seguir exemplifica o comando do/while que funciona de forma semelhante ao exemplo do for. 
A única diferença é que os <comandos> são executados para só então verificar se a <condição> é satisfeita. Nesse 
caso, caso a condição não seja satisfeita, as repetições são finalizadas e a próxima iteração não será executada. 
O resultado desse modo de funcionamento é que os <comandos>, usando este tipo de instrução, são sempre 
executados pelo menos uma vez.
Figura 18 – Exemplo de codificação do comando do/while
do {
 <comandos>
} while (<condição>);
Fonte: Elaborada pelo autor.
O incremento de uma variável em 1 (uma) unidade também pode ser representado na forma 
“i++”. Isto é equivalente a escrever “i = i + 1”. Da mesma forma, decrementar uma variável 
pode ser escrito como “i--” que é equivalente a “i = i – 1”.
Saiba mais
33
Programação Orientada a Objetos | Unidade 2 - Aprofundamento em Classes e Objetos
2.1.4.3 Abstração e encapsulamento em Java
O conceito de abstração é implementado em Java através do uso de classes como a definida na figura a seguir. 
A classe “Produto” abstrai os detalhes de cada tipo de produto, criando uma especificação que é válida inde-
pendente do seu tipo. O conceito de encapsulamento é implementado em Java colocando os atributos de uma 
classe como privados (private) permitindo assim o ocultamento desses dados para classes externas. Se uma classe 
externa deseja ter acesso a estes dados, seja apenas para leitura ou para escrita, basta fazer a chamada aos 
métodos públicos “get” e “set”, respectivamente. No exemplo desta figura, temos o atributo “preco” na l classe 
“Produto”. Se desejamos verificar o valor do preço de um produto, basta chamarmos o método “getPreco()”. Caso 
desejemos alterar o valor do preço de um produto para 200 reais, devemos chamar o método “setPreco(200)” 
para um determinado objeto da classe Produto
Figura 19 – Exemplo de código em Java utilizando abstração e encapsulamento
public class Produto {
 private String desc;
 private String foto;
 private double preco;
 public String getDesc() {
 return desc;
 }
 public String getFoto() {
 return foto;
 }
 public double getPreco() {
 return preco;
 }
 public void setDesc(String texto) {
 desc = texto;
 }
 public void setFoto(String texto) {
 foto = texto;
 }
 public void setPreco(double valor) {
 preco = valor;
 }
}
Fonte: Elaborada pelo autor.
34
Programação Orientada a Objetos | Unidade 2 - Aprofundamento em Classes e Objetos
Diversas vezes se faz necessária a criação de variáveis dentro um método. Estas variáveis são 
válidas (ou existem) apenas dentro do método em que foram declaradas. Isso é o que cha-
mamos de escopo de uma variável. Este tipo de variável possui o escopo local, pois existem 
somente dentro do método em que foram declaradas e são chamadas de variáveis locais.
Saiba mais
Síntese da unidade
Nesta unidade fizemos um aprofundamento no conteúdo sobre classes e objetos apresentado na unidade ante-
rior. Além disso, focamos em ilustrar os conceitos de abstração e encapsulamento, mostrando a implementação 
tanto na representação de diagrama de classes UML quanto na codificação em Java. Por fim, introduzimos a 
representação de vetores e matrizes em UML, além de estruturas de decisão e repetição na linguagem Java.
35
Considerações finais
Esta unidade abordou o aprofundamento em classes e objetos tendo 
como foco maior a abstração e o encapsulamento dos dados, dois gran-
des pilares da orientação a objetos. Entender os princípios deste tipo de 
programação permite com que saibamos desfrutar de todas as suas van-
tagens que a orientação a objetos proporciona sem correr o risco de com-
prometer a segurança do sistema.
36
 3Unidade 33. Herança e Polimorfismo
Para iniciar seus estudos
Nesta unidade, você compreenderá os conceitos de herança e polimor-
fismo, dois grandes pilares da programação orientada a objetos. Quando 
aplicados em sistemas, suas vantagens são muitas, como reúso de código, 
facilidade na manutenção e organização do código, etc. Aprendendo 
esses conceitos, você poderá aplicá-los no desenvolvimento de seus sis-
temas e desfrutar de todo o potencialdas linguagens orientadas a obje-
tos. Pronto para começar?
Objetivos de Aprendizagem
• Conhecer os conceitos de herança e polimorfismo dentro do 
contexto de orientação a objetos e da aplicação dos mesmos na 
representação de projeto de sistemas através de diagramas de 
classe UML e na codificação através da linguagem Java.
37
Programação Orientada a Objetos | Unidade 3 - Herança e Polimorfismo
Introdução da unidade
Nesta unidade, abordaremos os conceitos de herança e polimorfismo dentro da orientação a objetos. Conhe-
cer esses conceitos e saber utilizá-los é fundamental para o desenvolvimento de sistemas concisos e coerentes. 
Além disso, apresentaremos as diferenças entre classes abstratas e interfaces, e você conseguirá compreender 
quando utilizar cada uma das implementações nos seus projetos orientados a objetos. Por fim, mostraremos 
como empregar os conceitos abordados na modelagem utilizando diagramas de classe UML e no desenvolvi-
mento de sistemas através da linguagem Java. A compreensão desses conceitos e o conhecimento de sua apli-
cação despertarão em você o senso crítico sobre em quais situações e em que tipos de sistemas utilizar cada 
conceito abordado.
3.1 Herança e polimorfismo 
Na unidade anterior, estudamos dois dos pilares da programação orientada a objetos: abstração e encapsula-
mento. Nesta unidade, vamos abordar os dois pilares restantes ligados à orientação a objetos: herança e polimor-
fismo. Além disso, vamos mostrar como esses conceitos estão relacionados com os conceitos de classe abstrata e 
interfaces e como aplicá-los no diagrama de classes UML e na programação em linguagem Java.
3.1.1 Herança
A programação orientada a objetos permite com que o conceito de herança seja implementado. Neste contexto, 
herança possui o mesmo significado do mundo real. Assim como um filho pode herdar algumas características 
do pai, na orientação a objetos permite-se que uma classe herde atributos e métodos de outra classe.
Existem nomes especiais para as classes que estão envolvidas nessa relação de herança. A classe “pai” é chamada 
de superclasse. É ela que vai ceder suas características para a classe “filho”, a qual chamamos de subclasse ou 
classe derivada.
Na Figura 20, temos a superclasse Veículo. Esta superclasse possui três subclasses que herdam suas caracterís-
ticas: Carro, Ônibus e Avião. Outra maneira de entender é pensar que Carro, Ônibus e Avião são do tipo Veículo. 
Figura 20 – Exemplo de classes que herdam da classe veículo
Veículo
Carro Ônibus Avião
Fonte: Elaborada pelo autor.
38
Programação Orientada a Objetos | Unidade 3 - Herança e Polimorfismo
Para que o conceito de herança seja empregado, apenas uma restrição é necessária. Os especificadores de acesso 
das classes, métodos e atributos necessitam estar no modo “protegido” ou “público” para que outras classes pos-
sam herdá-los. Caso sejam “privados”, só poderão ser acessados via getters e setters.
A maior vantagem em se utilizar a herança é a possibilidade de reúso de código. No exemplo da Figura 20, todos 
os três tipos de veículos possuem capacidade de passageiros, tipo de combustível e número de rodas. Caso a 
superclasse Veículo não existisse, esses atributos deveriam ser especificados de forma repetitiva em cada uma 
das classes: Carro, Ônibus e Avião. Como essas classes herdam-se da classe Veículo, esses atributos são definidos 
uma única vez na superclasse e herdados pela mesma.
Além de reúso do código, existe outra questão afetada pela utilização de herança é a manutenção. A utilização 
desse conceito faz com que os códigos sejam desenvolvidos de forma mais clara, deixando o código mais orga-
nizado e facilitando bastante a manutenção e a correção de possíveis erros. 
3.1.1.1 Herança simples
Quando uma classe derivada herda propriedades (atributos e métodos) somente de uma classe, tem-se a cha-
mada herança simples. Na herança simples, uma classe tem somente uma superclasse. A Figura 21 exemplifica 
esse conceito. A classe Canário herda somente da classe Pássaro.
Figura 21 – Exemplo de herança simples
Pássaro
Canário
Fonte: Elaborada pelo autor.
3.1.1.2 Herança múltipla
Na herança múltipla, uma classe herda propriedades (atributos e métodos) de duas ou mais classes. Sendo assim, 
a classe derivada possui mais de uma superclasse. No exemplo da Figura 22, a classe Dragão herda de duas super-
classes: Pássaro e Lagarto. Isso significa que dragão é um pássaro e, portanto, possui as características de um 
pássaro, ao mesmo tempo em que também possui as características de um lagarto. 
39
Programação Orientada a Objetos | Unidade 3 - Herança e Polimorfismo
Figura 22 – Exemplo de herança múltipla
Pássaro Lagarto
Dragão
Fonte: Elaborada pelo autor.
Uma classe pode derivar mais de uma classe, e isso não é herança múltipla. Na herança múl-
tipla, tem-se mais de uma superclasse para uma mesma classe. O caso em que mais de uma 
classe herda de uma mesma superclasse é o de herança simples, sendo o caso mais comum 
nos projetos.
Saiba mais
Para que uma subclasse acesse métodos e atributos herdados facilmente, é necessário 
que eles sejam definidos na superclasse com os modificadores de acesso “protegido” ou 
“público”. Caso sejam privados, só poderão ser acessados via getters e setters.
Fique atento!
3.1.2 Classes abstratas e interfaces
Na programação orientada a objetos, existem dois mecanismos que permitem a criação de classes, contendo 
apenas descrições de atributos e assinaturas de métodos, sem efetivamente implementá-los. Essa possibilidade 
é útil para a definição de classes que nunca são instanciadas, como alguns exemplos de superclasses no rela-
cionamento de herança. Nesse caso, a implementação dos métodos fica, por exemplo, para suas subclasses. Os 
mecanismos que permitem isso se denominam classes abstratas e interfaces e são muitas vezes confundidos. 
Uma superclasse que possui métodos que não são implementados deve defini-los como abstratos. Métodos abs-
tratos são somente declarados (com nome, modificadores, lista de argumentos e retorno), mas não implementa-
dos. Se uma superclasse possui um método abstrato, a subclasse que herda esse método deve necessariamente 
implementá-lo, seguindo a declaração especificada na superclasse. Se uma classe possui ao menos um método 
abstrato, ela pode ser considerada uma classe abstrata. Uma classe pode ser declarada como sendo abstrata sem 
40
Programação Orientada a Objetos | Unidade 3 - Herança e Polimorfismo
possuir métodos abstratos; entretanto, caso possua algum método abstrato, suas classes derivadas devem imple-
mentá-lo. Classes abstratas em orientação a objetos são classes que não podem ser estanciadas de forma direta.
Assim, as classes abstratas podem conter métodos abstratos e não abstratos. Se a classe possuir apenas métodos 
abstratos, podemos criá-la como uma interface. Sua funcionalidade é semelhante a de classes abstratas; porém, a 
forma de declaração desse tipo de classe é feita através de palavras reservadas diferentes. Assim como classes abs-
tratas, interfaces também não podem ser instanciadas. Todos os métodos de uma interface são abstratos e públicos.
Uma das diferenças fundamentais entre interfaces e classes abstratas é que uma subclasse pode herdar de 
muitas interfaces, mas somente de uma classe (sendo ela abstrata ou não). Assim, as interfaces permitem a 
implementação do conceito de herança múltipla. O Quadro 2 descrita a seguir apresenta uma comparação entre 
algumas das características de interfaces e classes abstratas.
Quadro 2 – Comparação entre as características de interfaces e classes abstratas
Característica Interface Classe abstrata
Herança múltipla Uma classe pode implementar 
diversas interfaces. 
Uma classe pode implementar 
apenas uma classe.
Implementação Uma interface não pode conter 
nenhum tipo de implementação.
Uma classe abstrata pode 
conter métodos não abstratos 
implementados.
Instanciação Somente da classe que 
implementá-la. 
Somente da classe que herdasuas 
propriedades.
Métodos Todos públicos e abstratos. Dependem do modificador.
Velocidade Lenta, requer trabalho extra 
para encontrar o método 
correspondente na classe atual.
Rápida. 
Funcionalidades adicionais Se você incluir um novo método 
na interface, é necessário 
implementá-lo em todas as 
implementações dessa interface. 
Se você incluir um novo método 
em uma classe abstrata, você 
pode definir uma implementação-
padrão que todas as subclasses 
podem adotar. 
Fonte: Elaborado pelo autor.
Classes não abstratas são chamadas de classes concretas e podem ser instanciadas 
diretamente.
Fique atento!
3.1.3 Polimorfismo
O polimorfismo é um dos pilares da programação orientada a objetos e está diretamente relacionado ao con-
ceito de herança. A palavra polimorfismo significa “muitas formas” e está ligada à capacidade dos animais de se 
41
Programação Orientada a Objetos | Unidade 3 - Herança e Polimorfismo
adaptarem ou alterarem sua forma conforme a necessidade. Na herança, as classes “filhas” herdam as carac-
terísticas de seus “pais”, ou seja, métodos e atributos das superclasses são herdados pelas classes derivadas. 
Entretanto, podemos desejar que a ação que um método tome para determinada subclasse seja diferente da 
especificada na superclasse. Assim, o polimorfismo dinâmico ou de sobreposição consiste em alterar o fun-
cionamento de alguns métodos herdados dentro de uma subclasse.
No exemplo da Figura da seção anterior, temos as classes Carro, Ônibus e Avião, que herdam da classe Veículo. 
Imagine que a classe Veículo possua o método acelerar( ) que é herdado por todas as suas subclasses. Entre-
tanto, a forma como a aceleração é executada em um carro, ônibus ou avião é diferente. Portanto, cada uma 
dessas classes deve reescrever o método acelerar( ) a fim de adaptá-lo para a sua necessidade. 
Outra forma de utilizar o polimorfismo é definir, dentro uma classe, várias declarações de um mesmo método 
com assinaturas diferentes. A escolha de qual método será chamado é feita em tempo de compilação e depende 
da assinatura do método sobrecarregado. Esse tipo de polimorfismo é chamado de estático ou sobrecarga 
(overload), sendo muito utilizado para especificar construtores, por exemplo. Na instanciação de uma classe 
Círculo, poderíamos ter dois métodos construtores: um que recebe dois objetos do tipo Ponto (indicando o ponto 
do centro e um ponto do círculo) e outro que recebe quatro parâmetros, sendo dois valores correspondendo às 
posições x e y do ponto central e dois valores correspondendo às posições x e y de um ponto do círculo. A decisão 
por qual método chamar seria feita em tempo de compilação.
Diversas vezes, podemos ver o conceito de herança acompanhado do conceito de especia-
lização e generalização. A definição de uma superclasse pode ser vista como uma generali-
zação. A definição de subclasses específicas que herdam de uma superclasse é chamada de 
especialização.
3.1.4 Representação em UML
Assim como os outros conceitos abordados nas unidades anteriores, a herança e o polimorfismo também podem 
ser representados no diagrama de classes UML. Para representar relacionamentos em UML, utilizamos geral-
mente de ligações entre os retângulos que representam as classes. Para indicar o relacionamento de herança 
entre duas classes, utilizamos a seguinte representação gráfica:
Figura 23 – Representação gráfica de herança no diagrama de classes UML
Fonte: Elaborada pelo autor.
42
Programação Orientada a Objetos | Unidade 3 - Herança e Polimorfismo
Na Figura23, observamos o símbolo utilizado para representar a herança no diagrama de classes UML. A seguir, 
na Figura 24, exemplificamos o uso desse símbolo na herança entre a superclasse Veículo e suas classes deriva-
das: Carro, Ônibus e Avião. 
Figura 24 – Classes que herdam da classe Veículo no diagrama de classes UML
Veículo
Carro Ônibus Avião
- combustível: String
- capacidade: Int
+ acelerar( ): void
+ voar( ): void
+ acelerar( ): void
+ acelerar( ): void
- capacidadePortaMalas: Int
+ acelerar( ): void
- passageirosEmPe: Int - numeroHelices: Int
Fonte: Elaborada pelo autor.
O diagrama representado na Figura 24 descreve o relacionamento entre a classe Veículo e suas classes derivadas. 
O símbolo da herança é utilizado de forma que ele ligue uma subclasse à sua superclasse, fazendo com que a 
ponta do triângulo indique a direção da classe “pai”. Observando o diagrama, percebemos que Carro, Ônibus e 
Avião são do tipo Veículo. 
É importante notar que os atributos e métodos definidos na superclasse devem ser comuns a todas as suas sub-
classes. Em todos os veículos, existem um tipo de combustível utilizado e uma capacidade de passageiros, e 
todos têm a capacidade de acelerar. Assim, essas são características destinadas à superclasse Veículo e que são 
herdadas por suas subclasses. 
As particularidades de cada classe são definidas dentro das mesmas. A classe Carro, por exemplo, possui um 
atributo que define a capacidade em litros do porta-malas. Como os outros veículos não possuem porta-malas, 
este é um atributo específico de carros. Da mesma forma, apenas o ônibus pode transportar passageiros em 
pé. Sendo assim, somente ele possui o atributo que diz qual a capacidade de passageiros em pé. Por fim, como 
somente o avião possui hélices, só faz sentido definir o número de hélices de um veículo que seja um avião. 
No diagrama de classes UML da Figura 24, percebemos o polimorfismo através da reescrita do método acelerar( ), 
que é um método herdado da classe Veículo, mas cada subclasse implementa de modo diferente. Isso é necessário 
porque, apesar de todos os veículos possuírem a propriedade de aceleração, devido às diferenças entre cada um 
deles, esta propriedade é implementada de formas diferentes. Sendo assim, faz-se necessário sua reescrita nas 
subclasses. Nesse caso, estamos implementando o polimorfismo dinâmico.
43
Programação Orientada a Objetos | Unidade 3 - Herança e Polimorfismo
3.1.4.1 Classes abstratas e interfaces em UML
Classes abstratas no diagrama de classes UML diferenciam-se das classes concretas apenas pela fonte do texto, 
que é dada em itálico. Se uma classe possui pelo menos um método abstrato, dizemos que ela é abstrata. Para 
definir que uma classe é abstrata no diagrama de classes UML, colocamos seu nome em itálico. Para especificar 
quais métodos são abstratos, também colocamos seus nomes em itálico. A Figura 25 mostra a diferença na repre-
sentação da classe Pessoa como sendo concreta (lado esquerdo) e como sendo abstrata (lado direito). Podemos 
observar que, na classe do lado direito, o método getNome( ) e o nome da classe (Pessoa) estão em itálico. 
Figura 25 – Diferença entre uma classe concreta e uma classe abstrata
Pessoa
X
Pessoa
+ getNome( ): String
Classe Concreta Classe Abstrata
- nome: String
+ getNome( ): String
- nome: String
Fonte: Elaborada pelo autor.
Representar interfaces em diagramas UML é ainda mais simples. Basta colocar o termo reservado <<interface>> 
antecedendo o nome da classe. A Figura 26 ilustra um exemplo de uma interface chamada Pessoa.
Figura 26 – Descrição de uma interface no diagrama de classes UML
Pessoa
+ getNome( ): String
<<interface>>
Fonte: Elaborada pelo autor.
3.1.5 Codificação em Java
A implementação do conceito de herança na linguagem Java é realizada através da palavra reservada extends pre-
cedida pelo nome da subclasse (que está sendo definida) e seguida pelo nome da superclasse que ela “estende”/
herda. A Figura 27, a seguir, ilustra um exemplo de implementação da superclasse Veículo. 
44
Programação Orientada a Objetos | Unidade 3 - Herança e Polimorfismo
Figura 27 – Exemplo de superclasse Veículo
Fonte: Elaborada pelo autor.
A classe definida na Figura 27, por si só, não é ainda uma superclasse. No momento em que “estendemos” essa 
classe utilizando a palavra extends, ela se torna uma superclasse, porque, então, outra classe herda suas proprie-
dades. Como podemos observar,essa classe possui atributos que são comuns a veículos de forma geral, como: 
tipo de combustível, capacidade de passageiros e velocidade atual. Além disso, essa classe possui dois constru-
tores: um sem parâmetros, no qual se atribui à velocidade inicial do veículo o valor zero; e outro no qual o valor 
desse atributo é um parâmetro do construtor. Por fim, são definidos dois outros métodos: o método acelerar( ) e 
outro método que retorna o valor da velocidade atual. 
A classe Carro herdando as características de Veículo é definida no código da Figura 28. Podemos notar a palavra 
reservada extends na declaração da classe Carro indicando sua relação de herança com a classe Veículo. Assim, 
Carro é subclasse, e veiculo é superclasse. Além das propriedades herdadas, a classe Carro possui um atributo 
específico que denota a capacidade do porta-malas de um carro. Esta classe possui um construtor implemen-
tado que recebe como parâmetros a capacidade do porta-malas e a velocidade inicial do veículo. Este último 
atributo é comum a todos os veículos e, portanto, existe um construtor na superclasse que instancia um Veículo 
recebendo esse parâmetro. Dentro do construtor da classe Carro, podemos chamar o construtor de Veículo, pas-
sando esse parâmetro através do uso da palavra reservada super. Sua ação é chamar o construtor da superclasse 
que se enquadre com os parâmetros passados. 
Figura 28 – Subclasse Carro que herda os atributos da classe Veículo
Fonte: Elaborada pelo autor.
45
Programação Orientada a Objetos | Unidade 3 - Herança e Polimorfismo
Além do método construtor, a classe definida na Figura 28 reescreve o método acelerar( ) definido inicialmente 
na sua superclasse. Para indicar a reescrita de um método herdado em Java, utilizamos a palavra reservada @
Override na linha anterior à declaração do método. Isso indica que instâncias específicas da classe Carro, ao cha-
marem o método Acelerar, vão executar a ação definida na reescrita só método, e não a ação inicial do método 
herdado. A utilização de @Override indica a utilização do polimorfismo dinâmico. 
Na Figura 29, observamos a instanciação de dois objetos diferentes. O objeto v é do tipo veículo e possui veloci-
dade inicial igual a 100. O objeto c é do tipo Carro, possui capacidade de 50 litros no porta-malas e velocidade 
inicial também igual a 100. Primeiramente, chamamos o método acelerar( ) para o objeto v. Em seguida, chama-
mos o método acelerar( ) para o objeto c. O resultado dessa execução é apresentado na Figura 30. 
Figura 29 – Instanciação da superclasse Veículo e da subclasse Carro
Fonte: Elaborada pelo autor.
Figura 30 – Impressão na tela gerada pelo código da Figura 29.
150
110
Fonte: Elaborada pelo autor.
Como observado na Figura 30, apesar de ambos os veículos (o genérico v e o carro c) possuírem velocidades 
iniciais iguais, a velocidade atual de ambos após a aceleração é diferente. Isso acontece devido ao fato de que 
a classe Carro sobrescreve o método acelerar de modo que, ao invés de esse método causar um incremento de 
50 na velocidade atual, o valor desse incremento é igual a 10. Assim, quando chamamos c.acelerar(), estamos 
chamando o método acelerar( ) definido na classe Carro. Quando chamamos v.acelerar(), estamos chamando 
o método acelerar( ) definido na classe Veículo. 
46
Programação Orientada a Objetos | Unidade 3 - Herança e Polimorfismo
3.1.5.1 Classes abstratas e interfaces em Java
Na programação em Java, podemos aplicar os conceitos de classes abstratas e interfaces abordados anterior-
mente. Para fazer com que uma classe se torne abstrata, utilizamos a palavra reservada abstract, como mostrado 
a seguir:
public abstract class Pessoa {
}
Para definir um método como abstrato, utilizamos a palavra abstract de maneira semelhante:
public abstract String getNome( );
Métodos construtores nunca podem ser declarados como abstratos. Como construtores não 
podem ser herdados, nunca seriam implementados.
Fique atento!
Interfaces consistem em atributos que são constantes e métodos contendo apenas suas declarações, que vão ser 
herdadas pelas subclasses. Ou seja, uma interface implica que todos os seus métodos são abstratos. Em Java, a 
declaração de uma interface é definida pela palavra reservada interface substituindo a palavra class utilizada para 
classes, como mostra o exemplo a seguir, ilustrando a interface Pessoa:
public interface Pessoa { 
 String getNome ( );
}
A interface Pessoa possui apenas um método abstrato chamado getNome( ), que suas subclasses devem imple-
mentar. Caso uma subclasse não implemente todos os métodos de sua interface, devemos declarar essa sub-
classe como abstrata. Abaixo, visualizamos um exemplo de classe Funcionário que implementa a interface Pes-
soa. Essa classe possui seus próprios atributos, além de implementar o método abstrato getNome( ) declarado em 
Pessoa seguindo exatamente a declaração da interface.
47
Programação Orientada a Objetos | Unidade 3 - Herança e Polimorfismo
public class Funcionario implements Pessoa {
 private int idade;
 private String nomeFunc;
 public String getNome ( ) {
 return nomeFunc;
 }
}
Os métodos dentro de uma interface são todos abstratos e públicos. Entretanto, não utiliza-
mos os modificadores public e abstract para esses métodos, pois isso causaria redundância. O 
mesmo acontece com os atributos constantes dentro da interface.
Saiba mais
Outro exemplo de utilização de interfaces é dado na Figura 31. Nesta figura, exemplificamos a codificação da 
Interface FormaGeometrica, que descreve uma forma geométrica desenhada no plano cartesiano. Sendo assim, 
qualquer quer seja a forma geométrica, podemos requisitar os valores de seu centro X e Y no plano cartesiano. 
Além disso, também deve ser possível calcular e obter seu perímetro e área ocupada. 
Figura 31 – Codificação da Interface FormaGeometrica em Java
Fonte: Elaborada pelo autor.
Na Figura 32, temos a codificação de uma implementação da Interface FormaGeometrica: a classe Quadrado. A 
classe Quadrado possui os atributos referentes aos valores do ponto (X,Y) do seu centro. Além disso, para dese-
nhar um quadrado, basta saber o valor do lado e lembrar que um quadrado possui todos os quatro lados iguais. 
Além de implementar os métodos abstratos que retornam o ponto central do quadrado, também implementa-
mos o método que calcula seu perímetro – que, no caso do quadrado, é a soma de todos os lados – e que calcula 
sua área – que, no caso do quadrado, é o valor de (lado*lado). 
48
Programação Orientada a Objetos | Unidade 3 - Herança e Polimorfismo
Figura 32 – Codificação da implementação quadrado em Java
Fonte: Elaborada pelo autor.
Síntese da unidade
Nesta unidade, abordamos os conceitos de herança e polimorfismo na orientação a objetos. Dentro de herança, 
elucidamos as diferenças entre herança simples e herança múltipla. Após definir o conceito de herança, defini-
mos classes abstratas e interfaces fazendo um contraste entre ambos os conceitos. Por fim, mostramos como 
representar todos os conceitos abordados em diagramas de classes UML e como codificá-los em Java.
49
Considerações finais
Com esta unidade, finalizamos a abordagem dos quatro pilares da pro-
gramação orientada a objetos: abstração, encapsulamento, herança e 
polimorfismo. Conhecendo esses pilares, é possível ter-se uma boa ideia 
do imenso potencial das linguagens orientadas a objetos.
50
 4Unidade 44. Composição e Agregação
Para iniciar seus estudos
Nesta unidade, você será capaz de compreender as principais diferenças 
entre dois tipos de associação: composição e agregação. Apesar de mui-
tas vezes parecerem similares, são conceitos diferentes, cuja compreen-
são permite decidir em relação à quando a utilização de cada um deles 
pode ajudar no projeto e desenvolvimento de softwares mais coerentes. 
Pronto para começarmos? 
Objetivos de Aprendizagem
• Conhecer os tipos de associação através de composição e agrega-
ção dentro do contexto deorientação a objetos e da aplicação dos 
mesmos na representação de projeto de sistemas através de dia-
gramas de classe UML e na codificação através da linguagem Java. 
51
Programação Orientada a Objetos | Unidade 4 - Composição e Agregação
Introdução da unidade
Nesta unidade, vamos abordar dois tipos de associação/relacionamento entre classes: composição e agregação. 
A importância de se conhecer ambos os conceitos, compreender suas diferenças e entender quando utilizá-los 
é muito grande e pode impactar diretamente no projeto e desenvolvimento de softwares de qualidade. Além de 
apresentar esses conceitos, ilustramos suas aplicações na representação com diagrama de classes UML e na 
codificação em Java. Por fim, também abordamos o conceito de dependência entre classes mostrando sua apli-
cação, bem como a demonstração da utilização de interfaces em UML.
4.1 Composição e agregação
Nesta unidade, vamos abordar dois mecanismos de reaproveitamento de classes da programação orientada a 
objetos: composição e agregação. Esses conceitos são relacionamentos parte-todo (como mostrado a seguir) e 
acontecem quando o atributo de uma classe é objeto de outra. Os verbos associados a esses relacionamentos 
são “consiste em”, “contém” e “é parte de”. A compreensão desses conceitos é necessária para melhor enten-
dimento sobre quando aplicá-los em determinadas situações. Além de apresentar esses conceitos, mostramos 
como representá-los no diagrama de classes UML e como aplicá-los na codificação na linguagem Java. Por fim, 
esclarecemos também os conceitos de dependência e associação.
4.1.1 Composição
O primeiro mecanismo de reaproveitamento de classes na programação orientada a objetos é conhecido como 
composição ou delegação. Para entender melhor esse conceito, faremos inicialmente uma analogia com o 
mundo real, visto que a produção de um software consiste em representar e resolver um problema do mundo em 
que vivemos.
A composição também é chamada de delegação, pois, quando se instancia um objeto de 
determinada classe, de certa forma, delega-se o trabalho a ser feito a essa classe. 
Imagine o exemplo de uma casa. Ela possui paredes, telhado e portas. Sem esses compo-
nentes, uma casa não existiria. Logo, uma casa é composta de paredes, telhado e portas. A 
Figura 33 ilustra este exemplo. O mesmo se aplica ao conceito de composição no contexto 
de orientação a objetos. A composição é um relacionamento em que classes compostas por 
outras dependem destas para existir. Um exemplo disso seria a classe “Corpo”, que é com-
posta pela classe “Coração”. Sem a classe “Coração”, a classe “Corpo” não existiria. Dizemos 
que esse relacionamento é todo-parte, no qual há um alto grau de dependência entre eles (o 
todo não existe sem a parte). Sendo assim, existe um relacionamento de composição entre as 
classes “Corpo” e “Coração”.
Fique atento!
52
Programação Orientada a Objetos | Unidade 4 - Composição e Agregação
Figura 33 – Exemplo de composição na classe “Casa”
Fonte: SHUTTERSTOCK, 2018.
Em muitas situações, surge a dúvida sobre a utilização de herança ou composição. De forma 
geral, utilizar composição traz mais vantagens, principalmente em projetos mais simples. 
Para saber se a herança é mais adequada, analise se a classe “Filha” é “um tipo especial de”, 
e não “um papel desempenhado por”. 
Saiba mais
Na programação orientada a objetos, a composição acontece quando uma classe A instância dentro dela um 
objeto ou uma lista de objetos de outra classe (B). Nesse caso, quando o objeto da classe A é destruído, todos os 
objetos instanciados nesta classe, incluindo o da classe B, são destruídos também. O relacionamento entre as 
classes na composição pode ser representado pela expressão “contém”. No caso do exemplo mencionado, pode-
mos dizer que “a classe A contém um objeto/alguns objetos da classe B”. Em outras palavras, a instância de uma 
classe existente é utilizada como componente/atributo de outra classe.
4.2 Agregação
A agregação é outro mecanismo de reaproveitamento de classes e consiste em um relacionamento parecido 
com composição, mas que possui algumas importantes diferenças. Voltando ao exemplo de uma casa, podemos 
imaginar que nesta casa existam espelhos. Uma casa não existe sem paredes, telhado ou portas, mas existe sem 
espelhos. Portanto, dizemos que os espelhos agregam a casa. 
53
Programação Orientada a Objetos | Unidade 4 - Composição e Agregação
Considere agora o exemplo do corpo humano da seção anterior. Apesar de um corpo não conseguir existir sem 
um coração, podemos muito bem sobreviver sem unhas. Portanto, o relacionamento entre o corpo humano e 
suas unhas é um relacionamento de agregação. 
Na Figura 34, lustramos um exemplo de agregação entre as classes “Pessoa” e “Cabelo”. Se “Pessoa” e “Cabelo” 
são duas classes, a classe “Pessoa”, em seu interior, possui um objeto da classe “Cabelo” a fim de definir as carac-
terísticas de cabelo específicas de uma pessoa. Entretanto, a classe “Pessoa” não precisa da classe “Cabelo” para 
existir. Portanto, esse relacionamento é uma agregação. Na Figura 34 temos dois objetos do tipo “Pessoa”, em 
que uma possui “Cabelo”, e outra, não. Contudo, ambas continuam sendo pessoas. 
Figura 34 – Exemplo de agregação com a classe “Pessoa”
Fonte: SHUTTERSTOCK, 2018.
A relação existente na agregação também é uma relação todo-parte. A classe agregada (o todo) é construída 
pelo conjunto de componentes (as partes). Em comparação à composição, a diferença é que a parte pode existir 
sem o todo. Portanto, a dependência entre parte e todo é considerada fraca. 
Pensando na programação orientada a objetos, a agregação se dá quando uma classe A em seu interior possui 
instâncias/objetos de outra classe (B). Entretanto, esses objetos podem não existir para determinadas instâncias 
da classe A, e a classe A não deixa de existir por isso. Diferentemente da composição, quando um objeto da classe 
A é destruído, os objetos da classe B se mantêm intactos.
Uma diferença importante entre composição e agregação consiste no fato de que, na agre-
gação, um único objeto pode ser parte de vários objetos ao mesmo tempo. Entretanto, na 
composição, um único objeto pode ser parte de apenas um objeto. Assim, ao projetar um 
software, devemos analisar a necessidade da utilização de um conceito ou de outro. 
Saiba mais
54
Programação Orientada a Objetos | Unidade 4 - Composição e Agregação
Podemos visualizar a composição como sendo uma agregação mais forte. 
Pensando de forma mais prática, a principal diferença entre composição e agregação é que, 
na agregação, o objeto da classe A, por exemplo, recebe os objetos já prontos da classe B 
através de algum método. Ou seja, a classe A, apesar de possuir objetos da classe B, não 
tem que necessariamente instanciá-los. Eles são criados de fora da classe A e recebidos por 
parâmetro em algum método de agregação existente. 
4.2.1 Representação em UML
Para entender como representar os relacionamentos aqui abordados, é necessária a compreensão do conceito de 
associação, até então ainda não definido. Uma associação em UML é utilizada para representar o relacionamento 
entre duas classes. Relacionar duas classes indica que seus objetos podem se comunicar. Essa comunicação per-
mite a uma classe que um objeto de outra classe faça alguma coisa no lugar da primeira. O relacionamento de 
associação pode ser do tipo “é um” ou do tipo “é parte de”. Na Figura 35, exemplificamos o relacionamento de 
associação entre as classes “Pessoa” e “Dependente”.
Figura 35 – Relacionamento de associação entre “Pessoa” e “Dependente”
Pessoa Dependente
- idade: int
1 0..*
- nome: String
- nome: String
- Idade: int
- grauParentesco: String
Fonte: Elaborada pelo autor.
Para representar uma associação entre duas classes, simplesmente utilizamos uma linha para conectá-las. Junto 
a essa linha, podemos também determinar o papel de cada classe na relação pelo nomear da ligação. Além disso, 
nesse relacionamento, podemos tambémdefinir o que chamamos de multiplicidade. A multiplicidade indica 
quantos objetos de cada classe podem estar envolvidos no relacionamento. No caso do exemplo acima, a mul-
tiplicidade é definida de forma a indicar que uma pessoa pode ter 0 ou mais dependentes (símbolo: 0..*). Além 
disso, um dependente só pode ter uma (símbolo: 1) pessoa da qual ele depende. Esses símbolos são posicionados 
de forma estratégica a fim de estarem próximos ao objeto cuja quantidade eles estão representando. A Tabela 2 
a seguir exibe valores de multiplicidade possíveis e seus significados.
55
Programação Orientada a Objetos | Unidade 4 - Composição e Agregação
Tabela 2 – Valores de multiplicidade para os relacionamentos no diagrama de classes UML
Tipos Significado
0..1 0 ou 1 instância. Notação n..m indica no mínimo n e, 
no máximo, m. 
0..* ou * 0 ou mais instâncias.
1 Exatamente uma instância.
1..* Pelo menos uma instância.
Fonte: Elaborada pelo autor.
Dentre os tipos de multiplicidade definidos na tabela acima, podemos notar a possibilidade de definir um valor fixo, 
um valor mínimo e/ou um valor máximo para a dependência nos relacionamentos do diagrama de classes UML.
4.2.2 Composição e agregação em UML
A composição e agregação são tipos específicos de associação e, também, podem ser representados utilizando 
o diagrama de classes UML. Para este propósito, utilizamos dois símbolos diferentes para conectar as classes e 
indicar o tipo de relacionamento e o sentido. O símbolo no diagrama de classes UML para representar a relação 
de composição é o diamante cheio e pode ser visualizado na Figura 36. Colocamos a classe que contém objetos 
de outra classe na ponta da linha com o diamante e o da classe que a compõe na ponta oposta.
Figura 36 – Símbolo da composição no diagrama de classes UML
Fonte: Elaborada pelo autor.
O símbolo no diagrama de classes UML para representar a relação de agregação é o diamante vazio. A Figura 37 
exemplifica esse símbolo.
Figura 37 – Símbolo da agregação no diagrama de classes UML
Fonte: Elaborada pelo autor.
Em ambos os casos (na agregação e na composição), o diamante fica próximo à classe que contém o “todo”, ou 
seja, quem instancia as classes para formar o relacionamento é a classe que fica mais próxima do diamante. O 
exemplo na Figura 38, a seguir, mostra a relação de composição entre as classes A e B.
56
Programação Orientada a Objetos | Unidade 4 - Composição e Agregação
Figura 38 – Relação de composição entre a classe A e classe B em UML
Classe A Classe B
- instancia: ClasseB
< <constructor> > + ClasseB( )< <constructor> > + ClasseA( )
Fonte: Elaborada pelo autor.
O exemplo da figura anterior ilustra uma situação em que um objeto da classe B é um atributo da classe A. Esse 
atributo, apesar de não explicitado, é um objeto instanciado dentro do método construtor “ClasseA( )”. Desse 
modo, o relacionamento entre as classes A e B é de composição. A classe B compõe a classe A. 
Já em relação à agregação, o exemplo a seguir da Figura 39 demonstra como representar uma relação desse tipo 
entre as classes A e B.
Figura 39 – Relação de agregação entre a classe A e a classe B em UML
Classe A Classe B
- instancia: ClasseB
< <constructor> > + ClasseB( )< <constructor> > + ClasseA( )
+ setlnstancia(objB: ClasseB): void
Fonte: Elaborada pelo autor.
Na figura acima, podemos perceber que classe A possui um atributo que é um objeto de classe B. Entretanto, 
como o relacionamento entre essas classes é uma agregação, o objeto da classe B que será o atributo da classe A 
será instanciado fora da classe A. Portanto, existe um método chamado “setInstancia” que vai receber esse objeto 
e atribuí-lo ao atributo “instancia”. Este método é também chamado de método de agregação.
4.2.3 Dependência em UML
Em algumas situações, podemos querer deixar explícito em um projeto de software que uma mudança na forma 
como um elemento foi especificado pode alterar a especificação de outro elemento. Podemos pensar nisso como 
sendo a situação em que objetos de uma classe, ocasionalmente, utilizam serviços que outras classes oferecem. 
Neste caso, a relação entre essas duas classes é de dependência. 
Fazendo uma analogia com o mundo real, imagine a relação existente entre uma casa e sua rua. Uma casa 
depende da sua rua, porque, sem a rua, a casa não consegue ser uma casa. Pelo fato de essa dependência existir, 
qualquer alteração na rua pode gerar um efeito colateral na casa. Se, por exemplo, uma obra da rede de esgoto 
na rua paralisar o funcionamento da rede por uma semana, a casa será afetada diretamente, pois uma casa sem 
um sistema de esgoto não possui condições de uso. 
57
Programação Orientada a Objetos | Unidade 4 - Composição e Agregação
Uma relação de dependência acontece quando, por exemplo, uma classe chama métodos de outras classes. 
Outro exemplo seria em situações nas quais métodos de uma classe recebem como argumentos objetos de 
outras classes ou retornam objetos de outras classes. No diagrama de classes UML, representar dependências é 
importante, visto que é uma forma de perceber quais classes devem ser compiladas juntas. As classes das quais 
uma classe depende devem estar juntas na sua compilação. A Figura 40 ilustra o símbolo de dependência no 
diagrama de classes UML. Sua direção indica a relação “depende de”. 
Figura 40 – Símbolo de dependência no diagrama de classes UML
Fonte: Elaborada pelo autor.
A Figura 41 ilustra a relação de dependência entre as classes A e B. Esse relacionamento se estabelece devido 
ao fato de que o método “operacao” na Classe A recebe como parâmetro um objeto da Classe B. Sendo assim, 
a Classe A depende da Classe B.
Figura 41 – Relação de dependência entre as classes A e B
Classe A Classe B
+ operacao(arg1:Classe B): voind
Fonte: Elaborada pelo autor.
O fato de a Classe A depender da Classe B significa que existe alguma ligação entre as classes que resulta nessa 
dependência. Essa indicação ajuda o programador a compreender que, se ele quiser implementar a Classe A, 
deverá ter a Classe B já implementada.
4.2.4 Utilização de interface em UML
O uso de interfaces é muito comum na programação orientada a objetos; portanto, devemos poder represen-
tá-lo no diagrama de classes UML. Uma classe é definida como interface nesse diagrama quando colocamos o 
estereótipo <<Interface>> antes de seu nome na sua representação. Dessa forma, isso indica que outras classes 
realizarão a implementação de seus métodos. O relacionamento entre essas classes e a interface pode ser des-
crito como uma generalização/especificação, dependendo do ponto de vista. 
No exemplo da Figura 42, ilustramos o modo como representamos esse relacionamento no diagrama de classes 
UML. Ele é marcado pelo uso de setas pontilhadas apontando para a interface. Nesse caso, dizemos que a inter-
face “Funcionário” generaliza as classes “Diretor”, “Gerente” e “Assistente”, enquanto que essas mesmas classes 
especializam a interface “Funcionário”.
58
Programação Orientada a Objetos | Unidade 4 - Composição e Agregação
Figura 42 – Relacionamento de generalização/especialização em UML
<<Interface>>
+nome:String
+Setor:String
+Listar( ):void
Funcionario
Diretor Gerente Assistente
Fonte: Elaborada pelo autor.
No diagrama acima, o fato de as classes “Diretor”, “Gerente” e “Assistente” implementarem a interface “Funcio-
nario” sugere que as mesmas herdam seus atributos e métodos. Além disso, obriga as três classes a implementa-
rem os métodos existentes em “Funcionario”, que, no exemplo acima, seria apenas o método listar( ).
4.3 Codificação em Java
Todos os conceitos estudados nesta unidade são aplicáveis na programação em linguagens orientadas a objetos. 
Nesta seção, vamos mostrar como aplicar esses conceitos utilizando a linguagem de programação Java.
4.3.1 Associação em Java
A associação na linguagem Java pode ser dada, por exemplo, entre uma classe “Pessoa” e a classe “Dependente”, 
mostradas nos códigos das Figuras43 e 44, respectivamente. Podemos perceber que a classe “Pessoa” possui 
uma lista de objetos (representado por um objeto do tipo List< >, definido no pacote java.util do Java) do tipo 
“Dependente”. Essa lista é, por padrão, vazia, mas é possível atribuí-la através dos métodos “setDependentes” ou 
“addDependente”.
59
Programação Orientada a Objetos | Unidade 4 - Composição e Agregação
Figura 43 – Classe “Pessoa” na linguagem Java
public class Pessoa {
 private String nome;
 private int idade;
 private List<Dependente> dependentes = new Arraylist <Dependente> ( 0 );
 public void setDependestes (list<Dependente> dependentes) {
 } 
 public List<Dependentes> getDependestes {
 }
 public void addDependente ( Dependente de dependete ) {
 this.dependentes.add ( dependente );
 }
}
Fonte: Elaborada pelo autor.
Figura 44 – Classe dependente na linguagem Java
public class Dependente {
 private String nome;
 private int idade;
 private String grauParentesco;
 private Pessoa responsavel;
 public void setResponsavel (Pessoa resp) {
 responsavel = resp;
 }
 public Pessoa getResponsavel ( ) {
 }
}
Fonte: Elaborada pelo autor.
Como é notável pela Figura 44, a classe “Dependente” possui um atributo responsável, que é um objeto do tipo 
“Pessoa”. Um valor é atribuído a este atributo através do método “setResponsavel”; sendo assim, existe um rela-
cionamento ou uma associação entre as classes “Pessoa” e “Dependente”, visto que eles instanciam objetos 
entre si.
4.3.2 Composição em Java
A composição, como sendo um tipo específico de associação, se dá quando, dentro de uma classe, se instancia 
um objeto de outra classe e se define este objeto como seu atributo. Isso acontece de forma explícita no exemplo 
da Figura 45.
60
Programação Orientada a Objetos | Unidade 4 - Composição e Agregação
Figura 45 – Exemplo de composição na linguagem Java
public class A {
 private B b;
 public A( ) {
 b = new B( );
 }
}
public class B {
 public B( ){
 }
}
Fonte: Elaborada pelo autor.
Na codificação acima, temos a Classe A, que possui um atributo do tipo B e, em seu construtor, realiza uma ins-
tanciação de um objeto desse tipo para atribuir a b. Sendo assim, estabeleceu-se um relacionamento de compo-
sição entre as classes A e B.
4.3.3 Agregação em Java
A agregação também é um tipo específico de associação, com a diferença de que uma classe tem como atributo 
um objeto de outra classe e recebe seu valor – que, no caso, é um objeto – por parâmetro, ou seja, sua instancia-
ção é feita de fora da classe. Na Figura 46, temos um exemplo de agregação na linguagem Java.
Figura 46 – Exemplo de agregação na linguagem Java
public class A {
 private B b;
 public A ( ){
 }
 public void setB ( B b ) {
 this.b = b;
 } 
 public B getB( ) {
 return b;
 }
}
public class B {
 public B( ) {
 }
}
Fonte: Elaborada pelo autor.
A Classe A possui como atributo o objeto b pertencente à classe B. Entretanto, em nenhum momento esse objeto 
é instanciado explicitamente. O método “setB” (chamado método de agregação) é quem recebe como parâme-
tro um objeto do tipo B para atribuirmos ao atributo b da classe A. Dessa forma, existe um relacionamento de 
agregação entre a Classe A e a Classe B.
61
Programação Orientada a Objetos | Unidade 4 - Composição e Agregação
4.3.4 Dependência em Java
A dependência é uma relação em que fica explícito que a alteração na especificação de uma classe altera a espe-
cificação de outra classe, porque uma especificação de classe depende da outra. Nesse relacionamento, não há 
um envolvimento necessariamente de atributos. Podemos verificar pelo exemplo dado nas Figuras 47 e 48 como 
essa relação acontece. Na Figura 47, temos a classe “Cartucho”, cujo método construtor pode ou não receber a 
cor como parâmetro. Já na Figura 48, podemos observar a classe “Tinta”. Essa classe possui um método chamado 
“escolhaTinta” que possui como parâmetro, além do tipo da tinta, um objeto do tipo “Cartucho”. Apesar de este 
objeto não ser um atributo de “Tinta”, ele é utilizado como parâmetro para determinar a escolha da tinta par-
tindo do seu tipo e do cartucho a ser utilizado. Portanto, há uma dependência entre a classe “Tinta” e a classe 
“Cartucho”.
Figura 47 – Classe “Cartucho” na linguagem Java
public class Cartucho {
 public Cartucho (String cor) {
 / * * /
 }
 public Cartucho ( ) {
 / * * /
 }
}
Fonte: Elaborada pelo autor.
Figura 48 – Classe “Tinta” na linguagem Java
public class Tinta {
 public Tinta ( ) {
 / * * /
 }
 public void escolhatinta ( int tipoTinta, Cartucho cart ) 
 / * * /
 }
}
Fonte: Elaborada pelo autor.
A classe “Tinta” depende da classe “Cartucho” e não pode ser compilada sem a mesma. Portanto, para que con-
sigamos de alguma forma utilizar a classe “Tinta”, antes devemos implementar a classe “Cartucho”.
Síntese da unidade
Esta unidade abordou principalmente os conceitos de composição e agregação no contexto de orientação a 
objetos. Além disso, apresentamos uma ilustração de suas aplicações através da representação de sistemas com 
diagrama de classes UML e da codificação na linguagem Java. Por fim, também apresentamos conceitos como 
dependência e sua aplicação e a demonstração da utilização de interfaces no diagrama de classes UML.
62
Considerações finais
Agregação, Composição ou Heranças: todas são motivos de dúvida entre 
projetistas e desenvolvedores de sistemas. O aprofundamento na com-
preensão desses conceitos torna essa dúvida algo mais fácil de ser resol-
vido, facilitando a produção de sistemas, resultando em softwares mais 
coerentes e de alta qualidade.
63
 5Unidade 55. Boas Práticas I
Para iniciar seus estudos
Nesta unidade, você conseguirá compreender algumas das estratégias 
para o desenvolvimento de códigos de alta qualidade e que são extrema-
mente importantes. As estratégias apresentadas expõem questões sobre 
nomenclatura dos identificadores, alinhamento de código, visibilidade de 
atributos e métodos, duplicação de código, entre outras. Conhecer essas 
estratégias é essencial, visto que 80% do tempo gasto por uma empresa 
em um software é dedicado à sua manutenção. Se pudermos reduzir esse 
tempo através de boas práticas de desenvolvimento, isso seria algo muito 
significante. Pronto para começar?
Objetivos de Aprendizagem
• Compreender e dominar algumas das principais estratégias exis-
tentes para desenvolvimento de sistemas em linguagem Java a fim 
de obter códigos mais legíveis, reutilizáveis e de fácil manutenção.
64
Programação Orientada a Objetos | Unidade 5 - Boas Práticas I
Introdução da unidade
Escrever códigos bem descritivos e documentados aumenta a produtividade enquanto diminui o tempo gasto 
com treinamento devido à sua fácil compreensão. Na maioria das empresas, dificilmente será a mesma pessoa a 
manipular um mesmo software durante toda a sua vida útil. A adoção de boas práticas de programação permite 
com que o código se torne mais legível a todos, fazendo com que novos desenvolvedores compreendam o código 
mais rapidamente, o que acelera a produção da empresa. Por essas razões, algumas convenções foram adotadas 
após frutíferas experiências de aplicação dessas estratégias na codificação. Existem diversas práticas que podem 
levar a um software de qualidade e, nesta unidade, serão apresentadas algumas delas. A nomenclatura dos iden-
tificadores, a forma como o código é alinhado, a visibilidade de atributos e métodos, o surgimento de duplicação 
de códigos, entre outras, são algumas questões abordadas nesta unidade. 
5.1 Boas práticas I
A escrita decódigos em linguagens orientadas a objetos pode ser feita por qualquer pessoa. Com alguns meses 
de experiência em programação, um indivíduo já consegue escrever um código que funcione. Entretanto, escre-
ver códigos de forma correta leva mais trabalho do que simplesmente fazê-lo funcionar, sendo, portanto, uma 
arte que deve ser praticada para seu aperfeiçoamento. Nesta unidade, apresentamos práticas que levam a códi-
gos mais legíveis, menores, mais rápidos e com menos possibilidades de erros. Apontamos também algumas 
convenções em relação à nomenclatura, alinhamento de código, visibilidade das propriedades de uma classe, 
duplicação de código, entre outras.
5.1.1 Nomenclatura
A utilização de convenções e boas práticas na nomenclatura de identificadores nas linguagens de programa-
ção orientadas a objetos faz com que os códigos se tornem mais compreensíveis, facilitando diversas etapas 
na produção de um software. Nesta seção, apresentamos algumas regras para definição de nomes para classes, 
interfaces, atributos e métodos.
5.1.1.1 Classes e interfaces
A convenção quanto ao nome das classes e interfaces é que todo nome deve começar com letra maiúscula e não 
deve conter letras que não pertençam à tabela ASCII. Assim, caracteres especiais (%, #, !, ?, etc.) não devem estar 
no nome de classes. Em algumas situações, pode ser necessária mais de uma palavra para definir o nome de uma 
classe. Nesse caso, ambas as palavras devem começar com letra maiúscula e não deve haver espaço entre elas. Se 
quisermos, por exemplo, representar um porta-malas através de uma classe em Java, o nome desta classe pode-
ria ser PortaMalas (sem espaço e começando cada palavra por letra maiúscula). Outra convenção relacionada ao 
nome de classes é nomear o arquivo contendo o código-fonte com o mesmo nome da classe. A figura abaixo 
ilustra essa situação para a classe “Cartucho”, cujo arquivo de seu código-fonte é denominado “Cartucho.java”.
65
Programação Orientada a Objetos | Unidade 5 - Boas Práticas I
Figura 49 – Classe “Cartucho” representando um objeto do mundo real
Cartucho. Java
public class Cartucho {
 public Cartucho (String cor) {
 /**/
 }
 public Cartucho() {
 /**/
 }
}
Fonte: Elaborada pelo autor.
Ao definir classes, é importante lembrar que seus nomes devem fazer menção ao objeto do mundo real que estão 
representando. Quando, por exemplo, desejamos criar uma classe com os atributos volume, canal e sintonia e 
que contenha os métodos mudardeCanal( ) e aumentarVolume( ), esta classe só pode ter os nomes Televisão ou 
TV. Outro nome, como Bola, não faria menção nenhuma ao que esta classe representa, o que seria errado. Alguns 
exemplos de nomes de classe são “Pessoa”, “Conta”, “Banco”, “AgenciaDeEmprego”, etc.
5.1.1.2 Atributos e variáveis
Em relação aos atributos e variáveis, o ideal é que eles comecem com letra minúscula e possam conter qualquer 
letra, além dos caracteres “$” e “_” e números. Entretanto, não devem começar com números. Se o nome do 
atributo contém mais de uma palavra, a convenção é que elas comecem com letra maiúscula, com exceção da 
primeira. 
Já os nomes de atributos que são finais, ou seja, constantes que não podem ser alteradas (definidas com a pala-
vra reservada final), devem ser escritos em letra maiúscula. Para separar nomes compostos de atributos finais, 
utilizamos o caractere “_”. Um exemplo seria o atributo “PARAR_DE_EXECUTAR”. Os atributos finais também são 
compreendidos como constantes, já que, uma vez definidos, não se pode alterá-los. Na figura a seguir, mostra-
mos um exemplo da classe “Circulo” que contém três atributos variáveis e um atributo final (constante).
66
Programação Orientada a Objetos | Unidade 5 - Boas Práticas I
Figura 50 – Classe “Circulo” com três atributos variáveis e um atributo constante
public class Circulo{
 private double xCentro;
 private double yCentro;
 private double raio;
 final float PI = 3.14159265f;
 public Circulo (double xCentro, double yCentro, double raio){
 this.xCentro = xCentro;
 this.yCentro = yCentro;
 this.raio = xCentro;
 }
 public double get_XCentro ( ){
 return xCentro;
 }
 public double get_YCentro ( ){
 return xCentro;
 }
 public double calculaPerimetro( ){
 return (2*PI*raio);
 }
 public double calculaArea( ){
 return (PI*raio*raio);
 }
 /*...*/
}
Fonte: Elaborada pelo autor.
Quando declaramos constantes com a palavra reservada final, geralmente também coloca-
mos a palavra reservada static neste atributo. Isso faz com que a constante seja utilizada sem 
ter que necessariamente instanciar um objeto da classe. Por isso, muitas vezes definimos 
uma constante com o comando “public static final int NOMEDAVARIAVEL=10;”.
Saiba mais
67
Programação Orientada a Objetos | Unidade 5 - Boas Práticas I
É importante notar que somente o atributo final é descrito por um identificador contendo todas as letras em 
maiúsculo (PI). Os restantes dos atributos são nomeados por palavras com todas as letras em minúsculos. 
5.1.1.3 Métodos
Os métodos devem retratar as ações executadas pelos objetos de classe. Por esse motivo, os métodos geral-
mente são verbos. Podem conter letras maiúscula ou minúscula, mas, por convenção, começa-se o nome do 
método com letra minúscula. Além disso, podem conter “_” e “$”, além de dígitos. Caso mais de uma palavra seja 
utilizada para representar o método, com exceção da primeira palavra, todas devem começar por letra maiúscula.
Pacotes são conjuntos de classes agrupadas. Para definir que uma classe pertence a deter-
minado pacote, basta inserir o comando “package nomeDoPacote;” na primeira linha do 
arquivo que contém seu código-fonte. Para importar todas as classes de um arquivo, basta 
utilizar “import nomeDoPacote.*”. A convenção de nomes de pacote sugere nomes que 
comecem com letras minúsculas e que sigam as demais regras de nomes de classes.
Saiba mais
Além disso, os parâmetros e retornos de métodos devem conter nomes significativos, de forma que não seja 
necessária a leitura de um material adicional, além da documentação para entender a função do método. Exem-
plos de métodos são: “getNome( )”, “executar( )” e “pesquisarNome( )”. Na figura a seguir, o método “latir( )” 
ilustra a convenção adotada para métodos em orientação a objetos.
68
Programação Orientada a Objetos | Unidade 5 - Boas Práticas I
Figura 51 – Codificação da classe “Cachorro” em Java
class Cachorro{
 int tamanho;
 String nome;
 String cor;
 void latir(){
 if(tamanho > 30)
 System.out.println(“Au, Au!”);
 else if(tamanho > 10)
 System.out.println(“Ouo, Ouo!”);
 else
 System.out.println(“Auo!, Auo!”);
 }
]
Fonte: Elaborada pelo autor.
No caso da figura acima, a declaração do método “latir( )” contém apenas letras minúsculas, porque é composto 
somente de uma palavra, estando, portanto, dentro das convenções.
5.1.2 Alinhamento do código
Apesar de a linguagem Java não exigir o alinhamento/indentação do código para sua compilação, a compreen-
são de um código depende totalmente de um bom alinhamento. Por convenção, iniciamos o alinhamento do 
código com quatro espaços ou um tab. Além disso, é bom evitar linhas de código com mais de 80 caracteres, visto 
que, dependendo do computador, a leitura do código se tornaria difícil. 
Caso uma expressão não caiba em uma única linha, é necessário quebrá-la. Para isso, siga os seguintes princípios: 
quebre após uma vírgula; quebre antes de um operador; prefira quebra de linha no nível mais alto do que no nível 
mais baixo; e alinhe a nova linha com o mesmo nível que o começo da expressão da linha anterior. Caso essas 
regras levem a um código confuso, então apenas indente 8 espaços. Na figura abaixo, mostramos o exemplo de 
dois métodos que possuem declarações que não cabem em uma única linha. O método “algumMetodo( )” possui 
a indentação convencional, seguindo as regras citadas anteriormente. Já o método “nomeDoMetodoMaisLongo( 
)” passaria à segunda e terceira linhas à direita, e a declaração ficaria“espremida” no lado direito. Sendo assim, ao 
invés disso, foi utilizada a indentação de 8 espaços.
69
Programação Orientada a Objetos | Unidade 5 - Boas Práticas I
Figura 52 – Exemplo de indentação de declarações longas de métodos
//IDENTAÇÃO CONVENCIONAL
public algumMetodo(int artumento1, String argumento2, String argumento3, String argumento4) {
 /*...*/
}
//IDENTAÇÃO DE 8 ESPAÇÕS PRA EVITAR IDENTAÇÃO MUITO PROFUNDA
private static nomeDoMetodoMaisLongo(int artumento1, String argumento2, String argumento3, String 
argumento4) {
 /*...*/
}
Fonte: Elaborada pelo autor.
Na maioria das declarações de if com muitas condições, faz-se necessária a utilização da regra dos 8 espaços, 
uma vez que utilizar a indentação convencional (4 espaços) tornaria o corpo difícil de ser visto. A próxima figura 
contém um código com três formas diferentes de se indentar uma declaração de if com muitas condições.
Figura 53 – Exemplo de indentação para declarações if
//NÃO USE ESTA IDENTAÇÃO
if ((condicao1 && condicao2)
 || (condicao3 && condicao4)
 || ! (concicao5 && condicao6)) { //QUEBRA RUIM
 facaAlgoUtilComIsso( ); //DEIXA ESSA LINHA FÁCIL DE SE ENGANAR
}
//USE ESTA IDENTAÇÃO
if ((condicao1 && condicao2)
 || (condicao3 && condicao4)
 || ! (concicao5 && condicao6)) {
 facaAlgoUtilComIsso( );
}
//OU USE ESTA
if ((condicao1 && condicao2) || (condicao3 && condicao4)
 || ! (concicao5 && condicao6)) {
 facaAlgoUtilComIsso( );
}
Fonte: Elaborada pelo autor.
70
Programação Orientada a Objetos | Unidade 5 - Boas Práticas I
Na primeira declaração do if, o código das condições se confunde com o código do corpo da condição. Na segunda 
e terceira formas, utiliza-se a regra dos 8 espaços, e a separação entre declaração e corpo fica mais nítida.
5.1.3 Visibilidade de atributos e métodos
A visibilidade de atributos e métodos está relacionada a quem pode ter acesso a eles. Ou seja, podemos deter-
minar se classes externas podem ter acessos a essas propriedades e, caso possam, que tipos de classes o podem. 
Sendo assim, existem boas práticas para determinar a visibilidade de cada um deles. A visibilidade em Java é 
determinada pelas palavras reservadas public, protected e private posicionadas antes da declaração de um método 
ou atributo.
O ideal é deixar o acesso a métodos e atributos tão restritivo quanto possível, de forma a diminuir ao máximo o 
acoplamento entre as classes. Por convenção, os atributos são deixados como privados (private), e os métodos 
getters e setters são utilizados para acessá-los e modificá-los, respectivamente. Entretanto, quando nos referimos 
a superclasses, o ideal é deixá-los como protegidos (protected), visto que suas classes derivadas também poderão 
acessá-los.
Existe uma forma para que uma classe B veja todas as partes privadas de determinada classe. 
A solução é fazer com que a classe B seja amiga da A. Para isso, dentro da declaração de A, 
após o colchete de abertura de classe “{“, colocamos o comando “friend class B;” e seguimos 
com os atributos da classe A nas linhas seguintes.
Fique atento!
Em relação aos métodos, a mesma regra é válida: devemos deixar sua visibilidade tão baixa quanto possível. 
Métodos que só devem ser acessados no interior de uma classe e que são chamados por outros métodos inter-
nos devem ser privados (private). Métodos que podem ser acessados por subclasses devem ser deixados como 
protegidos (protected). Por fim, métodos que podem ser acessados externamente por qualquer classe, como é o 
caso de métodos getters e setters, devem ser deixados como públicos (public).
5.1.4 Duplicação de código
A própria utilização do conceito de orientação a objetos por si só evita a duplicação de código. Se pararmos para 
pensar, sempre que instanciamos um novo objeto de determinada classe, estamos evitando a duplicação de 
código, porque a definição de uma classe é dada uma única vez. Contudo, se não soubermos definir uma classe 
de forma coerente, podemos ainda assim ter duplicação de código desnecessária dentro de uma classe. O exem-
plo da próxima figura demonstra a duplicação de código em métodos pertencendo a uma mesma classe. Como 
podemos perceber, as três linhas que compõem o conteúdo do método são muito semelhantes.
71
Programação Orientada a Objetos | Unidade 5 - Boas Práticas I
Figura 54 – Métodos com código duplicado dentro de uma classe em Java
public Posicao caminheDireita(){
 jogador jog = getJogador();
 jog.mova(“D”);
 return jog.novaPosicao;
}
public Posicao caminheEsquerda(){
 jogador jog = getJogador();
 jog.mova(“E”);
 return jog.novaPosicao;
}
public Posicao caminheFrente(){
 jogador jog = getJogador();
 jog.mova(“F”);
 return jog.novaPosicao;
}
public Posicao caminheTraz(){
 jogador jog = getJogador();
 jog.mova(“T”);
 return jog.novaPosicao;
}
Fonte: Elaborada pelo autor.
Como os métodos do código da figura anterior são semelhantes, podemos substituí-los por um único método, 
que, agora, recebe um parâmetro, que é a direção na qual o jogador deve caminhar. Assim, um método para cada 
direção possível se torna desnecessário. A figura a seguir mostra um código de um método que pode substituir 
os métodos da figura anterior.
Figura 55 – Método “caminhe( )” em determinada classe em Java
public Posicao caminhe(String direcao){
 Jogador jog = getJogador();
 jog.mova(direcao);
 return jog.novaPosicao;
}
Fonte: Elaborada pelo autor.
72
Programação Orientada a Objetos | Unidade 5 - Boas Práticas I
Em relação à duplicação de algoritmos, apresentamos na figura a seguir dois métodos diferentes com implemen-
tações praticamente idênticas: os métodos “irCorrer( )” e “fazerMusculacao( )”. A única diferença entre os dois é 
a chamada aos métodos “correr( )” e “levantarPeso( )”, respectivamente.
Figura 56 – Métodos “irCorrer( )” e “fazerMusculacao( )” na linguagem Java
public void irCorrer(){
 seVestir();
 correr();
 tomarBanho();
}
public void fazerMusculacao(){
 seVestir();
 levantarPeso();
 tomarBanho();
}
Fonte: Elaborada pelo autor.
O código da figura anterior pode ser substituído por um único método, como mostra a próxima figura. Nesse 
caso, o tipo de atividade física seria passado por parâmetro, e o método “atividade( )”, que recebe como parâ-
metro o tipo de atividade, vai implementar o “correr( )” ou “levantarPeso( )” internamente, de acordo com o que 
receberem.
Assim, evitamos a duplicação dos códigos que são semelhantes, como as chamadas de “seVestir( )” e “tomarBa-
nho( )” em ambos.
Figura 57 – Método “fazerAtividadeFisica( )” substituindo os métodos “irCorrer( )” e “fazerMusculacao( )”
public void fazerAtividadeFisica(Acao tipo Atividade) {
 seVestir();
 atividade(tipoAtividade);
 tomarBanho();
}
Fonte: Elaborada pelo autor.
Uma estratégia importante para evitar duplicação de código em linguagens orientadas a objetos é o conceito 
de herança. Uma classe herda todos os atributos e métodos de sua superclasse utilizando a palavra reservada 
extends, evitando assim a reescrita dessas propriedades. Sua utilização leva a códigos ainda menores, mais 
rápidos, legíveis e de mais fácil manutenção. Na figura a seguir, observamos a classe “Eletrodomestico”, que 
possui os atributos “ligado”, “voltagem” e “consumo”. Estes são atributos de quaisquer eletrodomésticos. Caso 
73
Programação Orientada a Objetos | Unidade 5 - Boas Práticas I
codifiquemos classes de tipos específicos de eletrodomésticos, esses atributos apareceriam de forma repetitiva, 
causando duplicação de código. Ao invés disso, implementamos essa classe de forma que outras classes possam 
herdá-la.
Figura 58 – Codificação da superclasse “Eletrodomestico” em Java
public class eletrodomestico {
 private boolean ligado;
 private int voltagem;
 private int consumo;
 public Eletrodomestico(boolean ligado, int voltagem, int consumo) {
 this.ligado = ligado;
 this.voltagem = voltagem;
 this.consumo = consumo;
 }
 public void desligar(){
 ligado=false;
 }
 /*...*/
}Fonte: Elaborada pelo autor.
Utilizando a classe “Eletrodomestico” como superclasse, podemos definir algumas subclasses que herdam suas 
propriedades, como, por exemplo, as classes “Tv” e “Som”, exemplificadas na figura abaixo.
74
Programação Orientada a Objetos | Unidade 5 - Boas Práticas I
Figura 59 – Codificação das subclasses “Tv” e “Som”, que estendem “Eletrodomestico” na linguagem Java
public class Tv extends eletrodomestico {
 private int canal;
 private int volume;
 private int tamanho;
 public TV(int voltagem, int consumo, int canal, int volume, int tamanho) {
 super(false, voltagem, consumo);
 this.canal = canal;
 this.volume = volume;
 this.tamanho = tamanho;
 }
 /*...*/
}
public class Som extends eletrodomestico {
 private boolean mp3;
 private int volume;
 private int tamanho;
 public Som(int voltagem, int consumo, boolean mp3, int volume, int tamanho) {
 super(false, voltagem, consumo);
 this.mp3 = mp3;
 this.volume = volume;
 this.tamanho = tamanho;
 }
 /*...*/
}
Fonte: Elaborada pelo autor.
Na figura acima, ambas as classes estendem a classe “Eletrodomestico”, pois possuem características comuns 
a todo eletrodoméstico. Entretanto, também possuem atributos específicos, como “canal” para a “Tv” e “mp3” 
para o “Som”. Além de utilizar os atributos herdados, também podemos fazer uso dos métodos herdados, como 
o método “desligar( )”, por exemplo, que é comum ao “Som” e à “Tv”. 
75
Programação Orientada a Objetos | Unidade 5 - Boas Práticas I
5.1.5 Outras dicas
Apresentamos a seguir algumas dicas de estratégias que também são importantes para a codificação de softwa-
res com qualidade. 
1) Utilize linhas em branco para melhorar a legibilidade do código. Linhas brancas são úteis para:
• Separar definição de classes de definição de método.
• Separar variáveis locais de um método.
• Separar blocos de código. 
• Separar comentários das seções lógicas do código.
2) Documente o código-fonte através de comentários a fim de facilitar a compreensão do código. Separe o 
comentário do código de forma que fique legível e simples. Não é necessário escrever comentários para defini-
ções óbvias.
3) Agrupe classes comuns em um único pacote. Assim, quando a importação dessas classes for necessária, com 
uma linha importa-se o pacote inteiro. Caso o uso de pacote não seja feito, seriam necessários vários imports, um 
para cada classe que desejasse importar.
4) Crie classes enxutas. Isso significa que a definição de uma classe não deve ser muito extensa. Se perceber que 
a declaração de uma classe está ficando muito grande, é melhor quebrá-la em mais classes. 
5) Tente generalizar ao máximo. Generalizar é descobrir coisas que são comuns a muitos objetos e desenvolver, 
por exemplo, classes que agrupem esses objetos. Entretanto, isso não serve apenas para classes, mas também 
para métodos e atributos. 
6) Métodos devem fazer uma única tarefa. Observe o exemplo dos códigos abaixo. No exemplo da direita, o 
método, além de calcular o valor do desconto (que é o seu propósito), ainda envia um e-mail avisando ao cliente 
sobre o desconto. A coluna da esquerda sugere uma forma melhor de implementar esse método.
Figura 60 – Métodos devem fazer uma única tarefa: à esquerda, a versão correta; à direita, a versão incorreta
Bom Ruim
void calculaDesconto (int preco){
 //calcula desconto
 double desconto = preco * 0.25;
}
void calculaDesconto (int preco){
 //calcula desconto
 double desconto = preco * 0.25;
 //envia e-mail informando ao cliente
 enviaEmail( );
}
Fonte: Elaborada pelo autor.
7) Não utilize números fixos no seu código. Ao invés disso, utilize constantes. Constantes devem ser declaradas 
no início do arquivo e utilizadas ao longo do código. Dessa forma, caso você queira alterar o valor dessa constante 
em algum momento, você o teria que fazer apenas uma vez. A tabela abaixo exemplifica essa situação. No lado 
esquerdo da tabela, o uso de constantes é feito da melhor maneira.
76
Programação Orientada a Objetos | Unidade 5 - Boas Práticas I
Figura 61 – Não se deve utilizar números fixos no código: à esquerda, a versão correta; à direita, a versão incorreta
Bom Ruim
final int meses=12;
final int semanas = 52;
final int dias = 365;
final double diasPorSemana =(double) dias/(double)semana;
final double diasPorMes=(double)dias/(double)meses;
final double diasPorSemana=(double) 365/52;
final double diasPorMes = (double) 365/12;
Fonte: Elaborada pelo autor.
É importante ressaltar que a passagem de parâmetros de tipos primitivos (int, double, boo-
lean, etc.) é realizada por valor. Isso significa que, se tentarmos alterar seus valores dentro 
do método, não conseguiremos. Entretanto, a passagem de objetos de qualquer classe por 
parâmetro é realizada por referência. Qualquer alteração que fizermos no objeto será reali-
zada também fora do método.
Fique atento!
8) Realize a conversão de cadeias de caracteres para minúsculo ou maiúsculo antes de fazer comparações. Isso 
garantirá a realização correta da comparação, independentemente da maneira como ela foi escrita original-
mente. Ex.: “souza” e “Souza”. A tabela abaixo ilustra essa situação. Do lado direito, a comparação falhará se o 
valor de nome for “Souza”. Do lado esquerdo, a comparação é feita de forma correta, passando inicialmente o 
nome para minúsculo com o uso do método “toLowerCase()”.
Figura 62 – Deve-se converter strings para minúsculo antes de compará-las: à esquerda, a versão correta; à direita, a versão 
incorreta
Bom Ruim
If (nome.toLowerCase( ) == “souza”){
 //…//
}
If (nome == “souza”){
 //…//
}
Fonte: Elaborada pelo autor.
9) Não implemente mais de uma classe em um mesmo arquivo. Apesar de isso ser permitido, no momento da 
realização de testes, encontrar erros se torna muito mais difícil. Colocar cada classe em um arquivo permite com 
que se foque em um problema de cada vez, facilitando a manipulação dos códigos.
77
Programação Orientada a Objetos | Unidade 5 - Boas Práticas I
Síntese da unidade
Nesta unidade, abordamos algumas das convenções e boas práticas na programação orientada a objetos que 
têm sido adotadas por desenvolvedores após anos de experiência. Essas técnicas incluem: a nomenclatura de 
classes, interfaces, atributos, variáveis e métodos; a forma como se deve alinhar o código e determinar a visibili-
dade de atributos e métodos; questões sobre a duplicação de código, entre outras.
78
Considerações finais
Existem muitas convenções que podem ser adotadas para a obtenção de 
melhoria, principalmente no processo de manutenção de software. Desen-
volver um software de boa compreensão, legível, guiando-se por padrões 
não é apenas uma questão de burocracia, mas também uma questão 
financeira. Gastar muito tempo na manutenção devido, por exemplo, a 
um código mal escrito gera prejuízos financeiros para uma empresa. Por 
isso, aprender boas práticas de programação orientada a objetos é um 
ótimo investimento que será recompensado no futuro, durante toda a sua 
vida profissional e acadêmica.
79
 6Unidade 66. Armazenamento de Dados
Para iniciar seus estudos
Nesta unidade, você compreenderá as diversas formas de armazena-
mento de dados existentes na linguagem Java. Além das diversas estru-
turas de dados que podem ser utilizadas, também abordaremos a forma 
como arquivos são manipulados e a característica de interoperabilidade 
do Java. Pronto para começarmos?
Objetivos de Aprendizagem
• Compreender as estruturas existentes para armazenamento de 
dados em Java e as suas aplicações, bem como primitivas para 
tarefas de manipulação de dados em arquivo de textos, como lei-
tura e escrita.
• Assimilar o conceito de interoperabilidade, característica presente 
na linguagem Java. 
80
Programação Orientada a Objetos | Unidade 6 - Armazenamento de Dados
Introdução da unidade
Esta unidade aborda alguns conceitos principais relacionados ao armazenamento de dados, às estruturas de 
dados e à manipulaçãode dados em arquivos. As estruturas de dados são utilizadas em concordância com ana-
logias do mundo real. Em outras palavras, elas tentam armazenar dados de forma semelhante às estruturas reais 
que o sistema está representando. Por exemplo, um sistema que simula a montagem de um produto, onde as 
peças estão empilhadas e devem ser utilizadas na ordem descrita pela pilha, deve fazer uso de uma estrutura de 
dados do tipo "pilha" para representar o conjunto de peças. O mesmo serve para as outras estruturas de dados. 
Além disso, devido à volatilidade da memória principal e do seu tamanho restrito, muitas vezes, desejamos que 
os dados sejam armazenados em arquivos. Por isso, é importante saber manipular dados corretamente. Por fim, 
abordamos uma característica extremamente importante da linguagem Java, que pode, até mesmo, ter sido res-
ponsável por sua grande popularidade, que é a interoperabilidade, ou seja, a sua capacidade de permitir que 
um código rode em diferentes sistemas operacionais sem que seja necessária a realização de modificações no 
mesmo. Para uma empresa, isso se faz extremamente útil no desenvolvimento de sistemas onde não se tem cer-
teza sobre as características de arquitetura existentes no computador do cliente.
6.1 Armazenamento de dados 
A forma como dados são armazenados é importante, pois tem grande influência no desempenho de um sistema. 
As estruturas que utilizamos na codificação de sistemas têm que ser planejadas de acordo com os objetivos que 
desejamos atingir. Nesta unidade, abordamos algumas estruturas para armazenar dados em memória principal 
durante a execução de programas em Java. Abordamos também como deve ser feita a codificação para armaze-
namento e leitura de dados em arquivos. Por fim, ressaltamos a característica de interoperabilidade existente na 
linguagem Java.
6.1.1 Estruturas de dados
As estruturas de dados são úteis em qualquer linguagem de programação. Elas servem para armazenar e orga-
nizar dados de forma que seja fácil obter o seu acesso posteriormente. Em Java, existem diversas estruturas de 
dados que foram pensadas para diferentes utilizações. Nesta seção, abordamos algumas dessas estruturas.
6.1.1.1 Vetores (arrays) e arrayLists
Vetores em Java são conjuntos de variáveis do mesmo tipo. Imagine uma situação onde deseja-se criar um sis-
tema que contém uma classe Escola composta por 100 alunos. Cada aluno pode ser representado por um objeto 
da classe Aluno. Assim, teríamos que ter 100 objetos diferentes da classe Aluno para representar todos os alu-
nos da escola. Implementando dessa forma, o código ficaria extenso devido à necessidade de criar uma grande 
quantidade de objetos. Além disso, sempre que se fizesse necessária a inclusão de um novo aluno na escola, o 
código da classe Escola teria que ser alterado para adicionar o novo objeto do tipo Aluno. 
Sendo assim, existem maneiras mais eficientes de fazer uma implementação desse tipo. Na programação em 
Java, utilizamos o que chamamos de vetores ou Arrays. Os vetores devem ser definidos com uma capacidade 
81
Programação Orientada a Objetos | Unidade 6 - Armazenamento de Dados
fixa, definida na sua instanciação. Caso todas as posições de um vetor não sejam preenchidas, as posições vazias 
são atribuídas com valores default. Para indicar a instanciação de vetores, utilizamos colchetes. A sintaxe seria da 
seguinte forma: 
TipoDosObjetos[ ] nomedoVetor = new TipoDosObjetos[capacidade];
A Figura 63 exemplifica a utilização de vetores como membros de uma classe. A classe Escola é constituída por 
alunos. O número de alunos em uma escola é, no máximo, igual a 100. Por esse motivo, criamos um vetor com 
capacidade máxima de 100 objetos do tipo Aluno. Não é preciso que a capacidade seja necessariamente uma 
constante, como no exemplo da Figura 63. A capacidade também pode ser uma variável. 
Após a instanciação de um objeto chamado alunos, que é, na verdade, um vetor contendo objetos do tipo Aluno, 
definimos outro atributo, que é a quantidade de alunos já existentes no vetor. A princípio, esse valor é zero, pois 
o método adiciona( ) ainda não foi chamado. Esse método recebe um objeto do tipo Aluno e, na próxima posição 
livre do vetor (quantidade), insere esse objeto. Uma outra versão do método adiciona( ) também é implementada 
de modo que seja possível escolher em qual posição gostaríamos de inserir um objeto do tipo Aluno. Por fim, um 
método getQuantidade( ) retorna quantos elementos do tipo Aluno já foram inseridos no vetor. 
Figura 63 – Classe Escola contendo o vetor alunos
public class Escola{
 private Aluno[] alunos = new Aluno[100];
 private int quantidade=0
 public void adiciona(Aluno aluno) {
 alunos[quantidade]=aluno;
 }
 public void adiciona(int posicao, Aluno aluno) {
 alunos[posicao]=aluno;
 }
 public int getQuantidade() {
 return quantidade;
 }
 /*...*/
}
Fonte: Elaborada pelo autor.
82
Programação Orientada a Objetos | Unidade 6 - Armazenamento de Dados
O problema de definir vetores da forma como apresentamos na Figura 63 é que criamos vetores estáticos, ou seja, 
de tamanhos fixos. Caso seja necessário aumentar o número de alunos que a escola possui, precisamos alocar 
um novo vetor maior. Para solucionar esse problema, a linguagem Java propõe o uso de uma estrutura chamada 
ArrayList, que se torna disponível através da importação da classe “java.util.ArrayList”. A utilização de ArrayList per-
mite a criação de vetores sem ter que necessariamente informar o seu tamanho. É interessante perceber a sintaxe 
da sua instanciação, que não é muito diferente da convencional:
ArrayList<TipoDosObjetos> nomedoVetor = new ArrayList<TipoDosObjetos>( );
Na Figura 64, reescrevemos a classe Escola de modo que, ao invés de instanciar alunos como vetores da forma 
tradicional, fazemos uso da classe ArrayList. Além da diferença na forma de instanciação, percebemos que os 
métodos da classe também foram reescritos de modo a serem adaptados à estrutura ArrayList. O método adi-
ciona( ) recebe um aluno e adiciona esse aluno ao ArrayList através do método add( ) contido dentro da classe 
ArrayList, definida pelo Java. Esse método também possui uma segunda versão, onde é passada a posição onde 
deseja-se inserir o elemento. E essa versão é utilizada no segundo método adiciona( ), definido em seguida. Por 
fim, o valor da quantidade de alunos, no momento, não é mais um atributo da classe. Para saber a quantidade de 
atributo da classe, é necessário chamar o método getQuantidade( ), que volta o valor de retorno do método size( ), 
também definido em ArrayList pelo Java. Esse método retorna o tamanho atual do ArrayList.
Figura 64 – Classe Escola contendo o arraylist chamado Alunos
public class Escola{
 private ArrayList<Aluno> alunos = new ArrayList<Aluno>();
 public void adiciona(Aluno aluno) {
 alunos.add(aluno);
 }
 public void adiciona(int posicao, Aluno aluno) {
 alunos.add(posicao, aluno);
 public int getQuantidade() {
 return alunos.size();
 }
 /*...*/
}
Fonte: Elaborada pelo autor.
Dentro da classe ArrayList do Java existem diversos outros métodos que podem ser utilizados para a manipulação 
de vetores que não foram aqui mencionados. Para melhor utilização, vale a pena um estudo mais aprofundado 
da sua documentação. 
83
Programação Orientada a Objetos | Unidade 6 - Armazenamento de Dados
6.1.1.2 Listas (lists)
A utilização de vetores (listas implementadas com Arrays) em Java pode ser adequada em algumas situações, 
mas em outras pode não ser tão eficiente. A adição de novos elementos na primeira posição, por exemplo, exige 
que todos os elementos seguintes sejam deslocados a fim de liberar o espaço para a sua adição, o que consome 
tempo. Da mesma forma, remover um elemento de um vetor demanda o deslocamento de todos os elementos à 
sua frente para trás. E esse problema se amplifica à medida que o tamanho do vetor aumenta.
Em algumas aplicações, gostaríamos de implementar uma lista em que a adição ou a remoção de elementosseja 
computacionalmente eficiente. A estrutura que permite isso se chama Lista Ligada. A principal diferença dessa 
estrutura para os vetores tradicionais é que os seus elementos não são alocados de forma contígua na memória. 
Cada elemento pertencente à uma Lista Ligada é armazenado em uma posição da memória, e esses elementos 
são ligados através de endereços e ponteiros. Dessa forma, quando, por exemplo, um novo elemento é adicio-
nado no começo da lista, a estrutura aponta o novo elemento para o elemento que estava na primeira posição e 
isso é suficiente. Ou seja, não é necessário o deslocamento dos elementos seguintes.
Em Java, existe uma classe que implementa essa estrutura e que podemos instanciar. O nome dessa classe é Lin-
kedList e para a sua utilização é necessário a importação de java.util.LinkedList. Na Figura 65, ilustramos um exem-
plo da sua aplicação na mesma classe dos exemplos anteriores. A única diferença da classe Escola, mostrada na 
Figura 64, em relação à Figura 65 é a alteração de ArrayList para LinkedList na instanciação de alunos.
Figura 65 – Classe Escola contendo o objeto alunos do tipo LinkedList
public class Escola{
 private LinkedList<Aluno> alunos = new LinkedList<Aluno>();
 public void adiciona(Aluno aluno) {
 }
 public void adiciona (int posicao, Aluno aluno) {
 alunos.add(posicao, aluno);
 }
 public int getQuantidade() {
 return alunos.size();
 }
 /*...*/
}
Fonte: Elaborada pelo autor.
84
Programação Orientada a Objetos | Unidade 6 - Armazenamento de Dados
É possível a inicialização de uma lista do tipo LinkedList com elementos. Para isso, basta colo-
carmos os elementos que queremos adicionar dentro dos parênteses existentes na instan-
ciação. Isso significa que LinkedList também possui um construtor que recebe os elementos 
que ele deve conter. 
Saiba mais
Os métodos existentes na classe LinkedList são bastante semelhantes à classe ArrayList e também podem ser veri-
ficados na documentação do Java para o conhecimento mais profundo da estrutura.
6.1.1.3 Pilhas (stacks)
Imagine um produto que é composto por diversas peças: p1, p2,…, pn. Durante a montagem do produto, que é feita 
de forma automática, as peças devem ser colocadas em uma ordem específica (primeiro p1, depois p2, depois p3 e 
assim por diante). Para viabilizar essa montagem, as peças são empilhadas na ordem correta de forma que a base 
da pilha esteja com a peça pn e o topo da pilha esteja com a peça p1. A máquina retira peça por peça do topo da 
pilha para montar o produto. 
A mesma máquina realiza, de forma automática, a troca de uma peça defeituosa do produto. Para isso, a máquina 
desempilha as peças na ordem, colocando-as em outra pilha, até chegar na peça defeituosa. Em seguida, efetua-
-se a troca da peça e coloca-se as peças de volta no produto na ordem dada pela pilha, ou seja, do topo da pilha 
até a sua base. 
Para a implementação de um código que simule esse exemplo, faz-se necessária a implementação de uma estru-
tura semelhante à uma pilha. Em outras palavras, essa estrutura deve permitir a inserção de elementos somente 
após o último elemento inserido, e a remoção de forma que somente o elemento mais recentemente inserido 
possa ser removido por vez. Essa estrutura é chamada de pilha e possui implementação em Java através da classe 
Stack. Para a implementação de pilhas em Java, devemos realizar a importação de “java.util.Stack”. Na Figura 66, 
temos a implementação de uma classe Peca contendo os atributos nome e volume, além dos seus métodos get-
ters e setters. 
85
Programação Orientada a Objetos | Unidade 6 - Armazenamento de Dados
Figura 66 – Classe Peca
public class Peca {
 private String nome;
 private double volume;
 public String getNome() {
 return nome;
 }
 public void setNome(String nome) {
 this.nome = nome
 } 
 public double getVolume() {
 return volume;
 }
 public void setVolume(double volume) {
 this.volume = volume;
 }
}
Fonte: Elaborada pelo autor.
A instanciação de uma pilha deve ser dada de forma semelhante às estruturas já apresentadas:
Stack<TipoDosObjetos> nomedaPilha = new Stack<TipoDosObjetos>( );
Na Figura 67, exemplificamos a utilização de um objeto do tipo Stack (pilha) que contém uma coleção de objetos 
do tipo Peca. O código dessa figura define um método main( ), onde uma pilha é instanciada e uma peça é criada 
e inserida na pilha. Em seguida, a peça é removida da pilha e armazenada em um objeto chamado pecaRemovida. 
Por fim, verificamos se a pilha está vazia e informamos ao usuário. 
86
Programação Orientada a Objetos | Unidade 6 - Armazenamento de Dados
Figura 67 – Classe TestandoPilha contendo um objeto do tipo Pilha
public class TestandoPilha {
 public static void main(String[] args) {
 Stack<Peca> pilha = new Stack<Peca>();
 Peca pecaParaInserir = new Peca();
 pilha.push(pecaParaInserir);
 Peca pecaRemovida = pilha.pop();
 if(pilha.isEmpty()){
 System.ou.println("A pilha está vazia");
 }
 }
}
Fonte: Elaborada pelo autor. 
Na estrutura de dados Stack, não existe um método de inserção em que seja possível a esco-
lha da posição onde deseja-se inserir um elemento. Um método como esse quebraria a defi-
nição de pilha. Nessa estrutura, só é permitido inserir elementos no topo da pilha.
Fique atento!
Na Figura 67, podemos notar a utilização do método push( ) definido em Stack, que recebe como um argumento 
uma peça a ser inserida na pilha. É importante notar que esse método faz a inserção do elemento sempre no topo 
da pilha, pois isso é o que faz a estrutura ser uma pilha e não outro tipo de estrutura. O método pop( ) também é 
bem característico. Ele faz a remoção do elemento que está no topo da pilha, colocando-o como valor de retorno. 
Por fim, é utilizado o método isEmpty( ), que retorna um valor booleano que diz se a pilha está vazia (true) ou não 
(false). Assim como as outras estruturas já mencionadas, existem diversos métodos não abordados aqui, mas que 
podem ser verificados na documentação do Java. 
6.1.1.4 Filas (queues)
O conceito de fila está aplicado em todo lugar: bancos, escolas, hospitais, cinemas, entre outros. Nessas situa-
ções, filas são importantes pois determinam a ordem de atendimento de clientes, alunos, pacientes etc. O pri-
meiro a ser atendido é sempre o que está na fila por mais tempo, ou seja, o primeiro da fila. Quando isso acontece, 
o segundo da fila se torna o primeiro e a fila “anda”. Sempre que uma nova pessoa chega no estabelecimento 
para ser atendida, por exemplo, ela entra no final da fila, ou seja, na última posição. 
87
Programação Orientada a Objetos | Unidade 6 - Armazenamento de Dados
Nessa seção, vamos estudar uma estrutura de dados em Java que possui o mesmo comportamento das filas, as 
Queues. Assim como as estruturas mencionadas anteriormente, os seus elementos também são armazenados de 
maneira sequencial. Assim como as Pilhas, o comportamento das Filas também é bem restrito quando compa-
rado às Listas. Em uma Lista, os elementos podem ser inseridos ou removidos em qualquer posição. Nas filas, as 
inserções acontecem sempre após o último elemento já inserido e somente é permitida a remoção do primeiro 
elemento da sequência, ou seja, o elemento inserido há mais tempo.
A classe que vamos utilizar para criar objetos do tipo fila é a classe Queue. Para a sua utilização, devemos realiza-
ção a importação de “java.util.Queue”. A forma de instanciar uma fila em Java é um pouco diferente das estruturas 
até então mencionadas. Como a classe Queue em Java é uma interface, não se pode instanciá-la. O que podemos 
fazer é construir objetos com classes do pacote “java.util” que já implementam a interface Queue. Um exemplo 
disso é a classe LinkedList mencionada nas seções anteriores.
Na Figura 68, exemplificamos a utilização de objeto do tipo Queue em Java. No código dessa figura, temos a 
implementação de um método main( ). Dentro dele, cria-se um objeto do tipo Queue, que contém uma coleção de 
objetos do tipo Aluno.Entretanto, na instanciação desse objeto, escrevemos “new LinkedList<Aluno>( )”, ou seja, 
vamos criar, na verdade, uma LinkedList de objetos do tipo Aluno.
Figura 68 – Classe TestandoFila contendo um objeto do tipo Fila
public class TestandoFila {
 public static void main(String[] args) {
 Queue<Aluno> fila = new LinkedList<Aluno>();
 Aluno aluno = new Aluno();
 fila.offer(aluno);
 Aluno alunoRemovido = fila.poll();
 if(fila.isEmpty()){
 System.out.println("A fila está vazia.");
 }
 }
}
Fonte: Elaborada pelo autor.
A LinkedList implementa a interface Queue e, por isso, a instanciação pode se dar dessa forma. Em seguida, cria-
mos um objeto do tipo Aluno para ser inserido na fila. O método de inserção da fila é o offer( ). O objeto será inse-
rido conforme as regras de uma fila. Em seguida, removemos um aluno utilizando o método poll( ). O aluno a ser 
removido também seguirá as regras de uma fila e o mesmo será retornado pelo método. Por fim, verificamos se 
já não existem elementos na fila utilizando o método isEmpty( ) e informamos ao usuário.
88
Programação Orientada a Objetos | Unidade 6 - Armazenamento de Dados
6.1.1.5 Conjuntos (sets)
Os conjuntos são estruturas de dados que funcionam de maneira semelhante aos conjuntos da matemática. 
Essas estruturas não permitem elementos duplicados, ou seja, não é permitido a inserção de um elemento que 
já exista no conjunto. Outra característica importante de conjuntos, que também é válida nessa estrutura, é 
a não existência de uma ordem para os elementos, ou seja, a inserção deles pode variar de acordo com a sua 
implementação.
Em Java, a classe que representa a estrutura conjunto é a Set, que está definida em “java.util.Set”. Para a utilização 
dessa classe, é necessária a sua importação. Assim como a classe Queue, a classe Set é apenas uma interface e 
não pode ser instanciada. Desse modo, devemos instanciar uma das classes que implementam essa interface. 
Algumas das classes existentes no pacote “java.util” que implementam a interface Set são: HashSet, LinkedHashSet 
e TreeSet. A diferença entre as três classes é a forma como implementam a interface Set, que pode resultar em 
uma estrutura de acesso mais rápido ou mais lento. 
No exemplo de conjuntos dado no código da Figura 69, utilizamos a classe HashSet que implementa a inter-
face Set. É importante notar que na instanciação do objeto cargos, apesar de definirmos o seu tipo como sendo 
um Set<String> (conjunto de Strings), chamamos o construtor de HashSet, pois esse sim tem implementação. 
Em seguida, adicionamos as diversas cadeias de caracteres (Strings) referentes aos cargos existentes, utilizando 
o método add( ). A última adição tenta inserir o cargo “Diretor”. Entretanto, como esse cargo já existe dentro do 
conjunto, essa inserção não será realizada e o método add( ) retornará o valor booleano “false”.
Figura 69 – Classe TestandoSet contendo um objeto do tipo Conjunto
public class TestandoSet {
 public static void main(String[] args) {
 Set<String> cargos = new HashSet<String>();
 cargos.add("Gerente");
 cargos.add("Diretor");
 cargos.add("Presidente");
 cargos.add("Secretária");
 cargos.add("Funcionário");
 cargos.add("Diretor"); // repetido!
 if(cargos.isEmpty()){
 System.out.println("O conjunto está vazio");
 }
 }
}
Fonte: Elaborada pelo autor.
89
Programação Orientada a Objetos | Unidade 6 - Armazenamento de Dados
Na utilização de diversas classes de um mesmo pacote, ao invés de importar cada classe sepa-
radamente, podemos importar o pacote como um todo. Para isso, basta utilizar o comando 
“import java.util.*”. O asterisco indica que todas as classes do pacote devem ser importadas. 
Fique atento!
No final do código da Figura 69, utilizamos o método isEmpty( ) para verificar se o conjunto cargos está vazio e 
imprimir uma mensagem informando ao usuário. No exemplo dessa figura, o resultado dessa comparação é falso 
e nada será impresso. 
6.1.2 Armazenamento em arquivos
O armazenamento de dados em arquivo permite que dados não se percam por algum descuido, visto que a 
memória RAM (onde os programas rodam) é volátil, além de permitir que dados não utilizados, no momento, 
fiquem no disco rígido ao invés de ocupar espaço em memória RAM. Sendo assim, a leitura e a escrita de dados 
em arquivos, por programas, podem ser bastante úteis em diversos sentidos. Em Java, o processamento de arqui-
vos é dado através da importação das classes disponíveis no pacote “java.io”. A seguir, veremos como é feita a 
abertura, a criação, a leitura e a escrita de arquivos de texto nessa linguagem. 
6.1.2.1 Criação e abertura de arquivos
Para manipular arquivos, devemos importar a classe java.io.File definida no Java. Vamos supor, a princípio, que o 
arquivo que desejamos utilizar não existe e, portanto, desejamos criá-lo. Para a criação de um arquivo de texto 
(formato txt), por exemplo, basta utilizarmos o seguinte comando: 
File arquivo = new File (“nomedoarquivo.txt”);
arquivo.createNewFile( ) ;
Suponha agora que o arquivo chamado “nomedoarquivo.txt” já existe e só gostaríamos de abri-lo para manipulá-
-lo. Nesse caso, só utilizamos o comando: 
File arquivo = new File (“nomedoarquivo.txt”);
É interessante perceber que o objeto da classe File apenas representa um arquivo. Entretanto, o fato dele repre-
sentar um arquivo não significa que esse arquivo realmente exista. Por isso, existe um método dentro de File para 
a verificação da existência de um arquivo:
90
Programação Orientada a Objetos | Unidade 6 - Armazenamento de Dados
boolean existe = arquivo.exists( ); 
O método exists( ) retorna true caso o arquivo exista ou false caso o arquivo não exista. Assim, se o valor de existe 
é false, você poderá criar, de fato, o arquivo através do método createNewFile( ) anteriormente mencionado. Para 
excluir um arquivo ou diretório pode-se utilizar o seguinte comando: 
arquivo.delete( );
Além da criação de um novo arquivo, pode ser necessária a criação de um novo diretório. O método dentro da 
classe File, para isso, é o mkdir( ). Para a sua utilização, o endereço na instanciação do objeto do tipo File deve ser 
de um diretório e não de um arquivo. Esse diretório será criado ao chamar mkdir( ) para o objeto.
6.1.2.2 Leitura de arquivos
Existem duas classes em Java que são utilizadas para a leitura de arquivos no formato de texto, as classes FileRea-
der e BufferedReader contidas no pacote “java.io”. Para a sua utilização, basta a importação desse pacote. Primei-
ramente, o construtor da classe FileReader deve receber um objeto do tipo File:
FileReader fr = new FileReader(arquivo);
Para ler o arquivo, utilizamos agora um objeto da classe BufferedReader que recebe em seu construtor o objeto fr: 
BufferedReader br = new BufferedReader( fr );
A classe BufferedReader é quem provê os métodos para realizarmos, de fato, a leitura dos caracteres do texto no 
arquivo. Existem muitas formas de realizar essa leitura: linha por linha, a cada palavra ou de caracter em caracter. 
No exemplo abaixo, utilizaremos o método readLine( ), onde as linhas do arquivo serão lidas uma por uma e retor-
nadas por esse método. O método ready( ) auxiliará esse procedimento, retornando false no momento que todas 
as linhas já tiverem sido lidas:
while( br.ready() ){
String linha = br.readLine();
/*...*/
}
Por fim, ao terminar a leitura do arquivo, devemos fechá-lo. A seguir, exibimos os comandos que utilizamos para 
fechar objetos do tipo FileReader e BufferedReader:
91
Programação Orientada a Objetos | Unidade 6 - Armazenamento de Dados
br.close();
fr.close();
6.1.2.3 Escrita de arquivos
A escrita de textos em Java funciona de forma semelhante à leitura. Existem duas classes em Java que são utiliza-
das para a leitura de arquivos no formato de texto, as classes FileWriter e BufferedWriter contidas no pacote “java.
io”. Para a sua utilização, basta a importação desse pacote. Primeiramente, o construtor da classe FileReader deve 
receberum objeto do tipo File:
FileWriter fw = new FileWriter(arquivo);
Outra forma de criar um objeto do tipo FileWriter é usar o construtor com a opção de informar se o conteúdo será 
acrescentado ao arquivo ao invés de substituí-lo. Para ativar essa opção, o objeto fw deve ser criado da seguinte 
forma:
FileWriter fw = new FileWriter(arquivo, true);
Em seguida, criamos o objeto do tipo BufferedWriter de forma semelhante ao BufferedReader: 
BufferedWriter bw = new BufferedWriter( fw );
Com o objeto bw criado, podemos escrever no arquivo utilizando o método write( ):
bw.write( "Texto a ser escrito no txt" );
bw.newLine();
O método newLine( ) insere uma quebra de linha no texto. Por fim, assim como na leitura de arquivos, é necessário 
fechar os buffers e informar ao sistema que não utilizaremos mais o arquivo:
bw.close( );
fw.close( );
92
Programação Orientada a Objetos | Unidade 6 - Armazenamento de Dados
6.1.3 Interoperabilidade do Java
A linguagem Java, aqui abordada, é o que chamamos de interpretada. Isso significa que, após a etapa de compi-
lação, é gerado um código no formato de bytecode, que pode ser executado posteriormente em qualquer arqui-
tetura, seja Windows, Linux ou Unix. Para isso, basta que exista no sistema operacional uma máquina virtual Java 
(Java Virtual Machine - JVM) instalada. 
O fato de que o Java atua dessa forma não é uma mera coincidência. Essa linguagem foi projetada para executar 
em diversos hardwares e softwares, o que resulta em uma interoperabilidade maior do que em outras linguagens. 
A utilização do código intermediário no formato bytecode resulta na possibilidade de executar um mesmo código 
em diversas arquiteturas. 
Atualmente, a empresa Oracle detém a propriedade da empresa criadora do Java, a Sun Microsystems. Para fazer 
o download da máquina virtual que executa Java (JVM), basta acessar o seguinte website: https://www.java.com/
pt_BR/download/manual.jsp e escolher a versão de acordo com o seu sistema operacional.
Síntese da unidade
Nesta unidade, abordamos formas de armazenamento de dados, focando principalmente nas estruturas de 
dados existentes em Java e no armazenamento em arquivos. As estruturas de dados aqui abordadas foram: 
Arrays, Arraylists, Lists, Stacks, Queues e Sets. Em relação ao armazenamento de arquivos, mencionamos as pri-
mitivas para abertura, criação, leitura e escrita de arquivos em Java. Por fim, mencionamos sobre a característica 
de interoperabilidade presente nesta linguagem e seus benefícios.
https://www.java.com/pt_BR/download/manual.jsp
https://www.java.com/pt_BR/download/manual.jsp
93
Considerações finais
Conhecer bem as formas de armazenamento em uma linguagem permite 
que você tenha capacidade para escolher quando e como utilizá-las. As 
estruturas de dados aqui mencionadas são somente algumas das exis-
tentes na documentação do Java e mais informações podem ser obtidas 
através da consulta à documentação.
94
 7Unidade 77. Interface Gráfica
Para iniciar seus estudos
Nesta unidade, abordaremos alguns dos conceitos relacionados à inter-
face gráfica em Java. Compreender esses conceitos permite que você 
consiga criar softwares com importantes características relativas à usabi-
lidade de uma aplicação, por exemplo. Em muitos ambientes, o fato de 
existir uma boa interface gráfica possibilita que o usuário do software se 
mantenha mais motivado a utilizá-lo. Vamos nessa?
Objetivos de Aprendizagem
• Compreender os conceitos básicos de interface gráfica, principal-
mente os relacionados à criação de janelas, componentes, orga-
nização em layouts e tratamento de eventos.
95
Programação Orientada a Objetos | Unidade 7 - Interface Gráfica
Introdução da unidade
Nesta unidade, abordaremos os principais conceitos relacionados à criação de interfaces gráficas em Java uti-
lizando as bibliotecas AWT e Swing. Apesar da grande quantidade de possibilidades existentes na manipulação 
dessas bibliotecas, focaremos apenas nos elementos básicos para o desenvolvimento de uma interface simples. 
Primeiramente, apresentaremos a definição de diferentes tipos de caixas de diálogo, janelas já prontas que são 
úteis em aplicações mais simples e diretas. Então, estudaremos o desenvolvimento de uma aplicação desde a 
criação de uma janela, a construção e inserção de componentes, a organização em layouts até o tratamento dos 
eventos gerados pelos componentes devido à interação com o usuário. Por fim, uma aplicação simples com os 
conceitos abordados será apresentada. 
7.1 Interface gráfica 
No desenvolvimento de códigos em uma nova linguagem, as saídas e mesmo a interação com o usuário são 
dados através de um console, pois os códigos ainda são de aprendizagem. Quando se desenvolve softwares para 
usuários do mundo real, muitas vezes, deseja-se que a interação com o mesmo seja amigável, fazendo-se neces-
sária a utilização de interfaces gráficas. Nesta unidade, vamos abordar o desenvolvimento de interfaces GUI, Gra-
phical User Interface ou Interface Gráfica com Usuário, onde resultados e componentes para interação são apre-
sentados em modo gráfico. Apresentaremos classes das bibliotecas Swing e AWT, bem como a definição de caixas 
de diálogos, a criação e inserção de componentes, a organização em layouts e o tratamento de eventos em Java. 
7.1.1 Interface gráfica em Java 
Interfaces gráficas em Java podem ser implementadas através do uso de duas bibliotecas principais: AWT (java.
awt) e Swing (javax.swing). A AWT introduziu a interface gráfica no Java, enquanto que a Swing é uma especialização 
do AWT e, portanto, herda as suas características. Surgindo mais tarde, trouxe diversos benefícios em relação à 
sua antecessora. 
Na Figura 70, ilustramos a hierarquia de herança de classes que definem atributos e comportamentos que 
são comuns à maioria dos componentes Swing. A classe java.lang.Object está no topo de qualquer hierarquia de 
herança entre classes Java. As classes java.awt.Component e java.awt.Container pertencem ao pacote java.awt e, 
portanto, à biblioteca AWT. Por fim, apresentamos a classe base para a maioria dos componentes do Swing, a javax.
swing.JComponent.
96
Programação Orientada a Objetos | Unidade 7 - Interface Gráfica
Figura 70 – Hierarquia de herança entre classes em Java
java.lang.Object
javax.swing.JComponent
java.awt.Container
java.awt.Component
Fonte: Elaborada pelo autor.
Ainda que existam essas duas bibliotecas oficiais disponíveis no Kit de Desenvolvimento do Java, vamos focar, na 
maior parte do tempo, na utilização da biblioteca Swing. Uma característica importante dessa biblioteca é o fato 
de que sistemas construídos as utilizando terão a mesma aparência (cores, tamanhos etc.) e o comportamento 
independente do sistema operacional em que está sendo executado, seja ele Windows, Linux ou qualquer outro. 
Apesar dessa grande semelhança em aparência em diversos ambientes, por vezes, isso pode não ser suficiente. 
Por isso, no Java, temos a opção de escolher forçar a aparência de uma aplicação. Assim, podemos conseguir que 
uma aplicação fique mais parecida com determinado sistema operacional ou com outro. A Figura 71 exibe duas 
aparências diferentes que podem ser definidas para uma mesma aplicação em Java.
Figura 71 – A mesma aplicação rodando com duas aparências diferentes
Fonte: Elaborada pelo autor.
Na figura acima, temos à esquerda uma versão da aplicação que é semelhante ao sistema operacional Windows 
da Microsoft. Já à direita, temos uma aparência da aplicação que se assemelha às janelas do sistema operacional 
Macintosh da Apple. 
97
Programação Orientada a Objetos | Unidade 7 - Interface Gráfica
7.1.2 Caixas de diálogo 
Na programação de interfaces gráficas em Java, podemos, em algumas situações, necessitar de janelas de diálo-
gos simples, de estrutura já pronta, comuns a diversos programas. Essas janelas de diálogo são definidas através 
da utilização da classe JoptionPane do pacote Swing, e essa classe fornece uma série de métodos estáticos que,ao serem invocados, criam caixas de diálogos bem simples e diretas. Para fazer a utilização desses métodos, é 
necessária a importação da classe javax.swing.JOptionPane.
7.1.2.1 Entrada de texto e exibição de mensagem 
Em muitas situações, podemos desejar receber informações do usuário através de caixas de diálogos bem sim-
ples e diretas. As caixas de diálogo de entrada de texto têm esse propósito. Elas são criadas a partir do método 
estático showInputDialog( ), que retorna uma String contendo os caracteres digitados pelo usuário. Esse método 
é sobrecarregado, diversas vezes, dentro da classe JOptionPane, mas faremos uso da sua versão mais simples. A 
Figura 72 exibe um código que faz a utilização desse método para receber dois números e, posteriormente, rea-
lizar a soma entre eles.
Figura 72 – Classe Soma utilizando showInputDialog( ) e showMessageDialog( ) da classe JOptionPane
Fonte: Elaborada pelo autor.
98
Programação Orientada a Objetos | Unidade 7 - Interface Gráfica
É importante notar que o parâmetro do método showInputDialog( ) é uma String que se tornará o rótulo da caixa 
de texto de entrada e que informará ao usuário o que ele deve digitar. 
Além de realizar a leitura de dados do usuário, podemos querer implementar caixas de diálogo simples que ape-
nas exibem algum tipo de informação. Para isso, existe o método showMessageDialog( ), da mesma classe. De 
modo análogo, esse método é sobrecarregado diversas vezes, mas focaremos em uma das suas versões mais 
simples, como mostra a Figura 72. O valor do seu primeiro parâmetro será null, pois a janela do exemplo não tem 
dependência com nenhuma outra. Em seguida, colocamos o parâmetro correspondente à mensagem que será 
exibida. O terceiro parâmetro é o nome que será dado à janela. Por fim, o último parâmetro corresponde ao tipo 
de mensagem que será passado através da janela. A Figura 73 exibe as janelas que são lançadas ao executar o 
código da Figura 72. No lado superior, temos as janelas de entrada de texto. No lado inferior, temos a janela exi-
bindo a mensagem que contém o resumo da soma após a digitação dos valores 3 e 4. 
Figura 73 – Caixas de diálogo geradas pela classe soma
Fonte: Elaborada pelo autor.
O Quadro 3 exibe os possíveis valores para o quarto parâmetro da implementação do método showMessageDia-
log( ) utilizada nos exemplos anteriores, as suas descrições e o símbolo que é usado na janela para indicar a sua 
utilização.
Quadro 3 – Possíveis valores de parâmetros para indicar o tipo de caixa de diálogo no método showMessageDialog( )
Constante Descrição Símbolo 
ERROR_MESSAGE Mensagem de Erro 
QUESTION_MESSAGE Mensagem de Pergunta
PLAIN_MESSAGE Mensagem Simples Sem símbolo
INFORMATION_MESSAGE Mensagem de Informação
WARNING_MESSAGE Mensagem de Aviso
Fonte: Elaborado pelo autor.
As caixas de diálogo indicadas pelas constantes da tabela acima são as mais utilizadas em softwares de forma 
geral. Por isso, são necessárias constantes específicas para cada uma delas. 
99
Programação Orientada a Objetos | Unidade 7 - Interface Gráfica
7.1.2.2 Diálogo de confirmação
Outra caixa de diálogo contida na classe JOptionPane é a caixa de confirmação. Essa caixa consiste em uma 
mensagem, um ícone e três botões contendo os seguintes textos: “Sim”, “Não” e “Cancelar”. Apesar desse ser o 
aspecto padrão da caixa, também é possível personalizá-la, como as outras caixas existentes em JOptionPane. O 
método estático contido nessa classe para instanciar esse tipo de caixa é o showConfirmDialog( ), que contém dois 
argumentos. O primeiro diz respeito à dependência dessa caixa de diálogo com outras janelas. Como somente 
essa janela foi criada, deixaremos esse parâmetro como null. O segundo argumento é a mensagem que será exi-
bida ao usuário. A Figura 74 ilustra a sua utilização.
Figura 74 – Classe confirmacao utilizando uma caixa de confirmação criada pelo método showConfirmDialog( )
Fonte: Elaborada pelo autor.
Na figura acima, fazemos uma requisição, ao usuário, do seu nome através da caixa de diálogo de entrada. Em 
seguida, utilizamos uma caixa de confirmação para que o usuário confirme se o seu nome está realmente cor-
reto. Na Figura 75, ilustramos ambas as janelas, de entrada de texto (esquerda) e confirmação (direita), resultado 
do código acima.
Figura 75 – Caixas de diálogo geradas pela classe Confirmacao
Fonte: Elaborada pelo autor.
O método showConfirmDialog( ) sempre retorna uma constante que equivale à resposta escolhida pelo usuário. O 
valor retornado, o nome da constante e a descrição do seu significado são apresentados na Tabela 3.
Tabela 3 – Descrição dos possíveis valores retornados pelo método ShowConfirmDialog( ) da classe JOptionPane
Valor Nome da Constante Equivale
0 YES_OPTION Usuário clicou no botão Sim.
1 NO_OPTION Usuário clicou no botão Não.
2 CANCEL_OPTION Usuário clicou no botão Cancelar.
Fonte: Elaborada pelo autor.
100
Programação Orientada a Objetos | Unidade 7 - Interface Gráfica
A partir dos valores retornados pelo método showConfirmDialog( ), definidos pela tabela acima, é impossível imple-
mentar diferentes comportamentos. Exemplo disso seria questionar ao usuário se ele deseja prosseguir com 
determinada operação. Caso ele responda “Sim”, os passos para executar a operação serão realizados.
7.1.3 Criação de aplicativos GUI Swing
O desenvolvimento de interfaces gráficas utilizando apenas JOptionPane é bastante limitado, não apresenta con-
ceitos de orientação à objetos e não permite um nível de personalização suficiente para todas as aplicações. Por 
esses motivos, são disponibilizadas classes para prover o desenvolvimento e a personalização de uma interface 
gráfica completa. Nesta seção, abordaremos recursos para o desenvolvimento de uma aplicação simples para o 
controle de uma lista de presença. Vamos basear o desenvolvimento no conceito de Janela. Em seguida, mostra-
remos a criação e inserção de componentes no container Janela. Por fim, finalizaremos tratando a interação do 
usuário com os componentes da interface.
7.1.3.1 Criação de janela
Uma janela é um container de objetos gráficos. Os objetos, para serem exibidos, necessitam estar contidos em 
uma janela. Existem diferentes classes que podem realizar a criação de janelas. Entretanto, utilizaremos, nesta 
unidade, a classe JFrame, pois é ela quem fornece uma janela padrão comum na maioria dos aplicativos com 
interface gráfica. Para a sua utilização, basta importar a classe javax.swing.JFrame. A Figura 76 exibe um exemplo 
de código que pode ser utilizado na definição de uma janela referente à uma Lista de Presença. Uma forma de 
utilizar a classe JFrame é herdar as suas propriedades. Assim, o código fica mais organizado segundo os conceitos 
de orientação à objetos. Note que o construtor da classe que criamos, ListaPresenca( ), chama o construtor da 
classe pai, JFrame, passando como argumento uma String. Essa String será o título da janela.
Figura 76 – Criação de uma janela com o título “Lista de Presença”
Fonte: Elaborada pelo autor.
Na Figura 77, ilustramos a instanciação de uma janela através da classe derivada ListaPresenca. O primeiro passo 
é instanciar um objeto desse tipo. Em seguida, podemos determinar o tamanho da janela com o método setSize( ), 
herdado de JFrame, cujo os parâmetros são a largura e a altura, respectivamente. Por fim, para tornar a janela 
realmente visível, utilizamos o método setVisible( ), também herdado de JFrame, passando como parâmetro o valor 
booleano true.
101
Programação Orientada a Objetos | Unidade 7 - Interface Gráfica
Figura 77 – Classe principal que instancia uma janela do tipo ListaPresenca
Fonte: Elaborada pelo autor.
O resultado dos códigos das Figuras 76 e 77 é uma janela com o título “Lista de Presença” e pode ser visto na 
Figura 78.
Figura 78 – Janela gerada pelos códigos da classe Principal e ListadePresenca
Fonte: Elaborada pelo autor.
É importante notar que a classe JFrame também pode ser instanciada diretamente, sem 
necessariamenteter que ser herdada.
Fique atento!
102
Programação Orientada a Objetos | Unidade 7 - Interface Gráfica
Na Figura 78, é possível notar também que o tamanho da janela corresponde às medidas fornecidas no método 
setSize(). Além disso, somente é possível visualizar a janela devido à chamada ao método setVisible(), que recebe 
como parâmetro o argumento “true”.
7.1.3.2 Inserção de componentes
Existe uma infinidade de componentes definidos através de classes do Swing que podem ser inseridos em uma 
janela. Além disso, esses componentes podem ser estendidos a fim de gerar novos componentes personalizados. 
Nesta unidade, abordaremos quatro tipos de componentes que são utilizados na maioria das interfaces gráficas 
de softwares: Campos de Texto, Botões, Listas e Caixa de Seleção.
Os campos de texto são representados através da classe JTextField, que pode ser utilizada a partir da importação 
de javax.swing.JTextField. Esse componente consiste em uma área de uma única linha, que suporta a inserção ou a 
exibição de texto. A sintaxe para a criação de um campo de texto pode ser descrita da seguinte forma: 
JTextField campo1 = new JtextField(15);
onde 15 é a dimensão do campo criado. Para definir um texto padrão para o campo de texto, basta utilizarmos o 
método setText(“Campos de texto”), por exemplo. O que resultaria na seguinte caixa:
Outro exemplo de componente bastante importante e intuitivo são os botões. Eles são representados através da 
classe JButton, que pode ser utilizada a partir da importação de javax.swing.JButton. O pressionamento do botão 
dispara a ação especificada em seu rótulo, que pode conter texto ou ícones. A sintaxe para a criação de um botão 
pode ser descrita da seguinte forma:
JButton botao1 = new JButton("Inserir");
O comando acima criaria um botão com o rótulo “Inserir”, resultando na seguinte interface:
O componente "listas das interfaces gráficas" é descrito como uma coluna com uma série de itens que podem 
ser selecionados. As listas são representadas através da classe JList, que pode ser utilizada a partir da importação 
de javax.swing.JList. Nesse tipo de lista, a seleção pode ser múltipla ou de apenas um elemento. A sintaxe para a 
criação de uma lista pode ser descrita da seguinte forma:
103
Programação Orientada a Objetos | Unidade 7 - Interface Gráfica
String nomeDasCores[] = { "Verde", "Azul",”Vermelho”};
JList listaDeCores = new JList( nomeDasCores );
listaDeCores.setSelectionMode(ListSelectionModel.SINGLE_SELECTION );
listaDeCores.setVisibleRowCount( 3 );
No código acima, podemos perceber, em princípio, a declaração de um array chamado nomeDasCores. Esse array 
contará os elementos da lista que vamos definir. Em seguida, instanciamos um objeto do tipo JList passando 
como parâmetro o array criado. Então, definimos o modo de seleção da lista através do método setSelectionMode( 
), que recebe como parâmetro uma constante. Nesse exemplo, a constante é a SINGLE_SELECTION definida na 
classe ListSelectionModel. Essa constante indica que só poderá ser feita a seleção de um único item na lista e essa 
classe pode ser utilizada através da importação de javax.swing.ListSelectionModel. Por fim, utilizamos o método 
setVisibleRowCount( ) para especificar o número de linhas que ficarão visíveis. As demais, ficarão acessíveis através 
da barra de rolagem. O código acima resultaria no seguinte componente:
Outro componente bastante importante e que se assemelha a um botão, mas que quando clicado abre uma lista 
de opções ou valores, são as caixas de seleção. Essa caixa permite ao usuário fazer uma seleção a partir de uma 
lista de itens. É necessário tomar cuidado para que a lista, quando aberta, não ultrapasse os limites da janela. As 
caixas de seleção são representadas através da classe JComboBox, que pode ser utilizada a partir da importação 
de javax.swing.JComboBox. A sintaxe para a criação de uma caixa de seleção pode ser descrita da seguinte forma:
String nomes = { "Carlos”, “Manoel”, “Pedro”, “Silas”}; 
JComboBox comboBox = new JComboBox (nomes);
comboBox.setMaximumRowCount( 3 );
comboBox.setSelectedIndex(2);
No código acima, podemos perceber, em princípio, a declaração de um array chamado nomes. Esse array conterá 
os elementos da caixa de seleção que vamos definir. Em seguida, instanciamos um objeto do tipo JComboBox pas-
sando como parâmetro o array criado. Além disso, utilizamos o método setMaximumRowCount( ) para especificar 
o número de linhas que ficarão visíveis ao clicar. Por fim, informamos qual item do array ficará selecionado por 
padrão. No caso do exemplo acima, o item “Pedro” será selecionado por padrão. O código acima resultaria no 
seguinte componente:
104
Programação Orientada a Objetos | Unidade 7 - Interface Gráfica
7.1.3.3 Organização dos componentes em um layout 
Os componentes de uma interface gráfica devem estar anexados a um container cuja organização é definida 
através de um layout. Os layouts especificam a distribuição dos componentes dentro de um container. Existem 
diversas opções de layouts no Java, estando eles disponíveis dentro do pacote java.awt.*, sendo, portanto, 
necessária a sua importação. Para aplicar um layout, devemos primeiro definir um container ao qual queremos 
aplicá-lo:
Container container = frame.getContentPane();
A classe Container pode ser utilizada a partir da importação de java.awt.Container. O método getContentPane( ) 
acima retorna o conteúdo da janela do tipo JFrame e cria um container com ele. A partir de agora, podemos adi-
cionar componentes a esse container, como, por exemplo, o botão declarado na seção anterior:
container.add(botao1);
Sem a utilização de layouts, o programador teria o trabalho de posicionar, manualmente, todos os elementos na 
posição exata onde deseja que estejam. Os layouts organizam esses componentes de forma automática, bas-
tando escolhermos o estilo de layout a ser aplicado. Vamos exemplificar, na Figura 79, a utilização do FlowLayout, 
um layout mais elementar, definido em Java como java.awt.FlowLayout:
105
Programação Orientada a Objetos | Unidade 7 - Interface Gráfica
Figura 79 – Utilização do layout FlowLayout em um objeto do tipo Container
Fonte: Elaborada pelo autor. 
No exemplo do código acima, temos primeiro a instanciação de um objeto do tipo JFrame, ou seja, uma janela, 
cujo título é “Testando o FlowLayout”. Em seguida, definimos um container que vai abranger o conteúdo dessa 
janela. Então, criamos um layout do tipo Flowlayout e determinamos que este definirá a organização do container 
criado através do método setLayout( ). Em seguida, criamos três tipos de componentes: um rótulo, um botão e 
um campo de texto, e os adicionamos dentro do container criado. Por fim, determinamos o tamanho da janela e 
definimos a sua visibilidade como true. O resultado dessa operação é a janela mostrada na Figura 75.
106
Programação Orientada a Objetos | Unidade 7 - Interface Gráfica
Figura 80 – Interface gráfica gerada através da execução do código na classe TesteLayout
Fonte: Elaborada pelo autor.
O conteúdo digitado na caixa de texto, que é representado pelo objeto “campo1”, pode ser obtido após o pres-
sionamento do botão “Inserir”, e determinada ação pode ser tomada, como, por exemplo, adicionar o nome a 
uma lista de nomes. 
7.1.3.4 Tratamento de eventos e lógica do programa
O tratamento de eventos permite que o sistema se comporte de diferentes maneiras de acordo com a interação 
do usuário. Essa interação pode se dar através da movimentação do mouse, pressionamento de tecla, clique em 
botão, seleção de itens etc. As classes que fazem o tratamento de eventos em Java estão no pacote java.awt.event, 
sendo necessária a sua importação. O procedimento funciona da seguinte forma:
a. componentes de interface disparam rotinas ao “ouvir” um evento.
b. uma classe que trata eventos é definida.
c. ao receber um novo evento, a classe destinada a isso executa um método referente ao evento.
De forma geral, uma mesma classe pode tratar eventosde diferentes objetos. Existem algumas interfaces em Java 
que são comuns para o tratamento de eventos. A ActionListener está associada a eventos de ações do usuário, 
como cliques em botões ou alteração de campos texto. Já a ItemListener, a eventos relacionados à manipulação 
de lista de itens e a MouseListener a eventos do usuário lançados devido à interação através do mouse. Vamos 
exemplificar o uso de eventos do tipo ActionListener no código abaixo:
107
Programação Orientada a Objetos | Unidade 7 - Interface Gráfica
JButton botaoInserir = new JButton("Inserir");
botaoInserir.addActionListener( new EventoBotaoInserir() );
Podemos notar a criação do botão “Inserir” na interface gráfica, bem como a chamada ao método addActionLis-
tener( ). Esse método recebe como parâmetro um objeto de uma classe que implemente ActionListener. A classe 
EventoBotaoinserir, como mostra o código abaixo, deve, pelo menos, implementar o método actionPerformed(Ac-
tionEvent arg0), visto que esse método define a ação que será tomada ao pressionar o botão “Inserir”.
class EventoBotaoInserir implements ActionListener{
 public void actionPerformed(ActionEvent arg0){
 }
}
Esse é um exemplo de evento que pode surgir da interação com o usuário. Para mais informações sobre o trata-
mento de eventos, consulte a documentação do Java. 
7.1.4 Combinando tudo em uma aplicação 
Nesta seção, combinamos, em um único código, todos os componentes apresentados nesta apostila. Na Figura 
81, exibimos duas classes desenvolvidas que implementam ActionListener. Elas são úteis para tratar o evento 
gerado pelo pressionamento do botão “Inserir” e do botão “Remover”. Caso o botão “Inserir” seja pressionado, 
será chamado o método actionPerformed( ) da classe EventoBotaoInserir. Ela verifica se o texto digitado é diferente 
de vazio e, se sim, adiciona o nome digitado à lista de nomes, atribui vazio à caixa de texto e atualiza a lista visual 
de itens. Caso o botão “Remover” seja pressionado, será chamado o método actionPerformed( ) da classe Evento-
BotaoRemover, que recebe o índice da lista que foi selecionada. Então, se esse item não é vazio, ele é removido da 
lista de nomes e a lista visual de itens é atualizada.
108
Programação Orientada a Objetos | Unidade 7 - Interface Gráfica
Figura 81 – Classes EventoBotaoInserir e EventoBotaoRemover utilizadas para o tratamento de eventos de botões
Fonte: Elaborada pelo autor. 
A classe da Figura 82 ilustra a utilização das classes da Figura 81 e aplica grande parte dos conceitos desta unidade.
Figura 82 – Classe AppPresenca utilizada para controle de presença de alunos
Fonte: Elaborada pelo autor.
109
Programação Orientada a Objetos | Unidade 7 - Interface Gráfica
A classe AppPresenca estende a classe JFrame para a criação de uma janela e possui diversos componentes. O seu 
construtor, além de chamar o construtor da sua superclasse passando o nome da janela, seta o layout como nulo 
e chama os diversos métodos criados para incluir os componentes. É importante notar que cada componente do 
tipo JButton chama addActionListener passando como objeto um objeto do tipo da classe destinada a tratar os seus 
eventos. Os métodos add( ) fazem inclusão dos componentes no container, que, no exemplo, é a própria janela. 
Por fim, no método main( ), criamos um objeto janela do tipo AppPresenca. A Figura 83 exibe as interações com a 
interface gráfica gerada pelo código da figura anterior.
Figura 83 – Resultado da interação do usuário com a classe AppPresenca
Fonte: Elaborada pelo autor.
O objeto do tipo JScrollPane( ), criado dentro de incluiListaDeNomes( ), recebe como parâmetro um objeto do tipo 
JList e pode também ser chamado de painel de rolagem. Esse componente adiciona barra de rolagens ao compo-
nente que se localiza dentro dele.
Síntese da unidade
Nesta unidade, abordamos alguns conceitos relacionados à interface gráfica no Java. Foram apresentadas diver-
sas caixas de diálogo existentes na biblioteca Swing, bem como a criação de janelas, inserção de componentes, 
organização em layouts e tratamento de eventos.
110
Considerações finais
Entender a programação com interface gráfica permite o desenvolvi-
mento de softwares mais interativos e dinâmicos, resultando em aplica-
ções mais amigáveis.
111
 8Unidade 88. Boas Práticas II
Para iniciar seus estudos
Nesta unidade, você compreenderá algumas das boas práticas da progra-
mação orientadas a objetos que são utilizados no dia a dia de programa-
dores de grandes empresas. Essas boas práticas são úteis para o desen-
volvimento de códigos concisos, eficientes, escaláveis e também para o 
aumento da produtividade. Podemos começar? 
Objetivos de Aprendizagem
• Compreender algumas das boas práticas da programação orien-
tadas a objetos, com foco, principalmente, no tratamento de 
exceções, no desenvolvimento de softwares em camadas e no 
teste de software.
112
Programação Orientada a Objetos | Unidade 8 - Boas Práticas II
Introdução da unidade
Esta unidade realiza um aprofundamento em algumas das boas práticas que podem ser aplicadas no desenvol-
vimento de softwares que seguem o paradigma da orientação a objetos. O tratamento de exceções é uma das 
melhores formas de lidar com erros que podem ocorrer em tempo de execução e que levam à finalização de 
um programa. A compreensão de como realizar este tratamento resulta em softwares menos propensos a erros. 
Além disso, abordaremos, também, uma estratégia de desenvolvimento de softwares que é a baseada em cama-
das, uma das mais utilizadas atualmente devido a suas características relacionadas à escalabilidade, ao desen-
volvimento de classes em paralelo, bem como à facilidade de manutenção desse tipo de código. Por fim, apre-
sentaremos uma visão geral de testes em softwares com foco em testes unitários, testes de integração e testes 
caixa-preta, testes fundamentais na detecção de erros que impedem a geração de softwares robustos. 
8.1 Boas práticas II 
Nesta unidade, abordaremos algumas das boas práticas existentes na programação orientada a objetos. Focare-
mos inicialmente em mostrar quais os tipos de exceções, como podemos lançar exceções e como manipular uma 
exceção que foi capturada. Em seguida, abordaremos a modelagem de softwares em camadas tendo como foco 
as aplicações MVC, uma das mais utilizadas atualmente. Por fim, daremos uma visão geral do conceito de teste de 
software, com ênfase em três tipos de teste: teste unitário, teste de integração e teste caixa-preta. 
8.1.1 Tratamento de exceções
Na programação em Java, podem existir diversos tipos de erros. Os erros de compilação devem ser corrigidos 
manualmente pelo desenvolvedor para, assim, ele conseguir compilar o programa com sucesso. Entretanto, exis-
tem alguns tipos de erros que somente são detectados em tempo de execução (runtime). A este tipo de erro 
chamamos de exceção. Uma exceção pode ser lançada devido a erros de lógica, leitura ou escrita de arquivos, 
entrada de dados inválidos, acesso a elementos fora de índice ou até acesso a dispositivos ou arquivos externos. 
As exceções devem ser capturadas, pois só assim conseguimos realizar um tratamento adequado. Nesta seção, 
abordaremos alguns dos tipos de exceção existentes no Java, bem como a codificação do lançamento e manipu-
lação de uma exceção. 
8.1.1.1 Tipos de exceção
As exceções na linguagem Java podem ser classificadas em dois tipos: implícitas e explícitas. As exceções implíci-
tas não precisam de tratamento e demonstram ser contornáveis. Estas exceções originam-se da subclasse Error 
ou RunTimeException do Java. Já as exceções explícitas são exceções que precisam ser tratadas e que apresentam 
condições incontornáveis. Esse tipo de exceção herda da subclasse IOException. As exceções em Java são subclas-
ses da classe Throwable já definida internamente. Estas exceções podem ser as já existentes no Java ou definidas 
pelo programador. Alguns exemplos de classes de exceções existentes são: NullPointerException, IndexOutOfBou-ndsException e ClassCastException.
113
Programação Orientada a Objetos | Unidade 8 - Boas Práticas II
O Java divide as exceções em duas categorias: checked exceptions e unchecked exceptions (ou exceções verificadas 
e exceções não verificadas). Todas as subclasses da classe RunTimeException são exceções não verificadas; e todas 
as outras subclasses de Exception são exceções verificadas, conforme mostra a hierarquia de classes derivadas de 
Throwable na figura 84. 
Figura 84 – Hierarquia de classes derivadas de Throwable
Throwable
Error
Thread DeathAWTErrorIOException
Exception
RuntimeException
ArrayIndexOutOfBoundsException
ClassCastException NullPointerException InputMIsmatchExcption ArithmeticExcption
OutOfMemoryError
Fonte: Elaborada pelo autor.
A maior diferença entre as duas divisões é o fato de que exceções verificadas são operações nas quais o usuário 
deveria esperar que a operação pudesse falhar. Um exemplo disso seria uma escrita de disco em que, antes de 
tentar a escrita, foi verificado que o disco está cheio. Nesse caso, seria necessário checar se a operação foi con-
cluída com sucesso. Já as exceções não verificadas são erros em situações em que o código não deveria falhar 
em uma operação normal. Geralmente, elas indicam erros no programa. Um exemplo disso seria um programa 
tentar obter um item de uma posição em uma lista que não existe. Isso lançaria uma exceção do tipo unchecked. 
8.1.1.2 Lançando exceções
Lançar uma exceção é a maneira mais eficaz de indicar que uma determinada operação não pôde ser completada. 
A maior vantagem de utilizar exceções é o fato de que é algo bem difícil de ser ignorado, levando geralmente o 
programador a tentar tratar essa situação. O exemplo da Figura 85 mostra um código no qual uma exceção é 
lançada utilizando a palavra reservada throw. O método getDetalhes() lança uma exceção ao receber uma chave 
com valor igual a null para indicar que não faz sentido querer obter os detalhes de um contato cuja chave é nula. 
114
Programação Orientada a Objetos | Unidade 8 - Boas Práticas II
Figura 85 – Lançamento de uma exceção do tipo IllegalArgumentException em Java
Fonte: Elaborada pelo autor. 
A sintaxe para lançar uma exceção é dada da seguinte forma: 
throw new TipoDaExcecao ("string com o diagnóstico - opcional");
A string na sintaxe é opcional e serve como um diagnóstico sobre o problema ocorrido. Ela poderá ser lida pos-
teriormente quando a exceção for tratada. A string também é mostrada no prompt de comando antes de o pro-
grama ser finalizado, caso o usuário não trate esta exceção. No exemplo anterior, o tipo de exceção IllegalArgu-
mentException é definido no Java por padrão e indica que um valor de parâmetro inapropriado foi passado a um 
método. 
8.1.1.3 Manipulando exceções
Os fundamentos para o lançamento de exceções aplicam-se tanto em exceções verificadas quanto em não veri-
ficadas. Entretanto, de acordo com as regras do Java, é necessário fazer a manipulação ou tratamento somente 
de exceções do tipo checked. Esse tipo de exceção é uma subclasse de Exception, mas não de RunTimeException. 
Outra regra desse tipo de exceção é que o compilador exige que um método que possa lançar esse tipo de exce-
ção declare isso colocando a cláusula throws na assinatura do método, como mostrado a seguir:
public void salvaArquivo(String arquivoDestino) throws IOException{
}
Apesar de a cláusula throws na assinatura do método ser possível para exceções dos tipos checked e unchecked, é 
obrigatória apenas para a primeira. Outra regra para exceções do tipo checked é que elas exigem um tratamento. 
Usualmente, isso é feito por meio das cláusulas try e catch. A seguir exibimos como essas cláusulas devem ser 
utilizadas para tratamento de exceções:
try {
 // código a ser executado e que pode lançar uma exceção
} 
catch (ClasseDeExceção instânciaDaExceção) {
 // tratamento da exceção se capturada
}
115
Programação Orientada a Objetos | Unidade 8 - Boas Práticas II
No código anterior, temos o bloco try, no qual existe um código que pode lançar uma exceção de algum tipo. 
Em seguida, temos um bloco catch que irá capturar uma exceção do tipo “ClasseDeExceção” e cuja instância se 
chama “instânciaDaExceção”. Caso uma exceção desse tipo seja capturada dentro do bloco try, esse bloco será 
ativado e o tratamento para a exceção implementado dentro do bloco catch será executado. 
Note que utilizamos throw e throws em diferentes situações. A cláusula throw é utilizada para 
lançarmos uma exceção em algum código de forma quase forçada. Já a cláusula throws é 
utilizada na assinatura de um método para indicar que é possível que, em seu interior, seja 
lançada uma exceção de determinado tipo. Considere o exemplo do código a seguir: 
String nomeArquivo = “meuarquivo.txt” ;
catalogo.salvaArquivo(nomeArquivo);
Neste exemplo, temos um método que faz a escrita do catálogo em um arquivo chamado “meuarquivo.txt”. 
Entretanto, caso haja algum problema na escrita desse arquivo, será informado um erro em tempo de execução 
e o programa irá finalizar sem nenhum tratamento. Nesse caso, o erro pode surgir de um nome de arquivo que 
não existe ou até da tentativa de escrita em um arquivo protegido. É indicado que o programador verifique se 
algum tipo de exceção nesse sentido está sendo lançada e, se sim, que faça o tratamento correto. Na figura 81, 
reescrevemos o código acima de forma a tratar a exceção que pode ter sido lançada. 
Figura 86 – Tratamento de exceções de Entrada/Saída em Java
Fonte: Elaborada pelo autor.
É importante notar que, caso a exceção seja capturada dentro do bloco catch, o programa não será finalizado pela 
exceção. Uma sequência de passos deverá se seguir para que o programa continue. Um exemplo disso seria pedir 
ao usuário que digitasse o nome correto do arquivo que ele gostaria de abrir caso a variável sucesso tivesse valor 
igual a false ao final da execução desse código. Dessa forma, estaríamos tratando a exceção gerada que, no caso 
do exemplo dessa figura, é do tipo IOException. 
116
Programação Orientada a Objetos | Unidade 8 - Boas Práticas II
Os blocos try e catch devem estar sempre juntos, com o bloco catch seguindo o bloco try. Não 
se deve existir um sem o outro. Além disso, não deve existir código entre os dois blocos. 
Fique atento!
Se o bloco try lança diferentes tipos de exceção, podemos ter uma sequência de blocos do 
tipo catch seguindo o bloco try para capturar cada um dos tipos. Nesse sentido, todos os blo-
cos catch serão referentes ao try que os antecedem. 
Saiba mais
A clausula try pode incluir um terceiro componente que é o bloco finally. Essa cláusula é frequentemente omitida. 
Esse bloco inclui o código que deve ser executado independentemente de a exceção ser capturada no bloco try. 
Assim, um código que trata exceções acaba ficando da seguinte forma: 
try {
 // código a ser executado e que pode lançar uma exceção
} 
catch (ClasseDeExceção instânciaDaExceção) {
 // tratamento da exceção se capturada
}
finally {
 // ações comuns executadas se a exceção for lançada ou não 
}
Nesta unidade, foi ilustrada uma pequena parte das exceções existentes no Java. Para um 
conhecimento aprofundado, é necessário uma consulta à documentação dessa linguagem. 
Para sua utilização, deve ser importada uma diferente classe para cada tipo de exceção. 
Fique atento!
117
Programação Orientada a Objetos | Unidade 8 - Boas Práticas II
Muitas vezes, a cláusula finally é omitida e o código que deveria vir dentro de seu bloco simplesmente vem após 
a finalização do bloco catch. A diferença é que a cláusula finally é executada, ainda que dentro de try exista uma 
cláusula return do método sendo executada e mesmo que uma exceção seja lançada no bloco try, mas não cap-
turada. Por esse motivo, é uma boa prática utilizar blocos finally. 
8.1.2 Modelagem de software em camadas
O sucesso no desenvolvimento de aplicações que seguem o paradigma orientado a objetos (OO) está direta-mente ligado à arquitetura que se utiliza para construir a aplicação. A arquitetura de um software define quais 
são os seus componentes, os seus funcionamentos e os relacionamentos existentes. Nesta seção, abordaremos 
a modelagem de software seguindo padrões de arquitetura em camadas. A organização em camadas é bastante 
utilizada em softwares orientados a objetos, visto que suas vantagens condizem com os objetivos do paradigma. 
A modelagem por meio da arquitetura em camadas visa à criação de módulos dentro do software, que se comu-
nicam entre si de forma que uma camada se comunica apenas com sua camada imediatamente inferior. Desse 
modo, a dependência de uma camada só existente em relação à camada que está localizada imediatamente 
abaixo. Cada camada deve possuir um conjunto de classes que realizam um propósito bem definido ao mesmo 
tempo em que é necessário que haja pouca dependência entre as camadas. Na figura 87 a seguir, apresentamos 
duas arquiteturas genéricas em camadas:
Figura 87 – Arquiteturas genéricas para desenvolvimento em camadas
Interface Gráfica
Comunicação
Negócio
Dados
Interface Gráfica
Comunicação
Negócio
Fonte: Elaborada pelo autor.
A camada cujo nome é “interface gráfica”, neste modelo, é conhecida como Camada de Apresentação. Ela con-
tém as classes relacionadas à interface gráfica, ou seja, o código responsável por criar as telas gráficas e a intera-
ção com o usuário. A seguir (na arquitetura da esquerda), temos a Camada de Comunicação que é responsável 
por distribuir o sistema em diversas máquinas (caso seja necessário). Sendo assim, a arquitetura da direita ilus-
tra a situação em que isso não é necessário. A Camada de Negócio contém as classes do sistema responsáveis 
pelos serviços e regras de negócio. Por fim, a última camada, a Camada de dados, cuida do armazenamento e da 
recuperação de dados que devem ser persistentes no sistema, ou seja, dados que devem ser armazenados para 
utilização futura. Existem diversos tipos de padrões para a arquitetura em camadas. Nesta seção, focaremos no 
padrão MVC.
118
Programação Orientada a Objetos | Unidade 8 - Boas Práticas II
8.1.2.1 Aplicações MVC
O padrão de arquitetura MVC (Modelo, Visualização e Controle) surgiu nos anos 80 com a linguagem de progra-
mação SmallTalk, a primeira linguagem totalmente orientada a objetos, e é amplamente utilizado até os dias 
atuais. Desenvolvido por Trygve Reenskaug, é geralmente aplicado no projeto de aplicações desktop devido à 
facilidade no desenvolvimento em camadas de aplicações que seguem o paradigma da orientação a objetos. 
O objetivo na utilização deste modelo é separar possíveis mudanças na interface gráfica de usuário (GUI), de 
modo que não resultem em mudanças na camada de negócios da aplicação. 
Suas principais vantagens estão relacionadas à facilidade de manutenção no código, bem como a facilidade no 
desenvolvimento utilizando times multidisciplinares: desenvolvedores que possam criar códigos robustos e desig-
ners que possam criar interfaces gráficas que sejam amigáveis e que sigam os conceitos de usabilidade. Além 
disso, a criação de softwares utilizando essa estratégia permite que o software se torne escalável e o desenvolvi-
mento paralelo seja possível, uma vez que as camadas são independentes. O padrão MVC requer maior quanti-
dade de tempo para a análise e projeto do software, não sendo adequado para aplicações pequenas e simples. 
A camada de apresentação ou visualização não se preocupa com a forma como a informação foi obtida, mas sim 
em como exibi-la. Pode incluir elementos de exibição no cliente como, por exemplo, HTML, XML, ASP ou Applets. 
Já a camada de modelo, também chamada de camada de lógica da aplicação, é o coração do sistema e define 
tudo o que o software vai fazer. Seu objetivo é prover uma modelagem e um encapsulamento não somente dos 
dados, mas também do comportamento da aplicação que é independente da aplicação. A geração de dados, sua 
manipulação e armazenamento também são de responsabilidade dessa camada. Por fim, a camada de controle 
é a que determina o fluxo da apresentação, atuando como uma camada intermediária entre as camadas de 
modelo e visualização. Sua função principal é controlar e mapear as ações entre essas camadas. Na figura 88, exi-
bimos uma analogia do padrão MVC com a relação entre o usuário de uma TV, o controle, o videocassete e a TV. 
Figura 88 – Analogia do modelo MVC com um usuário que assiste a uma TV
Usuário
Visualização Modelo
Controlador
Fonte: Elaborada pelo autor.
Na figura, o controle estaria na camada de controle, o vídeocassete na camada de modelo e a TV na camada de 
visualização. Sendo assim, essa organização estaria seguindo o modelo MVC. 
119
Programação Orientada a Objetos | Unidade 8 - Boas Práticas II
8.1.3 Visão geral de testes
O teste de um software tem por principal objetivo revelar problemas ou erros que estão encobertos. Sua impor-
tância é imensa, visto que erros descobertos em softwares pelo usuário/cliente durante a sua utilização pode 
acabar levando a enormes prejuízos financeiros, bem como a danos irreparáveis, como a vida de uma pessoa, no 
caso de softwares relacionados à saúde. Sendo assim, compreendê-lo se faz extremamente necessário. 
Existem diversos tipos de teste de softwares que são classificados de acordo com sua categoria. Na categoria 
de testes em função do estágio do ciclo de vida do produto (software), estão presentes o teste unitário, teste de 
integração, teste de sistema e teste de aceite. Já na categoria de testes em função do objetivo de teste, enqua-
dram-se o teste caixa-preta (funcional), teste não funcional, teste caixa-branca (estrutural) e teste de regressão. 
Nesta seção, abordaremos algumas das principais técnicas de testes de softwares, especificamente, aplicados 
a softwares orientados a objetos. Daremos as definições e ilustraremos os conceitos de teste unitário, teste de 
integração e teste caixa-preta. 
8.1.3.1 Teste unitário
Na construção de um avião, caso este seja testado apenas após a sua finalização, um desastre aconteceria. Sendo 
assim, durante a sua montagem, todos os dispositivos são testados isoladamente até sua exaustão. Podemos cha-
mar esse tipo de verificação de teste unitário. Em um software, realizar um teste unitário é testar individualmente 
a menor unidade do seu projeto. Cada unidade lógica é testada com dados suficientes apenas para o seu teste. 
Em projetos orientados a objetos, esta pequena unidade pode ser um método, uma classe ou mesmo um objeto. 
Existem diversas vantagens associadas à utilização de testes unitários. A principal delas é que, testando indi-
vidualmente, encontrar a presença de erros e sua localização torna-se bem mais fácil. Essa técnica permite a 
identificação e a correção de forma precisa de erros que são provenientes de códigos mal escritos. Além disso, 
permite que sejam testadas situações de sucesso e falha, deixando o código testado de forma unitária bem 
mais confiável. 
As classes de teste devem ser projetas e escritas no início do desenvolvimento do software, antes mesmo da codi-
ficação das classes que possuem as regras do negócio (funcionalidades do sistema). Os testes podem e devem 
ser executados diariamente e várias vezes ao dia, pois é mais fácil solucionar erros/problemas pequenos do que 
solucionar problemas maiores no código. Quem deve projetar, escrever e executar os casos de teste para cada 
classe é, geralmente, o próprio desenvolvedor. Ao conjunto de casos de teste chamamos de Test Suit. Quem pro-
jeta, escreve e executa o test suit pode ser o desenvolvedor em conjunto com o coordenador do projeto. Em algu-
mas aplicações, as responsabilidades de teste podem ser destinadas a uma equipe especifica para isso, devido ao 
fato de que o programador pode não encontrar seus próprios erros no código.
Para saber o que testar no teste unitário em um código orientado a objetos, é necessário criatividade. Pode-se 
pensar primeiro nos testes mais simples e deixaros mais complexos para o final. Além disso, a utilização de ape-
nas dados suficientes é fundamental. Em outras palavras, não é viável colocar mais de um caso de teste testando 
a mesma coisa. Métodos triviais como setters e getters, entre outros, não devem ser testados. O teste de métodos 
setters só é necessário caso haja validação de dados. Na figura 89, temos a classe Calculadora que deverá ser tes-
tada. Vamos utilizar a imaginação para criar casos de teste para essa classe. 
120
Programação Orientada a Objetos | Unidade 8 - Boas Práticas II
Figura 89 – Classe Calculadora em Java
Fonte: Elaborada pelo autor.
Na classe descrita acima, podemos verificar o comportamento dos métodos realizaSoma( ), realizaSubtracao( ), rea-
lizaMultiplicacao( ) e realizaDivisao( ). Para isso, criamos objetos dessa classe, passando diferentes valores para os 
atributos, e verificamos os valores retornados por esses métodos. Podemos basear nossos testes na Tabela 4 
abaixo, que descreve valores possíveis para n1 e n2 e os valores esperados no retorno da soma, subtração, mul-
tiplicação e divisão. 
Tabela 4 – Casos de teste para a classe Calculadora
n1 n2 Soma Subtração Multiplicação Divisão
0 0 0 0 0 NaN
10 0 10 10 0 Infinity
0 10 10 –10 0 0
10 10 20 0 100 1
10 5 15 5 50 2
5 10 15 5 50 0,5
Fonte: Elaborada pelo autor.
É importante notar que os valores de n1 e n2 da tabela acima não foram escolhidos de forma aleatória, mas sim 
com o propósito de checar o funcionamento dos métodos mediante a diferentes combinações de valores para os 
atributos. A primeira linha checa os valores de retorno quando ambos os atributos são zero; a segunda e a terceira 
linha checam o comportamento dos métodos quando um dos atributos é zero; a quarta linha checa se os méto-
dos se comportam corretamente quando os atributos possuem os mesmos valores; e, por fim, a quinta e a sexta 
121
Programação Orientada a Objetos | Unidade 8 - Boas Práticas II
linha checam se os métodos conseguem lidar corretamente com a situação quando um dos atributos é menor 
que o outro. A figura 90 ilustra um código para a checagem dos valores esperados de retorno para os métodos da 
classe Calculadora usando os valores da Tabela 4. 
Figura 90 – Classe CalculadoraTeste que realiza o teste unitário de Calculadora 
Fonte: Elaborada pelo autor.
Podemos perceber que os diversos if’s existentes testam qual o tipo do teste a ser executado. Se a resposta obtida 
com aquele teste é diferente do esperado, o código exibe uma mensagem especificando qual o tipo de erro 
encontrado. 
8.1.3.2 Teste de integração
Após os testes unitários, geralmente realizam-se testes de integração. Nesse teste, deseja-se avaliar a interação 
entre componentes do software. Sendo assim, o objetivo desse tipo de teste é a verificação de que os componen-
tes do sistema, quando juntos, trabalham conforme o esperado, sem a produção de erros. Em muitas situações, 
componentes individuais funcionam bem separadamente. Entretanto, quando integrados, ocorre uma falha no 
sistema. Desde modo, a utilização de apenas testes unitários não seria suficiente para capturar erros desse tipo. 
Assim, faz-se necessária a utilização de testes de integração. 
122
Programação Orientada a Objetos | Unidade 8 - Boas Práticas II
Os tipos de falhas que podem ser encontrados no teste de integração são, na maior parte das vezes, na transmis-
são de dados. Um componente C1 pode aguardar o retorno de um valor X ao executar um método do compo-
nente C2. Entretanto, o método de C2 pode retornar a um valor Y, causando uma falha no sistema que só pode 
ser verificada no teste de integração. A funcionalidade dos componentes C1 e C2 não será testada, mas sim a 
interação existente entre os componentes. O tratamento de interfaces com outros sistemas também não faz 
parte do escopo de um teste de integração. 
A decisão sobre o que deve ser verificado depende da compreensão das funcionalidades que o sistema provê e 
que precisam de verificação. Em um sistema bancário, por exemplo, pode-se querer testar a operação de débito 
em uma conta corrente. Nessa situação, pode ser necessária a verificação de que o sistema debitou certa quantia 
de dinheiro no banco de dados e que o valor retornado ao saldo foi a quantia anterior subtraída do valor do débito. 
8.1.3.3 Teste caixa-preta
Esse teste caixa-preta, também chamado de teste funcional, é baseado nas funções descritas em documentos 
de especificação de requisitos, ou compreendidas pelos testadores como necessárias, e pode ser realizado em 
todos os níveis de teste (ex: teste unitário, teste de integração, etc.). Pode-se utilizar a especificação do software 
para gerar as condições e casos de teste de acordo com as funcionalidades do sistema. 
Esse tipo de teste considera apenas o comportamento externo do software e, por isso, também é chamado de 
caixa-preta. Este nome vem do fato de o comportamento interno do componente testado não ser levado em 
consideração, mas somente sua interação com o mundo externo. Os casos de teste consistem em dados de 
entrada. O teste é executado e o resultado obtido é comparado com o valor esperado. Caso sejam diferentes, 
ocorreu uma falha. 
O componente de software a ser testado pode ser um método, uma função interna, um programa, um compo-
nente, um conjunto de programas/componentes ou até uma funcionalidade. Sua aplicação pode depender da 
fase em que a execução de testes (unidade, integração, sistema e aceitação) se encontra. É possível a detecção 
de erros de interface, de comportamento e/ou de desempenho, entre outros. Esse teste é especialmente útil para 
a detecção de funções incorretas ou faltantes, erros de interface, erros de comportamento ou mesmo erros na 
inicialização e no término do programa. 
Existem diversas formas de executar o teste caixa-preta. Uma delas é a classe de equivalên-
cia, na qual particionam-se os valores de entrada para os casos de teste em classes e testa-se 
apenas com uma entrada de cada classe. Assim, se o teste funciona para o caso de teste 
representando aquela classe, aquela classe de equivalência toda está testada. 
Saiba mais
Para elaborar testes desse tipo, deve-se tentar pensar como um usuário do sistema e imaginar quais erros ele 
poderia cometer. Em um campo de data de nascimento, um exemplo seria utilizar como entrada a data atual ou 
uma data futura. Para testar campos de CPF e CEP, colocar valores impossíveis. Em um campo com o valor a ser 
pago, colocar valores negativos ou digitar uma String com letras em vez de números.
123
Programação Orientada a Objetos | Unidade 8 - Boas Práticas II
Síntese da unidade
Nesta unidade, abordamos algumas das principais práticas no desenvolvimento de softwares orientados a obje-
tos que visam à sua qualidade. Apresentamos como pode ser realizado o tratamento de exceções, focando prin-
cipalmente no lançamento de uma exceção e sua manipulação. Além disso, apresentamos a modelagem de soft-
wares em camada utilizando o padrão MVC. Por fim, demos uma visão geral de testes de softwares, conceituando 
os testes do tipo unitário, integração e caixa-preta. 
124
Considerações finais
O melhor aproveitamento da utilização das boas práticas mencionadas 
nesta unidade é obtido pela experiência. As decisões relacionadas às 
diversas boas práticas são melhor tomadas quando se tem bastante tempo 
de prática em sua utilização. Por esse motivo, não deixe de utilizá-las. 
125
BARNES, David John; KÖLLING, Michael; GOSLING, James. Objects first 
with Java: a practical introduction using Bluej. 5. ed. São Paulo: Pearson 
Prentice Hall Editora, 2006.
BOOCH, Grady; RUMBAUGH, James; JACOBSON, Ivar. UML: guia do 
usuário. 2. ed. Rio de Janeiro: Elsevier Brasil Editora, 2006.
DEITEL, Harvey M.; DEITEL, Paul J. Java Como Programar. 4. ed. São Paulo: 
Pearson Education Editora, 2003.
FOWLER, Martin. UML Essencial: um breve guia para linguagem padrão. 
3. ed. Porto Alegre: Bookman Editora, 2014.
GUEDES, Gilleanes T. A. UML 2: uma abordagem prática.3. ed. São Paulo: 
Novatec Editora, 2009.
SANTOS, Rafael. Introdução à programação orientada a objetos 
usando Java. 2. ed. Rio de Janeiro: Campus Editora, 2003.
Referências
	Unidade 1
	1. Classes e Objetos 
	Unidade 2
	2. Aprofundamento em Classes e Objetos
	Unidade 3
	3. Herança e Polimorfismo
	Unidade 4
	4. Composição e Agregação
	Unidade 5
	5. Boas Práticas I
	Unidade 6
	6. Armazenamento de Dados
	Unidade 7
	7. Interface Gráfica
	Unidade 8
	8. Boas Práticas II

Mais conteúdos dessa disciplina