Logo Passei Direto
Buscar
Material
páginas com resultados encontrados.
páginas com resultados encontrados.
left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

Prévia do material em texto

Introdução a Java: de Alunos para Alunos
Departamento de Computação de Sorocaba
Universidade Federal de São Carlos
2019 Departamento de Computação de Sorocaba
UNIVERSIDADE FEDERAL DE SÃO CARLOS
HTTPS://DCOMP.SOR.UFSCAR.BR/
Este livro foi desenvolvido como Projeto na Atividade de Extensão No. 23112.002872/2015-35 -
Introdução à Linguagem de Programação Java. Este livro é uma propriedade intelectual dos alunos
de graduação do curso de Bacharelado em Ciência da Computação da Universidade Federal de
São Carlos - UFSCar, campus Sorocaba, que o escreveram: Alex Sandro Momi Junior, Amanda
Carnio Pascon, Anathan Telles Pereira, Anderson Pinheiro Garrote, Arthur de Jesus Simas, Beatriz
Gonçalves de Carvalho, Bianca Gomes Rodrigues, Bruno Bevilacqua Rizzi, Bruno Frítoli Carrazza,
Diego Tupinambá, Eduardo Ravagnani de Melo, Giovanna Albuquerque Riello, Giovanni Marçon
Rossi, Giulia Silva Fazzi, Gustavo Alberto de França Waessman, Isabella Roberta Guimarães
Barbosa, Isabelle Tomazela Barizon, Jade Manzur de Almeida, João Victor Elias Costa, José
Vitor Novaes Santos, Lauan dos Santos Souza, Leandro Naidhig, Luis Felipe Dobner Henriques,
Luiz Felipe Guimarães, Matheus Henrique Cassatti, Mauricio Marques da Silva Junior, Michele
Argolo Carvalho, Pietro Zuntini Bonfim, Rafael Tofoli Sereicikas, Renan Oliveira de Barros Lima,
Renata Praisler Pereira da Silva, Rodrigo Gonçalves do Nascimento, Rodrigo Jundi Kinchoku,
Thiago Yussuki Uehara, Victor Fernandes de Oliveira Brayner e William Giacometti Dutra de
Oliveira. Também é propriedade intelectual do professor da Atividade de Extensão, Carlos Tadeu
Castilho Santos, dos monitores, Bruno Sacconi Peres, Gabriel Peres de Andrade e Thiago Borges
da Silva, e dos organizadores do Projeto, Lucas Penteado Sacchi e Sofia de Almeida Machado da
Silveira, também alunos de graduação do curso de Bacharelado em Ciência da Computação. O
desenvolvimento da Atividade de Extensão e do Projeto foram supervisionados pela Profa. Dra.
Sahudy Montenegro González, do Departamento de Computação de Sorocaba (DComp-So).
Primeira versão, Outubro 2019
Sumário
1 Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2 Primeiros Passos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.1 Criando um Projeto no Eclipse 7
2.2 Primeiro Código em Java 10
3 Orientação a Objetos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
3.1 O que é? 12
3.2 Classes 13
3.2.1 Atributos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
3.2.2 Construtor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
3.2.3 Métodos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
3.2.4 Métodos Acessores Get e Set . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
3.2.5 Escopo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
3.2.6 Static/Final . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
3.3 Atributos de Visibilidade 28
3.4 Encapsulamento 38
3.5 Objetos 38
3.6 Herança 45
3.7 Sobrecarga/Sobrescrita 46
3.8 Polimorfismo 50
3.9 Herança Múltipla 51
4 Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
4.1 Declaração de Variáveis 54
4.2 Leitura e Escrita 55
4.2.1 Scanner . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
4.2.2 System.out . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
4.3 String 57
4.4 If/Else 58
4.5 Estruturas de Repetição 59
4.5.1 While/Do While . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
4.5.2 For . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
4.5.3 Enhanced For . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
4.6 Switch 64
4.7 Tipos por Referência 67
4.8 Exceções 67
4.9 Cast 68
4.10 Interfaces 69
5 Conclusão . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
Bibliografia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
1. Introdução
Este livro visa ensinar os principais conceitos acerca da linguagem de programação Java,
além de características da Orientação a Objetos. O livro foi escrito por alunos de graduação de
Bacharelado em Ciência da Computação da Universidade Federal de São Carlos (UFSCar), campus
Sorocaba, como projeto final de uma atividade de extensão de aulas de Introdução a Java. Tanto as
aulas ministradas, quanto a edição do livro foram também realizadas por alunos de graduação em
Ciência da Computação da UFSCar.
A atividade de extensão envolveu cinco aulas teóricas e duas aulas destinadas ao desenvolvi-
mento das seções do livro, sendo que todas as aulas ocorreram ao longo do primeiro semestre de
2019. O curso envolveu alunos de diversos anos, desde os recém ingressados na faculdade até vete-
ranos que desejavam aprimorar seus conhecimentos em Orientação a Objetos ou especificamente
aprender a linguagem Java. A escrita do livro ocorreu em grupos de 3 a 4 alunos.
O livro permitiu consolidar os conceitos estudados ao longo do curso pelos alunos e criar um
livro com conteúdo sobre Java e Orientação a Objetos feito por alunos para alunos. Desta forma, o
livro possui uma proposta diferente de livros didáticos tradicionais, buscando demonstrar como
os alunos conseguiram entender os tópicos em que tiveram mais dificuldade, como conseguiram
entender conceitos de forma diferente da explicada no curso e como que os testes dos códigos e os
erros obtidos auxiliaram no entendimento do conteúdo. Assim, o livro busca apresentar a visão dos
alunos, explicando os conceitos de forma mais próxima dos estudantes.
Apesar de os assuntos referentes à Orientação a Objetos e à linguagem Java serem vastos e
importantes, foi necessário reduzir e selecionar os assuntos a serem abordados no livro para que os
alunos tivessem tempo suficiente para escrever sobre eles. Portanto, foi realizado um questionário
ao qual os alunos responderam individualmente, após o término das aulas teóricas, a fim observar
quais os tópicos ensinados e os exercícios em que eles tiveram mais dificuldade. Em seguida, os
assuntos foram designados a cada grupo de acordo com o nível de conhecimento, com base no ano
de ingresso dos integrantes, e com as respostas do questionário.
O livro conta com esta seção introdutória, uma seção sobre primeiros passos, uma sobre
características gerais da Orientação a Objetos, uma com conceitos específicos da linguagem Java e
uma seção de conclusão, além das referências bibliográficas. Esta seção introdutória visa fornecer
6 Capítulo 1. Introdução
ao leitor uma visão geral sobre o que é o livro. A segunda seção aborda os passos necessários
para a criação de programas orientados a objetos, abordando a criação de um novo projeto na
IDE Eclipse e o primeiro código em Java. A seção destinada à Orientação a Objetos (Seção 3) irá
apresentar desde conceitos básicos como classes e objetos até conceitos mais complexos como
encapsulamento, sobrecarga e sobrescrita. A seção sobre Java (Seção 4) apresenta os conceitos
especificamente na linguagem Java, sendo que tais conceitos podem existir também em outras
linguagens. Por fim, a última seção é o encerramento do livro. As referências bibliográficas são
referentes aos materiais utilizados para o desenvolvimento dos capítulos do livro pelos alunos do
curso. Olivro é composto de texto explicativo e também de exemplos de código criados pelos
alunos e realizados em aula, inclusive exemplos errados para explicar como os alunos aprenderam
os conceitos por meio da observação dos erros obtidos.
2. Primeiros Passos
Esta seção apresenta passos essenciais necessários antes de começarmos a aprender sobre Java
e Orientação a Objetos, demonstrando as dificuldades encontradas pelos alunos nessa fase inicial
do curso e como eles conseguiram superá-las. Para isso, primeiro será abordada a criação de um
novo projeto na IDE Eclipse, que foi a IDE utilizada pela maioria dos alunos ao longo do curso.
2.1 Criando um Projeto no Eclipse
Muitos podem pensar que a criação de um projeto no Eclipse é simples, de forma análoga à
criação de um arquivo na linguagem C. Porém, ao tentar criar um projeto, tais pessoas se deparam
com um ambiente completamente diferente do qual estavam acostumadas, tendo em vista outras
linguagens. No Eclipse é necessário criar um projeto, com pacotes (onde são armazenadas as
classes) e classes, que serão explicadas na Seção 3.2.
Assim, para a criação de um projeto devemos clicar em File (arquivo), depois em New (novo),
seguido de Java Project (projeto Java). A Figura 2.1 apresenta os passos descritos.
Figura 2.1: Criando o projeto no Eclipse
Em seguida, uma nova tela irá aparecer, como mostra a Figura 2.2, sendo necessário nomear o
projeto, escolher a localização no computador em que se deseja salvar a pasta do projeto e, caso
necessário, mudar algumas configurações, depois clicar em Finish (finalizar).
8 Capítulo 2. Primeiros Passos
Figura 2.2: Tela de criação do projeto
Após a criação do projeto, é preciso criar um pacote, clicando na seta, como demonstrado na
Figura 2.3, depois em New (novo), seguido de Package (pacote).
Figura 2.3: Criando o pacote
Os pacotes, por convenção, devem começar com letra minúscula. Ao colocar o nome do pacote
com letra maiúscula surge um aviso sobre a convenção dos pacotes, como é possível observar na
Figura 2.4.
2.1 Criando um Projeto no Eclipse 9
Figura 2.4: Aviso padrão do nome de pacotes
Após a criação do pacote, devemos finalmente criar a classe, essencial para a Orientação a
Objetos, como será explicado na Seção 3.2. Para isso, é preciso clicar em New (novo), depois em
Class (classe), como observado na Figura 2.5.
Figura 2.5: Criando uma nova classe
As classes, por sua vez, devem começar com a letra maiúscula. De forma análoga ao aviso de
que os pacotes geralmente começam com letra minúscula, é observado o aviso (Figura 2.6) de que
a convenção é utilizar nomes de classes com letra maiúscula, que surge ao colocar a primeira letra
do nome da classe em minúsculo.
10 Capítulo 2. Primeiros Passos
Figura 2.6: Aviso padrão do nome de classes
Após seguir esses passos, será obtida a estrutura vazia da classe Main, como mostra o Código
2.1.
1 package primeiroProjeto;
2
3 public class Main {
4 public static void main(String [] args) {
5 ...
6 }
7 }
Código 2.1: Estrutura da classe Main
2.2 Primeiro Código em Java
O primeiro código em Java (Código 2.2), assim como é geralmente realizado em outras
linguagens, é o “Hello, World!” (Olá, Mundo!). Para isso, utilizamos apenas o println, que será
explicado na Seção 4.2.2 para exibir a mensagem na tela do usuário.
1 public class Main {
2 public static void main(String [] args) {
3 System.out.println("Hello , World!");
4 }
5 }
Código 2.2: Hello World
2.2 Primeiro Código em Java 11
Além disso, também é interessante a utilização do Scanner para leitura de entrada do usuário.
Para isso, é preciso importar o pacote do Scanner, como demonstrado o Código 2.3.
1 import java.util.Scanner;
2
3 public class Main {
4 public static void main(String [] args) {
5 Scanner scanner = new Scanner(System.in);
6 System.out.println("Digite seu nome: ");
7 String nome = scanner.nextLine ();
8 System.out.println(nome);
9 scanner.close();
10 }
11 }
Código 2.3: Usando Scanner e println
3. Orientação a Objetos
Esta seção aborda o paradigma de Orientação a Objetos, explicando os seus principais con-
ceitos, comuns a quaisquer linguagens pertencentes a este paradigma. Serão abordados desde
conceitos mais simples e básicos, como classes e objetos, até mais complexos, como sobrecarga e
sobrescrita. Além de apresentar estes assuntos de forma didática e na visão dos alunos, também
serão apresentados exercícios já solucionados a fim de reforçar o conteúdo e apresentá-lo com
exemplos, mostrando o que funciona e o que gera erro.
3.1 O que é?
A Orientação a Objetos é um dos paradigmas de programação, dentre outros como o paradigma
Procedural, Orientado a Eventos e o Orientado a Aspectos, por exemplo. Paradigmas são formas de
pensar tanto na organização do algoritmo, quanto no algoritmo em si. A Orientação a Objetos não
necessariamente é o paradigma mais adequado para todas as situações. Como exemplo, podemos
ter o paradigma Procedural – ou Estruturado – que se faz mais vantajoso para sistemas embarcados,
isto é, sistemas dedicados ao dispositivo ou sistema que ele controla com tarefas pré-definidas. O
paradigma Procedural também pode ser mais adequado nos sistemas em que o conhecimento e
hardware se fazem mais necessários, já que esse paradigma apresenta vantagens por fornecer maior
liberdade com o hardware. Isso ocorre pois o paradigma Estruturado é sequencial, sendo que uma
linha é executada após a outra. Por isso, quando um programa no paradigma Procedural é bem
desenvolvido, ele possui grandes vantagens, tendo em vista que não há desvios como ocorre no
paradigma Orientado a Objetos.
Porém, a Orientação a Objetos traz uma ideia muito interessante de representação de cada
elemento em classes e objetos, aproximando, assim, o sistema do mundo real. Para ser mais preciso,
classes são moldes que possuem atributos e métodos. Partindo dessa ideia podem ser usados
animais, automóveis, entre outros, como exemplos de classes. Para um maior entendimento, é
interessante utilizar um animal, como o cachorro. Sabemos que o cachorro tem suas características
e ações. As características podem ser pelos, orelhas, rabo e focinho e como ações podemos ter
latir e comer. Em uma representação no código, o cachorro seria uma classe, suas características
seriam seus atributos e suas ações seus métodos. Entretanto, é evidente que a classe Cachorro é
3.2 Classes 13
genérica e pode ser utilizada para qualquer tipo de cachorro. Imaginando um cachorro específico
chamado “Spyke”, para representá-lo no código, é necessária uma projeção das suas características
e ações específicas, que pode ser feita a partir da classe Cachorro, para assim representar os
atributos e métodos. Essa projeção é denominada de objeto.
A Orientação a Objetos possui uma estrutura que se assemelha a elementos do mundo real,
facilitando o entendimento do sistema pelo programador e pelo usuário da aplicação. Partindo
desse pressuposto, ela tem algumas vantagens, como a reutilização de código, que faz com que
o tempo de desenvolvimento do software seja menor e também permite que o programa tenha
menos linhas de código. Outra vantagem é a facilidade de leitura e compreensão do código, que,
consequentemente, auxilia a manutenção futura do código, por conta do tipo de representação já
citada, que se aproxima do mundo real.
Por outro lado, há algumas desvantagens de utilizarmos a Orientação a Objetos, como o fato de
o processamento de uma aplicação ser mais lento por conta da complexidade do modelo, que tem
muitos desvios já que traz suas representações na forma de classe. Assim, esse paradigma não é
recomendado para sistemas com processamento limitado. Mas graças aos computadores atuais,
que possuem processadores mais potentes, isso já não representa mais um problema tanto quanto
antigamente.
Outro ponto a se citar é que a Orientação a Objetos é estruturadaem 4 pilares que são: abstração,
encapsulamento, herança e polimorfismo. Esses pilares são responsáveis pela representação do
algoritmo mais próxima do mundo real.
3.2 Classes
Uma das premissas das linguagens Orientadas a Objetos é a possibilidade de representar
elementos existentes no mundo real por meio de estruturas de código. Essa representação pode
ser feita utilizando classes, possibilitando um código mais limpo e organizado. Utilizamos um
elemento da vida real como classe, os atributos da classe serão as características desse elemento e
seus métodos representam as ações do elemento.
Um exemplo de conceito de classes pensando em coisas da vida real seria um cachorro, que
tem suas características como cor dos olhos, cor do pelo e altura (que são análogos aos atributos
da classe). A classe Cachorro também possui suas ações, como latir e comer (que são como os
métodos da classe). Os conceitos de atributos e métodos serão explicados em maiores detalhes na
Seção 3.2.1 e Seção 3.2.3, respectivamente.
Iremos elaborar a criação de uma classe que se refere a cachorros com algumas características
(atributos) e ações (métodos). As características serão: quantidade de patas, cor do pelo, peso,
idade, altura. Já as ações serão: andar, dormir, comer e latir. Vale ressaltar que poderíamos ter
características e ações diferentes para esta classe, porém, para o exemplo, vamos utilizar estas
mencionadas. No Código 3.1 temos a criação da classe Cachorro.
1 public class Cachorro {
2 int qtdPatas , peso , idade , altura;
3 String corPelo;
4
5 public void andar(int distancia) {
6 System.out.println("Cachorro andou " + distancia + "
metros.")
7 }
8
9
10 public void dormir () {
14 Capítulo 3. Orientação a Objetos
11 System.out.println("Cachorro dormiu");
12 }
13
14 public void comer () {
15 System.out.println("O cachorro agora está comendo");
16 }
17
18 public void latir() {
19 System.out.println("Au Au");
20 }
21 }
Código 3.1: Criação da classe Cachorro
3.2.1 Atributos
Em geral, podemos dizer que atributos são as características de objetos, e também podemos
chamar isso de variáveis especiais, que são declaradas diretamente no escopo da classe, portanto,
um tipo específico de variável. Vamos analisar um exemplo simples. Imagine que temos uma classe
Cachorro e para essa classe temos os atributos globais, que servem para todos os novos objetos
que criarmos relacionados a essa classe. Criamos então Fido e Rex, objetos da classe Cachorro.
Representamos isso da forma mostrada no Código 3.2.
1 public class Cachorro {
2 public String nome;
3 public float peso;
4 public String cor;
5 }
6
7 public class Main {
8 Cachorro Fido = new Cachorro ();
9 Fido.nome = "Fido";
10 Fido.peso = 20;
11 Fido.cor = "preto";
12
13 Cachorro Rex = new Cachorro ();
14 Rex.nome = "Rex";
15 Rex.peso = 10;
16 Rex.cor = "marrom";
17 }
Código 3.2: Criação de objetos da classe Cachorro
Com isso, passamos a imaginar que cada objeto, a princípio, poderia possuir seu próprio
atributo. Se tratarmos do exemplo acima, onde Fido e Rex são nossos objetos, embora possuam
características diferentes, como nome, peso e cor, o atributo é o mesmo para ambos, apesar do
valor do atributo mudar em cada um deles.
Ter objetos com atributos diferentes para a mesma classe, não funcionaria bem. Por exemplo,
criar o atributo pelo somente para Fido e não criar esse mesmo atributo em Rex, não permitiria
a flexibilidade que trabalhar com atributos próprios às classes permite.
O mais interessante de aprender dessa forma, é que você realmente passa a trazer situações
da vida real para a programação. Trabalhando com classes, objetos e atributos como no exemplo
3.2 Classes 15
dado, temos uma visão muito mais clara do problema a ser tratado, uma abstração do mundo real, o
que facilita a compreensão e ajuda a entender os conceitos principais da Programação Orientada a
Objetos em Java.
3.2.2 Construtor
Os construtores, também denominados métodos construtores, são responsáveis por instanciar a
classe em que foram definidos. Eles são sempre chamados quando se cria um objeto da respectiva
classe, devendo ser utilizados obrigatoriamente. Quando não é declarado um método construtor, a
linguagem Java o faz de maneira automática. No construtor as variáveis assumem o valor nulo.
Para criar um objeto usa-se a palavra new (novo) e, assim, o construtor é automaticamente
chamado. Normalmente, Java cria um construtor padrão, sem parâmetros. Por exemplo, como é
mostrado no Código 3.3.
1 public class Geladeira {
2 public Geladeira () {
3 /*
4 Aqui se insere os parametros e estruturas que deseja
em seus construtores , podendo possuir um diferente
numero de parametros , inclusive e possivel criar
mais de um construtor diferente por classe
5 */
6 }
7 }
8
9 public class Aplicacao {
10 public static void main(String [] args) {
11 Geladeira Consul = new Geladeira ();
12 }
13 }
Código 3.3: Construtor
Em geral, o método construtor é usado para inicialização de variáveis, devendo ter o mesmo
nome da classe a que ele se refere. Assim, o construtor deve seguir o formato padrão contido no
Código 3.4.
1 <modificador de acesso > <nome da classe > (<declaracao
parametros >) {
2 }
Código 3.4: Sintaxe do construtor
O Código 3.5 apresenta um exemplo de um programa que cria diversas geladeiras com o uso de
diferentes construtores. Para isso, foram utilizados construtores tanto sem quanto com parâmetros.
1 public class Geladeira {
2 private String cor;
3 private double valor;
4 private String marca;
5
6 // Construtor sem parametros
7 public Geladeira () {
8 ...
16 Capítulo 3. Orientação a Objetos
9 }
10
11 // Construtor com 2 parametros (sendo eles o nome do modelo
e o preco)
12 public Geladeira(String marca , double valor) {
13 //Caso ele nao inicie com uma cor , a cor padrao sera
branca
14 this.cor = "BRANCA";
15 this.marca = marca;
16 this.valor = valor;
17 }
18
19 // Construtor com 3 parametros (sendo eles a cor , o nome do
modelo e o preco)
20 public Carro(String cor , String marca , double valor) {
21 this.cor = cor;
22 this.marca = marca;
23 this.valor = valor;
24 }
25 }
26
27 //Apos definir as varias opcoes de construtores ao se usar ,
aplica -se da seguinte forma
28 public static void main(String [] args) {
29 // Construtor sem parametros
30 Geladeira GeladeiraModelo = new Geladeira ();
31 // Construtor com 2 parametros
32 Geladeira BrastempBranco = new Geladeira("Brastemp", "1115
");
33 // Construtor com 3 parametros
34 Geladeira ConsulPreto = new Geladeira("PRETO", "Consul", "
4799");
35 }
Código 3.5: Diferentes construtores
3.2.3 Métodos
Os métodos são funções que existem dentro de uma classe e que ditam o comportamento dos
objetos da classe. Como um objeto é constituído de atributos e definido por seu comportamento,
métodos possuem um papel essencial na Programação Orientada a Objetos, descrevendo o compor-
tamento do objeto e complementando o entendimento dos conceitos de polimorfismo, herança e
encapsulamento.
Por exemplo, podemos ter uma classe para um aparelho eletrônico que possua métodos para
ações de ligar e desligar (Código 3.6).
1 public class ApEletronico {
2 boolean on = false;
3
4 //Este e um metodo que liga o aparelho eletronico
5 public boolean ligar() {
3.2 Classes 17
6 on = true;
7 System.out.println("O aparelho foi ligado");
8 return on;
9 }
10 //Este e um metodo que desliga o aparelho eletronico
11 public boolean desligar () {
12 on = false;
13 System.out.println("O aparelho foi desligado");
14 return on;
15 }
16 }
Código 3.6: Métodos ligar e desligar
De modo análogo às funções em outras linguagens como C, métodos também podem ter
parâmetros, modificadores de acesso e valores de retorno, que serão explicados em mais detalhes
no decorrer desta seção. No exemplo doaparelho eletrônico (Código 3.6), temos dois métodos
definidos: o método ligar() e o método desligar(). É possível observar que ambos métodos
possuem três características em comum:
• não possuem parâmetros, ou seja, não recebem nada como “entrada”;
• seus modificadores de acesso são do tipo public;
• possuem valor de retorno do tipo boolean;
Os métodos exemplificados modificam o atributo booleano on para assumir valor true (ver-
dadeiro) ou false (falso), de acordo com o método chamado: ligar() faz on ser verdadeiro,
enquanto desligar() faz on ser falso. Em seguida, estes métodos retornam o atributo modifi-
cado.
Como será explicado mais detalhadamente na Seção 3.3, o acesso a atributos e métodos é
controlado pelos modificadores de acesso: public, protected e private. Os métodos
public podem ser acessados pela própria classe, classes derivadas dela e por seus outros métodos.
Os métodos protected só são acessíveis a métodos da própria classe e classes filhas (que
derivam da classe). Os métodos só podem ser acessados pela própria classe, portanto, private é
o modificador mais restritivo.
Qualquer método, assim como uma função, possui explicitado seu valor de retorno: é a palavra-
chave do tipo da variável que a função retorna, colocada após seu modificador de acesso. Um
método pode ter retorno de qualquer tipo (int, double, string, boolean, etc.), e se não
houver retorno, seu tipo deve ser void. A declaração de um método segue a sintaxe do Código 3.7.
1 <modificador de acesso > <tipo retorno > <nome metodo > (<
declaracao parametros >) {
2 <comportamento >
3 }
Código 3.7: Sintaxe do método
Parâmetros são as variáveis de entrada de uma função ou método, devendo ser declarados
dentro do parênteses do método, de forma que podem ser usados na inicialização das variáveis
da função. Na chamada da função, passa-se o parâmetro dela, e então o código contido nela é
executado, com aqueles valores vindos do parâmetro, como mostra o Código 3.8. Um método ou
função não precisa ter parâmetros, neste caso, declara-se o método com os parênteses vazios.
1 public int somaInt(int a, int b) {
2 return a + b;
18 Capítulo 3. Orientação a Objetos
3 }
Código 3.8: Método com parâmetros
A nomenclatura de métodos em Java segue o padrão CamelCase [1], em que a primeira letra é
minúscula e as palavras seguintes são escritas com a primeira letra maiúscula, sem espaços entre
elas.
Em Java, é possível ter dois métodos diferentes com o mesmo nome, dentro de uma mesma
classe, basta que sua assinatura seja diferente. A assinatura de um método é composta pelas
características definidas acima, ou seja: o nome do método, tipo de retorno, modificador de acesso
e sua lista de parâmetros. Porém, em Java, a diferenciação entre métodos é observada de acordo
com os parâmetros de cada um deles. Desta forma, podemos ter métodos diferentes com mesmo
nome na mesma classe contanto que o tipo e/ou quantidade dos parâmetros seja diferente em cada
um deles. Este conceito é chamado de sobrecarga e será explicado em mais detalhes na Seção 3.7.
Métodos estáticos são métodos que podem ser utilizados independente de se ter ou não um
objeto existente de sua classe. Para definir um método como estático, usa-se a palavra-chave
static em sua declaração, que será explicada na Seção 3.2.6. Um exemplo genérico de sua
utilização é o Código 3.9.
1 public static void nomeMetodo (...)
Código 3.9: Exemplo genérico método estático
Dentro de um método static somente outros métodos e variáveis static podem ser acessa-
dos. Um exemplo muito claro de métodos estáticos é a classe Math de Java (java.lang.Math),
pois seus métodos podem ser usados de maneira que não é necessário instanciar um objeto novo,
diferente da classe Scanner (java.util.Scanner), que precisa ter um . Uma utilização da
classe Math é apresentada no Código 3.10.
1 float pot = Math.pow(2,2);
Código 3.10: Método pow da classe Math
Nesse trecho do Código 3.10, é possível observar que usa-se o método pow da classe Math
sem que seja necessário criar nenhum novo método.
1 Scanner sc = new Scanner(System.in);
Código 3.11: Objeto Scanner
Já no trecho do Código 3.11, cria-se um novo objeto para poder usar os métodos de Scanner.
Métodos estáticos também são conhecidos como "métodos de classe", e os demais métodos que
precisam de um objeto declarado, são chamados de "métodos de instância".
Tendo em vista o conhecimento de classes e métodos até o momento, podemos completar
o trecho do Código 3.12 para que tenhamos uma classe chamada Carro com um método sem
retorno chamado ligar.
1 public Selecione (1) {
2 public Selecione (2) {
3 ...
4 }
5 }
Código 3.12: Completar as lacunas
Suponha que para cada uma das lacunas temos as opções a seguir:
1. a. Carro Class | b. method Carro | c. class Carro
2. a. method void | b. method ligar | c. void ligar
3.2 Classes 19
Para preencher a primeira lacuna, a alternativa correta é a "c.", pois para criar a classe Carro
é necessário utilizar a sintaxe de declaração de uma classe, portanto teremos: class Carro.
Para preencher a segunda lacuna, temos que a alternativa correta é a "c.", pois para se declarar um
método, é necessário primeiramente escrever o tipo dele e depois o seu nome. Como explicado
anteriormente, quando um método não possui retorno, o seu tipo de retorno deve ser void. Então,
para se ter um método chamado ligar, a única opção que apresenta um tipo de retorno antes do
nome da função é void ligar.
Tendo em vista as opções objeto, método, sobrescrita, sobrecarga ou atributo, uma pergunta
interessante é: qual o nome correto dado à estrutura que representa os comportamentos de uma
classe?
Como explicado anteriormente, a Orientação a Objetos tenta aproximar a programação do
mundo real, então podemos abstrair do mundo real o que seria o comportamento de um objeto (clas-
ses são moldes para objetos). Usando o exemplo de um carro, sabemos que ele tem comportamentos
que podem ser: ligar, acelerar, abastecer etc., mesmo que essas ações dependam de outro objeto,
que no caso é o motorista. Neste exemplo, percebemos que os comportamentos seguem um padrão,
todos são verbos, ou seja, uma ação. E é exatamente para isso que os comportamentos, ou métodos,
servem em um objeto na Orientação a Objetos. Portanto, o nome correto aos comportamentos de
uma classe é "métodos".
Na prática, normalmente criamos os métodos de um objeto a partir de verbos para melhorar a
semântica de nossos códigos. Assim, quando outro programador ler o código, ele saberá qual é o
comportamento descrito, tornando o entendimento mais fácil e, consequentemente, aumentando
sua produtividade e reduzindo os custos de manutenção.
3.2.4 Métodos Acessores Get e Set
Tendo em vista o que foi estudado até o momento, às vezes é preciso acessar alguns atributos
de forma indireta, pois a forma direta pode trazer várias inconsistências em nossos programas,
por exemplo um valor negativo para o saldo em uma conta bancária, caso o programador seja
descuidado. Os métodos acessores são padrões que nos ajudam a manter a integridade do programa,
fazendo a visualização e a alteração dos dados por meio de métodos especiais (métodos get e
set, respectivamente).
Imagine um atributo que tenha o valor de uma conta de luz, do tipo float. Caso esse atributo
esteja em uma classe pública, com seus atributos visíveis e alteráveis para todos que usarem a
mesma, um usuário terá acesso público a ela, podendo alterá-la conforme quiser.
Para este exemplo, o código da classe é Código 3.13.
1 package despesas;
2
3 public class Conta {
4 public float valorConta;
5 }
Código 3.13: Classe Conta
O método main do projeto é apresentada no Código 3.14 .
1 package exemploacessores;
2 import despesas.Conta;
3
4 public class ExemploAcessores {
5 public static void main(String [] args) {
6 Conta contaLuz = new Conta();
7 contaLuz.valorConta = 0.0f;20 Capítulo 3. Orientação a Objetos
8 }
9 }
Código 3.14: Método main
Não é seguro que o usuário tenha tamanha liberdade. Porém, mudar valor conta para
privado não é uma boa solução, já que dessa forma o usuário não poderá visualizar o atributo, o que
também não queremos. Assim, uma possível solução é deixar a variável como private (privada)
e criar um método acessor do tipo get que retorna o valor de valor conta.
Por exemplo, se criarmos um método de acesso chamado getValor, quando o usuário
chamar o método em seu código pelo objeto, por exemplo, contaLuz.getValor(), o valor
da expressão vai se tornar o valor da conta. Assim, o usuário consegue visualizar o dado quando
quiser, mas não tem o poder de alterá-lo.
O Código 3.15 mostra a classe Conta atualizada com um getter (método acessor get). Já
o Código 3.16 mostra um exemplo de chamada do get no método main.
1 package despesas;
2
3 public class Conta {
4 private float valorConta;
5
6 public float getValor () {
7 return valorConta;
8 }
9 }
Código 3.15: Classe Conta com get
1 package exemploacessores;
2 import despesas.Conta;
3
4 public class ExemploAcessores {
5 public static void main(String [] args) {
6 Conta contaLuz = new Conta();
7 System.out.println("Valor da sua conta: " + contaLuz.
getValor ());
8 }
9 }
Código 3.16: Chamada do getter no main
Outro uso comum para os métodos acessores é a validação de variável. Ao fazer alguma
alteração pelo método set, o valor pode passar por checagens, como tamanho do texto digitado ou
se uma variável é positiva. Isto pode ser útil em ocasiões como variáveis para contagem, valores que
não podem ser negativos, etc. No nosso exemplo de um objeto como conta de luz, uma conta não
deveria ser negativa porque isso não faz sentido neste contexto. Assim, um exemplo de setValor
seria fazer a verificação se o valor da conta é positivo.
O Código 3.17 representa o Código 3.15, mas agora com o acessor setValor adicionado.
1 public class Conta {
2 private float valorConta;
3
4 public float getValor () {
5 return this.valorConta;
3.2 Classes 21
6 }
7
8 public int setValor(float custo) {
9 if(custo > 0) {
10 this.valorConta = custo;
11 return 1;
12 }
13 else
14 return 0;
15 }
16 }
Código 3.17: Classe Conta com setValor
O método acessor foi feito com um tipo de retorno inteiro porque dessa forma, quem usa a
classe sabe se a variável foi alterada e pode usar isso em seu código. Mas também é possível fazer
um setter (método acessor set) com qualquer tipo, inclusive void.
O Código 3.18 é análogo ao Código 3.16, porém agora com uma chamada do novo método
acessor set no método main.
1 package exemploacessores;
2 import despesas.Conta;
3
4 public class ExemploAcessores {
5 public static void main(String [] args) {
6 Conta contaLuz = new Conta();
7
8 if(contaLuz.setValor (100.0) == 1)
9 System.out.println("Valor da sua conta: " +
contaLuz.getValor ());
10 else
11 System.out.println("Erro: o valor atribuido a
conta nao e valido. O valor precisa ser
positivo.");
12 }
13 }
Código 3.18: Chamada do set no main
Vale lembrar que o padrão é chamar os métodos de visualização de getVariavel, onde
“Variavel” é o nome da variável a qual ele se refere. O mesmo ocorre para setVariavel.
Além disso, é necessário ter em mente que nem sempre estes métodos são necessários.
No exemplo acima foram criados ambos os métodos acessores para a variável, mas às vezes
apenas um método get é o suficiente para resolver o problema, como por exemplo para saber um
valor de uma variável que só será alterada internamente na classe. Assim como pode ser possível
que apenas um set seja necessário, por exemplo em casos nos quais é preciso inicializar uma
variável de uma classe, mas este valor não será utilizado posteriormente, apenas no código interno
da classe. Código interno, neste contexto, é o código que apenas a classe consegue visualizar, como
funções auxiliares. No exemplo, poderia ser criado um método privado que verifica se a variável
é positiva ao invés de reescrever o código toda vez que fosse necessário realizar essa verificação.
Assim, esse código seria interno para a classe e o usuário não precisa saber sobre detalhes da
implementação desse método.
22 Capítulo 3. Orientação a Objetos
Um exercício interessante para entender melhor os métodos acessores é criar apenas os
getters e setters necessários à classe Conta como mostra o Código 3.19, pensando sempre
se é realmente preciso criar cada um deles.
1 public class Conta {
2 public String titular;
3 public int numero;
4 public double saldo;
5
6 // ...
7
8 public void saca(double valor) {
9 this.saldo = this.saldo - valor;
10 }
11 }
Código 3.19: Classe Conta com método saca
É importante pensar qual a importância de cada atributo na classe e se faz sentido existir um
get ou um set deles. Neste caso temos que:
• getTitular() faz sentido no código porque uma pessoa pode visualizar o titular da
conta;
• setTitular() faz sentido no código porque uma pessoa pode mudar o titular da conta;
• getNumero() faz sentido porque uma pessoa pode ver o número da sua conta;
• setNumero() não faz sentido, pois mudar o numero de conta não pode ser acessível para
qualquer um, já que número de conta é criado e alterado pelo banco;
• getSaldo() faz sentido, pois uma pessoa pode ter acesso ao valor de saldo da conta;
• setSaldo() não faz sentido, pois só é possível alterar o saldo de uma conta se algum
processo feito pela conta for realizado, como um saque ou uma transferência.
Outro exemplo que envolve métodos acessores é a verificação de um chassi para ver se
ele é válido ou não dentro do método setChassi, sendo que um chassi é válido se possui 5
caracteres e inválido caso contrário.
Para isso, é preciso criar um método que faria essa validação quando existisse uma entrada de
chassi, para avisar o usuário caso a entrada fosse inválida. Pegamos então o parâmetro, no caso
a String, e utilizamos o método length para calcular o tamanho da String. Se o resultado
desse método for igual a 5, então o chassi é válido e é feita a atribuição. Se não, ele é inválido e
é gerado um aviso para o usuário. Este exemplo é apresentado no Código 3.20.
1 public void setChassi(String chassi) throws Exception {
2 if(chassi.length () == 5) {
3 this.chassi = chassi;
4 }
5 else {
6 throw new Exception("O Chassi informado e invalido.");
7 }
8 }
Código 3.20: Validação do chassi
3.2.5 Escopo
As explicações dos conceitos de escopo, visibilidade e atributos static e final serão
baseadas na classe Conta do Código 3.21. É possível notar que a classe Conta contém o nome
3.2 Classes 23
do titular, o nome da agência, a data de abertura da conta, o número da conta e o saldo, além das
ações (métodos) de sacar, depositar, calcular o rendimento, recuperar o saldo e definir o saldo.
1 public class Conta {
2 private String nomeTitular;
3 private String agencia;
4 private String dataAbertura;
5 private int numero;
6 private double saldo;
7
8 public void sacar(double valor) {
9 double novoSaldo;
10 novoSaldo = this.getSaldo () - valor;
11 this.setSaldo(novoSaldo);
12 }
13
14 public void depositar(double valor) {
15 double novoSaldo;
16 novoSaldo = this.getSaldo () + valor;
17 this.setSaldo(novoSaldo);
18 }
19
20 public double calcularRendimento () {
21 double rendimento;
22 rendimento = this.getSaldo () * 0.1;
23 return rendimento;
24 }
25
26 public double getSaldo () {
27 return saldo;
28 }
29
30 public void setSaldo(double saldo) {
31 this.saldo = saldo;
32 }
33 }
Código 3.21: Classe Conta
Escopo, no âmbito de linguagens de programação, é um conceito que diz respeito à região de
um programa em que um identificador foi definido. Um identificador em uma linguagem pode ser
uma variável ou um método. Deste modo, o escopo de uma variável ou método é a região emque
eles foram declarados. Por exemplo, no método sacar() da classe Conta, as variáveis valor
e novoSaldo possuem escopo restrito ao método sacar(), ou seja, só podem ser utilizadas
dentro deste método. Podemos observar também que a classe Conta foi definida como pública e,
deste modo, seu escopo ultrapassa o pacote em que ela foi definida.
Em relação aos métodos e atributos da classe Conta, podemos afirmar que:
• Como todos os atributos da classe Conta foram declarados como privados, o escopo destes
é restrito à classe Conta, ou seja, só podem ser acessados por métodos dentro da classe;
• Como o método setSaldo() foi definido como privado, o escopo deste é restrito aos
métodos da classe Conta, ou seja, o método só pode ser acessado através dos métodos da
24 Capítulo 3. Orientação a Objetos
classe Conta;
• Todos os outros métodos além de setSaldo() foram definidos como públicos e, deste
modo, têm seu escopo livre a todos os pacotes e subclasses da classe Conta, além do escopo
global. Assim, estes podem ser acessados por qualquer objeto da classe Conta.
Como outro exemplo, suponha que queremos percorrer uma lista (classe List do Java) de
números inteiros, imprimindo seus elementos. Poderíamos definir este trecho como apresentado no
Código 3.22
1 List <int > numeros = new ArrayList <int >();
2 for (int i = 0; i < numeros.size(); i++) {
3 int numero = numeros.get(i);
4 System.out.println("O número é: " + numero)
5 }
Código 3.22: Classe List
Deste modo, o escopo da lista numeros é a região em que esta foi declarada, muito provavel-
mente dentro de um método. Já o escopo das variáveis i e numero é a região dentro do for, de
modo que se tentarmos acessar qualquer uma dessas variáveis fora do for o compilador irá acusar
um erro.
3.2.6 Static/Final
As palavras chaves static e final modificam o comportamento dos atributos e métodos
a que são vinculadas. Elas possuem propósitos diferentes, entretanto ambas estão relacionados a
como o usuário poderá usar esses componentes.
Vamos primeiro falar sobre a palavra-chave static. Observe o código Código 3.23: ele é
um programa que cria duas contas e depois exibe a quantidade total de contas.
1 public class Conta {
2 public int qtdContas;
3
4 private String nomeTitular;
5 private String agencia;
6 private int numero;
7 private double saldo;
8
9 public Conta() {
10 qtdContas ++;
11 }
12 }
Código 3.23: Contagem quantidade de Conta
Já o Código 3.24 apresenta a classe Main.java desta mesma aplicação:
1 public class Main {
2 public static void main(String [] args) {
3 Conta contaUm = new Conta ();
4 Conta contaDois = new Conta();
5
6 System.out.println(String.format("Total de contas: %d"
, contaUm.qtdContas));
7 }
3.2 Classes 25
8 }
Código 3.24: Classe Main
No Código 3.24 acima, o programador esperava receber o valor 2, pois ele criou duas novas
contas. Entretanto, o valor retornado foi 1. O motivo do programa ter retornado o resultado errado
se deve ao fato do atributo qtdContas estar vinculado ao objeto e não à classe.
Isso significa que cada vez que um novo objeto (uma nova Conta) é criado, o que será
aumentado é o valor da qtdContas do objeto criado e não da classe. Dessa forma, não importa
a quantidade de contas que sejam criadas, ao consultar o seu valor o resultado sempre será
sempre 1 (lembrando que em Java os valores numéricos são inicializados em 0), pois a instrução
qtdContas++ só foi usada uma vez em cada objeto criado, modificando somente o seu valor.
Uma possível solução para esse problema, seria perguntando diretamente para a classe o valor de
qtdContas, como mostra o Código 3.25.
1 public class Main {
2 public static void main(String [] args) {
3 Conta contaUm = new Conta ();
4 Conta contaDois = new Conta();
5
6 System.out.println(String.format("Total de contas: %d"
, Conta.qtdContas));
7 }
8 }
Código 3.25: Consulta quantidade de Conta
Porém, ao tentar fazer isso, uma mensagem de erro aparecerá. Esse erro se deve ao fato de que
a classe Conta não possui o atributo qtdContas. Para resolver esse erro, é necessário vincular o
atributo à classe e não a um objeto, adicionando a palavra-chave static ao atributo qtdContas
(Código 3.26).
1 public class Conta {
2 public static int qtdContas;
3 ...
4 }
Código 3.26: Modificação atributo estático
Desse modo, o programa entenderá que o atributo qtdContas é da classe e não de um objeto.
Ao criar agora uma nova conta, o atributo qtdContas não é recriado, é utilizado para todos os
objetos. Assim, realizando a operação qtdContas++, o valor da quantidade de contas da classe
toda estará sendo aumentado.
Isso também possibilita que esse valor seja consultado sem ser necessário instanciar nenhum
objeto, pois agora o atributo pertence a classe e não está ligado a nenhum objeto, como mostra o
Código 3.27.
1 public class Main {
2 public static void main(String [] args) {
3 System.out.println(String.format("Total de contas: %d"
, Conta.qtdContas));
4 }
5 }
Código 3.27: Consulta quantidade Conta com atributo estático
26 Capítulo 3. Orientação a Objetos
A saída desse código será: “Total de contas: 0”. Porém, é possível perceber que o
atributo está como public e isso vai contra o conceito de encapsulamento, pois não queremos
que alguém insira valores quaisquer no atributo qtdContas. Para resolver esse problema, basta
deixar o atributo como private e criar um método público para acessá-lo (Código 3.28).
1 public class Conta {
2 public static int qtdContas;
3
4 private String nomeTitular;
5 private String agencia;
6 private int numero;
7 private double saldo;
8
9 public Conta() {
10 qtdContas ++;
11 }
12
13 public int getQtdContas () {
14 return qtdContas;
15 }
16 }
Código 3.28: Encapsulamento corrigido
Com o encapsulamento do atributo corrigido, perdemos a possibilidade de acessar essa infor-
mação sem um objeto. Assim, ao fazer Conta.getQtContas() é apontado um erro. Também
é possível utilizar a palavra static em métodos e com isso conseguimos encapsular o atributo e
deixar métodos que possam ser acessados na classe mesmo sem instanciar um objeto, como mostra
o Código 3.29.
1 public static int getQtdContas () {
2 return qtdContas;
3 }
Código 3.29: Modificação método estático
Agora, o mesmo comando Conta.getQtContas() não aponta mais erro. Resumindo, com
a palavra static conseguimos criar métodos e atributos que não serão vinculados a objetos, e
com isso é possível consultá-los apenas perguntando à classe, sem a necessidade de ter um objeto
em mãos para fazer isso.
Já a palavra-chave final, assim como static é usada na declaração de atributos e métodos
em uma classe, entretanto seu funcionamento é bem diferente e pode ser adicionado a classes
também. Vamos primeiro observar o funcionamento ao adicionar a palavra-chave final em uma
classe (Código 3.30).
1 public final class Conta {...}
Código 3.30: Classe final Conta
Ao fazer isso, estamos dizendo que a classe Conta não pode ser superclasse de nenhuma outra,
ou seja, não pode ser herdada. Observe o Código 3.31:
1 public class ContaPoupanca extends Conta {...}
Código 3.31: Tentativa de criação de classe filha
3.2 Classes 27
Este código gera um erro devido ao fato de que a classe Conta agora é final, então
contaPoupanca não pode ser uma subclasse de Conta. A mensagem de erro recebida é:
"The type contaPoupanca cannot subclass the final class Conta", ou seja, diz que o tipo
contaPoupança não pode ser herdada da classe final Conta. A palavra-chave final
também pode ser adicionada a métodos, e ao fazer isso, estamos dizendo que o método não pode
mais ser sobrescrito (Código 3.32).
1 public class Conta {
2 public final void sacar(double valor) {
3 double novoSaldo;
4 novoSaldo = this.getSaldo () - valor;
5 this.setSaldo(novoSaldo);
6 }
7 }
Código 3.32: Método final
Agora, com a palavra-chave finalnão é mais possível alterar a forma como é feito um saque
nas subclasses de Conta.
1 public class ContaPoupanca extends Conta {
2 @Override public void sacar(double valor) {
3 ...
4 }
5 }
Código 3.33: Tentativa de sobrescrita
Com o Código 3.33, a mensagem de erro recebida é: "Cannot override the final method
from Conta", isto é, não é possível sobrescrever o método final da classe Conta. É possível
também adicionar a palavra-chave final em atributos de uma classe, fazendo com que o atributo
não possa ter seu valor modificado após sua instanciação, como mostra o Código 3.34.
1 public class Conta {
2 private String nomeTitular;
3 private String agencia;
4 private final String dataAbertura;
5 private int numero;
6 private double saldo;
7 }
Código 3.34: Atributos final
Ao adicionar a palavra final em algum atributo, torna-se obrigatória a sua inicialização. Por
isso, após o atributo dataAbertura ser modificado para final, aparece a seguinte mensagem
de erro: “The blank final field dataAbertura may not have been initialized”, ou seja, o
campo em branco final dataAbertura pode não ter sido inicializado. Essa inicialização deve
ser feita no momento em que o objeto é construído ou quando o atributo é declarado, não podendo
ser alterado em nenhum outro ponto do código. No Código 3.35 temos a inicialização quando o
atributo é declarado:
1 public class Conta {
2 private String nomeTitular;
3 private String agencia;
4 private final String dataAbertura = new Date().toString ();
28 Capítulo 3. Orientação a Objetos
5 private int numero;
6 private double saldo;
7 }
Código 3.35: Inicialização na declaração
A outra forma é a inicialização quando o objeto é contruído, apresentada no Código 3.36:
1 public Conta(String nomeTitular , String agencia , String
dataAbertura , int numero) {
2 this.nomeTitular = nomeTitular;
3 this.agencia = agencia + dataAbertura;
4 this.dataAbertura = dataAbertura;
5 this.numero = numero;
6 }
Código 3.36: Inicialização na construção do objeto
É obrigatória a inicialização do atributo em algum desses dois momentos. Se o atributo não foi
inicializado na sua declaração, será preciso fazer isso no método construtor (Código 3.37).
1 public Conta(String nomeTitular , String agencia , int numero) {
2 this.nomeTitular = nomeTitular;
3 this.agencia = agencia;
4 this.numero = numero;
5 }
Código 3.37: Inicialização com construtor
Caso contrário, ou seja, caso o atributo não seja inicializado nem na declaração nem na constru-
ção do objeto, será obtida a mesma mensagem de erro: "The blank final field dataAbertura
may not have been initialized", dizendo que o atributo pode ainda não ter sido inicializado. Caso o
usuário tente alterar o valor de dataAbertura (Código 3.38), um erro surgirá avisando que não
é possível fazer isso com atributos que são final.
1 public class Conta {
2 private String nomeTitular;
3 private String agencia;
4 public final String dataAbertura = new Date().toString ();
5 }
6
7 public static void main(String [] args) {
8 Conta conta = new Conta();
9 conta.dataAbertura = new.Date().toString ();
10 }
Código 3.38: Tentativa de alteração atributo final
Portanto, a mensagem de erro recebida será: "The final field Conta.dataAbertura
cannot be assigned", isto é, o campo final Conta.dataAbertura não pode ser atribuído.
3.3 Atributos de Visibilidade
Existem três tipos de atributos de visibilidade: public, private e protected. Podemos
utilizar esses tipos de atributos para definir a visibilidade de atributos e métodos de uma classe
específica. A visibilidade é definida para atributos e métodos de uma determinada classe em relação
3.3 Atributos de Visibilidade 29
a outras classes e objetos. Atributos e métodos definidos como private (privados), podem ser
visualizados apenas por objetos da própria classe, ou seja, apenas por objetos da própria classe em
que os atributos e os métodos foram declarados. No Código 3.39 é possível visualizar um exemplo
de atributos e métodos especificados na classe Conta como privados:
1 public class Conta {
2 private String nomeTitular;
3 private String dataNascimento;
4
5 private int calcularIdade (String dataNascimento) {
6 ...
7 }
8 }
Código 3.39: Atributos e métodos privados
Em contrapartida, atributos e métodos definidos como public, ou seja, públicos, podem ser
visualizados por objetos da própria classe e objetos de outras classes. Deste modo, eles ficam
visíveis e acessíveis. No Código 3.40 abaixo será possível visualizar um exemplo de métodos
especificados na classe Conta como públicos:
1 public class Conta {
2 public String getNomeTitular () {
3 return this.nomeTitular;
4 }
5 public void setNomeTitular(String novoNome) {
6 this.nomeTitular = novoNome;
7 }
8 }
Código 3.40: Métodos públicos
Apesar de podermos definir atributos como públicos, optamos por exemplificar apenas métodos
definidos como públicos. De modo geral, atributos de uma classe são definidos como private,
ou seja, privados, e métodos são definidos como public, ou seja, públicos. Vale ressaltar que isso
não é uma regra, mas acontece na maioria das vezes em Orientação a Objetos. Ao definirmos um
atributo como privado, este será acessado apenas através de métodos, fato que pode garantir sua
segurança e encapsulamento. Visto que os atributos são, normalmente, definidos como privados,
então é necessário que existam métodos públicos para que seja possível acessá-los e utilizá-los.
Atributos ou métodos definidos como protected, ou seja, protegidos, podem ser visualizados
apenas por objetos da própria classe ou por objetos de classes que herdam desta classe específica.
No Código 3.41 abaixo é possível visualizar um exemplo de atributos e métodos especificados na
classe Conta como protected:
1 public class Conta {
2 protected String cpfTitular;
3
4 protected void consultarRestricoes(String cpfTitular) {
5 ...
6 }
7 }
Código 3.41: Atributo e método protegidos
30 Capítulo 3. Orientação a Objetos
É importante ressaltar que também é possível definir atributos e métodos sem definir o atributo
de visibilidade, isto faz com que esse atributo ou método possa ser acessado apenas por objetos
da própria classe ou classes que estejam nesse pacote. A Tabela 3.1 abaixo apresenta o nível de
visibilidade de um atributo ou método de acordo com o seu atributo de visibilidade, um resumo do
que foi estudado nesta seção:
Atributos de visibilidade Classe Pacote Subclasse Global
Public Sim Sim Sim Sim
Private Sim Não Não Não
Protected Sim Sim Sim Não
Nenhum Sim Sim Não Não
Tabela 3.1: Atributos de Visibilidade
Vamos utilizar a classe Conta apresentada no Código 3.42 para exemplificar alguns casos:
1 public class Conta {
2 private String nomeTitular;
3 private String agencia;
4 private String dataAbertura;
5 private int numero;
6 private double saldo;
7 protected String cpfTitular;
8
9 public void sacar(double valor) {
10 int novoSaldo;
11 novoSaldo = this.getSaldo () - valor;
12 this.setSaldo(novoSaldo);
13 }
14
15 public void depositar(double valor) {
16 int novoSaldo;
17 novoSaldo = this.getSaldo () + valor;
18 this.setSaldo(novoSaldo);
19 }
20
21 public double calcularRendimento () {
22 double rendimento;
23 rendimento = this.getSaldo () * 0.1;
24 return rendimento;
25 }
26
27 public double getSaldo () {
28 return saldo;
29 }
30
31 public void setSaldo(double saldo) {
32 this.saldo = saldo;
33 }
34
35 private int calcularIdade(String dataNascimento) {
3.3 Atributos de Visibilidade 31
36 ...
37 }
38
39 protected void consultarRestricoes(String cpfTitular) {
40 ...
41 }
42 }
Código 3.42: Classe Conta
Ao criar um objeto do tipo Conta, é possível acessar todos os seus métodos definidos como
públicos (public). Entretanto, não é possível acessar seus métodos definidos como privados
(private). Por exemplo, o método calcularIdade()não pode ser acessado, caso isso seja
feito será exibida uma mensagem de erro. O mesmo ocorre com os atributos públicos e privados,
isto é, os atributos públicos podem ser acessados, enquanto os privados não podem. No caso de
métodos e atributos protected, todos podem ser acessados por objetos dessa classe e por objetos
de classes que herdam dessa classe sem que haja nenhum erro.
A visibilidade protected por ser intermediária entre public e private pode ser um
pouco confusa inicialmente. Assim, para entendermos esse atributo de visibilidade podemos fazer o
seguinte exercício: adicione uma classe Moto que herda de Veiculo e defina de forma adequada
sua quantidade de rodas. Durante a criação desse projeto, é necessário colocar em prática o
conceito de herança, que será estudado com maior profundidade na Seção 3.6. Quando uma
classe herda de outra, a classe mãe passa para as classes filhas todas os seus atributos e métodos,
promovendo a reutilização de código já escrito. Temos, no Código 3.43, a classe mãe Veículo e,
no Código 3.44, a classe filha Moto, derivada de Veículo.
1 package classesjdk;
2
3 import javaoo.excecoes.AbastecimentoVeiculoLigadoException;
4
5 public class Veiculo {
6 private String nome;
7 private int combustivel;
8 private String chassi;
9 protected int rodas;
10 private Boolean ligado;
11
12 public Veiculo () {
13 this.nome = "Carro";
14 this.combustivel = 0;
15 this.ligado = false;
16 }
17
18 public Boolean isLigado () {...}
19 public String getChassi () {...}
20 public void setChassi(String chassi) throws Exception
{...}
21 public String getNome () {...}
22 public int getCombustivel () {...}
23 public void setCombustivel(int combustivel) {...}
24 public void ligar() {...}
32 Capítulo 3. Orientação a Objetos
25 public void desligar () {...}
26 public void abastecer(int litros) throws
AbastecimentoVeiculoLigadoException {...}
27 public void status () {...}
28 public void andar(int km) {...}
29 }
Código 3.43: Classe mãe Veículo
1 package classesjdk;
2
3 public class Moto extends Veiculo {
4 public Moto() {
5 this.rodas = 2;
6 }
7
8 public Moto(String nome) {
9 this.rodas = 2;
10 this.setNome(nome);
11 }
12 }
Código 3.44: Classe filha Moto
É observado no Código 3.44 acima que não foi necessário escrever os atributos da classe mãe
Veículo, pois pelo conceito de herança a classe Moto herdou os atributos da classe mãe.
1 package classesjdk;
2
3 import java.util.Scanner;
4
5 public class Main {
6
7 /**
8 * @param args
9 * @throws AbastecimentoVeiculoLigadoException
10 */
11 public static void main(String [] args) throws
AbastecimentoVeiculoLigadoException {
12 Scanner ler = new Scanner(System.in);
13 String x;
14
15 System.out.println("Por favor , informe o nome da moto:
");
16 x = ler.nextLine ();
17 Veiculo motinha = new Moto(x);
18 System.out.println("A quantidade de rodas do veiculo e
" + motinha.getNome ());
19 }
20 }
Código 3.45: Classe Main
3.3 Atributos de Visibilidade 33
Como observado no Código 3.45, não foi necessário utilizar um método acessor (como o
setQtdRodas) para atribuir o valor 2 ao número de rodas. Isso ocorre porque o método construtor
da classe Moto fez isso automaticamente. Também não foi necessário usar um método para utilizar
o valor do número de rodas (get), apenas o referenciamos diretamente, pois o atributo é do tipo
protected. Um método acessor é útil quando queremos atribuir valor a algum atributo ou alterar
o seu valor, como estudado na Seção 3.2.4.
Outro exercício interessante é: considere que precisamos criar um método que deve ser acessado
somente pela própria classe. Qual dos atributos de visibilidade (final, protected, private
ou public) é o mais coerente para essa situação? Como o método deve ser particular da classe, é
importante que as demais classes não consigam acessar esse método livremente. O modificador
private garante exatamente isso, pois o que não faz sentido que outra classe possa alterar ou
visualizar fica privado na classe. Vamos imaginar algumas situações. Com a palavra-chave final
temos o Código 3.46.
1 public class Banco {
2 final double cofre;
3
4 public void deposito(double valorDepositado) {
5 this.cofre += valorDepositado;
6 }
7 }
Código 3.46: Palavra-chave final
Isso está errado, pois o fato de uma variável ser final significa que seu valor apenas pode
ser atribuído uma única vez e isso não protege a variável de ser acessada em outras classes. A
palavra-chave final não é um atributo de visibilidade.
1 public static void main(String [] args) {
2 Banco bankMoney = new Banco();
3
4 bankMoney.deposito (20);
5 }
Código 3.47: Classe Main
O Código 3.47 representa a classe Main referente ao Código 3.46. Aqui também temos um
erro, pois o valor de cofre não será alterado, já que ele é uma variável final. Agora, vamos
utilizar o atributo de visibilidade protected como apresenta o Código 3.48.
1 public class Banco {
2 protected double cofre;
3
4 void deposito(double valorDepositado) {
5 cofre += valorDepositado;
6 }
7 }
Código 3.48: Atributo de visibilidade protected
Isso está errado, pois, como vimos, o atributo protected permite que todas as classes que
herdarem de Banco possam acessar esse campo como se fosse público. Já o Código 3.49 utiliza
uma classe filha, que herda de Banco para modificar o valor de cofre.
1 public class BancoDigital extends Banco {
34 Capítulo 3. Orientação a Objetos
2 void esvaziaBanco () {
3 cofre = 0.0;
4 }
5 }
Código 3.49: Classe filha BancoDigital
Neste trecho de código seria possível zerar o cofre, porém devemos permitir que apenas a classe
possa acessar o banco e modificar este valor. Com a palavra-chave private temos o Código 3.50:
1 public class Banco {
2 private double cofre;
3
4 void deposito(double valorDepositado) {
5 cofre += valorDepositado;
6 }
7 public double getPagamento(Client c, Data d) {...}
8 }
Código 3.50: Atributo de visibilidade private
Este código está correto, pois, dessa forma, apenas a classe Banco possui acesso ao cofre, que
é o que desejamos fazer. O Código 3.51 apresenta a classe Main referente ao Código 3.50.
1 public static void main(String [] args) {
2 Banco bankMoney = new Banco();
3
4 bankMoney.cofre = 0.0;
5 }
Código 3.51: Classe Main
Entretanto, aqui teríamos um erro, pois zerar o cofre não será permitido, já que essa variável é
privada. Por fim, para o atributo de visibilidade public temos o Código 3.52. Por isso, o atributo
de visibilidade correto para este exercício é private.
1 public class Banco {
2 public double cofre;
3
4 void deposito(double valorDepositado) {
5 cofre += valorDepositado;
6 }
7 }
Código 3.52: Atributo de visibilidade public
Isso também está errado, pois com public qualquer um pode acessar esse campo diretamente,
que é o oposto do que desejamos fazer. A classe Main referente ao código Código 3.52 é
apresentada no Código 3.53. É possível observar que essa parte de zerar o cofre seria permitida,
porém não deveria. Por isso, a resposta correta para este exercício é o modificador private.
1 public class Main {
2 public static void main(String [] args) {
3 Banco bankMoney = new Banco();
4
5 bankMoney.cofre = 0.0;
3.3 Atributos de Visibilidade 35
6 }
7 }
Código 3.53: Classe Main
Outro exercício interessante seria adicionarmos o atributo de visibilidade (private, se ne-
cessário) para cada atributo e método da classe Conta. Podemos fazer um teste criando uma
Conta no método main para modificar ou ler um de seus atributos privados. Como trata-se da
modelagem de uma conta bancária, o saldo bancário deve ser um atributo privado. A Conta
deve ter métodos diferentes: saque, depósito e rendimento, todos com relação ao atributo
saldo.
Primeiros escolhemos os tipos dos métodos. Como podemos ter tanto números negativos quanto
positivose com diversas casas decimais foi escolhido o tipo float. Tanto depósito quanto
saque possuem interações diretas com o saldo, ou seja, diminuem e aumentam o valor dele,
respectivamente, lembrando sempre que a Conta não pode ficar negativada. Já o rendimento é
relacionado ao valor final da Conta, ou seja, aparece só no final de todas as transações ou quando é
requisitado. O rendimento irá possuir um valor fixo, decidido inicialmente (por exemplo, 20%).
Para uma implementação mais fácil, os modificadores de acesso foram mantidos como públicos, a
fim de primeiramente alterar a Conta bancária livremente, como apresenta o Código 3.54.
1 package bancoConta;
2
3 import java.util.Scanner;
4
5 public class Conta {
6 public float saldo;
7
8 public void saque(float dinheiroSaque) {
9 System.out.println("Deseja sacar quanto?");
10 Scanner saque = new Scanner(saque);
11 float dinheiroSaque = saque.nextFloat ();
12 saldo -= dinheiroSaque;
13 }
14
15 public void deposito () {
16 System.out.println("Deseja depositar quanto?");
17 Scanner dep = new Scanner(dep);
18 float dinheiroDep = dep.nextFloat ();
19 saldo += dinheiroDep;
20 }
21 }
Código 3.54: Classe Banco
Porém, tendo todos os modificadores de acesso públicos, qualquer um poderia mudar o valor
da Conta bancária e qualquer atribuição em código também poderia mudá-la. Assim, a Conta se
tornaria menos segura, já que todos podem alterar o valor do saldo, como já foi explicado em
exemplos anteriores.
Então, mudou-se o atributo para private e, com esta mudança de visibilidade, surgiu a
necessidade de uso de getters e setters (métodos acessores) para acessar o atributo saldo
dentro das funções da Conta, já que apenas eles podem mudar seu valor. Os getters e
setters são implementados pela IDE. A Figura 3.1 apresenta o primeiro passo para a criação
36 Capítulo 3. Orientação a Objetos
dos getters e setters. Primeiro, devemos clicar na tela com o botão direito, clicar na opção
Source (Origem), depois em Generate Getters and Setters..., ou seja, gerar getters e
setters. Já a Figura 3.2 apresenta o segundo passo para a criação dos getters e setters.
Neste segundo passo devem ser escolhidos os atributos para os quais serão criados o get e o set.
Quando queremos selecionar todos os atributos, podemos clicar em Select All. Depois, basta clicar
em OK.
Figura 3.1: Criar Getters e Setters (Passo 1)
Figura 3.2: Criar Getters e Setters (Passo 2)
Com os getters e setters configurados, não há mais problemas de segurança com o
atributo saldo, ou seja, o atributo mencionado anteriormente não poderá ser alterado e nem lido
por outro objeto de nossa implementação. Então o código pode ser finalizado sem mais nenhum
problema. Após a implementação de todos os métodos, temos a classe Conta no Código 3.55:
3.3 Atributos de Visibilidade 37
1 package bancoConta;
2
3 import java.util.Scanner;
4
5 public class Conta {
6 private float saldo = 0;
7
8 private void setSaldo(float saldo) throws Exception {
9 if(this.saldo + saldo < 0) {
10 throws new Exception("Saldo insuficiente.");
11 }
12 else {
13 this.saldo = saldo;
14 }
15 }
16
17 public void saque(float a) throws Exception {
18 if (a < 0) {
19 throws new Exception("Nao e possivel sacar valor
negativo.");
20 }
21 else {
22 try {
23 setSaldo(saldo -= a);
24 System.out.println(String.format("Voce sacou
R$%.2f.", a));
25 }
26 catch(Exception e) {
27 System.out.println("Erro: " + e.getMessage ());
28 }
29 }
30 }
31
32 public void deposito(float a) {
33 saldo += a;
34 System.out.println(String.format("Voce depositou R$%.2
f.", a));
35 }
36
37 public void rendimento () {
38 System.out.println(String.format("Rendimento = R$%.2f.
", saldo *0.1f));
39 }
40
41 public void verSaldo () {
42 System.out.println(String.format("Saldo: R$%.2f.",
saldo));
43 }
38 Capítulo 3. Orientação a Objetos
44 }
Código 3.55: Classe Conta completa
Os conceitos de métodos e atributos de uma classe podem ser confusos no começo: ao declarar
os métodos e atributos, podem surgir dúvidas acerca do tipo adequado a ser utilizado (float,
void, int, etc), por exemplo. Outra dificuldade pode ser referente aos getters, setters
e os modificadores de acesso, pois, inicialmente, é complicado de configurá-los na IDE, quando
ainda não se está familiarizado com o ambiente. Porém, à medida em que continuamos treinando e
estudando Orientação a Objetos, estas dúvidas são resolvidas.
3.4 Encapsulamento
É um mecanismo utilizado para restringir o acesso a algumas partes do objeto. Funciona por
meio dos 4 tipos de visibilidade (public, private, protected ou nenhum) para os atributos
e métodos, assim como explicado na seção anterior. Em outras palavras, o encapsulamento é o
agrupamento de dados em uma única unidade, de forma que os códigos são ligados, assim como os
dados que eles manipulam.
Quando separamos os atributos e métodos, estamos os agrupando em conjuntos, de acordo
com seu grau de relação. Assim, podemos esconder do usuário alguns dados de uma classe, os
tornando disponíveis somente através de métodos da própria classe, já que o usuário não deve se
preocupar com a construção e lógica de alguns métodos, mas sim chamar apenas os que forem de
seu interesse. Dessa forma, o encapsulamento pode ser pensado como um escudo impedindo que
os dados sejam acessados pelo código fora desse escudo.
De maneira geral, é considerada uma boa prática não expor os atributos de uma classe de
maneira direta, deixando-os como private (privados). E para se ter acesso aos atributos de uma
classe, são utilizados métodos acessores get e set, explicados na Seção 3.2.4. O método get é
responsável por retornar o valor de determinado atributo, enquanto o método set é responsável por
alterar o valor de determinado atributo. Não são todos os atributos da classe que contém os métodos
get e set, apenas quando for necessário alterar ou acessar o atributo, dessa forma garantimos o
encapsulamento.
As vantagens do encapsulamento são: ocultar dados, flexibilidade, reusabilidade e facilidade de
teste. Ao ocultar os dados, o encapsulamento permite que o usuário não saiba sobre a implementação
interna da classe. Não ficará visível para o usuário como a classe está armazenando valores nas
variáveis. Ele apenas saberá que os valores estão sendo passados para um método set e que as
variáveis estão sendo inicializadas com esses valores. A flexibilidade ocorre devido ao fato de que
é possível fazer com que as variáveis sejam read-only, ou seja, somente para leitura, ou write-only,
ou seja, somente para escrita, de acordo com a nossa necessidade. O encapsulamento também
melhora a reutilização de código, além de ser fácil alterá-lo caso ocorram mudanças nos requisitos.
Por fim, permite a facilidade de teste, pois torna mais fácil o teste de unidade [7]. Um teste de
unidade é um teste de sistema que busca validar dados válidos e inválidos de entrada e saída [12] de
uma única unidade do sistema, de forma isolada. Uma unidade do sistema, no caso de Orientação a
Objetos, geralmente é uma classe da aplicação [13].
3.5 Objetos
Como mencionado anteriormente, uma das vantagens de se utilizar a Orientação a Objetos é que
esse modelo é muito natural para o modo de pensar do ser humano. Por exemplo, ao imaginarmos
uma pessoa, temos uma ideia geral do que é uma pessoa e do que ela faz, mas sabemos que todas
as pessoas são diferentes entre si. Fazendo um paralelo à Programação Orientada a Objetos, a ideia
3.5 Objetos 39
que você tem de uma pessoa é a classe Pessoa e as diferentes pessoas que existem são os objetos
dessa classe. Por exemplo, observe o Código 3.56.
1 public class Pessoa {
2 public int idade;
3 public float altura;
4 public float peso;
5
6 public void fazerAniversario () {
7 this.idade ++;
8 }
9 }
10
11 public class Main {
12 public static void main(String [] args) {
13Pessoa giovanni = new Pessoa ();
14 Pessoa thiago = new Pessoa ();
15
16 giovanni.idade = 16;
17 thiago.idade = 13;
18
19 thiago.fazerAniversario ();
20 }
21 }
Código 3.56: Classes Pessoa e Main
Nesse exemplo, ao fim da execução da classe Main, a informação idade do objeto giovanni
será igual a 16, enquanto a idade do objeto thiago será igual a 14, já que apenas o objeto
thiago “fez aniversário”, ou seja, foi executado o método fazerAniversário() apenas
com o objeto thiago. Utilizando esse raciocínio, a lógica do código se torna mais natural e fácil
de se entender, já que tudo à nossa volta pode ser descrito como uma relação classe e objeto. Como
podemos ver na primeira linha dentro do método main, temos Pessoa giovanni = new
Pessoa(). Isso é necessário porque estamos falando que temos um novo objeto giovanni que
é da classe Pessoa, portanto giovanni deve ser alocado na memória.
É necessário tomar cuidado para não confundir classe com objeto. A classe é o molde, o objeto
é o que sai do molde. Para expor a diferença entre classe e objeto, observe o Código 3.57.
1 Bola bolaFutebol = new Bola();
2
3 Bola.encher ();
4 bolaFutebol.encher ();
Código 3.57: Diferença classe e objeto
Neste código, Bola.encher() está incorreto, pois não enchemos a ideia de bola, isto é, o
molde (classe). O correto é bolaFutebol.encher(), pois devemos encher a bola de futebol,
isto é, o objeto que sai a partir do molde. Lembre-se sempre que uma classe pode ter vários objetos,
mas um objeto não pode ser de várias classes. Neste caso, uma bola pode ser de vários tipos: bola
de basquete, bola de futebol, etc. Já a bola de futebol é um tipo de bola, mas não pode ser um
tipo de outra coisa. Assim, fica mais claro que Bola é a classe, já que pode ter vários objetos e
bolaFutebol é o objeto, pois está relacionado apenas à classe Bola.
40 Capítulo 3. Orientação a Objetos
Com o que aprendemos até agora podemos modelar algum elemento da vida real. Por exemplo,
é possível modelar uma conta bancária mais avançada das que modelamos nas seções anteriores.
Para isso, o nosso modelo deve ter:
• nome do titular da conta, que será uma String;
• número da conta que será um inteiro (int);
• a agência, que será uma String;
• o saldo bancário da conta, que será um double;
• a data de abertura da conta, que será uma String;
Além disso, o modelo deve permitir as seguintes ações: sacar, para retirar um determinado
valor do saldo da conta; depositar, para adicionar um determinado valor ao saldo da conta; calcular
o rendimento, para apresentar o rendimento mensal dessa conta.
Para resolver esse exercício, vamos começar criando um modelo bastante básico, utilizando,
para isso, as informações fornecidas, para depois irmos incrementando o modelo aos poucos. Temos
o nome da classe: Conta e seus atributos: nomeTitular, numeroConta, agencia, saldo
e dataAbertura e seus métodos: saca(), deposita() e calculaRendimento(). As-
sim, temos a versão inicial desse modelo no Código 3.58.
1 public class Conta {
2 String nomeTitular;
3 int numeroConta;
4 String agencia;
5 double saldo;
6 String dataAbertura;
7
8 public void saca() {...}
9 public void deposita () {...}
10 public void calculaRendimento () {...}
11 }
Código 3.58: Modelagem inicial
Agora vamos incrementando o modelo. O primeiro passo é encapsular os campos (Código
3.59), pois é perigoso deixar todos os métodos acessíveis para qualquer um, principalmente quando
estamos falando de contas bancárias.
1 public class Conta {
2 private String nome;
3 private String agencia;
4 private String dataAbertura;
5 private int numero;
6 private double saldo;
7 }
Código 3.59: Encapsulamento dos campos
Ainda não terminamos a modelagem, pois encapsular não é deixar todos os atributos como
private (privados), senão não conseguiremos acessá-los nem alterá-los. Portanto, é necessário
criar métodos acessores para alguns atributos (Código 3.60). Nesse modelo você pode consultar
e alterar o nome do titular e a agência da Conta. Mas não queremos que a data de abertura da
Conta nem o número da Conta sejam alterados. O saldo, por sua vez, só deverá ser modificado
pelos métodos deposita() e saca().
1 public class Conta {
2 public String getNome () {...}
3.5 Objetos 41
3 public void setNome(String nome) {...}
4 public String getAgencia () {...}
5 public void setAgencia(String agencia) {...}
6 public String getDataAbertura () {...}
7 public int getNumero () {...}
8 }
Código 3.60: Criação métodos acessores
Agora é necessário atualizarmos os outros métodos desta classe: (deposita(), saca() e
calculaRendimento()). Precisamos descobrir qual o tipo de retorno desses métodos e quais
são seus parâmetros, como mostra o Código 3.61. Os métodos deposita() e saca() devem
receber um valor, esse valor deverá ser processado junto com o atributo saldo, com isso
podemos inserir neles o parâmetro valor, que deve ser do tipo double, assim como é o atributo
saldo. Já no método calculaRendimento(), não queremos receber nenhum valor, apenas
retornar qual o rendimento da Conta, então como vamos mexer com o atributo saldo mais uma
vez, devemos retornar um valor do mesmo tipo.
1 public class Conta {
2 public void saca(double valor) {...}
3 public void deposita(double valor) {...}
4 public float calculaRendimento () {...}
5 }
Código 3.61: Atualização métodos
O próximo passo (Código 3.62) é criar um construtor para inicializar os atributos do novo
objeto. Como definimos que a data de abertura da Conta e o número não poderão ser alterados
depois, iremos atualizar esses valores no momento em que criamos o objeto.
1 public class Conta {
2 public Conta(String nome , String agencia , String
dataAbertura , int numero) {...}
3 }
Código 3.62: Criação construtor
Por fim, como definimos anteriormente que os atributos dataAbertura e numero não serão
alterados em nenhum momento, adicionaremos a palavra-chave final neles. Obtemos, então, o
Código 3.63.
1 public class Conta {
2 private final String dataAbertura;
3 private final int numero;
4 }
Código 3.63: Adição palavra-chave final
Assim, depois de todas as alterações realizadas, agora temos o modelo completo da classe no
Código 3.64.
1 public class Conta {
2 private String nome;
3 private String agencia;
4 private final String dataAbertura;
5 private final int numero;
42 Capítulo 3. Orientação a Objetos
6 private double saldo;
7
8 public Conta(String nome , String agencia , String
dataAbertura , int numero) {...}
9
10 public void saca(double valor) {...}
11 public void deposita(double valor) {...}
12 public double calculaRendimento () {...}
13 public String getNome () {...}
14 public void setNome(String nome) {...}
15 public String getAgencia () {...}
16 public void setAgencia(String agencia) {...}
17 public String getDataAbertura () {...}
18 public getNumero () {...}
19 }
Código 3.64: Modelo completo Conta
Em seguida, podemos transformar o modelo acima em uma classe Java. Iremos testá-la usando
uma outra classe que tenha o método main. A classe referente à conta bancária deve ser criada
com o nome Conta, enquanto que a classe de testes não tem um padrão para nomes, porém ela
deve possuir o método main. A classe Conta deve conter pelo menos os seguintes métodos:
• saca, que recebe um valor como parâmetro e retira esse valor do saldo da Conta;
• deposita, que recebe um valor como parâmetro e adiciona esse valor ao saldo da
Conta;
• calculaRendimento, que não recebe parâmetro algum e devolve o valor do saldo
multiplicado por 0.1.
Modelamos como seria uma conta de um banco, ou seja, já identificamos as informações e
ações mais importantes presentes em uma conta. Agora, devemos transformar estas ações em
código, ou seja, em uma classe Java chamada Conta, como mostra o Código 3.65.
1 public class Conta {
2 private String nomeTitular , agencia , dataAbertura;
3 privateint numero;
4 private double saldo;
5
6 public String getnomeTitular () {
7 return nomeTitular;
8 }
9
10 public void setNomeTitular(String nomeTitular) {
11 this.nomeTitular = nomeTitular;
12 }
13
14 public String getAgencia () {
15 return agencia;
16 }
17
18 public void setAgencia(String agencia) {
19 this.agencia = agencia;
20 }
3.5 Objetos 43
21
22 public String getDataAbertura () {
23 return dataAbertura;
24 }
25
26 public void setDataAbertura(String dataAbertura) {
27 this.dataAbertura = dataAbertura;
28 }
29
30 public int getNumero () {
31 return numero;
32 }
33
34 public void setNumero(int numero) {
35 this.numero = numero;
36 }
37
38 public double getSaldo () {
39 return saldo;
40 }
41
42 public void setSaldo(double saldo) {
43 this.saldo = saldo;
44 }
45
46 public void saca(double valor) {
47 this.saldo = this.saldo - valor;
48 }
49
50 public void deposita(double valor) {
51 this.saldo = this.saldo + valor;
52 }
53
54 public double calculaRendimento () {
55 return this.saldo * 0.1;
56 }
57 }
Código 3.65: Classe Conta
Nesta classe, temos as principais informações como o nome do titular da Conta, o saldo
da Conta, a agência da Conta, etc., além dos métodos que são as ações realizadas na Conta,
como o getSaldo() que retorna o saldo atual da Conta ou o setNomeTitular() que
registra o nome do dono da Conta. Porém, existem 3 métodos aos quais devemos prestar
atenção: o método saca(), que recebe um valor e retira esse valor do saldo da Conta, o
deposita(), que também recebe um valor porém o adiciona ao saldo e, por fim, o método
calculaRendimento(), que não recebe nenhum parâmetro mas devolve o valor do saldo
atual multiplicado por 0.1. Para testar a classe Conta devemos criar outra classe (Código 3.66),
uma principal que contenha o método main:
1 public class Teste {
44 Capítulo 3. Orientação a Objetos
2 public static void main(String [] args) {
3 Conta c = new Conta();
4 c.setNomeTitular("Robson de Salva Junior");
5 c.setSaldo (500);
6 c.saca (50);
7 c.deposita (10);
8 System.out.println("Titular da conta: " + c.
getNomeTitular () + "\nSaldo atual: " + c.getSaldo ()
+ "\nRendimento: " + c.calculaRendimento ());
9 }
10 }
Código 3.66: Classe para teste
Nela foi criado um objeto da classe Conta e, a partir dele, as ações começaram a ser efetuadas.
Primeiramente salvamos o nome do titular e o saldo da Conta e depois sacamos o valor 50 deste
saldo e depositamos o valor 10. Em seguida, escrevemos na tela o nome do titular, saldo ao final
das transações e rendimento daquela Conta, obtendo a saída apresentada no Código 3.67. Vale
ressaltar que tudo isso é feito sem de fato utilizar diretamente os atributos da classe Conta, sempre
utilizando os métodos para nos auxiliar durante todo o processo.
1 run:
2 Titular da conta: Robson da Silva Junior
3 Saldo atual: 460.0
4 Rendimento: 46.0
5 CONSTRUIDO COM SUCESSO (tempo total: 0 segundos)
Código 3.67: Saída no terminal
Um outro exercício interessante é construirmos duas contas com new e compará-las com “==”.
Se ambas tiverem os mesmos atributos será necessário criar outra referência, por exemplo, como
mostra o Código 3.68.
1 public class Teste {
2 public static void main(String [] args) {
3 Conta c1 = new Conta ();
4 c1.titular = "Danilo";
5 c1.saldo = 100;
6
7 Conta c2 = new Conta ();
8 c2.titular = "Danilo";
9 c2.saldo = 100;
10
11 if (c1 == c2) {
12 System.out.println("iguais");
13 }
14 else {
15 System.out.println("diferentes");
16 }
17 }
18 }
Código 3.68: Comparação entre objetos Conta
3.6 Herança 45
Neste exercício, utilizamos os conceitos de classes e instanciação. Para isso, foi criada uma
classe Conta, responsável por possuir os atributos: titular e saldo. O atributo titular é
do tipo String e o atributo saldo é do tipo float. O objetivo principal do exercício é declarar
dois objetos diferentes com atributos iguais, e comparar os objetos para verificar se eles são iguais.
Então, utilizamos uma instância de Conta para cada um dos dois objetos diferentes, através do
new, responsável por alocar um espaço de memória e retornar o endereço de memória que foi
alocado a cada um dos objetos.
Basicamente, foram construídos dois objetos do tipo Conta: c1 e c2. Foram declarados um
titular e um saldo para essas contas, que correspondem aos seus atributos. Depois de serem
feitas a criação dos objetos e a inserção de valores nos seus respectivos atributos, foi verificado
se a Conta c1 é igual à Conta c2 com o operador de igualdade “==”. Nessa verificação, o
programa verifica se os atributos de c1 possuem os mesmos valores dos atributos de c2 e, se forem
iguais, é exibida a mensagem “iguais” na tela, porém, se pelo menos algum deles não for igual
em ambas as contas, será exibida a mensagem “diferentes” na tela.
Com este exercício é possível perceber que em C++ também é possível construir exemplos
similares. Além disso, a linguagem Java assemelha-se muito com C++, no contexto de Orientação
a Objetos.
3.6 Herança
Herança é um dos conceitos mais importantes da Orientação a Objetos. Com a herança podemos
reaproveitar código de uma classe e implementações de demais classes. Fica fácil de pensar que se
uma classe X herda de Y , significa que X “é um” Y. Funciona mais ou menos como uma herança da
vida real, se Paulo é filho de alguém da família Silva, Paulo é um Silva [6]. Observe o Código 3.69.
1 public class Silva {
2 ...
3 }
4
5 public class Paulo extends Silva {
6 ...
7 }
Código 3.69: Classes Silva e Paulo
Observe que, quando queremos representar uma relação de herança, a palavra-chave que deve
ser utilizada é extends. Se X herda de Y, então devemos utilizar X extends Y. No nosso
exemplo, temos, na linha 5, Paulo extends Silva. Isso significa que a classe Paulo herda
da classe Silva. Chamamos de subclasses (ou classes filhas) as classes inferiores na hierarquia,
isto é, aquelas que foram herdadas de outra classe e de superclasses (ou classes mães) as classes
superiores na hierarquia, ou seja, aquelas de quem outras classes herdaram. Vamos agora observar
um outro exemplo, no Código 3.70.
1 public class Alienigena {
2 ...
3 }
4
5 public class Kriptoniano extends Alienigena {
6 ...
7 }
8
9 public class Main {
46 Capítulo 3. Orientação a Objetos
10 public static void main(String [] args) {
11 Kriptoniano clarkKent = new Kriptoniano ();
12
13 Alienigena alien , etevaldo;
14
15 alien = clarkKent;
16
17 clarkKent = etevaldo;
18 }
19 }
Código 3.70: Herança da classe Alienigena
Neste exemplo, temos que alien = clarkKent, como mostra a linha 14. Esta é uma
atribuição correta, pois todo Kriptoniano é também um Alienigena. Porém, a atrbiuição da
linha 16, clarkKent = etevaldo não é uma atribuição correta, pois nem todo Alienigena
é Kriptoniano.
Usar herança nos fornece grandes poderes, como estender funcionalidades e especializar classes.
Porém, também é necessário nos atentarmos a alguns detalhes. Por exemplo, se criarmos uma classe
X que herda de Y, é necessário ter em mente que, com o passar do tempo, se ocorrerem alterações
na implementação da classe Y, X herdará as mudanças, o que pode gerar erros, problemas de
funcionamento e incompatibilidade em X. Então, sempre atente-se à forma como está implementada
a classe mãe, para não surgirem imprevistos indesejáveis nas classes herdadas dela.
3.7 Sobrecarga/Sobrescrita
A sobrecarga é utilizada criando métodos de mesmo nome e assinaturas diferentes dentro
de uma mesma classe, ou seja, é feita criando métodos que tenham o mesmo nome, porém com
parâmetros diferentes, seja a diferença a quantidade total de parâmetros ou o tipo dos parâmetros.
A sobrecarga serve para evitar a utilização de novos nomes para métodos que deveriam ter o mesmo
nome, ou para evitar conflitos entre métodos com nomes já existentes em um outro escopoevitando
a "poluição" do espaço de nomes usados em programas. Desse modo, a principal função é de
permitir que expressões e nomes já definidos possam ser utilizados em diferentes contextos. Vamos
tomar como exemplo o Código 3.71.
1 public class Aluno {
2 public Aluno(String nome , int RA) {
3 this.nome = nome;
4 this.RA = RA;
5 media = 0;
6 p1 = 0;
7 p2 = 0;
8 }
9
10 public Aluno(int RA, int media) {
11 nome = "Isaias";
12 this.RA = RA;
13 this.media = media;
14 p1 = 0;
15 p2 = 0;
16 }
3.7 Sobrecarga/Sobrescrita 47
17 }
Código 3.71: Sobrecarga com tipos diferentes
Nesse exemplo, podemos ver dois construtores com a mesma quantidade de parâmetros, porém
com tipos de parâmetros diferentes. No primeiro método é utilizado um argumento do tipo
String e um do tipo int, já no segundo método ambos os argumentos são int. Os dois métodos
funcionam corretamente caso sejam declarados com os tipos e quantidade de parâmetros na ordem
correta.
1 public class Aluno {
2 public Aluno(String nome , int RA, int media) {
3 this.nome = nome;
4 this.RA = RA;
5 this.media = media;
6 p1 = 0;
7 p2 = 0;
8 }
9
10 public Aluno(int RA, int media , String nome) {
11 this.nome = nome;
12 this.RA = RA;
13 this.media = media;
14 p1 = 0;
15 p2 = 0;
16 }
17 }
Código 3.72: Sobrecarga com ordem diferente
Já no exemplo do Código 3.72, os parâmetros possuem os mesmos tipos e apenas a ordem de
inserção dos argumentos é diferente. Caso a declaração dos métodos mantenha a ordem correta
definida dos parâmetros, nenhum dos dois métodos apresentará erros.
1 public class Aluno {
2 private float media;
3
4 public float VerificaAprovacao () {
5 return media;
6 }
7
8 public void VerificaAprovacao () {
9 if(media > 6) {
10 System.out.println("Reprovado!");
11 }
12 else {
13 System.out.println("Aprovado!");
14 }
15 }
16 }
Código 3.73: Tipo de retorno diferente
Entretanto, no caso do Código 3.73, a sobrecarga não funcionaria pois o tipo de retorno não pode
ser utilizado na diferenciação entre os métodos que possuem mesmo nome e mesmos parâmetros.
48 Capítulo 3. Orientação a Objetos
Note que o primeiro método retorna um float, já o segundo método não possui retorno (tipo
void).
1 public class Aluno {
2 public Aluno(String nome , String curso) {
3 this.nome = nome;
4 this.curso = curso;
5 }
6
7 public Aluno(String situacao , String nome) {
8 this.nome = nome;
9 this.situacao = situacao;
10 }
11 }
Código 3.74: Parâmetros em mesmo tipo e quantidade
Também ocorrerá erro quando os parâmetros forem do mesmo tipo e houver a mesma quantidade
de parâmetros (Código 3.74), pois para o computador são exatamente a mesma coisa. Isso
ocorre porque o computador receberia, nos dois métodos, a instrução de que serão fornecidos
dois argumentos do tipo String, portanto ele não conseguiria identificar diferença entre eles,
resultando em erro. Note que, apesar de serem atributos diferentes, ambos são do tipo String
e ambos os métodos possuem exatamente dois parâmetros. Apenas o tipo de acesso (public,
protected ou private) também não pode ser utilizado na diferenciação (Código 3.75). Dessa
forma, ocorrerá erro.
1 public class Aluno {
2 public void VerificaAprovacao () {
3 if(media < 6) {
4 System.out.println("Reprovado");
5 }
6 else {
7 System.out.println("Aprovado");
8 }
9 }
10
11 protected void VerificaAprovacao () {
12 if(media < 7) {
13 System.out.println("Reprovado");
14 }
15 else {
16 System.out.println("Aprovado");
17 }
18 }
19 }
Código 3.75: Modificador de acesso diferente
Já a sobrescrita, também chamada de sobreposição, é utilizada para alterar métodos específicos
herdados da classe mãe. Para isso, deve-se implementar um método com o mesmo nome, mesmo
valor de retorno e mesmos parâmetros da classe mãe, mas com implementação diferente. Para
realizar a sobrescrita é necessário usar a annotation chamada @override. Caso a classe mãe
tenha construtor, devemos declarar a função super com os parâmetros da classe mãe no construtor
da classe filha. Por exemplo, o Código 3.76:
3.7 Sobrecarga/Sobrescrita 49
1 public class Main {
2 public static void main(String [] args) {
3 Professor p = new Professor("Fernando", "Matematica",
"Professor");
4
5 p.trabalhar (6);
6 }
7 }
8
9 public abstract class Trabalhador {
10 protected String nome;
11 protected String profissao;
12 protected int horasTrabalhadas;
13
14 public Trabalhador(String nome , String profissao) {
15 this.nome = nome;
16 this.profissao = profissao;
17 }
18
19 public void trabalhar(int horas) {
20 System.out.println("Trabalhou " + horas + "horas hoje!
");
21 horasTrabalhadas = horasTrabalhadas + horas;
22 }
23 }
24
25 public class Professor extends Trabalhador {
26 private String materia;
27
28 public Professor(String nome , String materia , String
profissao) {
29 super(nome , profissao);
30 this.materia = materia;
31 }
32 }
Código 3.76: Sobrescrita Trabalhador
No exemplo do Código 3.76, chamamos o método trabalhar de um objeto p do tipo
Professor. A classe Professor não possui um método trabalhar, porém Professor é
uma subclasse de Trabalhador e herda todos os seu métodos, sendo que um deles é o método
trabalhar. Então quando chamamos o método trabalhar, estaremos acessando o método
herdado e a saída sem a sobrescrita será o Código 3.77 referente ao Código 3.76.
1 Trabalhou 6 horas hoje!
Código 3.77: Saída terminal
Agora, para demonstrar o funcionamento da sobrescrita, adicionaremos o método trabalhar
na subclasse Professor com os mesmos nome, tipo de retorno e parâmetros do método
trabalhar da classe mãe, porém com uma implementação diferente. Então, ocorrerá a so-
brescrita deste método e o método acessado será o da subclasse. A nova saída, com sobrescrita,
será a apresentada no Código 3.78.
50 Capítulo 3. Orientação a Objetos
1 Deu 6 horas de aula hoje
Código 3.78: Saída terminal com sobrescrita
Com o conhecimento adquirido até o momento, torna-se simples chegar à resposta para
a seguinte pergunta: tratando-se de Java e do paradigma Orientado a Objetos, qual o nome
correto (objeto, método, sobrescrita, sobrecarga, atributo) que é dado à estrutura que representa
as características de uma classe? Para resolver este exercício, vamos utilizar os conceitos de
Orientação a Objetos, explicando cada item e chegando ao resultado correto através da comparação
entre as alternativas. Para isso, iremos relembrar vários conceitos estudados até o momento.
Objeto é a representação de qualquer coisa concreta ou abstrata, que possui informações sobre
como esse elemento deve se comportar, definidas por seus métodos (suas ações) e seus atributos
(suas propriedades). Atributos representam as características de um objeto, aquilo que o define.
Métodos são funções que realizam determinada ação do objeto em questão. Assim, para um carro
podemos ter como atributo a sua cor, enquanto que um de seus métodos pode ser ligar o carro.
A Sobrescrita consiste em criar um novo método na classe filha contendo a mesma assinatura e
mesmo tipo de retorno do método sobrescrito. A Sobrecarga é um conceito de polimorfismo que
consiste basicamente em criar variações de um mesmo método, ou seja, a criação de dois métodos
com assinaturas totalmente iguais em uma classe. Portanto, o nome correto dado à estrutura que
representa as características de uma classe é "atributos".
3.8 Polimorfismo
O polimorfismo surgiu como uma extensão à ideia de herança e é definido como uma prática
da Programação Orientada a Objetos. Sua aplicação em Java permite que o desenvolvedor possa
utilizar métodos de classe com o mesmo nome de maneira diferente. O polimorfismo é de grande
importância pois colabora com a simplicidade, flexibilidade e redução do código.
Um tipo de polimorfismo é a sobrecarga, que foi descrita na seção anterior. Por exemplo, temos
os métodos de uma classeConta no Código 3.79.
1 public class Conta {
2 public void registraConta(String nome) {...}
3 public void registraConta(String nome , String cpf) {...}
4 }
Código 3.79: Métodos com sobrecarga
Esses métodos possuem significados diferentes de acordo com a quantidade de parâmetros que
são passados, isto é, ambos os métodos possuem o mesmo nome, porém podem se comportar de
maneira diferente. Por isso, a sobrecarga é um tipo de polimorfismo.
Uma outra aplicação do polimorfismo ocorre quando utilizamos a herança entre classes. Quando
uma subclasse recebe os atributos da classe mãe, podemos sobrescrever os métodos da classe mãe
inserindo um método de mesmo nome para a subclasse. Vamos tomar como exemplo as seguintes
classes do Código 3.80:
1 public class Empregado {
2 protected String nome;
3 protected String profissao;
4
5 public void trabalhar () {
6 System.out.println(this.nome + "esta indo trabalhar
com um " + this.profissao);
7 }
3.9 Herança Múltipla 51
8 }
9
10 public class Professor extends Empregado {
11 protected String disciplina;
12
13 public void trabalhar () {
14 System.out.println("O professor " + this.nome + "esta
ministrando a disciplina de " + this.disciplina);
15 }
16 }
17
18 public class Medico extends Empregado {
19 protected String especialidade;
20
21 public void trabalhar () {
22 System.out.println("O medico " + this.nome + "ira
trabalhar na area de " + this.especialidade);
23 }
24 }
Código 3.80: Métodos sobrescritos
As classes Medico e Professor recebem por herança os atributos e métodos da classe
Empregado, porém, como podemos observar, o método trabalhar está presente nas 3 classes.
Levando em consideração o conceito de sobrecarga, a chamada empregado.trabalhar() e
medico.trabalhar() irão possuir comportamentos diferentes. Esse tipo de polimorfismo é
chamado de sobrescrita (ou sobreposição).
3.9 Herança Múltipla
Discutimos na Seção 3.6 sobre o conceito de herança existente em Orientação a Objetos. Como
estudamos, a herança permite que uma classe filha herde características como atributos e métodos
da classe da qual ela herda, chamada classe mãe ou superclasse.
Agora, vamos tratar sobre um tipo específico de herança, existente em algumas linguagens
Orientadas a Objetos, que é a herança múltipla. A herança múltipla ocorre quando uma classe herda
de duas ou mais classes. Desta forma, de acordo com o que aprendemos anteriormente, teremos
uma classe filha com duas ou mais classes mães, herdando características de todas elas.
Para ilustrar melhor a explicação, iremos observar o Código 3.81.
1 public class Animal {
2 ...
3
4 public void locomover () {
5 System.out.println("Locomover no planeta");
6 }
7 }
8
9 public class AnimalTerrestre extends Animal {
10 ...
11
12 @Override
52 Capítulo 3. Orientação a Objetos
13 public void locomover () {
14 System.out.println("Andar na terra");
15 }
16 }
17
18 public class AnimalAquatico extends Animal {
19 ...
20
21 @Override
22 public void locomover () {
23 System.out.println("Nadar no lago");
24 }
25 }
26
27 public class Sapo extends AnimalTerrestre , AnimalAquatico {
28 ...
29 }
Código 3.81: Herança múltipla
Neste caso, temos uma classe mãe Animal e duas classes que herdam dela, as classes fi-
lhas AnimalTerrestre e AnimalAquatico. Também temos a classe Sapo, que herda de
AnimalTerrestre e de AnimalAquatico. Portanto, a classe Sapo representa o caso de
herança múltipla, herdando características de duas classes diferentes.
Podemos observar que a classe Animal possui o método locomover(). Este método é
sobrescrito nas classes filhas AnimalTerrestre e AnimalAquatico, como mostra a palavra-
chave @Override. Já a classe Sapo recebe as características das classes AnimalTerrestre
e AnimalAquatico mas não sobrescreve nenhum método. Agora, vamos criar uma classe de
teste, apresentada no Código 3.82:
1 public class Main {
2 public static void main(String [] args) {
3 Sapo sapo_cururu = new Sapo();
4 sapo_cururu.locomover ();
5 }
6 }
Código 3.82: Classe de Teste
Talvez, neste momento, você já tenha percebido a ambiguidade decorrente da herança múltipla.
Esta ambiguidade é chamada de "Problema do Diamante", em decorrência da forma apresentada
quando uma classe A possui duas classes filhas B e C e ambas classes filhas possuem uma mesma
classe filha D (Figura 3.3) [11].
No exemplo apresentado no Código 3.81 temos exatamente esta situação. Assim, o "Problema
do Diamante" é o nome dado à ambiguidade que ocorre na classe que possui herança múltipla, que
não saberá qual versão do método utilizar.
No Código 3.82, ao chamar o método locomover(), como a classe Sapo não sobres-
creve este método e herda duas versões dele, uma de cada classe mãe, então qual versão de
locomover() ele irá usar? A versão de AnimalTerrestre ou a de AnimalAquatico?
Java não consegue saber qual método locomover() deve ser executado e, por isso, não suporta
herança múltipla. Além disso, o uso da herança múltipla pode tornar o código pode ficar confuso,
dificultando a sua a compreensão e manutenção [8].
3.9 Herança Múltipla 53
Figura 3.3: Problema do Diamante
Porém, um recurso tão poderoso não poderia simplesmente deixar de existir em Java. Por isso,
como veremos na Seção 4.10, Java possui o mecanismo de interfaces, que são uma alternativa à
herança múltipla.
4. Java
Nesta seção serão abordados conceitos especificamente com relação à linguagem Java, apresen-
tando suas particularidades e comparando com outras linguagens. Estes conceitos também estão
presentes em outras linguagens de programação, mesmo aquelas que não são orientadas a objetos,
porém sua sintaxe é diferentes em cada uma delas.
4.1 Declaração de Variáveis
Em um programa, normalmente é necessário armazenar determinadas informações, por exemplo,
o nome do usuário, sua idade, ou data de aniversário. Para isso, temos o conceito de variáveis.
Assim como o próprio nome já diz, a variável é uma informação que muda de valor dependendo das
condições. Para exemplificar, imagine que a variável é um balde onde você coloca coisas dentro.
Em certo momento, podemos colocar água no balde. Enquanto, em outro momento, podemos
substituir a água por areia. Com essas mudanças, o conteúdo de dentro do balde mudou, mas o
balde continuou existindo. Fazendo uma analogia à Orientação a Objetos, o balde é a variável,
enquanto a água e a areia são o seu conteúdo, isto é, o valor da variável. O exemplo no Código 4.1
apresenta a declaração de variáveis:
1 public class Main {
2 public static void main(String [] args) {
3 int soma;
4 int a = 100;
5 int b = 5;
6 int c, d, g;
7 int t = 3, s = 3;
8 char letra = ’a’;
9 String Rodolphinho = "O fim está próximo";
10 }
11 }
Código 4.1: Declaração de variáveis
4.2 Leitura e Escrita 55
Os tipos de variáveis existentes e seus respectivos tamanhos são descritos na Tabela 4.1 a seguir:
Variável Tipo Tamanho (em bits)
Números Inteiros
byte 8
short 16
int 32
long 64
Números Reais float 32
double 64
Verdadeiro ou Falso boolean 1
Caracteres char 8
Cadeia de Caracteres String Total de Caracteres * 8
Tabela 4.1: Tipos de variáveis
Vale ressaltar que, apesar de ser um tipo assim como os demais, em Java, a String é uma
classe e, portanto, deve ser escrita sempre com letra maiúscula. A Seção 4.3 irá explicar essa classe
em maiores detalhes.
4.2 Leitura e Escrita
Esta seção visa explicar sobre a leitura e escrita em Java. A leitura de entrada do usuário é
realizada por meio do Scanner, enquanto que a escrita na tela é feita por meio do System.out.
4.2.1 Scanner
No momento, temos o conhecimento necessário para manipular variáveis, estruturar o código
de forma clara, etc. Contudo, ainda não sabemos como receber nenhuma informação do usuário.
Para isso, precisamos do Scanner, uma classe que possui um conjunto de métodos emJava e que
permite que o código leia receba as informações vindas do usuário.
Inicialmente, devemos definir como será a entrada do programa, ou seja, se ela é um conjunto
de números inteiros, uma linha toda, ou apenas um número real, etc. Com essa classe, podemos
utilizar, dentre os métodos oferecidos, aquele que mais se adequa ao tipo da entrada. Os principais
métodos usados são:
• nextInt()
Tipo de retorno: int
Para entradas que são números inteiros, utilizamos esse método, que interpreta a entrada
como um número inteiro e o retorna. Podemos colocar esse valor de retorno em uma variável
e utilizá-lo em nosso programa.
• nextFloat()
Tipo de retorno: float
Também podemos ter como entrada um valor real. Esse método interpreta a entrada como um
valor float e o retorna. Por exemplo, podemos receber um valor em Dólares para converter
para Euro, ou até mesmo, receber uma série de medidas de um sensor, e armazená-las em um
banco de dados.
• nextDouble()
Tipo de retorno: double
Quando a entrada for um número real muito grande, é utilizado esse método, que suporta
números reais maiores e possui tipo de retorno double. Portanto, ele irá interpretar a
entrada como um valor double e depois retorná-lo.
• nextLine()
56 Capítulo 4. Java
Tipo de retorno: String
Além de números, podemos receber uma cadeia de caracteres (String), que é alcançada
por este método, que captura uma sequência de caracteres terminada por uma quebra de linha
(\n). Por exemplo, utilizamos esse método quando vamos receber um nome de pessoa, país,
uma descrição, etc.
• nextBigInteger()
Tipo de retorno: BigInteger
Quando deseja-se utilizar números inteiros maiores que os alcançados com int, trabalha-se
com o tipo BigInteger, que é bastante usado no ramo de criptografia. Este método
interpreta a entrada como um valor BigInteger e o retorna. Assim como a String, o
BigInteger também é uma classe e, portanto, deve ser escrito com letra maiúscula.
Por exemplo, suponha que devemos fazer um programa que receba um número inteiro,
multiplique-o por 42, depois some o resultado com um número real, que também será rece-
bido, para exibir na saída padrão. Sabemos que há dois tipos distintos de entrada, uma delas é
int e a outra é float. Para receber esse tipo de entrada, são utilizadas as funções nextInt()
e nextFloat(), respectivamente. Tendo em vista o que foi discutido anteriormente, podemos
escrever o seguinte trecho apresentado no Código 4.2:
1 import java.util.Scanner;
2
3 public class MinhaClasse {
4 public static void main(String [] args) {
5 Scanner entrada = new Scanner(System.in);
6 int a = entrada.nextInt ();
7 float b = entrada.nextFloat ();
8 System.out.println ((a * 42) + b);
9 }
10 }
Código 4.2: Recebe número inteiro e real
Neste exemplo, precisamos nos atentar a alguns detalhes. Primeiramente, devemos importar a
classe Scanner para que ela seja possa ser utilizada posteriormente. Sabendo-se que Scanner é
uma classe, devemos armazenar o que estamos recebendo em uma variável do tipo Scanner, que
é chamado de objeto a partir desse momento. Na inicialização deste objeto, usamos o construtor
que recebe um parâmetro especificando de onde a entrada a ser processada deverá vir. Neste caso,
temos o System.in, que significa que será utilizada a entrada padrão. Após a criação do objeto,
usamos os métodos deste objeto para extrair informações da entrada. No exemplo, foi utilizado
o nextInt() para obtermos o número inteiro desejado a partir da entrada, e nextFloat()
para obtermos o número real a partir da entrada. Por fim, são realizadas as operações matemáticas
necessárias e o resultado final é impresso na tela com System.out.println(), que será
explicado na seção seguinte.
Além dos métodos explicados nesta seção, existem muitos outros que são apresentados na
página da documentação do Scanner [3]. Em outras linguagens, como C, por exemplo, para
se obter entrada do usuário, usamos a função scanf(), que recebe a entrada de acordo com a
formatação que foi especificada. Já em Python, a função input() simplesmente recebe uma linha
da entrada por vez, fazendo com que o programador tenha que extrair as informações que nela
estão contidas.
4.3 String 57
4.2.2 System.out
As variáveis, bem como outros tipos de informações, devem ser exibidas na tela do computador
para que elas possam ser interpretadas por nós humanos, ou seja, pelos usuários da aplicação.
Para atingir esse objetivo, usamos o comando System.out. A informação mais básica possível
de imprimir é uma frase. Podemos imprimir uma frase usando o comando System.out e
especificando que desejamos imprimir a saída, então, para isso usamos o comando print. Temos,
por exemplo o Código 4.3:
1 System.out.print("Ola , mundo!");
Código 4.3: Olá mundo
Assim, a frase "Ola, mundo!" será impressa na tela do usuário. Geralmente, é mais útil usar o
comando println (Código 4.4), porque ele imprime a saída e, em seguida, quebra uma linha de
texto, ou seja, pula para a linha de baixo. Com isso, as informações tornam-se mais legíveis no
terminal, que é onde elas são exibidas.
1 System.out.println("Ola , mundo!");
Código 4.4: Olá mundo com quebra de linha
Agora, a frase "Ola, mundo!" será impressa na tela, seguida por uma quebra de linha. Dentro
das aspas, também é possível escrever uma variável, além de uma frase. Para imprimir uma variável,
basta usar o nome da variável como parâmetro do System.out, ao invés de uma frase. Se
quisermos exibir na tela uma variável chamada num, por exemplo, temos o Código 4.5.
1 System.out.println(num);
Código 4.5: Imprime valor da variável
É possível exibir frases e variáveis juntas em um único comando System.out. Para isso,
usamos o sinal "+" para unir estas informações diferentes na ordem em que elas aparecem, como
mostra o Código 4.6. Chamamos isto de concatenação.
1 System.out.println("Resultado: " + num);
Código 4.6: Concatenação na saída
Informações técnicas acerca de outros comandos relacionados a System.out e print estão
disponíveis na documentação Java sobre print [2] e sobre a classe System [4].
4.3 String
Em Java, Strings são objetos ou instâncias da classe java.lang.String. Ao contrário
do que ocorre em C e C++, Strings em Java não são tratadas como sequências de caracteres
terminadas por NULL portanto, devem ser declaradas e instanciadas. Os objetos String, no
entanto, têm uma ajuda especial do compilador Java que os faz parecer mais com tipos primitivos
(como int, por exemplo). Suponha que temos o objeto nome da classe String, como mostra o
Código 4.7:
1 String nome = "Jose";
Código 4.7: String nome
É possível utilizar o símbolo “+”, que em Java é “sobrecarregado” para realizar a concatenação
de Strings. Assim, podemos fazer como o Código 4.8.
1 String nome = "Jose" + "Roberto";
2 String sobrenome = nome + "Pereira";
Código 4.8: Concatenação de Strings
58 Capítulo 4. Java
4.4 If/Else
A estrutura condicional if segue a seguinte sintaxe do Código 4.9:
1 if (condicao) {
2 ...
3 }
Código 4.9: Sintaxe if
Uma condição pode ser verdadeira ou falsa, ou seja, ela pode ser satisfeita ou não. Caso ela
seja satisfeita, será executado o código, dentro das chaves da estrutura if. Caso contrário, isto é,
caso a condição não seja satisfeita, então o código não será executado.
1 int poupanca = 500, saque = 600;
2
3 if (saque > poupanca) {
4 System.out.println("Nao e possivel realizar o saque");
5 }
Código 4.10: Condição saque maior que poupanca
O Código 4.10 apresenta o funcionamento do if. Se o saque é maior do que a poupança, ou
seja, se a condição “saque maior que poupança” é satisfeita, a mensagem “Não é possível realizar
o saque” será impressa na tela. Já a estrutura condicional if else segue o seguinte padrão do
Código 4.11:
1 if (condicao) {
2 ...
3 }
4 else {
5 ...
6 }
Código 4.11: Sintaxe if else
Quando a condição if for analisada, e for falsa,ou seja, não for satisfeita, o código dentro
do das chaves do if não será executado. Então, será executado o código dentro das chaves da
estrutura else. Portanto, percebemos um fato importante: o else só irá ser executado quando o
if não for executado, isto é, o código dentro do else será executado apenas se a condição do if
não for satisfeita.
1 int poupanca = 500, saque = 400;
2
3 if (saque > poupanca) {
4 System.out.println("Nao e possível realizar o saque");
5 ...
6 }
7 else {
8 System.out.println("E possivel realizar o saque");
9 ...
10 }
Código 4.12: Exemplo if else
Comparando o exemplo do Código 4.12 com o exemplo do Código 4.10, observamos a diferença
de que agora a condição do if é falsa, pois o valor do saque não é maior do que o valor da poupança,
4.5 Estruturas de Repetição 59
então a condição não é satisfeita. Isso faz com que o código siga o fluxo do else, ou seja, o código
"pula"o if, não executa o código dentro das chaves do if e executa o código dentro das chaves do
else.
Também podemos encadear as condições if e else. Isso nos permite ter mais de duas
possibilidades de execução de comando, sendo que o if e todos os else if devem ser checados
para analisar se a condição de cada um é satisfeita e, caso nenhuma delas seja satisfeita, o else é
executado. Caso mais de uma das condições seja satisfeita, os códigos delas serão executados.
1 int N1 = 4, N2 = 5;
2
3 if (N1 == N2) {
4 System.out.println("Os dois numeros sao iguais");
5 }
6 else if (N1 > N2) {
7 System.out.println("N1 e maior que N2");
8 }
9 else {
10 System.out.println("N1 e menor que N2");
11 }
Código 4.13: Encadeamento de condições
No Código 4.13, primeiro verificamos se N1 é igual a N2 e, como eles são diferentes, a condição
não é satisfeita e entramos na condição else if. Por sua vez, o else if verifica se N1 é maior
do que N2. Como N1 não é maior do que N2, ou seja, temos que a condição não é satisfeita, portanto
o código dentro das chaves do else if não é executado. Assim, será executado somente o código
dentro do else, sem que seja necessária nenhuma verificação de condição para executá-lo, tendo
em vista que apenas chegamos neste ponto do código porque nenhuma das condições verificadas
anteriormente foram satisfeitas.
4.5 Estruturas de Repetição
Esta seção irá abordar as estruturas de repetição existentes na linguagem Java, que são while,
do while, for e enhanced for. O while e o do while são estruturas muito parecidas,
portanto são explicadas juntas na Seção 4.5.1. Apesar do enhanced for ser um for aprimorado,
sua explicação exige um pouco mais de cuidado. Portanto, a Seção 4.5.2 é destinada especificamente
ao for e a Seção 4.5.3 é destinada especificamente ao enhanced for.
4.5.1 While/Do While
O while é um comando de repetição, ou seja, um recurso que permite que um certo trecho do
código seja repetido enquanto certa condição for verdadeira, isto é, enquanto ela for satisfeita. A
principal característica do comando while é que ele possui apenas um argumento, o que diferencia
este de outros comandos como, por exemplo, o comando for, que possui 3 argumentos em sua
estrutura. O comando while, em Java, é utilizado da mesma forma que em outras linguagens,
como C e C++. Um exemplo da sintaxe do while pode ser observado no Código 4.14:
1 while (condicao) {
2 ...
3 }
Código 4.14: Sintaxe while
60 Capítulo 4. Java
A condição do while, como dito anteriormente, é o único argumento da sua estrutura. A
estrutura irá rodar as instruções dentro do comando enquanto a condição for satisfeita. Quando
a condição deixar de ser satisfeitas, as instruções deixam de ser executadas e o programa segue
adiante para as próximas partes do código, caso existam.
Portanto, a primeira forma de sair da estrutura de repetição é fazendo com que a condição não
seja satisfeita. Outro modo de sair do laço de repetição é usando a instrução break. Para isso, o
programador deve colocar uma outra condição dentro das instruções, para que o código saiba o
momento de parar, e caso esta condição seja satisfeita, a ação retornada é o comando break.
Devemos nos atentar ao código na hora de implementar o comando while, pois, já que
estamos trabalhando com uma estrutura de repetição, há a possibilidade de ocorrer um loop infinito,
isto é, quando o programa nunca atinge a condição falsa e continua infinitamente dentro do laço
executando as instruções.
Um exemplo interessante é a utilização do comando while na implementação de uma cal-
culadora. Suponha que o programa deve ser executado até que ele receba o dígito “0”. Como
essa é a única condição de parada do programa, o comando while se encaixa perfeitamente neste
cenário. Assim, enquanto a entrada do programa for diferente de “0”, o programa será executado
normalmente, ou seja, todos as instruções dentro do comando são executadas. Caso o programa
receba “0” como entrada, a execução é encerrada.
Já o comando do while realiza as instruções dentro de seu corpo enquanto a sua condição for
satisfeita. Esta instrução, apesar de muito parecida com o while, possui uma diferença importante:
ela primeiro executa o código presente dentro das chaves e depois verifica se a condição é satisfeita.
Ou seja, o do while faz o que está especificado dentro das chaves, verifica se a condição é
verdadeira, e fica neste laço até que a condição não seja satisfeita. Desta forma, as instruções dentro
do comando do while são executadas, no mínimo, uma vez, enquanto as instruções dentro do
comando while podem não ser executadas nenhuma vez, caso a condição não seja satisfeita logo
de início. A instrução do while segue a estrutura apresentada no Código 4.15:
1 do {
2 ...
3 } while (condicao);
Código 4.15: Sintaxe do while
Observe que a verificação da condição é feita depois de o programa passar pelas instruções
dentro das chaves e, por isso, temos esta diferença com relação ao comando while. Vamos agora
analisar o exemplo do Código 4.16:
1 package doWhile;
2
3 public class DoWhile {
4 public static void main(String [] args) {
5 int contador = 0;
6 do {
7 System.out.println(contador);
8 contador ++;
9 } while(contador != 10);
10 }
11 }
Código 4.16: Exemplo do while
Neste código o contador começa em 0. Ele vai sendo incrementado e tendo seu valor
impresso na tela enquanto a condição for satisfeita, ou seja, enquanto o valor do contador for
4.5 Estruturas de Repetição 61
menor do que 10. Assim, as instruções dentro das chaves serão executadas um número fixo de
vezes. A saída obtida será a apresentada no Código 4.17:
1 0
2 1
3 2
4 3
5 4
6 5
7 6
8 7
9 8
10 9
Código 4.17: Saída exemplo do while
Se, durante a execução do programa, a condição não se tornar falsa, as instruções dentro das
chaves continuarão sendo executadas e ocorrerá um laço infinito. Isto é, o código ficará executando
para sempre até que seja parado manualmente. Nestas situações, depois que é realizada a primeira
execução da função, ou seja, após as instruções serem executadas pela primeira vez, a condição da
instrução do while sempre continua sendo satisfeita, fazendo com que o programa nunca saia
deste laço. Vamos agora analisar a situação, apresentada no Código 4.18:
1 package doWhile;
2
3 public class DoWhile {
4 public static void main(String [] args) {
5 int contador = 10;
6 do {
7 System.out.println(contador);
8 contador ++;
9 } while(contador != 10);
10 }
11 }
Código 4.18: Exemplo do while
Neste caso, podemos observar que o valor inicial do contador é 10, portanto a condição não
é verdadeira. Temos que a condição logo de início não é satisfeita, já que o valor do contador
começa sendo igual a 10. Quando isso ocorre, as instruções presentes na função serão realizadas
pelo menos uma vez. Diferentemente da função while, que caso sua condição seja falsa desde o
início, as instruções não serão executadas nenhuma vez.
No do while asinstruções dentro das chaves são realizadas antes da verificação, então elas
são executadas pelo menos uma vez. Podemos observar que, neste caso, elas serão executadas mais
de uma vez, pois uma das instruções dentro das chaves é o incremento do valor do contador.
Assim, temos que o contador terá seu valor incrementado, aumentando para 11. Dessa forma,
quando o código chegar no ponto da verificação ela será satisfeita. Além disso, para todos os
valores seguintes ela será satisfeita, pois todos serão diferentes de 10, já que todos são maiores
que 10. Assim, o contador nunca mais terá valor 10, fazendo com que a condição sempre seja
satisfeita. Temos, então um laço infinito, sendo que a parada da execução do código teve que ser
realizada manualmente, sendo possível obter a saída expressa no Código 4.19:
1 ...
2 254385
62 Capítulo 4. Java
3 254386
4 254387
5 254388
6 254389
7 254390
8 254391
9 254392
10 254394
Código 4.19: Saída laço infinito
Ao testarmos o código, caso não ocorra a parada da execução dele, temos um laço infinito.
Isto significa, como dito anteriormente, que a condição em momento algum tornou-se falsa, como
apresentou o Código 4.18. E, caso a instrução só tenha sido executada uma vez, isso significa
que a condição já não era satisfeita desde o início, ou seja, ao ser realizada a primeira verificação
a condição não foi satisfeita, ou que, inicialmente a condição era satisfeita, mas as instruções
realizadas antes da verificação fizeram com que a condição não fosse mais satisfeita, portanto as
instruções foram executadas apenas uma vez. Essa última situação é apresentada no Código 4.20:
1 package doWhile;
2
3 public class DoWhile {
4 public static void main(String [] args) {
5 int contador = 9;
6 do {
7 System.out.println(contador);
8 contador ++;
9 } while(contador != 10);
10 }
11 }
Código 4.20: Exemplo do while
Neste exemplo, o contador começa com valor 9, satisfazendo a condição do do while.
Porém, ao executar o as instruções dentro das chaves, há uma instrução que incrementa o valor
do contador. Desta forma, o contador passa a ter valor 10 e deixa de satisfazer a condição.
Portanto, a execução do do while é encerrada. Para o Código 4.20, a saída obtida é apresentada
no Código 4.21:
1 9
Código 4.21: Saída de uma repetição
4.5.2 For
O for é uma estrutura de repetição. Esta funcionalidade permite que haja a replicação zero ou
mais vezes dos comandos inseridos dentro do seu escopo. Esta funcionalidade, em Java, é usada da
mesma forma que em outras linguagens, como em C ou C++. Para entendê-lo, devemos ter em
mente que o comando for toma como argumento três parâmetros: a inicialização das variáveis,
condição de execução e o incremento. O comando for segue a estrutura do Código 4.22 a seguir:
1 for (inicializacao_variaveis; condicao_execucao; incremento) {
2 ...
3 }
Código 4.22: Sintaxe for
4.5 Estruturas de Repetição 63
• Inicialização das variáveis: Neste primeiro argumento, podemos definir a inicialização da
variável que irá controlar quantas vezes ocorrerá o loop, ou seja, a repetição das instruções;
• Condição de execução: É necessário definir qual será o elemento de parada. Ou seja, a
condição que definirá se os comandos dentro do escopo do for podem ser executados. Se a
condição de execução for satisfeita, os comandos são executados, caso contrário, não serão
executados;
• Incremento: Define quanto que a variável inicializada no primeiro parâmetro (inicialização
das variáveis) será incrementada ou reduzida após cada loop, permitindo que seja controlada
a quantidade de vezes que o for será executado.
1 for (inicializacao_variaveis; codicao_execucao ;) {
2 ...
3 }
Código 4.23: Estrutura for sem incremento
Qualquer um destes três argumentos pode ser omitido, basta que não seja escrito nenhum parâ-
metro. Então, por exemplo, se desejarmos omitir o parâmetro que toma o incremento, declaramos
como mostra o Código 4.23.
É interessante notarmos que apesar de ser possível omitir todos ou algum desses três argumentos,
não faz sentido não termos a condição de execução. Omitindo essa condição, o for não terá
uma condição de parada, provocando um loop infinito, já que a condição de execução sempre será
considerada verdadeira.
Uma aplicação prática do comando for é, por exemplo, a sua utilização para somar todos os
elementos que constam em um vetor. Podemos percorrer este vetor, atribuindo a uma variável a
somatória de elementos do vetor, um a um. Para controlar a condição de execução, começamos na
posição zero e incrementamos até que o tamanho do vetor seja atingido, como mostra o Código
4.24:
1 public class MinhaClasse {
2 public static void main(String [] args) {
3 int vetor[] = {1, 2, 3};
4 int soma = 0;
5
6 for(int i = 0; i != vetor.length; i++) {
7 soma = soma + vetor[i];
8 }
9
10 System.out.println(soma);
11 }
12 }
Código 4.24: Soma de elementos do vetor
Para utilizar o for, assim como as outras estruturas de repetição descritas, é necessário tomar
cuidado com o loop infinito, mencionado anteriormente. O loop infinito ocorre quando o escopo
dentro do for é executado infinitas vezes, sem uma condição de parada que permita que a execução
do código seja encerrada.
4.5.3 Enhanced For
O enhanced for é um loop aprimorado, que consiste na iteração de coleções e matrizes,
percorrendo cada um dos elementos dessas coleção, que podem ser listas, filas, pilhas, etc. ou
percorrendo uma matriz. Essa operação não existe em determinadas linguagens como C, por
64 Capítulo 4. Java
exemplo, e é equivalente ao foreach de linguagens como C++ e Python. Todas as operações
que serão realizadas em cada um dos seus elementos devem ser escritas dentro do seu escopo. Sua
sintaxe é especificada da seguinte forma no Código 4.25:
1 for (objeto: colecao/matriz) {
2 ...
3 }
Código 4.25: Sintaxe enhanced for
• Objeto: Esse parâmetro especifica o item equivalente ao tipo da coleção ou matriz, em que
esse elemento estará disponível para acesso dentro do bloco do for para alguma manipulação;
• Coleção/Matriz: Esse parâmetro especifica a coleção ou matriz que será percorrida.
O enhanced for pode ser utilizado, por exemplo, para percorrer uma coleção genérica,
como uma lista. Para ter melhor entendimento do funcionamento dessa operação, podemos abstrair
ideia, utilizando o enhanced for de forma parecida com a estrutura de repetição for para
iterar cada elemento. Suponha que os elementos dessa lista são compostos por nomes, represen-
tando alguns alunos de Ciência da Computação cursando a disciplina de Cálculo I (Código 4.26).
Cada elemento deve exibido em tela dentro do escopo do enhanced for em ordem crescente.
Devemos lembrar que podemos importar uma biblioteca denominada java.util.Arrays para
realizar a operação de ordenação dos nomes.
1 String alunos [] = new String [] {"Carlos", "Sofia", "Bruno", "
Lucas"};
2
3 Arrays.sort(alunos);
4
5 for (String nome: alunos) {
6 System.out.println(nome);
7 }
Código 4.26: Iteração elementos de lista
4.6 Switch
A ideia principal do switch é agrupar uma série de condições (e ações a serem executadas)
baseadas em um único parâmetro, fazendo o papel de diversos if. A estrutura do switch é
formada por:
• um parâmetro: que deve vir dentro dos parênteses do switch;
• n casos: o parâmetro é analisado em cada um deles e, a partir disso, um trecho de código é
executado se satisfeita a condição de cada caso;
• break: (em cada um dos n casos) identifica o fim da execução do switch, ou seja, a partir
do break, o programa segue seu fluxo após o switch, sem analisar os casos seguintes;
• default: (opcional) é um caso a ser executado quando o valor do parâmetro não satisfaz
nenhum dos outros casos.
A explicação acima fica mais clara a partir do exemplo seguinte, apresentado no Código 4.27:
1 int numero = 3;
2
3 switch(numero) {
4 case 1:
5 System.out.println("Um");6 break;
4.6 Switch 65
7 case 2:
8 System.out.println("Dois");
9 break;
10 case 3:
11 System.out.println("Tres");
12 break;
13 case 4:
14 System.out.println("Quatro");
15 break;
16 case 5:
17 System.out.println("Cinco");
18 break;
19 default:
20 System.out.println("Valor fora do intervalo 1..5");
21 }
Código 4.27: Exemplo switch
O código acima irá, por sua vez, analisar a variável inteira numero, que foi inicializada com
valor 3. Primeiramente, o case 1 irá verificar se o valor é 1. Como o resultado desta comparação
é falso, o programa irá então para o próximo caso, isto é, case 2, analisando agora se o valor é 2.
Mais uma vez, o resultado da comparação é falso, então o programa seguirá para o próximo caso,
que é o case 3. Ao comparar a variável numero com o valor do próximo caso, isto é, valor 3, o
programa identifica que a condição é satisfeita e, portanto, o código dentro do bloco é executado,
imprimindo na tela a mensagem “Tres”. Ao executar o break, o programa sairá do switch, sem
analisar os demais casos, seguindo a continuação do código.
Se nenhum dos casos tivesse sua condição satisfeita, ou seja, se a variável numero possuísse
um valor que não fosse dentro do intervalo de 1 a 5, o default seria, por fim, executado. Se a
variável, ao invés de ser do tipo int, fosse String, o valor de cada caso do código acima deveria
estar entre aspas duplas. Se as aspas não forem colocadas, o programa apresentará erro.
O switch em Java, a partir da versão Java 7, possibilita o uso de String nos valores dos
casos, diferente da linguagem C, em que os valores de cada caso podem ser apenas do tipo int ou
char. Uma funcionalidade presente em C, mas não em Java, é o uso de intervalo dentro do caso,
ou seja, “case 1 ... 5”, por isso em Java é necessário tratar cada caso separadamente. Por
outro lado, o switch em Java possui algumas similaridades com o switch em C, como o uso
do break. Caso não exista essa instrução em um switch, o sistema executa todos os casos até
encontrar um break ou até que acabem as opções de casos do switch.
Um exemplo de utilização do switch é na resolução do problema da calculadora. Suponha
que devemos implementar uma calculadora que contenha as funções de soma (+), subtração (-),
multiplicação (*), divisão (/), potência (exp) e resto da divisão inteira (mod). Além disso, suponha
que esta calculadora deverá guardar as operações feitas pelo usuário (utilizando arrayList) e
permitir que o usuário as visualize. O programa deverá ser executado até o usuário digitar "0".
Em suma, devemos fazer uma calculadora que realize as operações matemáticas básicas,
potenciação, resto da divisão inteira, e que também registre o histórico das operações realizadas
para exibi-las quando o usuário encerrar o programa. Para isso, como temos que realizar instruções
diferentes para cada caso, ou seja, para cada uma das operações escolhidas, podemos utilizar o
comando switch.
Construir uma calculadora pode parecer fácil, pois é comum pensar que é só realizar as opera-
ções matemáticas dentro do programa que ele as fará corretamente. Porém, como o computador é
uma máquina que segue exatamente os passos explicitados no código, ele precisa de instruções
precisas para realizar ações simples. Provavelmente será necessário utilizar cast (conversão
66 Capítulo 4. Java
explícita do valor de um tipo de variável para outro tipo de variável), que será aprendido na Seção
4.9. Por exemplo, caso o usuário escolha a divisão e queira efetuar a divisão 3 / 2 e as variáveis
numero1 e numero2 tenham sido declaradas como inteiras (int), o resultado da divisão inteira
será 1, mas o resultado correto esperado é 1,5. Logo, para corrigir isso teríamos que usar cast e
escrever o código da seguinte forma, apresentada no Código 4.28:
1 resultado = numero1 / (float)numero2;
Código 4.28: cast para divisão
Isto acontece devido ao compilador interpretar o resultado da divisão de um int por um int
como um int, logo ele desconsiderará as casas decimais no momento de executar a operação. Para
isso, fazemos cast em pelo menos um operador.
Outra dificuldade que pode ser encontrada é a realização de potências maiores que elevado
ao quadrado. Para que o programa consiga cumprir essa tarefa, é preciso aumentar o tamanho da
variável que for receber o resultado. Por exemplo, substituir pelo tipo double, caso a variável
seja de um tipo que suporta apenas resultados menores. Para realizar a potenciação de um número
devemos incluir a biblioteca no início do código: import java.lang.Math. A potência pode
ser utilizada com o comando Math.pow(), como no exemplo do Código 4.29 a seguir:
1 double resultado = Math.pow(numero1 , numero2);
Código 4.29: Exemplo potência
O numero1 será a base da potência, o numero2 o expoente, e a variável do tipo double
resultado recebe o valor do resultado do numero1 elevado ao numero2.
É possível implementar o histórico das operações utilizando o ArrayList para salvar
os cálculos feitos pelo usuário. Para utilizá-lo, é necessário importar a biblioteca: import
java.util.ArrayList. Logo em seguida, devemos criar o objeto historico, que armaze-
nará a sequência de caracteres inseridas pelo usuário e o resultado das operações realizadas pelo
programa. Declaramos esse objeto com o tipo desejado como no exemplo do Código 4.30:
1 ArrayList <String > historico = new ArrayList <String >();
Código 4.30: Exemplo aramazenamento de histórico
A cada operação matemática realizada, deve ser usado o comando historico.add(), para
adicionar e armazenar os valores em historico. Para exemplificar, vamos apresentar o trecho
do Código 4.31 para o caso em que a operação escolhida é a soma:
1 switch(operacao) {
2 case ’+’:
3 resultado = numero1 + numero2;
4 System.out.println("Resultado = " + resultado);
5 historico.add(numero1 + " + " + numero2 + " = " +
resultado);
6 break;
7 }
Código 4.31: Caso da soma
Para ver o conteúdo de historico, basta utilizar o comando de saída, que a formatação é
feita automaticamente, como mostra o Código 4.32:
1 System.out.println(historico);
Código 4.32: Imprimir histórico
Para aprender mais e ver mais exemplos de como utilizar ArrayList, acesse o site em inglês
[10] ou em português [5].
4.7 Tipos por Referência 67
4.7 Tipos por Referência
Um ponteiro é um tipo que armazena a referência de um objeto, ao invés de seu valor. Caso
um objeto seja alterado por um ponteiro, a alteração será aplicada no objeto e refletida nos outros
ponteiros que possuem referência para este mesmo objeto. Podemos pensar em um ponteiro como
se fosse o ingresso de um show e o objeto como sendo o show propriamente dito. Assim, se o
horário do objeto show for alterado, o horário será alterado em todos os ingressos, pois todos se
referem ao mesmo objeto. Essa ideia é um pouco complexa de compreender, já que essa mudança
impacta no restante do código, ou seja, se o objeto é modificado, a próxima vez que ele for utilizado,
ele estará modificado, não mais da forma original.
Em Java, o uso do ponteiro é bem restrito, já que não temos acesso direto a ele, com isso é usado
apenas para instanciar e apontar para objetos. Devemos nos atentar a alguns detalhes ao utilizarmos
ponteiros. Por exemplo, ao tentarmos copiar um objeto utilizando o operador de atribuição (=)
percebemos que os dois objetos (objeto1 e objeto2) passaram a ter a mesma referência, ou
seja, se tornam a mesma coisa, portanto a mudança feita no objeto1 será refletida no objeto2.
4.8 Exceções
As exceções são importantes para que não sejam permitidas falhas no sistema, assim consegui-
mos rejeitar uma informação, caso ela não seja apropriada para determinada variável, já que isso
pode impactar em um erro no programa.
O tratamento desses erros é responsabilidade do programador. Assim, utilizamos as instruções
try e catch para identificar qual foi o erro e facilitar o trabalho do programador ao trataro erro.
O try, é utilizado para que possamos definir o código que esperamos que seja executado com
sucesso, ou seja, o que iremos tentar executar, e o catch serve para capturarmos o código de erro,
ou seja, a situação de erro.
Um exemplo da utilização do try e catch é quando definimos um valor único que é válido,
e, quando um valor diferente do definido é inserido ao rodar o try, o catch captura um erro. A
sintaxe de try e catch pode ser observada no Código 4.33 a seguir:
1 try {
2 comando ();
3 }
4 catch (Exception e) {
5 System.out.println("Erro: " + e.getMessage ());
6 }
Código 4.33: Exemplo try catch
Utilizando o conceito de exception, que é uma maneira de especificar para o usuário
informações sobre o erro e a medida tomada pelo programa para contorná-lo, é possível lidarmos
com situações inesperadas, ou seja, exceções. O exception possibilita lançar erros para o
usuário de maneira mais elegante, como podemos observar no Código 4.34 abaixo:
1 try {
2 x2 = Scanner.nextDouble ();
3 }
4 catch (InputMismatchException e) {
5 System.out.println(String.format("Ocorreu um erro de
formato: %s. O segundo numero assumira o valor 1." + e.
getMessage ()));
6 Scanner.nextLine ();
7 x2 = 1;
68 Capítulo 4. Java
8 }
Código 4.34: Lidando com exceções
No Código 4.34, é utilizada a MissmatchException para tratar os casos nos quais o
usuário informa um dado diferente do esperado, neste caso, diferente do tipo int. Ao ser utilizado
um valor inválido para o segundo número, isto é, um char ao invés de int, quando o código
entra na MissmatchException, o erro é informado na tela (Código 4.35). Além de informar
informar o erro, também é informado ao usuário que o segundo número assumirá o valor 1, já que
foi inserido um valor inválido.
1 Digite o primeiro numero:
2 1
3 Digite o operador:
4 +
5 Digite o segundo numero:
6 K
7 Ocorreu um erro de formato: null. O segundo numero assumira o
valor 1.
8 Resultado: 2.0
Código 4.35: Saída com a exceção
Ao longo deste livro, vimos muitos exemplos relacionados a bancos. Um dos problemas mais
simples que temos no nosso sistema de contas é que o método saca permite sacar um valor da
conta mesmo que o saldo dela seja insuficiente. Suponha que Conta possui os atributos titular,
número e saldo, e o método saca, que recebe um número e o desconta do saldo. Este método
não possuía uma restrição para quanto o usuário poderia sacar, logo o mesmo poderia sacar um
valor maior que o saldo.
Para resolver esse problema, utilizamos a funcionalidade do Java de gerar exceções. A partir do
momento que uma entrada no método saca é maior que o saldo atual a execução do método
para e ele lança uma exceção que será capturada no método main e será validada.
4.9 Cast
Uma forma de entender como o cast funciona é pensar que ele traduz algo de um idioma para
outro. Em uma tradução de um texto em inglês para português o tradutor vai interpretar o que foi
escrito em inglês e reescrever tudo em português. O conteúdo do que foi escrito continua o mesmo,
foi mudada apenas a forma como ele foi interpretado. Isso é exatamente o que acontece com o cast.
Cast é uma maneira de dizermos para o compilador que ele deve ler aquele dado de uma forma
diferente. Por exemplo, observe o Código 4.36.
1 String numero = "42";
2 int num = (int) numero;
Código 4.36: cast de String para inteiro
No exemplo do Código 4.36, a String numero contém o número 42 escrito em forma de
uma cadeia de caracteres. Quando a variável num é criada, o número é passado para ela como um
parâmetro de inicialização, mas ele possui um cast para int já que ele precisa ser “traduzido” de
uma String para uma int. Vale lembrar que existem casts que o compilador faz para nós sem
que percebamos, como no Código 4.37. Neste exemplo, o compilador automaticamente transforma
o inteiro 95 do num em uma String quando realizamos a operação da linha 3, frase + num.
4.10 Interfaces 69
1 String frase = "o seu numero e: ";
2 int num = 95;
3 String novaFrase = frase + num;
4 System.out.println(novaFrase);
Código 4.37: cast do compilador
4.10 Interfaces
Na Seção 3.9, discutimos sobre o que é herança múltipla e as suas desvantagens. Também
apontamos o fato de que a linguagem Java não possui este recurso, porém possui o recurso de
interface. Portanto, vamos estudar um pouco mais sobre este conceito nesta seção.
Interface é um recurso de Programação Orientada a Objetos, e a sua aplicação em Java nos
permite “obrigar” um determinado grupo de classes a ter propriedades ou métodos em comum
para que exista um contexto entre elas. A interface pode definir métodos, mas não pode conter a
implementação deles. De modo geral, ela descreve o que o objeto deve executar no sistema [9],
criando um contrato entre classes. A maneira como esses métodos serão executados vai ser definida
por meio da implementação da interface.
1 public class interface FormaGeometrica {
2 public float calcularArea ();
3 public float calcularPerimetro ();
4 }
5
6 public class Circulo implements FormaGeometrica {
7 private float tamanhoLado;
8 private float raio;
9 final float PI = (float) 3.14;
10
11 public float calcularArea () {
12 return raio * raio * PI;
13 }
14
15 public float calcularPerimetro () {
16 return 2 * raio * PI;
17 }
18 }
19
20 public class Quadrado implements FormaGeometrica {
21 private float tamanhoLado;
22 private float raio;
23
24 public float calcularArea () {
25 return tamanhoLado * tamanhoLado;
26 }
27
28 public float calcularPerimetro () {
29 return 4 * tamanhoLado;
30 }
70 Capítulo 4. Java
31 }
Código 4.38: Exemplo interface
A classe FormaGeometrica é a interface do Código 4.38. Por isso, para identificá-la
devemos usar a palavra-chave interface. Isso significa que a classe não pode implementar seus
métodos para calcular área ou perímetro por conta própria, porém, por outro lado, estes mesmos
métodos devem obrigatoriamente ser implementados nas subclasses Quadrado e Circulo,
que receberam por herança os dados da interface. Portanto, quando uma classe for implementar
uma interface ela deve implementar também os métodos da interface. Para isso, é utilizada a
palavra-chave implements, já que a classe irá implementar a interface.
5. Conclusão
Este livro apresentou conceitos de Orientação a Objetos e da linguagem Java, buscando ensiná-
los de forma mais próxima dos alunos, com linguagem e exemplos mais fáceis de compreender.
Foram discutidos conceitos mais básicos, desde como criar um projeto em Java e de conceitos de
classes, métodos e atributos, até conceitos mais complexos, como herança, sobrecarga, sobrescrita
e exceções.
Apesar de ser uma forma de consolidar os temas aprendidos nas aulas do curso de extensão,
o objetivo principal deste livro era permitir que esse conhecimento fosse passado para quaisquer
pessoas querendo aprender sobre Java e conceitos de linguagens Orientadas a Objetos. Para isso,
os alunos do curso de extensão, oferecido pela Universidade Federal de São Carlos (UFSCar), ao
escreverem este livro, focaram em sempre explicar como fizeram para entender um determinado
conceito quando tiveram dificuldade ao longo do curso e como os testes dos códigos permitiram
que eles entendessem o que deu errado e por quê.
Desta forma, seria possível expor como eles entenderam e fixaram o conteúdo, o que pode
ajudar outros alunos. Portanto, esperamos que você tenha gostado deste projeto e aprendido
bastante com o livro! Esperamos que os conceitos tenham sido apresentados de forma mais fácil
de compreender e que os exemplos contidos neste livro tenham lhe ajudado a entender com maior
facilidade os conceitos de Orientação a Objetos e da linguagem Java. Obrigado por ler "Introdução
a Java: de Alunos para Alunos"!
Bibliografia
[1] CamelCase. 2019. URL: https://pt.wikipedia.org/wiki/CamelCase.
[2] Class PrintStream. 2018. URL: https://docs.oracle.com/javase/7/docs/api/java/io/PrintStream.html.
[3] Class Scanner. 2018. URL: https://docs.oracle.com/javase/7/docs/api/java/
util/Scanner.html.
[4] Class System. 2018. URL: https://docs.oracle.com/javase/7/docs/api/java/
lang/System.html#out.
[5] Como usar ArrayList em Java: principais métodos. URL: http://www.javaprogressivo.
net/2012/09/como-usar-arraylist-em-java.html.
[6] Encapsulamento, Polimorfismo, Herança em Java. URL: https://www.devmedia.com.
br/encapsulamento-polimorfismo-heranca-em-java/12991.
[7] Encapsulation in Java. 2018. URL: https://www.geeksforgeeks.org/encapsulation-
in-java/.
[8] Herança, conceitos e o problema diamante - Parte I. URL: https://www.devmedia.com.
br/heranca-conceitos-e-o-problema-diamante-parte-i/2697.
[9] Interfaces: Java e Orientação a Objetos. URL: https://www.caelum.com.br/apostila-
java-orientacao-objetos/interfaces/#interfaces.
[10] Java ArrayList. URL: https://www.w3schools.com/java/java_arraylist.asp.
[11] Multiple inheritance. 2019. URL: https://en.wikipedia.org/wiki/Multiple_
inheritance.
[12] Teste de unidade. 2019. URL: https://pt.wikipedia.org/wiki/Teste_de_unidade.
[13] Unidade, integração ou sistema? Qual teste fazer? 2014. URL: https://blog.caelum.
com.br/unidade-integracao-ou-sistema-qual-teste-fazer/.
	1 Introdução
	2 Primeiros Passos
	2.1 Criando um Projeto no Eclipse
	2.2 Primeiro Código em Java
	3 Orientação a Objetos
	3.1 O que é?
	3.2 Classes
	3.2.1 Atributos
	3.2.2 Construtor
	3.2.3 Métodos
	3.2.4 Métodos Acessores Get e Set
	3.2.5 Escopo
	3.2.6 Static/Final
	3.3 Atributos de Visibilidade
	3.4 Encapsulamento
	3.5 Objetos
	3.6 Herança
	3.7 Sobrecarga/Sobrescrita
	3.8 Polimorfismo
	3.9 Herança Múltipla
	4 Java
	4.1 Declaração de Variáveis
	4.2 Leitura e Escrita
	4.2.1 Scanner
	4.2.2 System.out
	4.3 String
	4.4 If/Else
	4.5 Estruturas de Repetição
	4.5.1 While/Do While
	4.5.2 For
	4.5.3 Enhanced For
	4.6 Switch
	4.7 Tipos por Referência
	4.8 Exceções
	4.9 Cast
	4.10 Interfaces
	5 Conclusão
	Bibliografia

Mais conteúdos dessa disciplina