este material foi elaborado por edmar welington oliveira,...

119
1

Upload: others

Post on 02-Jan-2021

2 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

1

Page 2: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

2

Direitos: Esta obra foi disponibilizada sob uma Licença Creative Commons Atribuição Uso não-comercial 3.0 Brasil.

Direitos de distribuição e publicação: CAPES/MEC, conforme Parágrafo Único, do Artigo 5º, da Resolução CD/FNDE nº 24 de 04 de Junho de 2008.

Universidade Federal de Juiz de Fora Reitor: Henrique Duque de Miranda Chaves Filho Instituto de Ciências Exatas Diretor: Rubens de Oliveira Departamento de Ciência da Computação Chefe: Custodio Motta Curso de Licenciatura em Computação Coordenação: Fernanda Claudia Alves Campos

Organização Edmar Welington Oliveira

Comissão Editorial

Eduardo Barrére Fernanda Claudia Alves Campos

Revisão Gramatical Hortência Cezar Pinto

Editoração Eletrônica

Eduardo Barrére

Oliveira, Edmar W. Linguagem de Programação II / Edmar Welington Oliveira – 2013. 119 f. : il.

Material Didático — Curso de Licenciatura em Computação da

Universidade Federal de Juiz de Fora, Juiz de Fora, 2012. 1. Educação à Distância. 2. Linguagem de Programação. 3.

Orientação a Objetos. 4. Java. I. Título.

Page 3: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

3

Apresentação

Este material foi elaborado por Edmar Welington Oliveira,

Professor Efetivo do Departamento de Ciência da Computação

da Universidade Federal de Juiz de Fora (UFJF). Conforme

proposta para a disciplina, o material didático aborda a

Orientação a Objetos, seus conceitos e aplicações. A

linguagem utilizada para exemplificar o uso da Orientação a

Objetos é Java.

Sucesso e bom aprendizado!!!

Prof. Edmar Welington Oliveira.

Page 4: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

4

Iconografia Conheça os diversos ícones utilizados nos materiais didáticos desenvolvidos pelos professores e tutores do curso de Licenciatura em Computação – DCC/UFJF:

Pesquise.

Exercícios.

Material complementar (texto, vídeo, etc.) disponível na Internet.

Leitura Complementar.

Comentário do Autor.

Tome nota.

Conclusão ou síntese de conteúdo.

Fique atento.

Page 5: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

5

Sumário

1. Orientação a Objetos .................................................................................... 6 1.1. Orientação a Objetos ............................................................................ 6 1.2. Vantagens da Orientação a Objetos ..................................................... 7 1.3. Surgimento da Orientação a Objetos .................................................... 8 1.3. Abordagem Estruturada ...................................................................... 10 1.4. Limitação da Abordagem Estruturada ................................................. 12 1.5. Paralelo entre Estruturada e a OO ...................................................... 25

2. Objetos......................................................................................................... 26 2.1. Objeto ................................................................................................. 26 2.2. Estado de um Objeto .......................................................................... 27 2.3. Comportamento de um Objeto ............................................................ 28 2.4. Identidade de um Objeto ..................................................................... 28

3. Classes ........................................................................................................ 29 3.1. Paralelo entre Estruturada e a OO ...................................................... 29 3.2. Identificação de Classes ..................................................................... 30 3.3. Representação de Classes em UML .................................................. 31 3.4. Objetos – Instâncias de Classes ......................................................... 32 3.5. Codificação de Classes em Linguagem Java ..................................... 33 3.6. Composição das Classes ................................................................... 34 3.6.1 – Atributos........................................................... ...............................35 3.6.2 – Métodos.................. ........................................................................36 3.7. Instanciação de Objetos de Java ........................................................ 39 3.8. Objetos que Instanciam Objetos ......................................................... 41 3.9. Mensagens ......................................................................................... 42 3.10. Chamadas de Método – Interna e Externa ....................................... 45 3.11. Variável de Classe ............................................................................ 47

4. Construtores ............................................................................................... 55 4.1. Construtores ....................................................................................... 55 4.2. Construtores Sobrecarregados ........................................................... 65 4.2. Referência THIS em Construtores ...................................................... 67

5. Pacotes ........................................................................................................ 72 5.1. Pacotes ............................................................................................... 72 5.2. Importação Implícita e Explícita .......................................................... 82

6. Visibilidade .................................................................................................. 86 6.1. Visibilidade .......................................................................................... 86 6.2. Métodos Públicos e Privados .............................................................. 96

7. Encapsulamento ......................................................................................... 99 7.1. Encapsulamento ................................................................................. 99 7.2. Utilizando Encapsulamento .............................................................. 100 7.3. Métodos de Acesso e Modificação ................................................... 102

Apêndice A - Introdução à Tecnologia Java................................................... 109 A.1. Máquina Virtual................................................................................. 110 A.2. Processo de Compilação e Interpretação ......................................... 112 A.3. Execução de Programas - Exemplo ................................................. 114

Apêndice B - Método Main................................................................................ 116 B.1. Método main ..................................................................................... 117

Page 6: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

6

1. Orientação a Objetos A construção de uma solução computacional consiste no mapeamento do

problema a ser resolvido no mundo real - chamado de Espaço do Problema - em

um modelo de solução no meio computacional - chamado de Espaço de

Soluções. À distância existente entre o mundo real e o modelo abstrato

construído, convencionou-se chamar de gap semântico. Obviamente, quanto

menor o mesmo, mais rapidamente serão construídas soluções para o problema.

Sob esse contexto, percebe-se que o gap semântico representa a área de

atuação da Engenharia de Software. Diversas técnicas e modelos têm sido

propostos ao longo dos anos, para as várias fazes do processo de

desenvolvimento de sistemas, buscando minimizá-lo. A Orientação a Objetos

(OO) é um dos paradigmas existentes para apoiar o desenvolvimento de sistemas

de software que busca fornecer meios para se diminuir o gap semântico. A

computação é parte da sociedade moderna. Em termos de hardware e software, a

evolução é constante, atingindo níveis altos. Pode-se dizer que grande parte dos

problemas do mundo real que precisam ser mapeados em sistemas

computacionais – particularmente, sistemas de softwares – possuem as

tecnologias necessárias. Assim, a preocupação passa a ser como resolver os

problemas da forma mais eficiente possível. Orientação a Objetos está entre as

formas mais eficientes para se desenvolver sistemas computacionais grandes e

complexos. Aprender a raciocinar em OO, bem como utilizar corretamente suas

técnicas e conceitos, não é trivial. Contudo, pode-se afirmar que é essencial para

qualquer profissional relacionado à área de desenvolvimento de sistemas.

1.1. Orientação a Objetos

A Orientação a Objetos (OO) pode ser definida como um paradigma de

desenvolvimento de software que fornece meios para representar os elementos

existentes no espaço do problema. Esses elementos, bem como sua

contrapartida no espaço de programação, são referidos como “objetos”. Dessa

forma, torna-se possível descrever o problema em termos análogos àqueles que

seriam utilizados para descrever o problema em linguagem natural. É claro que,

Page 7: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

7

ainda, existe uma conexão com o computador, visto que este será o responsável

por resolver o problema em última instância. Portanto, em termos práticos, a

proposta da OO é possibilitar representar situações do mundo real nos sistemas

computacionais o mais fielmente possível. Isto é possível, visto que o mundo

pode ser visto como um conjunto de entidades que se relacionam umas com as

outras. Portanto, a OO considera os sistemas computacionais como uma coleção

de entidades que se relacionam.

Orientação a Objetos Paradigma de análise, projeto e programação de sistemas computacionais baseados na composição e interação entre diversas unidades de software chamadas objetos.

Os fundadores da Orientação a Objetos formularam a chamada analogia

biológica, na qual se imaginou como seria um sistema de software que

funcionasse como um “ser vivo” - onde cada “célula” interage com outras, através

do envio de mensagens, para atingir um objetivo comum. De forma mais ampla,

pensou-se em se construir um sistema de software a partir de agentes

autônomos, que interagem entre si. Os princípios da OO estabeleciam, dentre

outros, que qualquer coisa podia ser vista como um agente, que agentes

realizavam tarefas através de requisição a outros agentes e que cada agente

pertencia a um conjunto (uma categorização ou agrupamento de agentes

similares). Essas premissas iniciais nortearam a definição dos vários conceitos

que forma o paradigma OO.

1.2. Vantagens da Orientação a Objetos

Em termos de vantagens, podemos citar que OO reduz o gap semântico.

De fato, se o mundo real é composto por agentes (objetos) que interagem entre si

para a realização de tarefas, o mesmo ocorre com o mundo computacional,

facilitando a compreensão dos problemas e a solução dos mesmos. Além disso,

verifica-se que OO centraliza funções e dados em uma única unidade de

software, o objeto; permite a modularização (divisão do sistema em várias partes

Page 8: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

8

distintas e tão independentes quanto possível); facilita a manutenção dos

sistemas, já que trata da separação de dados e funções, além de promover o

ocultamento de informações, provendo segurança e, igualmente, facilitando a

manutenção. De certa forma, OO permite aos sistemas assumirem um maior grau

de organização e simplicidade – favorecendo, direta e indiretamente, o reuso de

código, as alterações no código e a não propagação de erros em função destas, a

extensão rápida do sistema, o controle da complexidade, etc. OO, também,

permite melhor entendimento do domínio do problema, uma vez que buscamos

enxergar as entidades desse domínio - bem como suas características,

relacionamentos e ações - para criar agentes de software que representam tais

entidades. Sob o aspecto do desenvolvimento em si, podemos obter

independência de implementação até estágios mais avançados. Isso quer dizer

que não começamos um sistema pela codificação, mas pela análise do domínio e

da solução. A codificação, portanto, passa a ser uma das etapas finais e

complementares do desenvolvimento de sistemas. Além disso, podemos dizer

que sistemas OO, quando corretamente desenvolvidos sobre os preceitos do

paradigma, são mais estáveis, de melhor qualidade e mais propensos a

mudanças.

1.3. Surgimento da Orientação a Objetos

No início da década de 70, os computadores eram utilizados somente por

grandes empresas, devido ao seu alto valor comercial. Entretanto, a queda

constante desse valor e a consequente difusão desses equipamentos fizeram

crescer a demanda por software. O interessante é notar que as técnicas de

desenvolvimento de sistemas, até então, eram insuficientes para contornar os

problemas existentes e inerentes ao processo de construção de softwares,

principalmente quando produzidos em larga escala, como era exigido. De fato,

pouco se possuía em termos de técnicas que fossem realmente viáveis e que

favorecessem este processo. Foi nesse contexto inicial que surgiu a programação

estruturada, seguida pelo conceito de desenvolvimento estruturado de sistemas.

Neste, pregava-se que para resolver um problema, o analista deveria analisá-lo

separadamente dos demais (isto é, abstrair dos detalhes) – princípio da

Page 9: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

9

abstração. Além disso, o analista deveria seguir um caminho rigoroso e metódico

para solucionar um problema – princípio da formalidade. Considerava-se, ainda,

que o analista deveria dividir o problema em partes menores, independentes e

com possibilidade de serem simples de se entender e solucionar – princípio da

divisão e conquista. Por fim, o analista deveria organizar os componentes da

solução na forma de uma árvore, com estrutura hierárquica. O sistema seria,

então, entendido e construído nível a nível, onde cada nível acrescentaria mais

detalhes. Essas técnicas tiveram rápida disseminação, sendo amplamente

adotadas. Entretanto, as aplicações tornavam-se cada vez mais complexas,

exigindo grande interação com o usuário, uso de interfaces gráficas, necessidade

crescente de alteração e expansão, interação com outros sistemas (possibilitando

troca de dados), portabilidade para diversas plataformas e sistemas operacionais,

processamento paralelo, etc. As técnicas oferecidas pela programação

estruturada superavam, com certa dificuldade, as complexidades no

desenvolvimento de sistemas com tais características. O problema é que

evolução é permanente, contribuindo para o aumento da complexidade.

Demandavam-se, portanto, novas técnicas para construção de sistemas. Foi

nesse contexto que as atenções se voltaram para a Orientação a Objetos. Devido

às suas características, OO permitia contornar a complexidade crescente das

novas aplicações, reduzindo as dificuldades de desenvolvimento. De fato, as

linguagens do analista e do usuário passaram a ser semelhantes, por se referirem

a objetos do mundo real. Além disso, tornava-se possível utilizar um mesmo

objeto em diferentes sistemas, aumentando a produtividade do processo. De fato,

a metodologia de objetos oferecia uma solução alternativa para o

desenvolvimento de sistemas, e significava uma grande evolução se comparada à

programação estruturada. Era uma evolução, pois apesar de OO utilizar os

mesmos princípios da abordagem estrutura (abstração, hierarquização e

decomposição), ela acrescentava novos e poderosos conceitos: objetos, classes,

herança, etc. Podemos dizer que o surgimento de OO foi, então, motivado pelo

aumento na complexidade dos sistemas, bem como a necessidade de se

organizar o desenvolvimento dos mesmos. De fato, quanto mais complexos os

sistemas se tornam, mais complexo se torna seu desenvolvimento, manutenção e

extensão.

Page 10: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

10

1.3. Abordagem Estruturada

O paradigma estruturado adota uma visão de desenvolvimento baseada

em um modelo de entrada e saída. Os dados são considerados separadamente

das funções que os transformam e a decomposição funcional é usada

intensamente. É clara a distinção entre funções e dados. Funções, a princípio,

são ativas e possuem comportamento, enquanto dados são repositórios passivos

de informação, afetados pelas funções. A figura a seguir representa a relação

entre dados e funções em um sistema estruturado.

Figura 1.1 – Representação do paradigma estruturado

Um sistema desenvolvido usando abordagem estruturada, em geral, é

difícil de ser mantido. A ênfase na construção desses sistemas está na

construção de algoritmos, com aplicação de refinamentos sucessivos. Os

subproblemas são codificados como unidades denominadas procedimentos ou

funções. Além disso, percebe-se um forte uso de decomposição funcional. Os

problemas com esse tipo de desenvolvimento são vários. Programas grandes

tornam-se complexos e difíceis de entender e manter. Existe uma dificuldade

natural em se modelar problemas do mundo real com enfoque em algoritmos. De

fato, é mais fácil e compreensível simular o funcionamento de sistemas

complexos quando o foco está em suas partes constituintes (bem como suas

relações) do que em seus algoritmos. Por exemplo, um veículo é entendido de

forma mais clara em termos de suas peças e das relações entre elas do que em

função dos algoritmos que o fazem funcionar. Além disso, linguagens ditas

procedimentais não oferecem facilidades para a criação de novos tipos de dados

Page 11: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

11

que possam funcionar como os tipos primitivos. Apesar da inviabilidade de sua

utilização na construção de sistemas complexos, o paradigma estruturado, como

dito, propôs características – tais como abstração e modularização – que

nortearam a estruturação de outras técnicas de desenvolvimento. A figura a

seguir ilustra a modularização em um código estruturado.

Figura 1.2 – Algoritmos modularizado

No paradigma estruturado, modularização é uma técnica de desenvolver

algoritmos através de refinamentos sucessivos, podendo-se fazer uso de

módulos. Um módulo é um conjunto de comandos que constitui uma parte de um

algoritmo principal e que possui uma tarefa bem definida e independente em

relação ao resto do algoritmo. Para inserir módulos em um algoritmo, são

utilizados procedimentos e/ou funções. As ações dos procedimentos e funções

estão hierarquicamente subordinadas a um algoritmo principal, geralmente

chamado módulo principal. Além disso, pode haver vários outros procedimentos

ou funções dentro de um procedimento ou uma função. A figura a seguir ilustra a

estrutura interna de um algoritmo estruturado

Figura 1.3 – Exemplo interno de algoritmo modularizado

Page 12: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

12

Apesar de utilizar conceitos e técnicas que ajudam no desenvolvimento de

sistemas grandes e complexos, a abordagem estruturada, em si, não consegue

atender a construção desse tipo de sistema. O que se observa é uma limitação na

possibilidade de utilização desses conceitos. É possível, por exemplo, pensar na

modularização até certo nível, a partir do qual não se pode mais avançar. Assim,

não se consegue atender às demandas dos sistemas. A seção seguinte

apresenta um exemplo que ilustra a limitação da abordagem estruturada.

1.4. Limitação da Abordagem Estruturada

Considere o código da figura 1.4, em linguagem de programação C. Esse

pequeno programa em C apresenta a função main (linha 3), dentro da qual são

declaradas duas variáveis inteiras (linhas 5 e 6), uma estrutura de repetição (linha

8) e uma instrução de retorno (linha 14).

Figura 1.4 – Programa exemplo em C não modularizado

O objetivo desse programa é imprimir o número total de combinações para

um número X de bits. Realizando um teste de mesa, temos os resultados

ilustrados na tabela 1.1. A primeira coluna representa o número de bits; a

segunda, o número total de combinações. Ambas representam o que será

impresso pelo programa. A terceira coluna ilustra as combinações possíveis,

apenas para conferência. Essas combinações não são impressas pelo programa.

Page 13: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

13

Tabela 1.1 – Teste de mesa

Pela tabela 1.1, podemos observar que para 1 bit, temos 2 combinações

possíveis. Para 2 bits, esse número sobre para 4. Para 3 bits, obtemos 8

combinações. Embora o programa seja simples e com poucas linhas de código, é

possível modularizá-lo. A figura a seguir ilustra a tela com o resultado da

execução do programa.

Figura 1.5 – Resultado da execução do programa

A figura 1.6 apresenta a primeira tentativa de modularização do programa

da figura 1.4. Observe que mantivemos a função main (linha 16), embora

ligeiramente alterada. Em relação ao código da figura 1.4, criamos a função

combinar (linha 10) e o procedimento inicializar (linha 5). Além disso, criamos uma

variável global: combinações (linha 3). A função combinar multiplica, por 2, o valor

atual da variável combinacoes e atualiza a própria variável com o resultado, além

de retornar o valor atualizado. O procedimento inicializar inicializa a variável

global combinacoes, com valor 1.

Page 14: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

14

Figura 1.6 – Primeira modularização

Agora, observe o código da função main (linha 16) e note as diferenças

para o código da figura 1.4. Continuamos a declarar a variável inteira i (sem

inicialização), mas deixamos de declarar e inicializar a variável combinacoes. Na

linha 19, especificamos uma chamada ao procedimento inicializar. Dentro da

estrutura de repetição, retiramos a instrução para cálculo das combinações e

mantivemos a instrução para impressão do resultado (linha 23). Observe que

criamos dois módulos novos: a função combinar e o procedimento inicializar.

Cada um possui uma responsabilidade bem definida e realiza uma função bem

específica – exigências do paradigma OO que começamos a observar nesse

código estruturado. Antes, na figura 1.4, tudo (inicialização e cálculo) estava

inserido na função main. Nosso código, então, passa a contar com três módulos:

o módulo principal (main) e os módulos auxiliares (inicializar e combinar). Apesar

deste benefício, há problemas com esse primeiro nível de modularização.

Page 15: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

15

Depender da variável global combinacoes é arquiteturalmente ruim, uma

vez que o programa principal se torna responsável por detalhes de

implementação dos módulos, causando dependência e, consequentemente,

prejudicando a reutilização. Além disso, toda vez que um programa quiser reusar

os módulos (inicializar e/ou combinar), precisará declarar a variável combinacoes

em seu código. O problema é que isso pode causar conflito de variáveis, já que

essa nova variável pode entrar em conflito com uma já existente, forçando

mudanças (em geral, complexas) em nível de código. Precisamos resolver esse

problema, mas sem perder os benefícios da modularização conquistados.

Figura 1.7 – Segunda modularização

Problemas - 1ª Modularização Os módulos são dependentes da variável global combinação.

Page 16: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

16

A figura 1.7 ilustra uma possibilidade de solução. Neste código, inserimos a

variável combinacoes no módulo inicializar (linha 6) e no módulo combinar (linha

12). O objetivo é a remoção da dependência desses módulos com o programa

principal, causa dos problemas de reuso. Ao analisarmos o código da figura 1.7,

percebemos que, de fato, eliminamos a dependência. Entretanto, introduzimos um

problema.

Observe a proposta de solução, apresentada na figura 1.8. Neste código,

voltamos a declarar uma variável global combinacoes (linha 3). A diferença é que

estamos passando a mesma, por parâmetro, para os módulos inicializar e

combinar – utilizando ponteiros.

Figura 1.8 – Terceira modularização

Problemas - 2ª Modularização A variável combinacoes é criada e destruída a cada entrada/saída de cada um dos módulos. Isso impossibilita a continuidade correta do programa, causando um resultado incorreto ao final de sua execução.

Page 17: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

17

A vantagem dessa solução é que a variável combinacoes do programa

principal se torna independente da variável combinacoes dos módulos – isto é, os

nomes podem ser diferentes. Logo, eliminamos o problema de um possível

conflito de variáveis. Observe a figura 1.9 e note que os nomes das variáveis dos

módulos são diferentes entre si e diferentes em relação ao programa principal.

Apesar desse benefício, ainda temos problemas.

De fato, observando o programa 1.9, mesmo alterando os nomes das

variáveis nos módulos, a variável combinacoes ainda precisa ser declarada no

programa principal e, indiretamente, os módulos continuam dependentes deste.

Figura 1.9 – Solução do problema de conflito de nomes

Em termos de modularização estruturada (decomposição funcional), não há

mais o que fazer. Sob a ótica da abordagem estruturada, esgotamos as

possibilidades. Para conseguir continuar com a proposta de modularização, sem

Problemas - 3ª Modularização O programa principal continua precisando declarar e manter a variável combinacoes, o que ainda causa dependência dos módulos.

Page 18: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

18

os problemas observados, precisamos adotar novas estratégias. Uma proposta é

dividir o programa em arquivos diferentes. Em primeiro lugar, criamos um arquivo

de cabeçalho, com a definição dos nomes da função e o do procedimento. A

figura a seguir ilustra o código do mesmo.

Figura 1.10 – Arquivo de cabeçalho

Para esse arquivo, atribuímos o nome “bits.h”. Observe que apenas

definimos as assinaturas da função e do procedimento. Em segundo lugar,

criamos um arquivo para, de fato, inserir os códigos da função e do procedimento.

Nomeamos esse arquivo como “bits.c” (figura 1.11). Note, na linha 1, que estamos

incluindo o arquivo de cabeçalho. A diretiva #include provoca a inclusão de outro

arquivo no programa fonte. Na verdade, o compilador substitui a linha contendo

essa diretiva pelo conteúdo do arquivo indicado. Essa substituição é realizada

antes de o programa ser compilado. Após a inclusão, temos a ligação entre o

arquivo de cabeçalho e o arquivo da figura 1.11. Neste caso em específico, a

linha 1 (figura 1.11) poderia ser retirada e o programa funcionaria sem problemas.

Contudo, é importante mantê-la para sabermos que as funções implementadas no

código da figura 1.11 foram definidas no arquivo de cabeçalho cujo nome é

“bits.h” – e não em outro arquivo que possa existir no nosso programa. Para um

exemplo como o nosso, com apenas 3 arquivos, não ter esse conhecimento

acabando sendo irrelevante. Entretanto, imagine se tivéssemos vários outros

módulos como o arquivo bits.c, além de vários outros arquivos de cabeçalhos.

Muito provavelmente, teríamos dificuldade em, caso necessário, saber qual a

relação entre os vários arquivos. Lembre-se que problemas ocorrem em grandes

e complexos sistemas, e não em pequenos programas. Na linha 3, criamos a

variável global combinacoes. Contudo, esta variável é exclusiva deste módulo

(“bits.c”) e só possui efeito dentro do mesmo. Logo, não temos problemas em

termos de dependência com outros módulos.

Page 19: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

19

Figura 1.11 – Arquivo com função e procedimento

A figura 1.12 ilustra como fica nosso programa principal após as

intervenções anteriores. Na linha 1, incluímos a biblioteca STDIO – como

vínhamos fazendo. Na linha 2, incluímos nosso arquivo de cabeçalho. Assim,

temos a ligação indireta entre o programa principal e o módulo que contém a

função e o procedimento. Isso é essencial, causando erro de compilação se assim

não definirmos. De fato, o programa principal não sabe de onde são a função

(chamada na linha 11) e o procedimento (chamado na linha 7). Neste caso, é

obrigatório realizarmos esta inclusão.

Figura 1.12 – Arquivo principal

Nosso programa está completo e modulado. Temos nosso programa

principal e um módulo. A vantagem é que o módulo é quem controla e mantém o

Page 20: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

20

estado da variável combinacoes. Como dito anteriormente, esta variável é visível

apenas dentro do módulo. O programa principal não possui qualquer

conhecimento da mesma. Isso é bom, pois o módulo pode se reutilizado sem

causar as dependências e eventuais conflitos de nomes. Se outro programador

precisar utilizar esse módulo, basta saber o que o módulo faz: retornar a

quantidade de combinações possíveis para certo número de bits. O programador

não precisa saber como isto é feito, isto é, como é implementado. Logo,

escondemos detalhes de implementação. Em resumo, obtemos modularização e

ocultamento de informações, dois dos principais conceitos utilizados por

paradigmas mais avançados, como OO. Então, ao que parece, conseguimos um

nível alto em termos de modularização, mesmo utilizando a abordagem

estruturada. Como conseguimos para este exemplo, provavelmente

conseguiríamos para programas maiores. Contudo, apesar de obtermos bom

nível de organização do código, além de incorporar caraterísticas essenciais para

um bom desenvolvimento de sistemas, ainda temos problemas. Na verdade,

limitações. Lembre-se que o paradigma estruturado foi “substituído” não pelos

conceitos e técnicas que possuía, mas por suas limitações em atender às

demandas dos novos sistemas. Entre as várias demandas, havia a questão do

processamento paralelo. Embora o programa da figura 1.12, com toda a

modularização, funcione perfeitamente, temos problemas se quisermos realizar

mais de um cálculo de combinações ao mesmo tempo. Embora não seja

impossível, a solução torna o código complexo. Se há complexidade para este

simples programa, imagine como pode ser em programas que, por natureza, já

são complexos.

Foi devido às limitações do paradigma estruturado que outras abordagens

para construção de sistemas, como a Orientação a Objetos (OO), foram

adotadas. Usando os conceitos de OO, vamos remodelar o exemplo anterior e

Problemas - 4ª Modularização O módulo funciona apenas para uma instância de cálculo de combinações, não sendo viável, devido à complexidade, a realização de dois ou mais cálculos em paralelo – isto é, ao mesmo tempo.

Page 21: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

21

mostrar que o problema de calculo em paralelo pode ser resolvido. Observe o

código da figura a seguir.

Figura 1.13 – Arquivo principal

Estamos utilizando linguagem de programação Java, uma das várias

Orientadas a Objetos. Em Java, o conceito de módulo denomina-se classe (do

inglês, class). Basicamente, uma classe corresponde ao bloco de programação (e

desenvolvimento) fundamental do paradigma OO. Não iremos entrar em detalhes

sobre esse conceito ou mesmo a sintaxe do código acima. Isso será visto em

unidades posteriores. O objetivo, agora, é apenas traçar uma comparação com o

programa em C, modularizado. Na linha 1, temos a indicação da nossa classe,

que possui o mesmo nome (bits) do nosso módulo em C. Por padrão OO, apenas

definimos a primeira letra como maiúscula. Note que os códigos da nossa classe

Java e do nosso módulo C são idênticos, salvo alguns termos específicos da

linguagem Java (por exemplo, private e public). Observe que temos a mesma

função combinar (linha 9) e o mesmo procedimento inicializar (linha 5). Note,

ainda, que as implementações de ambos, em nossa classe, são idênticas às do

módulo C. Além dessa classe, precisamos especificar nosso módulo principal.

Observe a figura a seguir.

Page 22: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

22

Figura 1.14 – Classe principal

Essa é a nossa classe Principal, equivalente ao módulo principal do

programa em C. Ao contrário deste, nomeamos a classe como Principal e não

“main”, uma vez que main é uma palavra-chave na linguagem Java. Note, na linha

3, que especificamos a função main. As linhas 8 e 10 são idênticas,

respectivamente, às linhas 6 e 9 do código da figura 1.12. A linha 11 é quase

idêntica à linha 11 da figura 1.12, salvo pela forma de chamada à função

combinar. As demais diferenças nessa linha são pontuais, em virtude das

diferenças de sintaxe entre as linguagens Java e C. Já a linha 7 é, também,

quase idêntica à linha 7 da figura 1.12. A diferença está na forma de chamada ao

procedimento inicializar. A real diferença entre os dois códigos se resume à linha

5 da figura 1.14. Sem discutir os detalhes, estamos criando uma instância

(denominada “i1”) da classe Bits. A partir dessa instância, podemos chamar o

procedimento inicializar (linha 7) e a função combinar (linha 11). A chamada é

realizada especificando-se o nome da instância, seguida de um ponto (.) e do

nome do procedimento (ou função). Então, note que a chamada de ambos está

ocorrendo apenas para a instância i1. Executando o programa, obtemos o

resultado ilustrado na figura a seguir.

Figura 1.15 – Resultado da execução do programa Java

Page 23: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

23

Sabemos que o programa funciona. A questão, então, é possibilitar que

múltiplas instâncias de cálculo executem ao mesmo tempo. Da mesma forma que

criamos a instância i1, podemos criar quantas outras forem necessárias, no

mesmo código. Uma vez criadas, podemos fazê-las chamar tanto a função quanto

o procedimento. Note que essas instâncias são instâncias do módulo “Bits”.

Observe a figura a seguir.

Figura 1.16 – Execução de múltiplas instâncias

Nas linhas 6 e 7, estamos criando mais duas instâncias da classe (módulo)

Bits, i2 e i3. Nas linhas 10 e 11, estamos realizando a chamada ao procedimento

inicializar para ambas. Já nas linhas 17 e 18, estamos imprimindo o resultado da

chamada à função combinar, para ambas as instâncias i2 e i3. A instância i1 foi

mantida. Observe, na figura a seguir, o resultado da execução do código 1.16.

Page 24: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

24

Figura 1.17 – Resultado da execução para múltiplas instâncias

Observe que foi impresso o resultado para todas as instâncias ( i1, i2 e i3).

Para 1 bit, temos duas combinações e o resultado foi impresso para as três

instâncias. Para 2 bits, temos 4 combinações e, igualmente, o resultado foi

impresso para as três instâncias. O mesmo ocorre com o resultado ao se

considerar 3 bits. A figura 1.18 apresenta o mesmo resultado, apenas tentando

mostrá-lo separado para as instâncias, Se necessário, podemos criar outras

instâncias e solicitar a execução das mesmas. Portanto, o uso da Orientação a

Objetos resolve o problema de se possibilitar a execução de múltiplas instâncias.

Não existe, aqui, a limitação imposta pela abordagem estruturada. Percebemos,

então, que OO agrega novas técnicas e conceitos aos que já existem no

paradigma estruturado, permitindo a construção de sistemas maiores e mais

complexos.

Figura 1.18 – Separação do resultado

Page 25: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

25

Para finalizar esta primeira unidade, vamos traçar um rápido paralelo de

como a abordagem estruturada e a orientada a objetos norteiam a forma de

pensar dos programadores. Para a primeira, o importante é, basicamente, a

definição das funções que o sistema deve possuir. Já a segunda se baseia na

definição das entidades do mundo real que terão uma representação em nível de

software. Observe na seção abaixo.

1.5. Paralelo entre Estruturada e a OO

Considere um exemplo simples: de construção de um sistema de

biblioteca. Se cogitarmos a construção desse sistema através da abordagem

estruturada, seremos forçados a pensar nas funções que o mesmo deve ter.

Inevitavelmente, consideraríamos funções do tipo: realizar empréstimo, cobrar

multa, realizar devolução, realizar reserva, pesquisas livro (por título, autor, etc.),

etc. Ao pensarmos em OO, nosso foco muda das funções para as entidades que

devem fazer parte do sistema, incluindo os relacionamentos entre elas. Então,

poderíamos considerar as entidades biblioteca, livro, catálogo, bibliotecário, etc.

Iremos perceber, ao longo desta apostila, que as funções necessárias para a

execução do programa não estão “soltas” no código, mas associadas a entidades

especificas. Isso quer dizer que cada entidade que faz parte do sistema possui

suas responsabilidades (funções) muito bem definidas. As funções existem, mas,

em OO, elas estão ligadas a alguma entidade (classe). Veremos que isso facilita,

em muito, o desenvolvimento de sistemas computacionais, principalmente

aqueles considerados grandes e complexos. A figura a seguir ilustra, para nosso

exemplo, a diferença, entre OO e estruturado.

Figura 1.19 – Separação do resultado

Page 26: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

26

2. Objetos A Orientação a Objetos corresponde a um paradigma de desenvolvimento

de sistemas em que se procura representar, computacionalmente, cenários do

mundo real, visando à solução de um problema. Em outras palavras, propõe-se

que entidades do mundo real, bem como suas relações, sejam

computacionalmente representadas. Podemos dizer, ainda, que a OO permite aos

desenvolvedores raciocinar e solucionar problemas em termos de objetos, os

quais estão diretamente associados às coisas reais. Como resultado deste

mapeamento natural, podemos os concentrar nos objetos que compõem o

sistema, em vez de tentarmos vislumbra o sistema como um conjunto de

procedimentos (funções) e dados.

2.1. Objeto

Um objeto, em Orientação a Objetos, é uma unidade de software utilizada

para representar, dentro do contexto computacional, um elemento do mundo real.

Para explicarmos com mais detalhes, considere que nos seja proposto o

desenvolvimento de um sistema de matrículas escolares, com objetivo de

oferecer suporte ao processo de matrícula de alunos em um dado curso. Ao

analisar o mundo real, verificamos que alunos são matriculados em disciplinas, as

quais são ministradas por certos professores. Inicialmente, percebemos a

existência de três entidades: aluno, disciplina e professor. Verificamos que essas

entidades se relacionam da seguinte forma: aluno e professor se relacionam com

disciplina. Além disso, aluno se relaciona com professor, indiretamente (através

da entidade disciplina). Como essas são as nossas entidades principais no

cenário real, elas são fortes candidatas a se tornarem objetos do sistema.

Convêm ressaltarmos, entretanto, que desenvolver sistemas não é tarefa simples,

principalmente quando a complexidade envolvida é alta. Portanto, a definição dos

objetos do sistema demanda experiência e análise cuidadosa. De fato, essa é

uma tarefa em que muitos desenvolvedores, principalmente os iniciantes, falham

– comprometendo a realização de todo um projeto. É comum, ao se pensar em

objetos, considerarmos o seguinte:

Page 27: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

27

• Um objeto é feito de material tangível

• Um objeto é algo que pode ser identificável

• Um objeto possui características

• Um objeto pode realizar ações

A primeira consideração parece ser bastante restritiva. Uma conta

bancária, por exemplo, é uma entidade que existe dentro do contexto de um

banco, assim como as entidades “cliente” e “gerente”. Portanto, ela poderia ser

representada como um objeto do sistema. Acontece que conta bancária não é

algo feito de material tangível. Logo, objetos não são representações apenas de

coisas tangíveis, como uma pessoa ou um carro. Podemos dizer que objetos

podem representar entidades do mundo real que sejam tanto concretas quanto

abstratas. Exemplos de objetos concretos: pessoa, veículo, mamífero, etc.

Exemplos de objetos abstratos: pedido de compra, conta bancária, solicitação de

venda, pedido de reserva de filme, locação de CD, etc. Já para as demais

considerações, podemos dizer que parecem intuitivas. De fato, objetos podem (e

devem) ser (univocamente) identificáveis dentro de um sistema – assim como

deve ser possível, por exemplo, identificar um aluno dentre um conjunto de alunos

em um cenário real. Sobre o fato de objetos executarem ações, basta analisarmos

o mundo real: alunos, por exemplo, podem se matricular em uma disciplina,

realizar um trabalho escolar, assistir a uma aula, etc. Logo, entidades reais

realizam ações. Da mesma forma, ações podem ser executadas por objetos.

Pode-se sacar dinheiro de uma conta bancária – modelada como um objeto em

um sistema computacional bancário. Podemos, com base nessas considerações,

afirmar que objetos, em OO, possuem as seguintes características:

• Objetos possuem estado

• Objetos possuem comportamento

• Objetos possuem identidade

2.2. Estado de um Objeto

Por estado, se entende os valores assumidos pelas propriedades de um

objeto em um dado momento, as quais podem mudar ao longo da vida do objeto.

Em OO, tais propriedades são denominadas atributos. Um objeto conta bancária

Page 28: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

28

pode possuir saldo como característica. Logo, o valor do saldo em um dado

momento determina o estado desse objeto. Assim como no mundo real, o valor

pode ser alterado diversas vezes (para mais ou para menos), enquanto esta conta

existir.

2.3. Comportamento de um Objeto

Por comportamento, se entende as ações que um objeto pode executar.

Em OO, chamamos essas ações de métodos. Um objeto conta bancária pode, por

exemplo, executar ações do tipo: sacar um valor X, depositar um valor Y, imprimir

o nome do dono da conta, imprimir o saldo atual, imprimir o tipo de conta

(corrente, poupança, salário), etc.

2.4. Identidade de um Objeto

Por identidade, se entende a propriedade segundo a qual um objeto pode

ser identificado. Todos os objetos possuem existência própria, ou seja, são

distintos (ainda que seus estados e comportamentos sejam iguais).

Page 29: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

29

3. Classes

A palavra classe vem da taxonomia da biologia. Todos os seres vivos de

uma mesma classe biológica possuem uma série de características e

comportamentos comuns. Da mesma forma, uma classe em Orientação a Objetos

representa um conjunto de objetos com características e comportamentos

comuns. Classes são elementos fundamentais na composição de softwares OO.

Podemos entendê-las como descrições genéricas ou coletivas de entidades do

mundo real. A definição das classes de um sistema deve procurar inspiração nas

entidades do mundo real, visando representá-las computacionalmente. Ao

contrário dos objetos, que representam entidades individualizadas, podemos dizer

que classes são representações de um coletivo de entidades semelhantes.

3.1. Paralelo entre Estruturada e a OO

É comum encontrarmos no mundo real diferentes objetos desempenhando

um mesmo papel. Considere duas cadeiras. Apesar de serem objetos diferentes,

ambas compartilham mesma estrutura e o mesmo comportamento. Se pensarmos

em dois cachorros, observaremos que, da mesma forma, ambos compartilham

mesma estrutura e comportamento. Para quaisquer outras entidades reais, o

mesmo será verificado. Assim, podemos sempre definir um modelo genérico que

descreva a estrutura de objetos de um mesmo tipo. Em Orientação a Objetos,

este modelo é denominado classe. Uma classe é, portanto, uma estrutura que

descreve um conjunto de objetos com as mesmas características (atributos),

comportamentos (métodos) e relacionamentos com outros objetos. É importante

ressaltar que a ênfase do desenvolvimento OO está na especificação de classes,

e não de objetos, como se poderia imaginar pelo nome. Modelamos o mundo real,

em OO, através de um conjunto de classes que se relacionam entre si. Perceba

que uma classe não é um conjunto de objetos, mas a representação da estrutura

de um conjunto de objetos. A questão é que essa estrutura é idêntica para todo e

qualquer objeto de uma classe. Em outras palavras, objetos podem ser

categorizados e uma classe descreve – de maneira abstrata – todos os objetos de

Page 30: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

30

um tipo particular. Imaginando, por exemplo, os alunos envolvidos em um sistema

acadêmico, cada aluno é uma entidade individualizada que, dependendo do

contexto do sistema, seria representada por um objeto. Observando-se esses

objetos e comparando-os, pode-se constatar que o conjunto de suas

características (nome, sexo, idade) e de seus comportamentos é análogo -

embora, obviamente, os valores dos atributos sejam diferentes. A partir da

observação de características e comportamentos comuns a um conjunto de

entidades similares, é possível estabelecermos um modelo genérico para este

coletivo, contento os atributos e comportamentos comuns ao mesmo.

3.2. Identificação de Classes

Não existe um conjunto de conceitos do mundo real que sempre serão

definidos como classes ao construirmos um sistema computacional OO. Cabe ao

desenvolvedor analisar o domínio do problema, as necessidades do sistema a ser

desenvolvido e, então, especificar as classes que o mesmo deve possuir. Logo

abaixo, apresentamos uma lista – que embora não exaustiva – descreve alguns

exemplos de classes. Classes podem ser:

• Entidades Externas: outros sistemas, dispositivos, pessoas, etc.

• Coisas: relatórios, sinais, displays, etc.

• Ocorrências ou eventos: vendas, voos de avião, locação, empréstimo, etc.

• Unidades organizacionais: grupos, times, departamentos, divisões, etc.

• Estruturas: sensores, veículos, etc.

• Locais: locais de carga, locais de entrega, etc.

Como podemos observar pela lista acima, praticamente tudo pode ser

definido como classe. Essa lista é apenas um exemplo de categorias a partir das

quais classes podem ser definidas. A definição das classes necessárias para a

realização de todas as funcionalidades de um sistema envolve um processo

detalhado de estudo e análise do domínio do problema, dos requisitos

(propriedade ou comportamento que o sistema deve atender) do sistema e das

possibilidades de separação dos dados e processos.

Page 31: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

31

3.3. Representação de Classes em UML

A UML (Linguagem de Modelagem Unificada) corresponde a um padrão

para a representação de modelagens utilizadas no desenvolvimento orientado a

objetos. A notação para classes em UML é um retângulo com três

compartimentos, nos quais são representados: identificação da classe, atributos

(características) e métodos (comportamento). A identificação da classe é um

nome para a mesma. É recomendável, sempre que possível, nomear classes de

modo que seja fácil compreender seu significado. Habitualmente, escrevemos o

nome como um substantivo no singular e com a 1ª letra maiúscula. O segundo

compartimento é destinado à especificação das características (atributos). O

terceiro compartimento declara os métodos que a classe possui. A figura a seguir

ilustra a representação genérica UML para uma classe e um exemplo de classe.

Figura 3.1 – Representação de classes em UML

Observe que para a identificação da classe, atribuímos o nome Carro. A

palavra Carro nos oferece uma ideia clara do que a classe representa. Como

atributos, definimos: modelo, placa e potencia. Isso quer dizer que todos os carros

desta classe possuem estes atributos. Como método, apenas o imprimeDados foi

definido. Da mesma forma, todos os carros desta classe possuem o mesmo. Os

símbolos de “+” e “-“ que aparecem antes dos atributos e dos métodos possuem

significado: são os chamados modificadores de acesso - assunto a ser discutido

em unidades posteriores. Os termos String e int – definidos para os atributos –

definem o tipo de dado de cada atributo. Já o termo void determina o tipo de

retorno para o método da classe. A representação de classes para sistemas OO

segue o modelo UML representado na figura 3.1. A especificação e representação

das classes que compõem um sistema OO forma o modelo de classes (diagrama

Page 32: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

32

de classes). A título de exemplificação, a figura a seguir apresenta um diagrama

de classes.

Figura 3.2 – Exemplo de diagrama de classes

Como podemos verificar pela figura acima, além de classes, um diagrama

de classes também especifica os relacionamentos entre as mesmas. Esta

estrutura, como um todo, é parte essencial para o desenvolvimento de qualquer

sistema OO.

3.4. Objetos – Instâncias de Classes

Considerando o mundo real, podemos citar vários exemplos de classes:

funcionários, pessoas, alunos, etc.– cada uma definindo suas próprias

características e comportamentos. Uma classe, portanto, captura a estrutura e

Page 33: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

33

comportamento comum a todos os objetos que ela descreve. Representa um

gabarito para objetos, especificando a forma segundo a qual os mesmos estão

estruturados internamente. Em resumo, uma classe representa um molde para a

criação de objetos, determinando que atributos e métodos os objetos criados a

partir dela possuirão. Ao processo de criação de objetos, dá-se o nome de

instanciação. Portanto, objetos são ditos serem instâncias de classes. A figura a

seguir apresenta um exemplo de classe e a instanciação de objetos a partir da

mesma.

Figura 3.3 – Classe e instâncias

Na figura acima, podemos observar a especificação de uma classe -

Empregado - e de duas instâncias (objetos) da mesma: Empregado A e

Empregado B. A classe Empregado apenas especifica os atributos e métodos. As

instâncias criadas, como ilustrado, possuem os mesmos atributos e métodos, mas

especificando valores distintos para os mesmos. Se outro objeto fosse instanciado

(criado), o mesmo teria os mesmos atributos e métodos especificados em

Empregado. Portanto, como havíamos dito, uma classe responde a um molde

para a criação de objetos.

3.5. Codificação de Classes em Linguagem Java

Para representação de classes, utilizamos a linguagem de modelagem

UML. Já a codificação da mesma pode ser feita por qualquer linguagem de

Page 34: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

34

programação OO. A figura a seguir apresenta a representação UML da classe

Pessoa e seu respectivo código em linguagem de programação Java.

Figura 3.4 – Classe em UML e respectivo código Java

A declaração de classe é feita na linha 1. A palavra-chave public é um

modificador de acesso. Cada declaração de classe contém a palavra-chave class,

seguida, imediatamente, do nome da classe. O corpo da classe é delimitado pelas

chaves de abertura e fechamento (linhas 1 e 12). As linhas 3, 4 e 5 descrevem os

atributos, formados por um modificador de acesso, seu tipo e do nome do atributo.

No nosso exemplo, todos os atributos foram definidos como sendo do tipo String e

com modificador de acesso private. Assim como classes, atributos devem ser

nomeados com substantivos que deixem claro o sentido (significado) do mesmo.

Em seguida, na linha 7, declaramos o método definido na classe Pessoa. Da

mesma forma que os atributos, métodos são definidos por um modificador de

acesso, seu tipo (de retorno) e nome. Para nosso exemplo, o método foi definido

como sendo do tipo String e com modificador public. Por representarem ações,

métodos, habitualmente, são nomeados com verbos. Após o nome do método, há

um abre e fecha parênteses - dentro dos quais podemos especificar valores a

serem recebidos pelo mesmo.

3.6. Composição das Classes

Vimos que uma classe é composta por identificação, atributos e métodos.

Contudo, nem toda classe possui atributos ou métodos. Dependendo da

necessidade, podemos especificar classes que possuam apenas atributos, ou

Page 35: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

35

apenas métodos. Embora pareça não fazer sentido, em casos muito específicos

classes podem ser especificadas sem ambos, visando futura extensão de código.

Atributos armazenam dados que correspondem às características de objetos.

Métodos implementam o comportamento desses objetos. Há, ainda, um terceiro

elemento, chamado de construtor, o qual é utilizado para que cada objeto seja

adequadamente configurado ao ser criado (instanciado).

3.6.1 - Atributos

Para que possamos entender conceitos posteriores, precisamos detalhar

como os valores dos atributos de objetos são gerenciados durante a execução de

um sistema OO. Basicamente, a execução de um sistema OO implica na criação

de vários objetos, cada um possuindo valores específicos para seus atributos. Ao

instanciarmos um objeto, um espaço de memória é usado para o armazenamento

tanto do objeto quanto os valores de seus atributos. A figura a seguir ilustra essa

situação.

Figura 3.5 – Ilustração do armazenamento de atributos

Para a figura acima, estamos considerando um objeto da classe Pessoa,

ilustrada na figura 3.4. A figura acima é ilustrativa, visando prover uma visão

minimalista (em detalhes) de como objetos e atributos são armazenados. Observe

que cada atributo possui um espaço de memória específico, onde seus valores

são armazenados. Dizemos que os atributos são, em código, representados como

variáveis em uma declaração de classe. Estas são comumente chamadas de

atributos. Quando cada objeto de uma classe mantém sua própria cópia de um

atributo, como vimos anteriormente, o atributo que representa o atributo também é

Page 36: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

36

conhecido como uma variável de instância – ou seja, os valores são específicos a

uma única instância (objeto). Esse é um conceito importante, pois veremos, mais

adiante em nosso estudo, que também temos as chamadas variáveis de classes.

Por agora, entenda que cada objeto, uma vez criado, terá um espeço para cada

atributo declarado em sua classe. E a razão para que cada objeto tenha esses

atributos específicos é simples: os valores podem ser alterados, dependendo do

sistema, ao longo da vida do objeto. Considere uma classe Conta, com um

atributo saldo. Agora, considere dois objetos dessa classe, denominados conta1 e

conta2. Suponhamos que conta1 e conta2 tenham, como saldo, respectivamente,

R$1.000,00 e R$2.000,00. Se fosse necessário alterar o valor do saldo de conta1,

essa alteração deve afetar apenas o atributo saldo deste objeto – ou seja, não

deve haver influência com nenhum outro objeto da classe Conta. Alterando o

saldo de conta1 para R$3.000,00, o saldo da conta2 deve permanecer

R$2.000,00. Contudo, isso só é possível se cada atributo - de cada objeto -

possuir um espaço de memória particular, de modo que a alteração em um

desses espaços não afete os demais. Os atributos, em resumo, definem o que um

objeto tem (e não o que ele faz). O que um objeto faz em termos de ações (ou

serviços) é definido pelos métodos.

3.6.2 - Métodos

Dentro de cada classe, também declaramos o que cada objeto da mesma

faz (e como é feito). Pense, novamente, na classe Conta - utilizada para a

explicação de atributos. Sabemos que ela possui saldo como atributo. Logo, todo

e qualquer objeto desta classe também possuirá esse atributo. Agora, considere

que ela tenha um método “sacar” – utilizado para sacar (retirar) dinheiro de uma

conta (ou seja, de um objeto conta). Retirar dinheiro de uma conta significa alterar

o valor corrente do atributo saldo, para menos. Para nosso exemplo, queremos

um método que permita sacar uma determinada quantia e que não devolva

nenhuma informação – isto é, seja definido com tipo de retorno void. Uma

possível implementação para tal método é ilustrada na figura a seguir.

Page 37: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

37

Figura 3.6 – Exemplo de método

Como sabemos, um método é definido por um modificador de acesso, tipo

de retorno e nome – além de valores passados dentro do abre e fecha

parênteses, se necessário. Tudo isso forma a assinatura do método (linha 5). A

palavra chave void significa que quando esse método for executado, nenhuma

informação será devolvida pelo mesmo – ou seja, não existe um retorno. Para que

nosso método possa ser executado, precisamos informar um valor: variável

quantidade, do tipo double (linha5). Chamamos o que vem dentro desses

parênteses como parâmetro ou argumento. Essa variável é uma variável local,

utilizada somente dentro do escopo do método (linhas 5 e 8). Isso significa que

quando o método terminar sua execução, os valores de suas variáveis locais são

perdidos. Dentro do método, estamos declarando uma variável do tipo double

(linha 6) – que, como o argumento, vai morrer ao fim da execução do método. No

momento em que vamos acessar o atributo da classe, utilizamos a palavra-chave

this (linha 6). Essa palavra-chave serve para indicar que estamos acessando um

elemento específico (neste caso, o atributo saldo) da classe - ou seja, indica que

saldo é um atributo da classe e não uma variável local. O que o método realiza,

afinal, é diminuir do valor corrente do atributo saldo o valor definido no parâmetro

quantidade. Após a execução, o valor do atributo é atualizado com o novo saldo

(linha 7). Note que o valor do saldo pode ficar negativo caso o valor definido em

quantidade seja maior que o valor corrente de saldo. Se for necessário um

método para depositar um determinado valor, precisaremos de uma

implementação como a apresentada na figura a seguir.

Page 38: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

38

Figura 3.7 – Exemplo de método sem retorno

Observe, agora, que temos um método denominado depositar em nossa

implementação (linha 10). Este método também recebe um valor como

argumento. Dentro do método, o valor do atributo saldo é atualizado com a soma

entre o valor corrente do mesmo e o valor recebido por parâmetro. Ambos os

métodos não retornam informação após serem executados. A título de

exemplificação, vamos alterar o método depositar, fazendo com que o mesmo

retorne uma informação após a execução.

Figura 3.8 – Exemplo de método com retorno

Para simplificação, omitimos o método sacar. Compare o método depositar

nas duas figuras anteriores (3.7 e 3.8). Podemos verificar que, nessa última, a

assinatura do método mudou. Se antes ela era definida como tendo o tipo de

retorno void, agora ela possui o tipo de retorno String. Isso significa que o método

Page 39: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

39

depositar deve, ao final da sua execução, retornar uma String. Veja o uso da

palavra-chave return (linha 9). Portanto, após sua execução, o método devolve o

texto definido nesta linha. Agora que estamos cientes dos conceitos de métodos e

atributos, vamos nos aprofundar em nosso estudo, destacando como é o

processo de criação de objetos.

3.7. Instanciação de Objetos de Java

Precisamos entender como, em linguagem de programação, ocorre a

instanciação de objetos. A figura a seguir ilustra a instanciação em linguagem

Java.

Figura 3.9 – Instanciação de objetos

Para ilustrar a criação de objetos, criamos a classe Sistema e

implementamos na mesma o método main (no apêndice A, discutimos sobre este

método). Observe a linha 5. Essa linha não cria um objeto. Inserimos essa linha

para que possamos fazer uma comparação com as linhas 7 e 8 – que, de fato,

criam objetos. A linha 5 especifica um trecho de código separado pelo sinal “=”.

Na primeira parte dessa linha, antes desse sinal, temos a especificação de uma

variável chamada valor, do tipo int. Na segunda parte, temos o valor inteiro 10.

Essa linha de código atribui o valor 10 para a variável inteira valor. Agora, observe

atentamente a linha 7. Veja que a estrutura é praticamente a mesma a apresenta

na linha 5. Na primeira parte dessa linha de código, antes do sinal “=”, temos a

especificação da variável conta1, do tipo Conta. Conta é uma classe, previamente

especificada. Ela é o que denominamos tipo de referência. A segunda parte dessa

linha, posterior ao sinal “=”, é a mais importante, pois é ela quem cria o objeto.

Utilizamos a palavra-chave new, seguido do nome da classe e por parênteses. A

Page 40: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

40

linha 7 pode, então, ser definida da seguinte forma: cria-se, com a palavra-chave

new, um objeto de uma classe e atribui-se o mesmo a uma variável do tipo da

classe. A linha 8 realiza o mesmo procedimento, mas criando um objeto diferente,

chamado de conta2. Ao final da execução do método main, temos dois objetos

instanciados, nomeados como conta1 e conta2. Note que se a classe Conta não

tivesse sido criada antes de implementarmos as linhas 7 e 8, teríamos um erro de

compilação. O compilador informaria que não reconhece o tipo de referência

Conta, especificado em ambas as linhas 7 e 8.

Tipo por Referência Os tipos de dados, em Java, estão divididos em duas categorias: tipos primitivos e tipos por referência (às vezes, chamados de tipos não primitivos). Os tipos primitivos são: boolean, char, short, int, long, float e double. Todos os tipos não primitivos são tipos por referência. Portanto, as classes que especificam os tipos de objetos são tipos por referência. String é um tipo por referência.

Os programas utilizam as variáveis por tipo de referência para armazenar

as localizações de objetos na memória do computador. Essas variáveis

referenciam objetos no programa. Logo, considerando o nosso exemplo de código

de instanciação, as variáveis de referência conta1 e conta2 mantém a localização

dos objetos criados com a palavra-chave new. A figura a seguir ilustra melhor

essa questão. Os objetos referenciados podem conter variáveis de instância

(atributos) e métodos.

Figura 3.10 – Referenciação de objetos

Page 41: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

41

Como ilustrado, as variáveis conta1 e conta2 referenciam objetos de Conta,

armazenados na memória. Cada variável referencia o objeto que lhe foi atribuído,

conforme especificado no código da classe Sistema (figura 3.9). As setas

pontilhadas mostram a relação existente entre as variáveis e as posições de

memória.

3.8. Objetos que Instanciam Objetos

Analisando o código da figura 3.9, percebemos que os objetos conta1 e

conta2 foram criados dentro do método main. Contudo, em vários casos, a

instanciação de objetos não ocorre neste método. De fato, objetos podem ser

criados por quaisquer outros objetos. Vamos considerar as implementações das

classes Conta e Sistema, ilustradas, respectivamente, nas figuras 3.6 e 3.9.

Figura 3.11 – Objetos que criam objetos

Observe a figura acima. Note que há um método criarConta (linha 3),

dentro do qual criamos um objeto da classe Conta e atribuímos o mesmo para a

variável conta. Isso significa que quando este método for executado, um objeto de

Conta é criado. Observe que não detalhamos o que será feito com este objeto –

apenas ilustramos que é perfeitamente possível criamos um objeto a partir de

outro objeto. É obvio que deve existir um objeto instanciado da classe Sistema

para que seja possível invocar o método da linha 3. No cenário geral, teremos um

objeto da classe Sistema invocando seu método criarConta para criar um objeto

da classe Conta. A figura a seguir ilustra este cenário.

Page 42: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

42

Figura 3.12 – Objeto criando outro objeto

A figura acima especifica a criação da classe Teste, dentro da qual

definimos o método main (linha3). Na linha 4, instanciamos um objeto da classe

Sistema (definida na figura 3.11). Uma vez instanciado o objeto, podemos realizar

uma chamada para seu método criarConta (linha 5). Quando esse método é

chamado, conforme definido no código da classe Sistema, cria-se um objeto de

Conta. Portanto, o objeto sistema (do tipo Sistema) cria um objeto conta (do tipo

Conta).

3.9. Mensagens

Todo sistema OO funciona com base nas interações entre seus objetos.

Considere o seguinte cenário do mundo real: um aluno precisa realizar o aluguel

de um livro em uma biblioteca. Nesse caso, ele precisa ir à biblioteca para solicitar

o aluguel do livro. Observe que houve uma interação entre entidades – entre

aluno, livro e biblioteca. Entidades reais estão, portanto, sempre interagindo com

o ambiente à sua volta. Se sistemas OO são representações do mundo real – no

qual as entidades desse mundo são representadas, individualmente, através de

objetos –, então faz todo sentido pensar que o sistema funciona com base nas

interações entre os diversos objetos existentes no mesmo. Precisamos pensar,

agora, em como essas interações ocorrem em nível de sistema – ou seja, como

são implementadas. A noção de comportamento incorporado por um objeto é

caraterizado por um conjunto de métodos que podem ser requisitados por outros

objetos. Para que um objeto realize uma tarefa, é necessário enviar a ele uma

mensagem, solicitando a execução de um método específico. Uma mensagem

estimula a ocorrência de alguma ação no objeto que a recebe. Por padrão, as

informações utilizadas na emissão de uma mensagem são: destino (o objeto

Page 43: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

43

receptor), método (a operação que deve ser realizada) e parâmetros (informações

necessárias à execução do método). Vamos analisar, em código Java, como esta

emissão de mensagens ocorre.

Figura 3.13 – Especificação da classe Pessoa

Observe a classe Pessoa, ilustrada na figura acima. Note que ela possui

um atributo nome e um método. Analisando o método, podemos verificar que ele

recebe um parâmetro e altera o valor do atributo da classe para o valor deste

parâmetro. Em outras palavras, ele recebe um nome como argumento e

armazena este nome no atributo nome da classe (especificado no código pela

palavra-chave this). Sabemos, portanto, que se invocarmos este método, ele irá

alterar o valor do atributo nome. Precisamos, agora, saber como realizar a

invocação. Vamos analisar o código abaixo.

Figura 3.14 – Primeiro exemplo de invocação de métodos

Temos a classe Sistema, dentro da qual definimos o método main. Dentro

desse método, há a criação de um objeto de Pessoa (linha 5). A linha 6 é a nossa

fonte de discussão. Nela, temos a variável pessoa, seguida de um operador ponto

(.) e do nome do método de Pessoa que queremos invocar. Note que, na classe

Pessoa, o método precisa receber uma String como argumento. É por esta razão

que especificamos, na linha 6 da figura 3.14, o texto “Novo Nome”. O operador

Page 44: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

44

ponto (.) na linha 6 é utilizado para realizar a invocação do método alterarNome.

Em outras palavras, estamos enviando uma mensagem para o objeto

armazenado na variável pessoa, solicitando que o mesmo execute o método

alterarNome. O objeto, então, executa o método e altera o valor de seu atributo

nome para “Novo Nome”.

Figura 3.15 – Segundo exemplo de invocação de métodos

Observando a figura acima, percebemos que a classe Sistema, agora,

possui um método para cadastrar uma pessoa (linha 3). Esse método cria um

objeto de Pessoa (linha 4) e invoca o já conhecido método alterarNome (linha 5).

Agora, temos um exemplo claro da relação de um objeto chamando outro objeto.

Para que possamos invocar o método cadastrarPessoa, é necessário que

tenhamos instanciado um objeto da classe Sistema. O que temos, então, é um

objeto da classe Sistema invocando um método de outra classe. No nosso

exemplo, o método alterarNome de Pessoa. Como precisamos de um objeto de

Sistema para poder invocar seu método, é óbvio que, em algum lugar do sistema,

deverá haver um código como o ilustrado abaixo.

Figura 3.16 – Terceiro exemplo de invocação de métodos

Page 45: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

45

O código acima cria um objeto da classe Sistema (linha 3) e utiliza o

mesmo para invocar seu método cadastrarPessoa (linha 4). Essa invocação

realiza as ações ilustradas na figura 3.15 – isto é, cria um objeto de Pessoa e

invoca seu método alterarNome. Logo, temos um objeto, sistema, enviando uma

mensagem para que outro objeto, pessoa, execute um método específico.

Portanto, a interação entre objetos, essencial para o funcionamento de qualquer

sistema OO, é realizado por meio de mensagens (ou invocação de métodos).

3.10. Chamadas de Método – Interna e Externa

Observe a figura 3.17. A instrução da linha 6 chama o método alterarNome

do objeto pessoa. Note que estamos realizando isto de dentro do método main

que, por sua vez, está dentro da classe Sistema. Logo, estamos invocando

alterarNome a partir de outra classe. Uma chamada de método para um método

de outro objeto é referida como uma chamada de método externa. A sintaxe, em

linguagem Java, para uma chamada deste tipo é definida conforme a seguir:

objeto.nomeDoMétodo(lista de parâmetros). Esta sintaxe é conhecida como

notação de ponto. Ela consiste no nome de um objeto, um ponto, o nome de um

método e os parâmetros para a chamada. É particularmente importante

utilizarmos o nome do objeto (ou melhor, da variável que referencia o objeto) e

não o nome da classe.

Figura 3.17 – Chamada a método externo

Além da chamada de método externa, temos a chamada de método

interna. Vimos que a chamada de método externo se caracteriza por um método

de um objeto estar sendo invocado externamente por outro objeto. A chamada

Page 46: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

46

interna, por outro lado, se caracteriza pelo fato do método que está sendo

chamado estar na mesma classe que o método chamador – ou seja, o objeto

chama, através de um método X, outro método próprio, digamos Y. Por esta

razão, denomina-se chamada interna. Vejamos um exemplo em código.

Figura 3.18 – Chamada a método interno

Observe o código acima. Temos dois métodos implementados (linhas 5 e

10). O método depositar, além de atualizar o valor do atributo saldo, invoca (linha

7), o método imprimir (linha 10), que apenas imprime um texto informando que a

operação de depósito foi realizada. Uma chamada de método interna é

exatamente isso: um método dentro de uma classe realizando a chamada de

outro método, definido na própria classe. Observe, agora, o código abaixo.

Figura 3.19 – Exemplo de chamada externa e interna

Note que estamos criando um objeto conta (linha 5), do tipo Conta, no

código da figura acima. Em seguida, invocamos o método depositar, passando o

inteiro 1000 como parâmetro (linha 6). Ao invocarmos esse método, o próprio

Page 47: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

47

objeto invocará o método imprimir. Neste exemplo, temos, ao mesmo tempo, uma

chamada externa e interna. A externa ocorre entre as classes Sistema de Conta.

A chamada interna ocorre entre a classe Conta e ela mesma.

3.11. Variável de Classe

Vimos, pela figura 3.5, que cada objeto possui seu próprio espaço de

memória, para si e para seus atributos. Também vimos que cada variável que

representa um atributo de um objeto é conhecida como variável de instância, pois

está relacionada a uma instância em particular. Neste caso, cada objeto possui

uma cópia (analogamente, um espaço de memória) de todos os atributos

definidos para a classe. Isso evita que a alteração no valor de um atributo do

objeto X afete o valor do atributo de mesmo nome do objeto Y. E isso faz sentido,

já que estes atributos estão em espaços de memória diferentes. Contudo, há

casos em que precisamos que um atributo de classe esteja relacionado a todos

os objetos da classe, e não a cada objeto isoladamente. Em outras palavras,

precisamos que certo atributo seja compartilhado por todos os objetos da classe.

Neste caso, temos apenas uma posição de memória referente ao atributo, e esta

posição é compartilhada por todos os objetos. De início, podemos nos questionar

que, com essa estratégia, se um objeto X alterar o valor desse atributo, outro

objeto Y será afetado, já que eles compartilham um mesmo atributo (com uma

única posição de memória). Embora pareça problemática, a ideia é essa mesma.

A proposta é que atributos desse tipo estejam associados à classe

(consequentemente, a todos os seus objetos) e não a cada objeto em particular.

Em linguagem Java, esse atributo especial é denominado atributo estático (do

inglês static) e é chamado de variável de classe. Logo, uma variável static

representa informações de escopo de classe – todos os objetos da classe

compartilham os mesmos dados. Além de poder ser aplicado a atributos, a

palavra-chave static pode, também ser aplicada em métodos. Nesse caso,

teremos métodos estáticos. A vantagem dos métodos estático, em relação aos

demais, é que ele pode ser invocado mesmo que não tenha sido instanciado um

objeto da classe. Em outras palavras, se uma classe X possui um método estático

XPTO, este pode ser invocado sem que seja necessário criar um objeto da classe

X. Lembre-se que métodos não estáticos só podem ser invocados se houver um

Page 48: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

48

objeto instanciado. O método main (apêndice A) é um bom exemplo de método

estático. Observe a figura a seguir, sobre atributos estáticos.

Figura 3.20 – Representação de atributo estático em memória

Para o exemplo da figura acima, estamos considerando uma classe Conta,

com atributos: numero, saldo e limite. Especificamente, definimos limite como

sendo do tipo static. Criamos, então, dois objetos dessa classe Conta. Como já

discutido, cada objeto possui sua própria posição de memória. Os nomes conta1

e conta2 (atribuídos para identificar os dois objetos criados) são referências para

os mesmos. Observe, pelas lupas, que cada objeto possui uma posição para cada

um dos seus atributos nome e saldo. Note, no entanto, que tanto conta1 quanto

conta2 não possuem, em suas respectivas posições de memória, o espaço para o

atributo limite. Se olharmos atentamente, veremos que limite está em um local

separado da memória. Apesar disso, existe uma ligação entre os objetos conta1 e

conta2 para esta posição – eles compartilham a mesma posição de memória

relacionada ao atributo limite. O fato de ambos os objetos não possuírem uma

posição particular para o atributo limite não significa que eles não o possuem. O

objeto conta1 possui numero, saldo e limite como atributos. O objeto conta2

possui numero, saldo e limite como atributos. Como limite é estático, só se define

uma única posição de memória, compartilhada por todos os objetos de conta,

para armazenar seu valor. Convém apenas ressaltarmos que a figura 3.20 é

apenas uma ilustração do conceito – não quer dizer que seja exatamente assim

que estes atributos são gerenciados pelo computador. Agora, observe a figura

abaixo.

Page 49: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

49

Figura 3.21 – Classe Conta

A figura acima representa a classe conta. Note o uso da palavra-chave

static para definir que o atributo limite é estático. Para ilustrar o gerenciamento

dos valores de limite, definimos um método para alterar seu valor e outro método

para capturar o mesmo. Observe o código da figura abaixo.

Figura 3.22 – Exemplo de programa com atributo estático

Page 50: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

50

No código da figura acima, criamos os dois objetos, conta1 e conta2 (linhas

5 e 6). Nas linhas 8 e 9, definimos os valores do atributo limite para ambos, como

sendo 0. Em seguida, nas linhas 11 e 12, solicitamos que estes valores fossem

impressos, apenas para conferência. A figura abaixo ilustra o resultado da

execução deste programa, somente até essa impressão. Observe a mesma.

Figura 3.23 – Resultado parcial da execução

Note que, como solicitado, ambos os valores estão com valor 0. Na linha

14, definimos que o valor do atributo limite de conta1 é 1000. Em seguida, nas

linhas 16 e 17, solicitamos a impressão dos valores do atributo limite sejam

impressos, para ambos os objetos. A figura abaixo ilustra o resultado da execução

deste programa, somente até essa impressão. Observe a mesma.

Figura 3.24 – Resultado parcial da execução

Como solicitado, o valor do atributo limite para o objeto conta1 foi alterado

de 0 para 1000. Contudo, observe o resultado obtido ao imprimir o valor desse

atributo para o objeto conta2. Em vez de imprimir 0, o valor impresso foi 1000. O

curioso é que nosso código não possui instrução – antes da linha 14 – para alterar

o valor de limite do objeto conta2 para 1000. A razão para isto está no fato de

Page 51: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

51

limite estar definido como estático. Como dizemos, um atributo estático é

compartilhado por todos os objetos da classe que o define. Se um objeto alterar o

valor deste atributo, essa alteração será compartilhada por todos os demais

objetos da classe. Continuando a análise do programa, temos que na linha 19,

definimos a instrução para alterar o valor atual do limite da conta2, para 2000. Em

seguida, nas linhas 21 e 12, solicitamos a impressão dos valores do atributo

limite, para ambos os objetos. A figura abaixo ilustra o resultado da execução

deste programa, somente até essa impressão. Observe a mesma.

Figura 3.25 – Resultado final da execução

O valor do limite de conta2 foi alterado para 2000. Contudo, note que o

valor do limite para conta1 também foi alterado para 2000. A alteração realizada

pelo objeto conta2 foi compartilhada com os demais objetos da classe. Este é o

comportamento que se deseja ao se definir um atributo como sendo estático. Para

comparação, suponha que alteramos o atributo limite da classe Conta, de estático

para não estático, e executamos o programa novamente. Observe o resultado

abaixo.

Figura 3.26 – Resultado final da execução com atributo não estático

Page 52: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

52

Observe que o resultado foi diferente, como esperado. Começamos

atribuindo valores 0 para o limite de ambos os objetos. O resultado impresso foi 0.

Em seguida, alteramos o valor de limite de conta1, de 0 para 1000. Solicitamos a

impressão desse atributo para ambos, conta1 e conta2, e o resultado foi 100 para

conta1 e 0 para conta2. Como o atributo deixou de ser estático, e como alteramos

apenas o valor do limite para conta1, o valor para conta2 continua 0. Em seguida,

fizemos a alteração do valor do limite de conta2, do valor atual para 2000. Após a

impressão, obtivemos 100 para conta1 (valor antigo) e 2000 para conta2 (valor

para o qual solicitamos a alteração). Então, perceba que o uso de static afeta, em

muito, o resultado de um programa. Agora que entendemos o uso dos atributos

estáticos, convém verificamos a real utilidade dele. Observe a figura abaixo.

Figura 3.27 – Classe Livro

Suponha uma classe livro, como definida no código da figura acima.

Suponha que ela esteja definida no sistema de uma livraria. Esta livraria oferece

descontos para os livros que vende – razão pela qual temos o atributo desconto

(linha 5). Além deste, definimos os atributos titulo e preco. Nas linhas 7 e 11,

definimos métodos para retornar, respectivamente, os valores de preco e

desconto. Na linha 15, definimos um método para alterar o valor de desconto.

Agora, observe o código abaixo.

Page 53: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

53

Figura 3.28 – Classe Livraria

No código da classe Livraria, definimos um método (linha 5) para calcular o

valor final de um livro. Este método recebe um objeto livro como parâmetro e

invoca os métodos para obter valor do livro e valor do desconto. Da forma como

implementada a classe Livro, cada livro possui um desconto particular,

relacionado apenas a si. Isso quer dizer que um objeto livro1 pode ter desconto

de R$10,00 e um livro2 pode ter desconto de R$50,00. Suponha que o dono da

livraria não queira oferecer descontos diferentes para cada livro – ou seja, deseja-

se um valor de desconto único para todos os livros. A questão, então, é como

garantir que todos os livros tenham o mesmo desconto. A resposta é simples: uso

de static. Basta alterarmos o atributo desconto para estático. Assim, toda vez que

ele precisar aumentar ou diminuir valor do desconto, basta fazê-lo para apenas

um objeto – os demais (por compartilharem uma única posição de memória) terão

os valores de seus descontos alterados automaticamente. Sem o uso do atributo

estático e, ainda, adotando a mesma política de descontos, teríamos que utilizar o

método da linha 15 (figura 3.27) para cada objeto livro do sistema – ou seja, se

tivermos um livro1 e um livro2, e se quisermos alterar o valor do desconto,

teríamos que chamar o método da linha 15 para livro1 e, depois, para o livro 2.

Imagine fazer isto para uma biblioteca com 1000 livros. É para cenários assim que

a palavra-chave static é útil.

Page 54: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

54

Fique Atento Assim como tipos primitivos, objetos também podem ser passados como parâmetros. A figura 3.28 apresenta um exemplo. Ao passarmos um tipo primitivo, declaramos o tipo e, em seguida, a variável local que será usada dentro do método.

Exemplo: public void calcular(int a); A passagem de objetos por parâmetro acontece da mesma forma. Primeiro, declara-se o tipo de objeto que está sendo passado – ou seja, o nome da classe a partir do qual o mesmo foi instanciado. Em seguida, define-se um nome de variável local, que será utilizada dentro do escopo do método. Para o nome da variável, pode-se adotar qualquer termo (ou conjunto de termos) permitido pela linguagem. Exemplo: public void calcular(Livro livro) Onde: • Livro (maiúsculo) é o nome da classe (ou seja, o tipo do objeto) • livro (minúsculo) é o nome da variável local do método

Page 55: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

55

4. Construtores

Toda classe, além de atributos e métodos (ambos opcionais) possuem o

que chamamos de construtores. Estes não são opcionais. Logo, toda e qualquer

classe possui construtor (pelo menos um, mas podendo ter mais). Os construtores

de uma classe possuem um papel especial a cumprir: é sua responsabilidade

colocar cada objeto da classe, quando ele está sendo criado, em um estado

previamente definido para ser utilizado. É o que chamamos em OO de

inicialização. O construtor inicializa o objeto para um estado adequado de uso.

4.1. Construtores

É importante citarmos que um construtor pode (não é obrigatório) receber

parâmetros, os quais serão utilizados para inicializar seus atributos. Vejamos em

código.

Figura 4.1 – Exemplo de método construtor

Observe a classe Pessoa, definida na figura acima. Podemos verificar que

esta classe possui dois atributos, nome e CPF (linhas 3 e 4). Agora, analise com

cuidado o código na linha 6. Este é o construtor da classe Pessoa. Um dos

recursos característicos dos construtores é que eles possuem o mesmo nome da

classe onde estão definidos. Portanto, a linha 6 possui um modificador de acesso

public, seguido do nome da classe, Pessoa, e por dois parâmetros, nome e CPF.

Podemos notar que um construtor é bem parecido com um método. A diferença é

que não existe um tipo de retorno (por exemplo, void). Essa é a assinatura de

Page 56: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

56

construtores e deve ser sempre seguida. Obrigatoriamente, o modificador de

acesso deve ser public – para que possa ser invocado por classes externas. As

linhas 7 e 8 apenas atualizam os valores dos atributos nome e saldo com os

valores dos argumentos nome e saldo. O uso da palavra-chave this, novamente,

foi realizado para indicar que nome e CPF, lado esquerdo do símbolo “=”, indicam

atributos e não variáveis locais. Por este exemplo, já começamos a entender

outro uso da palavra this (além de referencia membros da própria classe): evitar

ambiguidade de nomes entre atributos e variáveis locais. Agora, observe o código

abaixo.

Figura 4.2 – Instanciação de objetos com construtor

Podemos notar que, em virtude do construtor da classe Pessoa (figura 4.1),

toda vez que quisermos criar um objeto dessa classe, obrigatoriamente, teremos

que passar duas strings como parâmetro. Se tentarmos criar um objeto da classe

Pessoa sem especificar a passagem dessas duas strings como parâmetro,

teremos um erro de compilação. Algo curioso sobre os construtores é o fato de

podermos escolher os atributos que serão inicializados – em outras palavras, não

há qualquer obrigatoriedade de inicializarmos, com valores específicos, todos os

atributos. Observe o código abaixo – o qual altera o construtor da classe Pessoa

(figura 4.1).

Figura 4.3 – Exemplo de método construtor

Page 57: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

57

Observe o construtor da classe acima. Podemos perceber que, agora,

apenas um dos atributos da classe Pessoal, nome, está sendo inicializado pelo

construtor. Ao tentarmos instanciar um objeto desta classe Pessoa, apenas um

atributo do tipo string será cobrado. A figura a seguir ilustra este cenário.

Figura 4.4 – Instanciação de objetos com construtor

Precisamos nos perguntar o que acontece com o atributo CPF, já que o

mesmo não está sendo inicializado pelo construtor. Vamos discutir mais sobre

construtores antes de respondermos esta questão. A instanciação de qualquer

objeto requer a existência de um construtor. No caso de Java, a instanciação é

realizada através do uso da palavra-chave new – conforme vimos nos exemplos

sobre criação de objetos. Analise o código da figura 4.5. Observe que estamos

realizando a instanciação de um objeto da classe Aluno (linha 4). A instrução da

linha 4, primeiro, utiliza a palavra-chave new para criar um objeto da classe Aluno.

Os parênteses vazios depois de new Aluno indicam uma chamada ao construtor

da classe. Observe que, nesse caso, não há parâmetro sendo passado. O que

está sendo realizado aqui é uma chamada ao construtor padrão da classe Aluno.

Figura 4.5 – Chamada ao construtor de classe

Vamos analisar a classe Aluno, ilustrada na figura 4.6. A princípio,

poderíamos dizer que a classe Aluno não possui um construtor. Contudo, ela o

possui. Por padrão, o compilador fornece um construtor-padrão sem parâmetros

Page 58: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

58

para toda e qualquer classe que não inclua, explicitamente, um construtor em seu

código. O construtor-padrão não é codificado na classe.

Figura 4.6 – Classe Aluno

Analise, novamente, o código da classe Pessoa (figura 4.1). Observe que

definimos um construtor para ela, o qual recebe dois parâmetros (nome e CPF).

Ao contrário da classe Pessoa, nenhum construtor foi definido para a classe Aluno

(figura 4.6) – ela possui apenas os atributos nome e matrícula. Entretanto, assim

como os objetos da classe Pessoa, os objetos da classe Aluno precisam ser

inicializados. Precisamos verificar, então, com que valores os atributos da classe

Aluno são inicializados. Ademais, para o exemplo da figura 4.4, precisamos definir

com que valor o atributo CPF de Pessoa é inicializado – já que o atributo nome

está sendo, por meio do construtor (linha 4 – figura 4.4). Para respondermos essa

questão, precisamos entender que todo atributo (variável, em código, que

representa um atributo) em Java tem um valor inicial padrão: um valor fornecido

pelo Java quando o programador não especifica o valor inicial do atributo. Para

simplificação, trataremos os termos atributo e atributo como sinônimos (que, de

certa forma, são). A partir deste ponto, usaremos somente o termo atributo.

Tipo por Referência Cada tipo primitivo em Java possui um valor como valor-padrão. Esses valores, como vimos, são utilizados pelo compilador para inicialização dos atributos (atributos) das classes. Os tipos byte, short, int e long, float e double possuem valor-padrão 0. O tipo boolean possui valor-padrão false. Os tipos por referência também possuem valores-padrão. Para estes, o valor-padrão é null.

Observe o código da figura 4.6 e perceba que não definimos valores iniciais

para os atributos nome e matrícula – apenas declaramos os mesmos, como

sendo dos tipos String e int. Logo, se não inicializarmos os mesmos, o compilador

Page 59: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

59

o fará por nós. Portanto, não se exige que os atributos sejam, explicitamente,

inicializados antes de serem utilizados em um programa – a menos que devam

ser inicializados com valores diferentes de seus valores-padrão. Note que todos

os atributos serão inicializados: ou por seus valores-padrão ou por um valor

específico, que o programador deve informar através de um construtor. No caso

da classe Pessoa (figura 4.3), temos os atributos nome e CPF. Observe o

construtor na linha 6 e perceba que estamos inicializando (com um valor passado

por parâmetro) apenas o atributo nome. Portanto, esse atributo nome é

inicializado por um valor a ser passado no momento em que um objeto de Pessoa

é criado (figura 4.4). O valor de CPF também é inicializado, mas com o valor

padrão para o tipo String (no caso, null). Já no código da classe Aluno (figura 4.6)

- que não possui um construtor definido pelo programador (embora possua um

construtor-padrão), - ambos os atributos serão inicializados com seus valores-

padrão.

Fique Atento Toda classe possui um construtor: ou o construtor-padrão ou um construtor definido pelo programador. Se nenhum construtor for definido pelo programador, o compilador criará e usará o construtor-padrão. Contudo, se um construtor for definido pelo programador, o compilador usará sempre este e não mais o construtor-padrão.

Se o construtor-padrão for utilizado, todos os atributos da classe serão

inicializados com seus respectivos valores-padrão. Se um construtor for definido

pelo programador, este poderá decidir com que valores específicos os atributos

serão inicializados. É curioso observarmos que no caso de definirmos um

construtor, não somos obrigados a inicializar todos os atributos da classe com

valores específicos (diferentes dos valores-padrão). Isso quer dizer que podemos

decidir quantos atributos iremos, de fato, inicializar com o construtor. No caso da

classe Pessoa (figura 4.3), o construtor inicializa apenas o atributo nome. O

atributo CPF, como já explicado, será inicializado com seu valor-padrão. Neste

caso, criamos o construtor (linha 6) apenas para inicializarmos o atributo nome

com um valor diferente do seu valor padrão.

Page 60: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

60

Fique Atento Podemos dizer que o construtor-padrão serve para inicializar (com valores-padrão) os atributos da classe - possibilitando que, ao serem criados a partir da mesma, os objetos assumam um estado adequado de uso. Os construtores definidos pelo programador servem para inicializar, com valores diferentes dos valores-padrão, certos (alguns ou todos) atributos da classe.

Suponha que seja necessário inicializar, para a classe Aluno (figura 4.6),

ambos os atributos nome e matrícula, com valores diferentes dos valores padrão.

Neste caso, precisamos definir um construtor para essa classe, visto que o atual

construtor (construtor-padrão) não atende à nova necessidade. O código da

classe fica como ilustrado a seguir.

Figura 4.7 – Construtor da classe Aluno

Observe que, agora, ambos os atributos de Aluno são inicializados pelo

construtor (linha 5). Isso obriga que, no ato de instanciação de qualquer objeto de

Aluno, sejam passados valores para ambos os atributos. Se nenhum valor for

passado, ou se apenas um valor for passado (em vez de dois), teremos um erro

de compilação. A figura a seguir ilustra a instanciação de objetos da classe Aluno.

Page 61: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

61

Figura 4.8 – Exemplos de instanciação de objetos Aluno

Observe o código da figura acima. Nas linhas 9 e 10, definimos a

instanciação de dois objetos de Aluno: aluno1 e aluno2. Note que,

obrigatoriamente, tivemos que especificar a passagem de dois parâmetros. Isto

porque o construtor da classe Aluno precisa receber dois valores. Portanto, toda

vez que um objeto de Aluno for criado, teremos que passar valores para os

atributos nome e matricula. Caso esses dados não sejam passados, teremos erro

de compilação. As linhas 16, 17 e 18 ilustram exemplos de instruções que

causariam erro. Nas linhas 16 e 17, apenas um parâmetro está sendo passado.

Na linha 18, nenhum. A instrução da linha 18 não causaria erro, caso tivéssemos

optado por não especificar o construtor de Aluno (linha 5, figura 4.7). Neste caso,

seria usado o construtor padrão da linguagem e ambos, nome e matrícula, seriam

inicializados com seus valores-padrão. Mas como definimos um construtor

específico, o construtor padrão deixa de ser utilizado. O compilador entende que

se definimos um construtor específico é porque não precisamos do construtor

padrão. Falta entendermos porque deixaríamos de usar o construtor padrão da

linguagem para especificar um próprio. Talvez fosse mais fácil deixar que todos

os atributos fossem inicializados pelo compilador. Contudo, observe a figura a

seguir.

Page 62: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

62

Figura 4.9 – Exemplo de uso dos construtores

Considere, ainda, que exista um sistema responsável pelo cadastro de

alunos. Pensando em termos de interface gráfica, poderia haver algo parecido

com o formulário Cadastro de Alunos da figura 4.9. O formulário é extremamente

simples, contendo apenas dois atributos a serem preenchidos: nome e matrícula.

Esses dados, dentro do nosso exemplo, devem ser fornecidos para se cadastrar

qualquer aluno. No nosso contexto, cadastrar um aluno significa instanciar um

objeto da classe Aluno no sistema de cadastro. Observe que a classe Aluno

(figura 4.7) possui um construtor, o qual deve receber os dados de nome de

matrícula, por parâmetro. Assim, os dados informados no formulário de cadastro

são usados na chamada ao construtor (instrução da figura 4.9). O nome

“Fernando” é passado como primeiro parâmetro, visto que este se refere ao nome

do aluno. A matrícula é passada como segundo parâmetro, uma vez que o

segundo parâmetro do construtor espera um valor inteiro de matrícula. Observe o

código a seguir.

Figura 4.10 – Método para cadastro de alunos

No código acima, temos a classe Sistema, a qual implementa o método

cadastrarAluno. Note o que este método faz: recebe dois dados como parâmetros

(nome e matrícula) e instancia um objeto da classe Aluno, passando, também

Page 63: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

63

como parâmetro, os dois dados recebidos. A linha 7 corresponde à chamada de

construtor da classe Aluno (figura 4.7). Vamos considerar que o botão Cadastrar

(figura 4.9), ao ser acionado, invoque o método cadastrarAluno, passando os

dados Fernando e 201322001 para o mesmo. Em tempo de execução, teremos

algo como na figura a seguir.

Figura 4.11 – Representação da chamada de método em tempo de execução

O código acima apenas simula o que ocorre em tempo de execução.

Observe que é o mesmo método da figura 4.10 – substituímos, respectivamente,

os parâmetro nome e matrícula por “Fernando” e “201322001”. Assim, quando

preenchermos o formulário e acionarmos o botão Cadastrar, os dados Fernando e

201322001 serão passados para o método cadastrarAluno, o qual repassará os

mesmos para o construtor de Aluno – criando (cadastrando) o objeto aluno1. Este

exemplo simula, com menor nível de detalhe, o que acontece em um sistema real

de cadastro. Não entraremos em detalhes sobre criação e manipulação de

interface gráfica nesta apostila. Continuando nosso estudo sobre construtores,

vamos pensar em como possibilitar que todo objeto de uma classe seja

inicializado com um valor pré-definido para um de seus atributos. Considere o

código a seguir.

Figura 4.12 – Inicialização de atributo com valor pré-definido

Page 64: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

64

Observe o construtor da linha 6. Esse construtor recebe apenas um dado

por parâmetro e o utiliza para inicializar o atributo nome. Note a instrução da linha

8. Perceba que o valor do atributo matrícula está sendo definido como

000000000. Isso quer dizer que todo objeto da classe Aluno terá, inicialmente, o

mesmo valor para matrícula ao ser instanciado. O valor para nome deverá ser

passado no momento da instanciação. Veja como fica o código para a criação de

um objeto de Aluno.

Figura 4.13 – Chamada ao construtor de Aluno

A linha 4 cria um objeto da classe Aluno. Esse objeto terá o nome

Fernando e matrícula com valor 000000000. Se outro objeto for criado, ele terá o

nome especificado pelo valor passado por parâmetro, mas valor 000000000 para

matrícula. Evidentemente, os valores de matrícula poderão ser alterados depois,

desde que haja métodos específicos para isto na classe Aluno. A questão é

entender porque se criar objetos com valores pré-definidos para alguns atributos.

A verdade é que cada sistema terá uma razão para fazê-lo. Pode ser somente

uma questão de se impedir que atributos sejam inicializados com valores-padrão,

ou pode ser uma necessidade imposta pela própria natureza do domínio do

problema. Suponha um cenário hipotético para cadastro de usuários de um

sistema. Para este caso, considere o código abaixo.

Figura 4.14 – Chamada ao construtor de Aluno

Page 65: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

65

O sistema deste cenário, inicialmente, cadastra todo novo usuário com uma

senha de acesso padrão, que deve ser alterada, posteriormente, pelo próprio

usuário. Pelo código da classe Usuario, podemos observar que o construtor (linha

6) recebe apenas um parâmetro, nome – utilizado para inicializar o atributo nome.

O atributo senha, por sua vez, é inicializado através de um valor padrão. Logo,

todo objeto de Usuario terá, inicialmente, a mesma senha. Certamente, métodos

para alteração da mesma deverão ser fornecidos pela classe, permitindo que

cada usuário altere sua senha pessoal.

4.2. Construtores Sobrecarregados

Quando tratamos de construtores, uma das possibilidades é a definição de

não apenas um, mas de dois ou mais construtores para uma mesma classe. Até o

momento, vimos classes com apenas um construtor, inicializando alguns ou todos

os seus atributos. Veremos, agora, casos em que especificamos mais de um

construtor para uma classe. Considere o seguinte cenário: um sistema realiza o

cadastro de pessoas. Para realizar este cadastro, ele precisa de, ao menos, um

dos seguintes documentos: RG e CPF. Considere o código a seguir.

Figura 4.15 – Construtor único em classe

No código acima, temos a classe Pessoa, com os atributos RG e CPF. Na

linha 6, especificamos um construtor para a mesma, recebendo dois parâmetros –

utilizados para inicializar ambos os atributos. O problema com esta

implementação é que ela obriga que seja passado tanto o RG quanto o CPF no

momento de instanciarmos um objeto de pessoa. Se a instanciação significar o

cadastro de uma pessoa, esta terá que ter ambos os documentos. Contudo,

Page 66: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

66

dizemos que ao menos um teria que ser utilizado – não necessariamente os dois.

Pode ser que, em um cenário real, a pessoa tenha apenas um dos dois

documentos em mãos. Para a pessoa que estiver com ambos, não teremos

problemas com o cadastro. Mas se a pessoa tiver apenas com um, pelo código

acima, não teremos como cadastrá-la. A solução para este impasse é possível:

basta especificarmos outros construtores para esta classe. A figura a seguir

apresenta o código para esta situação.

Sobrecarga Recurso usual em OO que consiste na definição de mais de uma implementação para um mesmo método em uma classe. A sobrecarga ocorre quando uma classe especifica métodos com mesmo nome, mas com assinaturas diferentes. Assim, mesmo que vários métodos tenham o mesmo nome, eles são distinguíveis pelo compilador. A assinatura de um método é uma combinação entre seu nome, tipo (retorno) e quantidade (e tipo) de argumentos que são passados para o mesmo.

Figura 4.16 – Construtores sobrecarregados

Observe a presença de três construtores no código acima: linhas 6, 11 e

15. O primeiro recebe dois parâmetros (RG e CPF) e inicializa os atributos RG e

CPF com estes valores. Já o segundo recebe apenas um valor (RG) e inicializa o

atributo RG. O terceiro, da mesma forma, recebe um único valor (CPF) e inicializa

do atributo CPF. A primeira questão a responder é porque não ocorre erro de

compilação nesta classe, visto que temos construtores com nomes iguais. Em

primeiro lugar, devemos nos lembrar de que um construtor é uma estrutura

semelhante a um método, diferenciando-se deste pelo fato de não retornarem

Page 67: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

67

valores (não podem especificar um tipo de retorno, nem mesmo void). Logo,

assim como métodos, construtores podem ser sobrecarregados (ter nomes iguais,

mas assinaturas diferentes). No caso dos construtores, a assinatura é a

composição de seu nome e quantidade (e tipo) de argumentos que ele possui.

Portanto, não ocorre erro de compilação na classe Pessoa pelo fato dos

construtores estarem sobrecarregados. Convém entendermos qual o benefício de

se especificar vários construtores em uma classe. Considerando o problema do

cadastro de pessoas, essa nova classe Pessoa permite que pessoas sejam

cadastradas utilizando-se somente CPF, ou RG ou mesmo ambos. Assim, se uma

pessoa precisar ser cadastrada e estiver apenas com o RG em mãos, ela o será.

Neste caso, basta usar o construtor da linha 11. Se ela estiver apenas com o

CPF, basta usar o construtor da linha 15. E se ela estiver com ambos os

documentos, pode-se utilizar qualquer um dos três construtores. Em resumo,

construtores sobrecarregados permitem que objetos sejam inicializados de

diferentes maneiras.

4.2. Referência THIS em Construtores

Já vimos em unidade anterior que a palavra-chave this é utilizada para se

evitar ambiguidade de nomes. De fato, ela evita que nomes de atributos de

classes sejam confundidos com nomes de variáveis locais. Em se tratando de

construtores, a palavra-chave this assume outra possibilidade de uso: reutilizar

código de inicialização fornecido por outro construtor da classe. Em outras

palavras, podemos - dentro de um construtor de uma classe - referenciar outro

construtor da mesma classe, de forma a reaproveitar o código deste último. Com

isso, evitamos ter que ficar repetindo código de inicialização quando temos vários

construtores em uma mesma classe. Analise o código a seguir.

Page 68: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

68

Figura 4.17 – Problema com construtores sobrecarregados

A classe Pessoa apresenta dois construtores, linhas 7 e 12. Observe que o

primeiro construtor recebe dois parâmetros: nome e telefone, utilizados para

inicializar os atributos nome e telefone. Note que os nomes dos parâmetros deste

construtor possuem os mesmos nomes dos atributos da classe. Para que seja

possível diferenciá-los, precisamos da palavra-chave this. A figura a seguir ilustra

a relação entre estes termos.

Figura 4.18 – Utilização de this para evitar ambiguidade de nomes

Com a figura acima, ilustramos o uso de this para se evitar ambiguidade de

nomes. Veja que o termo nome, recebido por parâmetro, se refere a uma variável

local e não ao atributo nome. O termo nome, ao lado da palavra-chave this, se

refere ao atributo da classe. Portanto, a palavra this é utilizada para referenciar

Page 69: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

69

entidades da classe (neste caso, atributo). Contudo, métodos também podem ser

referenciados por this. A figura 3.18, unidade 3, ilustra um exemplo (linha 7).

Agora que recordarmos esse primeiro uso da palavra-chave this, vamos voltar ao

caso dos construtores. Observe, novamente, o código da figura 4.17. Veja que o

segundo construtor, a exemplo do primeiro, recebe nome e telefone como

parâmetros, além de RG. Note que temos, dentro dos construtores, repetição de

código - nas linhas 8, 9, 13 e 14. Para este pequeno exemplo, isto não representa

um problema. Contudo, considere que a classe tenha vários atributos, e que esse

repetição de código seja da ordem de algumas dezenas de linhas. Neste caso, a

repetição começa a incomodar. Entretanto, podemos usar o this para resolver

essa questão. Observe o código a seguir.

Figura 4.19 – Utilização de this para evitar ambiguidade de nomes

Na figura 4.17, tanto o primeiro quanto o segundo construtores estão

inicializando nome e telefone, causando repetição de código. O segundo

construtor, em relação ao primeiro, apenas inicializa um atributo a mais: RG.

Portanto, o segundo construtor pode aproveitar o código de inicialização do

primeiro. É isto o que está sendo feito na figura 4.19. Observe que as linhas 13 e

14 da figura 4.17 foram substituídas pela linha 13 da figura 4.19. Nesta linha,

temos o a palavra this, seguida de parênteses e das variáveis locais nome e

telefone dentro dos mesmos. Quando disposta desta forma, a palavra-chave this

faz referência a outro construtor da classe Pessoa – neste caso, o primeiro

construtor. A linha 13 repassa ao construtor da linha 7 as variáveis nome e

telefone, recebido por parâmetro. O construtor da linha 7, então, inicializa os

Page 70: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

70

atributos nome e telefone com estas variáveis. Em seguida, a linha 14 inicializa o

atributo RG, com o parâmetro RG, recebido por parâmetro. Temos, portanto, a

inicialização dos atributos nome, telefone e RG. Agora, observe o código a seguir.

Figura 4.20 – Distinção entre vários construtores

Observe que, em relação ao código da figura 4.19, apenas acrescentamos

na figura 4.20 o construtor da linha 17. O que queremos verificar, aqui, é como a

palavra-reservada this (linha 13) sabe que deve fazer referência ao construtor da

linha 7 e não a qualquer outro construtor, como o da linha 17. A resposta é

simples: através da assinatura do construtor. Observe que a instrução da linha 13

realizada uma chamada a um construtor que receba duas strings como

parâmetro. O construtor da linha 17 recebe apenas uma e o construtor da linha 7,

duas. Logo, a instrução da linha 13 acaba fazendo referência ao primeiro

construtor, linha 7. Agora, podemos nos perguntar como o this faria a distinção

caso tivéssemos na classe Pessoa dois construtores recebendo suas strings.

Page 71: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

71

Figura 4.21 – Construtores com mesma assinatura

Observe pela figura 4.21 que os construtores das linhas 7 e 17 possuem a

mesma assinatura e, portanto, geram um erro de compilação. Logo, não pode

haver dois construtores com uma mesma assinatura e, assim, a palavra-chave

this sempre consegue distinguir, pela assinatura dos construtores, qual deve

referenciar.

Page 72: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

72

5. Pacotes As classes representam a forma básica de estruturação de um sistema

orientado a objetos. Embora elas sejam úteis, é necessário algo mais para a

estruturação de grandes e complexos sistemas, os quais podem conter centenas

ou milhares de classes. Podemos, então, dispor de um tipo de módulo que sirva

para agrupar classes relacionadas, tornando mais fácil a tarefa de se encontrar

uma determinada classe entre as várias pertencentes a um sistema. A ideia de

agrupamento de classes oferece um nível a mais de abstração (a habilidade de se

concentrar nos aspectos essenciais de um contexto qualquer) em relação à

abstração apresentada por uma classe isolada. Para apoiar a proposta de

agrupar classes relacionadas, a Orientação a Objetos oferece o conceito de

pacotes.

5.1. Pacotes

Colocar classes relacionadas em um pacote é, claramente, um mecanismo

conveniente para organização de classes. Entretanto, há uma razão mais

importante para os pacotes: evitar conflito de nomes. Em grandes projetos de

desenvolvimento de sistemas OO, em geral, é inevitável atribuir o mesmo nome

para diferentes elementos, por exemplo classes. Ainda que o programador

controle o desenvolvimento, evitando atribuir um mesmo nome para mais de uma

classe, devemos considerar que, em algum momento, o programador pode

precisar fazer uso de alguma biblioteca que contém uma classe com um nome

idêntico ao atribuído para uma de suas classes. Neste caso, o conflito de nomes é

inevitável. Entretanto, o conceito de pacotes cria seu próprio espaço de nomes,

evitando o conflito. É preciso considerar, também, que o desenvolvimento de

grandes sistemas engloba vários programadores participando da implementação

ao mesmo tempo. Neste caso, suponha que um deles precise adicionar uma

classe Conta no sistema, mas descobre que outro programador implementou uma

classe com esse mesmo nome, mas com implementação diferente. O primeiro

programador não ficará impossibilitado de utilizar a nomenclatura Conta desde

que as classes estejam em pacotes diferentes. Então, muito mais do que

Page 73: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

73

simplesmente organizar as inúmeras classes do sistema, os pacotes permitem

expandir o espaço de nomes. A princípio, qualquer grupo de classes pode ser

organizado dentro de um pacote. Porém, o mais natural é organizá-las em função

de alguma relação entre elas. Um exemplo seria organizar em um pacote as

classes relacionadas ao controle financeiro do sistema, classes que realizam

algum tipo de ordenação de dados, etc. Precisamos enfatizar, ainda, que mesmo

estando em pacotes diferentes, classes podem se relacionar umas com as outras.

A organização de pacotes não impede a comunicação entre as classes.

Evidentemente, em termos de implementação, precisamos informar ao compilador

que uma classe está fazendo uso de outra classe (localizada em outro pacote).

Sem essa informação, o compilador emite erros. O uso de pacotes é tão natural

em desenvolvimento de sistemas que podemos prover uma representação para

os pacotes de um sistema, bem como a relação existente entre eles. Observe a

figura a seguir.

Figura 5.1 – Exemplo de pacotes relacionados

Na figura acima, é possível vermos a especificação de três pacotes:

Faturamento, Financeiro e Cadastros. Dentro destes, há um conjunto de classes.

Observe que existe um relacionamento entre os pacotes. O relacionamento é

definido em função das relações entre as classes. Isso quer dizer que se existe

uma relação entre Cadastros e Financeiro, certamente alguma classe do primeiro

se relaciona com outra classe dos segundo. Contudo, o relacionamento não está

restrito às classes. Há outros elementos que compõem um sistema (por exemplo,

interfaces) que podem estar relacionadas com outros elementos. Embora não

Page 74: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

74

esteja representado neste exemplo, há relações entre as classes dentro de um

pacote. Observe outro exemplo na figura a seguir.

Figura 5.2 – Exemplo de pacotes relacionados

Podemos perceber, pela figura acima, os relacionamentos entre classes de

diferentes pacotes e entre classes de um mesmo pacote. Precisamos entender,

agora, como é definido o conceito de pacotes em código. Observe a figura a

seguir.

Figura 5.3 – Exemplo de pacotes relacionados

No exemplo da figura acima, definimos dois pacotes: Controle e Classe.

Além disso, definimos a classe Sistema, fora de ambos. Convém ressaltarmos

que embora não pareça, Sistema está dentro de um pacote, que chamamos de

padrão. Pense nos pacotes como se fossem diretórios em um sistema de

Page 75: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

75

arquivos (na verdade, são organizados exatamente como tais). Observe a figura

abaixo e note como fica a organização, no sistema de arquivos do computador,

para o exemplo da figura acima.

Figura 5.4 – Organização de classes e pacotes no computador

Para os elementos da figura 5.3, a organização em computador fica

conforme ilustrado na figura 5.4. Todos os arquivos mostrados nesta figura estão

dentro de uma pasta raiz, chamada SRC. Observe que temos duas pastas na

figura 5.4: Classe e Controle – que são os nossos pacotes. Há, ainda, a classe

sistema (na raiz). De certa forma, podemos dizer que Sistema está dentro de um

pacote padrão. Observe a figura abaixo.

Figura 5.5 – Árvore de arquivos do nosso projeto

Note que, dentro da pasta SRC, temos os nossos pacotes Classe e

Controle, além de um pacote padrão (default package). Esse pacote é a própria

raiz SRC. Na figura 5.4, perceba que ele não é representado como uma pasta. Na

verdade, ele não existe como pacote. Alguns ambientes de desenvolvimento

usam essa representação de pacote “padrão” (como na figura 5.5) para ajudar o

programador na visualização da organização dos arquivos do projeto. Outros,

Page 76: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

76

porém, não fazem referência ao mesmo - embora se considere que a raiz onde o

projeto está inserido seja o “pacote” padrão. Temos que ressaltar que cada

ambiente de desenvolvimento organiza os arquivos de certa forma. No nosso

exemplo, foi criada a pasta SRC. Em outros ambientes, por exemplo, ela não é

criada. Agora que entendemos essa organização de pacotes no sistema de

arquivos, vamos continuar com o exemplo. Dizemos que o uso de pacotes, além

de prover a organização do projeto, permite resolver problemas de conflitos de

nomes. Neste contexto, considere que o nosso projeto conte com duas classes,

ambas de nome Aluno, conforme a figura abaixo.

Figura 5.6 – Classes dentro dos pacotes Controle e Classe

Propositalmente, criamos as duas classes com o mesmo nome, simulando

um cenário que, em geral, ocorre no desenvolvimento de grandes sistemas.

Suponha que a classe sistema precise fazer uso dessas classes. Precisamos

verificar como ela pode utilizar ambas, sem que ocorram problemas. Observe a

figura a seguir.

Figura 5.7 – Implementação da classe Aluno do pacote Controle

A figura acima ilustra a implementação da classe Aluno, do pacote

Controle. Observe a linha 1. Toda vez que alguma classe (ou outro elemento) é

criada dentro de um pacote específico, este é identificado dentro do código. A

Page 77: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

77

linha 1 apenas informa que esta classe Aluno pertence ao pacote Controle. Se

esta classe estivesse no “pacote padrão”, não haveria esta linha. É o que

acontece com a classe sistema, ilustrada abaixo.

Figura 5.8 – Implementação da classe Sistema

Sistema está na pasta raiz do projeto. Embora a mesma seja considerada o

pacote padrão, não é representado como tal. Portanto, não se especifica sua

localização, como feito na linha 1 da figura 5.7. A figura a seguir ilustra a

implementação da classe Aluno, do pacote Classe.

Figura 5.9 – Implementação da classe Aluno do pacote Classe

Os nomes dos pacotes foram definidos aleatoriamente, apenas para título

de exemplificação. Note que as implementações de ambas as classes Aluno são

diferentes, simulando um cenário real. Se as implementações fossem iguais, não

haveria qualquer sentido em se criar as duas classes. Vamos considerar que a

classe Sistema, em um primeiro momento, precise utilizar a classe Aluno de

Controle. Observe a figura a seguir.

Figura 5.10 – Uso da classe Aluno pela classe Sistema

Page 78: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

78

Observe que temos erro de compilação na classe Sistema. A mensagem

emitida informa que o compilador não conhece o símbolo Aluno. De fato, o erro

era esperado. Lembre-se, pela figura 5.3, que Sistema não está no mesmo pacote

de Aluno. O compilador, ao compilar a classe Sistema, verifica se, na mesma

pasta (diretório) onde a mesma se encontra, existe uma classe chamada Aluno.

Se não existir, com é o caso acima, gera-se um erro. Precisamos, então, informar

ao compilador que a classe Sistema está fazendo uso da classe Aluno, do pacote

Controle. Veja a figura abaixo.

Figura 5.11 – Importação de pacote

Observe a linha 1: estamos importando a classe Aluno. Em outras

palavras, estamos dizendo ao compilador que a classe Sistema está fazendo uso

da classe Aluno. Note que não há mais o erro de compilação. Agora, o compilador

entende que o símbolo Aluno, linha 6, se refere à classe Aluno, localizada no

pacote Controle. Toda vez que uma classe precisar utilizar algum elemento

localizado em outro pacote, é necessário fazer uma importação, usando a

palavra-chave import. O uso do import é semelhante à instrução #include da

linguagem C. Suponha que, vem vez de utilizar a classe Aluno de Controle, seja

necessária a classe Aluno de Classe. Observe o que muda em Sistema.

Figura 5.12 – Importação de pacote

Page 79: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

79

Observe a linha 1. Note que, em relação à figura 5.11, apenas alteramos o

nome do pacote de onde a classe Aluno é importada. Agora, o Aluno da linha 6 se

refere à classe Aluno do pacote Classe. Suponha, agora, que seja necessário à

classe Sistema utilizar ambas as classes Aluno, dos pacotes Controle e Classe.

Analise o código da figura a seguir.

Figura 5.13 – Tentativa de importar duas classes Aluno

Suponha que queremos instanciar dois objetos alunos (aluno1 e aluno2,

linhas 7 e 8) – um da classe Aluno, do pacote Controle, e outro da classe Aluno,

do pacote Classe. Ao compilarmos o código acima, temos erro de compilação.

Basicamente, a mensagem emitida quer dizer que o compilador não consegue

distinguir de que classe os símbolos Aluno (linhas 7 e 8) pertencem – ou seja, se

são da classe Aluno, de Controle, ou da classe Aluno, de Classe. O compilador

não sabe qual importação (linhas 1 e 2) usar para definir o tipo Aluno destas

linhas 7 e 8. Temos, portanto, que resolver esse problema. Uma primeira solução

possível seria especificar, nas instruções de instanciação (linhas 7 e 8), de que

tipo são os objetos aluno1 e aluno2. Observe o código da figura 5.14. Observe

que alteramos as linhas 7 e 8, acrescentando os termos Controle e Classe,

respectivamente. Note que o mesmo erro de compilação foi emitido. Logo, nossa

tentativa de solução falhou. Podemos, no entanto, ser mais específicos, alterando

a segunda parte das mesmas instruções, após o símbolo “=”. Observe o código

da figura 5.15 e compare com a figura 5.14.

Page 80: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

80

Figura 5.14 – Primeira tentativa de solução

Figura 5.15 – Segunda tentativa de solução

Note que na figura 5.15, obtivemos o mesmo erro de compilação. Acontece

que fomos bem claros nas instruções das linhas 7 e 8, informando ao compilador

que o objeto aluno1 é do tipo Aluno, do pacote Controle, e que o objeto aluno2 é

do tipo Aluno, do pacote Classe. Se há um conflito nas instruções 1 e 2, vamos

tentar retirar as mesmas e ver o resultado. Observe a figura abaixo.

Figura 5.16 – Terceira tentativa de solução

Note que, agora, não temos mais o erro de compilação. O compilador

consegue entender que o objeto aluno1 é do tipo Aluno (Controle) e que aluno2 é

do tipo Aluno (Classe). Embora não seja feito uso da palavra-chave import, o

Page 81: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

81

compilador entende o caminho Controle.Aluno e Classe.Aluno, diferenciando

ambas as classes Aluno. Se quisermos, podemos fazer uso do import apenas

para uma delas. Veja o código abaixo.

Figura 5.17 – Importação do pacote Controle

Note que o código acima não gera erro de compilação. A linha 1 importa a

classe Aluno do pacote Controle. Na linha 7, esta classe é utilizada na

instanciação do objeto aluno2. O curioso está na linha 6. Note que não há

qualquer especificação do tipo do símbolo Aluno. Contudo, o compilador entende

que este Aluno se refere à classe Aluno, do pacote Controle. Isto porque este

pacote está sendo importado via import. Portanto, se quisermos utilizar qualquer

outra classe Aluno - de qualquer outro pacote que não o Controle -, teremos que

especificar de que pacote ela é. Na linha 7, se não especificássemos que aluno2

é do tipo Classe.Aluno, o compilador entenderia que aluno2 seria do tipo

Controle.Aluno. Toda vez que precisarmos fazer referência à classe Aluno do

pacote Classe, temos que fornecer o caminho completo: nome do pacote, seguido

do operador ponto (.) e o nome da classe. Agora, observe o código da figura

abaixo.

Figura 5.18 – Importação do pacote Classe

Page 82: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

82

Em relação à figura 5.17, o que alteramos na figura 5.18 foi a classe

importada via import. Antes, importávamos a classe do pacote Controle e, agora,

importamos do pacote Classe. Note que, em função dessa alteração, tivemos que

especificar, na linha 6, que o objeto aluno1 se refere à classe Aluno do pacote

Controle. Para a linha 7, note que retiramos a especificação de que o objeto é do

tipo Classe.Aluno.

5.2. Importação Implícita e Explícita

Quando utilizamos a palavra-chave import para importar elementos de um

pacote, podemos fazê-lo de suas formas: explicitamente ou implicitamente.

Embora ambas funcionem, há uma diferença pontual entre elas. Como exemplo,

vamos considerar as bibliotecas da linguagem Java. Java possui várias classes

com operações previamente definidas. Sempre que necessário, os

desenvolvedores podem fazer uso destas classes, importando-as – assim como

fizemos em nossos exemplos nessa unidade. A figura a seguir apresenta algumas

das várias bibliotecas da linguagem Java.

Figura 5.19– Exemplos de bibliotecas Java

Page 83: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

83

Note que elas estão organizadas na forma de pacotes. As bibliotecas são,

de fato, conjuntos de elementos (classes, interfaces, outros pacotes, etc.).

Observe na figura acima que temos um pacote denominado java.util. Vamos

acessar o mesmo e verificar seus elementos internos. A figura a seguir ilustra o

interior desse pacote.

Figura 5.20 – Elementos do pacote UTIL

Observe que, dentro do pacote UTIL, temos classes, interfaces e outros

pacotes, que podem conter mais classes, interfaces ou pacotes. Suponha que

nossa classe Sistema precise utilizar uma das classes específicas do pacote UTIL

para realizar alguma operação. Observe no código abaixo como podemos fazer a

importação.

Figura 5.21 – Componente específico do pacote UTIL

No exemplo da figura acima, estamos importando a classe ArrayList (linha

1). Se necessário, podemos importar outras classes, deste ou de outro pacote.

Observe o código da figura abaixo e perceba as duas importações.

Page 84: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

84

Figura 5.22 – Realizando mais de um import

Note que não temos erro de compilação ao especificar mais de um import.

A questão do erro no nosso exemplo da classe Aluno era o fato de importarmos

uma classe com o mesmo nome, mesmo sendo de pacotes diferentes. Neste

caso, sempre ocorre erro. Se importarmos – no código de uma mesma classe -

duas classes com o mesmo nome, teremos conflito e erro de compilação.

Observe o exemplo a seguir.

Figura 5.23 – Problema com conflito de nomes

Como dizemos, se dois (ou mais) elementos com mesmo nome forem

importados para uma mesma classe, teremos conflito de nomes e erro de

compilação. Já apresentamos a solução para este cenário nessa unidade.

Sabendo dessas questões, vamos continuar a explicação das importações

explícitas e implícitas. Observe a figura abaixo.

Page 85: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

85

Figura 5.23 – Importação implícita

Observe a instrução da linha 1. Note que estamos importando algo do

pacote UTIL. Para os exemplos anteriores, o que há de diferente nessa instrução

é o símbolo “*”. Essa é a chamada importação implícita. Ela, na verdade, importa

tudo (classes, interfaces, outros pacotes, etc.) que existe dentro do pacote UTIL.

Essa importação chama-se implícita, pois não estamos especificando,

explicitamente, que elemento está sendo importado. No caso da explícita, como

ilustrado na figura 5.21, por exemplo, estamos especificando que elemento está

sendo importado (a classe ArrayList). É sempre uma boa pratica o uso da

instrução import de forma explícita ao invés do uso da forma implícita. Essa é uma

boa pratica porque possibilita ao programador determinar, rapidamente, quais

classes foram importadas. Observe que na figura 5.23, não há qualquer

informação dos elementos que estão sendo importados. Pelo “*”, sabemos que

estamos importando tudo, mas não sabemos qual elemento está, de fato, sendo

utilizado na classe.

Page 86: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

86

6. Visibilidade

Se cada membro de uma classe/objeto fosse acessível a qualquer outra

classe/objeto, então o entendimento, a depuração (processo de encontrar e

reduzir defeitos, erros, em um sistema de software) e a manutenção de sistemas

seria uma tarefa quase impossível. O contrato apresentado por classes não seria

de confiança, uma vez que qualquer parte do código poderia acessar diretamente

um atributo e alterá-lo (modificar seu valor). Um dos pontos fortes da Orientação a

Objetos é seu suporte à visibilidade e restrição de acesso a membros (atributos e

métodos) de uma classe – consequentemente, de objetos. De fato, OO possibilita

controlar quem possui acesso aos membros de uma classe. Esse controle é

fundamental para qualquer sistema OO.

6.1. Visibilidade

O mecanismo para controle de acesso em Orientação a Objetos é simples,

sendo realizado por meio dos modificadores de acesso. Precisamos entender,

antes, que todos os membros de uma classe são sempre disponíveis ao código

da própria classe – ou seja, os métodos da classe podem acessar, sem qualquer

restrição, os atributos (ou outros métodos) declarados na mesma, seja para

acessar seus valores ou modificar estes. A questão, então, é controlar o acesso

aos membros da classe por outras classes. Sem o devido controle, o programa

está fadado aos problemas citados no início da unidade. Em Orientação a

Objetos, são definidos quatro níveis de acesso. A tabela abaixo descreve os

mesmos.

Tabela 6.1 – Modificadores de acesso

Todos os membros públicos de uma classe são acessíveis onde quer que o

programa tenha uma referência a um objeto dessa classe. É o nível de acesso

Page 87: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

87

menos restritivo. Se um atributo da classe X é declarado como público, ele pode

ser acessado pelos membros de objetos de outra classe. Por acessar, estamos

considerando a possibilidade de tanto visualizar seu valor quanto modificar o

mesmo. Os membros declarados como privados são acessíveis apenas por

membros pertencentes à própria classe. Assim, um método da classe pode

acessar os atributos privados da mesma, para visualizar seus valores ou para

alterá-los. O mesmo não pode ser realizado por métodos declarados em outras

classes. É o nível de acesso mais restritivo, e que garante maior segurança.

Utilizar acesso protegido provê um nível intermediário de restrição – entre os

níveis privado e público. É um modificador utilizado muito em hierarquias de

classes. Membros protegidos de uma superclasse podem ser acessados por

membros dessa superclasse, por membros de suas subclasses e por membros de

outras classes no mesmo pacote. Neste momento, não se preocupe com os

termos superclasse, subclasse e hierarquia de classes. Em unidades posteriores,

discutiremos os mesmos. Por fim, os membros de uma classe declarados como

padrão possuem nível de acesso similar ao protegido – isto é, podem ser

acessados por membros de outras classes no mesmo pacote. Para este

modificador, não se aplica o conceito de hierarquia de classes. A figura a seguir

ilustra o nível de acesso público.

Figura 6.1 – Classe Aluno

Observe o código da figura acima. Note que ambos os atributos da classe,

matrícula e nome (linhas 3 e 4), estão definidos como privados. Logo, apenas

membros da própria classe podem acessar os mesmos. Na linha 6, definimos um

método para alterar o valor do atributo matricula. Note que na linha 7, estamos

Page 88: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

88

acessando o mesmo através da referência this. Esse acesso é permitido, pois

embora o atributo esteja definido como privado, ele pertence à própria classe. O

acesso na linha 11 também é possível, pela mesma razão. Agora, observe se um

membro de outra classe tentar realizar este acesso.

Figura 6.2 – Acesso a atributo privado de Aluno

O código da figura acima especifica a classe Sistema. Dentro da mesma,

definimos o método main, no qual criamos um objeto da classe Aluno (linha 5) e

tentamos, com o mesmo, acessar seu atributo matricula (linha 6), atribuindo o

valor 200222015 para o mesmo. Observe que o compilador emitiu uma

mensagem (parte final da figura), dizendo que o atributo matricula possui acesso

privado em Aluno. Vamos discutir o que aconteceu. Embora matricula seja, de

fato, um atributo do objeto aluno, ele possui acesso privado. Observe que esta

tentativa de acesso ao atributo está sendo realizado da classe Sistema. Podemos

dizer, então, que a classe Sistema está tentando acessar, através de um objeto

Aluno que ela criou em seu método main, o atributo matricula deste objeto. Isso

não quer dizer que não possamos, de alguma forma, acessar este (ou qualquer

outro atributo privado de aluno). Em OO, contamos com meios para este tipo de

procedimento, o que discutiremos em unidade posterior. Observe, pelo código

abaixo, o que acontece quando alteramos o modificador de acesso do atributo

matricula (linha 4, figura 6.1) de privado para público.

Page 89: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

89

Figura 6.3 – Acesso a atributo público de Aluno

Note que não temos mais erro de compilação. E nem deveria haver, pois

dizemos que matricula teve o modificador de acesso alterado para público. Esse é

um grande problema de segurança, pois uma classe (Sistema) consegue acessar,

sem qualquer restrição, o atributo de outra classe (Aluno) e alterar seu valor. Há

um conceito em OO, chamado de Encapsulamento, que deve sempre ser utilizado

para se evitar este tipo de situação. Discutiremos sobre o mesmo em unidade

posterior. Agora, vamos analisar a tentativa de acesso a um método declarado

como privado. Observe o código abaixo.

Figura 6.4 – Exemplo de método privado

No código da figura acima, declaramos todos os membros da classe Aluno

como privados. Em teoria, nenhum deles poderá ser acessado por membros de

outras classes. Observe o código abaixo, o qual tenta realizar acesso ao método

privado de Aluno.

Page 90: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

90

Figura 6.5 – Acesso a método privado de Aluno

Observe que obtivemos o mesmo erro descrito na figura 6.2, mas, agora,

relativo ao método. Na parte inferior da figura 6.5, o compilador emite um aviso

dizendo que o método possui acesso privado. Portanto, não podemos - da classe

Sistema - invocar este método. É certo que é o objeto aluno quem está, de fato, o

invocando (linha 6). Mas quem está solicitando isto é a classe Sistema, através de

seu método main. Observe a figura abaixo.

Figura 6.6 – Acesso a método privado

Observe a linha 10 do código da figura 6.6. Nesta linha, definimos um

método chamado teste, cujo objetivo é apenas invocar o método privado definido

na linha 6. Observe que não há erro neste código, apesar do método de teste

estar acessando um método privado. Como o acesso está sendo feito dentro da

própria classe, não há erro de compilação. Membros privados da classe são

acessíveis por (e somente por) membros da própria classe. No código abaixo,

note o que acontece quando alteramos o método alterarMatricula (linha 6, figura

6.6) de privado para público.

Page 91: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

91

Figura 6.7 – Acesso a método público

Note que não há erro de compilação. Sendo o método alterarMatricula

público, seu acesso é liberado para qualquer classe externa. Vejamos, agora,

exemplos do modificador padrão. O código abaixo ilustra a definição de um

atributo com este nível de acesso. Observe que, para o caso do modificador

padrão, basta não especificarmos nada.

Figura 6.8 – Acesso a método privado de Aluno

O código acima especifica a classe Aluno, com apenas um atributo, nome

(linha 3). Note que nos exemplos anteriores, imediatamente antes do tipo do

atributo, definimos sua visibilidade (privativo ou público). No caso do modificador

de acesso padrão, como dito, basta não definir nenhum outro modificador – ou

seja, basta especificar o tipo do atributo e seu nome. Fazendo isto, o compilador

entende que a visibilidade do atributo é padrão (default). O mesmo vale para os

métodos. Vamos analisar o caso deste modificador. Observe o código da figura

abaixo.

Page 92: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

92

Figura 6.9 – Acesso a método privado de Aluno

No código acima, definimos todos os membros da classe Aluno como

padrão. Agora, considere a classe Sistema da figura 6.3. Considere, ainda, que

ambas as classes Aluno e Sistema estão dentro do mesmo pacote. Note que a

linha 6 da classe Sistema (figura 6.3) realiza um acesso direto ao atributo

matricula do objeto aluno. Vimos que não há problemas de compilação quando

este atributo está definido como público. Mas, obtemos erro quando ele é definido

como privado. Veja, na figura a seguir, o que acontece quando compilamos

ambas as classes.

Figura 6.10 – Acesso a atributo default de Aluno

Como esperávamos, não houve erro de compilação – uma vez que as

classes estão no mesmo pacote. Logo, neste caso, membros default de classes

podem ser acessados por outras classes. Vejamos o que ocorre quando estas

classes são inseridas em pacotes diferentes. Observe a figura abaixo.

Page 93: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

93

Figura 6.11 – Acesso a atributo default de Aluno

Na figura acima, estamos ilustrando o fato das duas classes, Aluno e

Sistema, estarem em pacotes diferentes. O código da classe Aluno é o mesmo

apresentados na figura 6.9. Apenas uma alteração foi necessária no código da

classe Sistema. A figura abaixo apresenta o novo código desta classe.

Figura 6.12 – Alteração na classe Sistema

Observe que no código cima, inserimos uma instrução (linha 2) para

importar a classe Aluno (que está em outro pacote). Utilizamos a palavra-chave

import, seguida do nome do pacote e do nome da classe de interesse. Sem esta

instrução, a classe Sistema não consegue identificar o tipo Aluno, definido na

linha 8. A instrução da linha 1 é inserida automaticamente ao criar a classe. O

mesmo vale para a classe Aluno (embora no código desta classe, esteja definido

que ela pertence ao pacote Classe – e não Controle, como é o caso da classe

Sistema). Vejamos, então, o que ocorre quando compilamos ambas as classes.

Page 94: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

94

Figura 6.13 – Acesso a membro default de outro pacote

Observe a mensagem de erro emitida pelo compilador, dizendo que o

atributo matrícula não é público na classe Aluno (pacote Classe) e que, por isso,

não pode ser acessado por classe de pacote externo. Em outras palavras, há erro

de compilação porque as classes não estão dentro do mesmo pacote. Se

alterarmos a visibilidade do atributo matrícula (linha 4, figura 6.9) de padrão para

privado, teremos o mesmo erro. Contudo, o erro será em função da visibilidade

estar como privativa e não pelo fato das classes estarem em pacotes separados.

Independente de estarem ou não em um mesmo pacote, classes (e objetos) não

conseguem acessar qualquer membro privado de outra classe. Observe o código

da figura abaixo. Para este caso, como dito, alteramos a visibilidade do atributo

matrícula (linha 4, figura 6.9) para private. Note o erro emitido pelo compilador,

avisando que matrícula possui visibilidade privativa em Aluno.

Figura 6.14 – Acesso a membro privado de outro pacote

Page 95: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

95

Vamos considerar, agora, que o atributo matricula (linha 4, figura 6.9) seja

definido como público. Na teoria, não há erro de compilação, uma vez que

membros públicos de uma classe podem ser acessados por qualquer outra

classe, desde que esta última faça uma referência à primeira. O código abaixo

ilustra o resultado da compilação após esta alteração.

Figura 6.15 – Acesso a membro público de outro pacote

Considere, agora, que o mesmo atributo matrícula (linha 4, figura 6.9)

tenha sua visibilidade alterada para protegida. A figura abaixo ilustra o resultado

da compilação da classe Sistema.

Figura 6.16 – Acesso a membro público de outro pacote

Observe que há erro de compilação. A mensagem de erro informa que o

atributo matricula tem visibilidade protegida. O erro era esperado, visto que as

classes continuam em pacotes separados. Lembre-se que membros protegidos

de classes só podem ser acessados por outras classes do mesmo pacote – além

Page 96: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

96

do caso das hierarquias (que não veremos por agora). Se as classes foram

inseridas no mesmo pacote, teremos o mesmo resultado obtido na figura 6.10 -

não haverá erro de compilação. Quando estudarmos hierarquias de classes,

voltaremos a ver sobre a visibilidade protegida.

6.2. Métodos Públicos e Privados

Vimos que atributos de classe devem ser declarados como privados e,

caso seja necessário acesso aos mesmos, devemos fazê-lo via da interface

pública da classe (métodos públicos). Os métodos devem, em geral, ser públicos.

A razão é que métodos correspondem à interface de comunicação entre

diferentes classes (consequentemente, objetos). Se declararmos métodos como

privados, eles não serão vistos por outras classes e, consequentemente, não

poderão ser invocados por elas. Os métodos públicos oferecem serviços aos

usuários de uma classe. Contudo, há casos em que podemos e precisamos

definir métodos como sendo privados. É o caso de quando precisamos dividir uma

tarefa maior em várias outras menores dentro da própria classe. Certamente,

estas tarefas menores não precisam ser visíveis aos usuários da classe, apenas a

tarefa maior. Neste caso, as tarefas menores são declaradas como privativas,

pois são de uso exclusivo da própria classe. Outra boa razão para termos um

método privado é uma tarefa que é necessária (como subtarefa) por vários

métodos de uma classe. Em vez de escrevermos o código desta subtarefa várias

vezes (repetição de código), podemos escrevê-lo uma única vez em um único

método privado. Assim, toda vez que outro método da classe precisar do mesmo,

basta invocá-lo. Por ser privado, ele será visto apenas pelos pela própria classe

em que está declarado – as demais classes do sistema não terão visibilidade ao

mesmo.

Fique Atento Métodos privados são criados para que possam ser utilizados apenas pela classe onde são declarados. Se precisar que um método não possa ser visto por classes externas, declare-o com o modificador de acesso private. Se o método é um serviço oferecido pela classe para outras classes, ele deve ser declarado como public – caso contrário, não poderá ser acessado. Atributos devem, sempre, ser declarados como privados. Isso permite que o objeto

mantenha mais controle sobre seu estado. Se o acesso para um atributo privado for realizado através de métodos, o objeto terá a capacidade de assegurar que o atributo nunca será configurado com um valor inconsistente pra o objeto. Esse nível de integridade não é possível se os atributos foram definidos como públicos.

Page 97: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

97

A figura 6.17 ilustra um exemplo de uso de método privado dentro de uma

classe. Para esta figura, estamos considerando o caso da classe conter um

método para efetuar o pagamento (de uma compra, por exemplo) via cartão de

crédito. Para que o pagamento seja possível, é necessário, antes, verificar se o

cartão está liberado para ser utilizado (se possui limite e se é válido). Essa

verificação é feita junto à operadora do cartão. Observe os dois métodos, nas

linhas 3 e 17. Para simplificação, não os implementamos. O método da linha 3 é

público e, portanto, pode ser requisitado por usuários da classe. Quando ele é

invocado, deve-se passar o número do cartão. Na linha 4, efetuamos uma

chamada a um método privado da classe, passando o número do cartão como

parâmetro. Este método (linha 17) recebe o número do cartão e verifica, junto à

operadora, se o mesmo é válido e se possui limite disponível para a compra. Em

caso positivo, ele é liberado e o método retorna um valor booleano true (cartão

liberado). Em caso negativo, o valor retornado é false (cartão não liberado). A

execução volta para linha 4. Recebendo valor true, segue-se para a

implementação dentro (linha 5). Em caso de valor false, segue-se para a linha 10.

Page 98: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

98

Figura 6.17 – Exemplo de uso de método privado

Observe que não há necessidade (e talvez seja inseguro) deixar que o

método da linha 17 seja acessível aos usuários da classe (externos). Este método

realiza uma operação complementar para o método da linha 3 e, portanto, deve

ser privado. Neste caso, a justificativa para criar este método (linha 17) poderia

ser que o método da linha 3 ficaria demasiadamente extenso se todo o código do

método 17 fosse inserido no mesmo.

Page 99: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

99

7. Encapsulamento

No mundo real, um objeto A pode interagir com outro objeto B sem

qualquer conhecimento do funcionamento interno de B. Uma pessoa, por

exemplo, geralmente utiliza um aparelho de TV sem saber, efetivamente, como é

a estrutura interna da TV ou com seus mecanismos são ativados. Para utilizá-la,

basta saber realizar algumas operações básicas, como ligar, desligar, mudar de

um canal para outro, regular do volume, etc. Como essas operações produzem

resultados satisfatórios, não interessa ao usuário entender como a TV funciona

internamente. O encapsulamento consiste na separação dos aspectos externos

de um objeto, acessíveis por outros objetos, de seus detalhes internos de

implementação, que ficam ocultos dos demais objetos. Encapsulamento está

relacionado ao propósito de ocultamento de informações, fator essencial para

qualquer sistema OO.

7.1. Encapsulamento

A interface de comunicação de um objeto deve ser definida de tal forma a

revelar o menos possível sobre seu funcionamento interno. Um objeto deve ter

conhecimento das operações que podem ser solicitadas, mas precisa estar ciente

do que as operações fazem, e não como elas fazem (ou seja, como estão

implementadas). Em linguagens OO, o interior de uma classe (sua

implementação) deve ser ocultado de outras classes. Há dois aspectos para isto:

não se deve ter permissão para conhecer detalhes internos das classes e não

deve ser necessário conhecer esses detalhes. O segundo princípio – necessidade

de conhecer – possui relação com o conceito de modularização. Se fosse

necessário conhecer o interior de todas as classes que precisamos utilizar, jamais

terminaríamos a implementação de grandes sistemas. Lembre-se que sistemas

grandes e complexos possuem várias classes. O primeiro princípio – não ter

permissão para conhecer – é diferente. Também possui relação com a

modularização, mas em um contexto diferente. A linguagem de programação não

permite acesso à seção privativa de uma classe. Isso assegura às classes não

Page 100: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

100

depender, exatamente, de como outras classes são implementadas. Isso é muito

importante para o trabalho de manutenção – isto é, alteração ou extensão da

implementação de uma classe visando melhorias ou correção de erros. De

maneira geral, a alteração da implementação de uma classe não deveria provocar

alterações em outras classes. Se uma classe X necessitar conhecer os detalhes

internos de outra classe Y – em outras palavras, se ela depender destes detalhes

internos – uma alteração na classe Y pode gerar problemas sérios para X, a ponto

de ela não mais funcionar corretamente. Logo, conhecer detalhes internos de

implementação das classes é perigoso, pois facilita a criação de outras classes

que dependam dessas implementações específicas, e não dos resultados que

elas podem gerar. Suponha uma a classe X dependa de um método de

verificação de CPF, implementado na classe Y. Considere, ainda, que X dependa,

especificamente, da forma como o método de Y esteja implementado. Imagine o

que pode ocorrer caso a forma como é feita a verificação de CPF precise ser

alterada por Y. Provavelmente, X não funcionaria da forma como deveria. Este é

um exemplo dos riscos de se conhecer detalhes de implementação das classes.

Por esta razão, o ocultamento de informações é importante no desenvolvimento

de sistemas OO. O intuito é garantir estabilidade aos sistemas computacionais.

Encapsulamento também é utilizado para facilitar a reutilização de código. Um

encapsulamento bem realizado pode servir de base para a localização de

decisões de projetos que necessitem ser alteradas – um exemplo seria uma

operação implementada de maneira ineficiente e que, agora, precise de um

algoritmo mais eficiente. Se a operação está encapsulada, apenas o objeto que a

define precisará ser modificado. Os demais que, por ventura, dependam dele, não

sofrerão qualquer alteração.

7.2. Utilizando Encapsulamento

A diretriz de encapsulamento sugere que somente as informações sobre o

que uma classe pode fazer devem ser visíveis externamente, não como ela é.

Isso representa uma grande vantagem: se nenhuma outra classe sabe como

nossas informações estão armazenadas e nem como nossas operações estão

implementadas, podemos facilmente alterar tanto a forma de armazenamento

Page 101: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

101

quanto a implementação sem “quebrar” outras classes. Sobre as operações,

podemos impor essa separação entre o que e como evitando criar métodos que

independam da forma como outro método é implementado. Se apenas soubermos

o que um método faz (e não como ele faz), ficaremos dependentes apenas da

execução do método. Assim, se ele for alterado futuramente, mas realizando o

que sempre realizou (retornando alguma informação, imprimindo algo, etc.), não

teremos problemas. No caso das informações em si, podemos impor a separação

tornando os atributos das classes privados e utilizando métodos de acesso para

obter seus valores e métodos de modificação para alterar os mesmos. Para as

operações, o cuidado com o encapsulamento depende unicamente do

programador. É ele quem deve programar suas classes de forma que elas não

dependam das implementações de outra. Para as informações, embora ainda

dependa do programador, o uso do encapsulamento é mais direto: um atributo

jamais deve ser acessado/modificado diretamente – ou seja, ele nunca deve ser

marcado como público. Os atributos sempre devem ser definidos como privativos

e o acesso/modificação deve ser realizado exclusivamente por meio de métodos.

A figura 7.1 ilustra uma representação para este cenário. Observe que os

métodos ficam “protegendo” os atributos. Não se consegue atingir qualquer

atributo sem seja através de algum dos métodos da classe.

Figura 7.1 – Representação do encapsulamento de atributos

O potencial de fazer alterações no código, sem interromper o código de

outras classes que o estiverem usando, é um benefício essencial do conceito de

encapsulamento. Percebemos, então, que encapsulamento está intimamente

relacionado com o conceito de visibilidade.

Page 102: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

102

7.3. Métodos de Acesso e Modificação

Até agora, entendemos o que é o encapsulamento e como podemos obtê-

lo ao implementar nosso código. Precisamos analisar isso na prática. Iremos focar

na questão da modificação e acesso aos atributos, já que para os métodos, não

existe um padrão de implementação para torna-los encapsulados. O que há,

neste sentido, é a necessidade de se implementar de forma a não depender de

outras implementações (ou seja, de como ela é feita, codificada). No caso dos

atributos, garantimos encapsulamento ao impedir que sejam acessados

diretamente. Em linguagem Java, existem métodos específicos para possibilitar o

acesso aos atributos de uma classe: há métodos para obter a informação

armazenada no mesmo e métodos para alterar essa informação. Note que o

intuito do encapsulamento não é impedir o acesso, mas controlar o mesmo.

Lembre-se que sistemas OO são fundamentados nas interações entre objetos, e

que estas interações dependem das operações (métodos) e dados (atributos)

oferecidos pelos próprios objetos. Assim, impedir acesso aos dados é impedir a

interação e o funcionamento dos sistemas. Observe a figura abaixo.

Figura 7.2 – Definição de atributo público

No código da figura acima, definimos a classe Aluno com dois atributos

públicos, matrícula e nome. Por serem públicos, sabemos que podem ser

acessados, sem qualquer restrição, por outras classes. Observe, agora, o código

da figura abaixo.

Figura 7.3 – Acesso a membros públicos da classe Aluno

Page 103: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

103

Note que o código da classe Sistema está criando um objeto de Aluno

(linha 4) e acessando seus atributos, atribuindo valores específicos aos mesmos

(linhas 5 e 6). Note que o acesso está sendo realizado de forma direta – ou seja,

especificando o nome do objeto, seguido da notação de ponto e do membro que

queremos acessar: nome na linha 5 e matricula na linha 6. Isso é acesso direto.

Portanto, os atributos não estão encapsulados: estão definidos como públicos

(linhas 3 e 4 – figura 7.2) e permitem acesso direto. O objeto que criamos no

código acima possui nome Fernando e matrícula 200222015. Se existe acesso

direto, na impede de qualquer outra classe, que tenha referência ao objeto aluno,

de alterar os valores de matrícula ou nome de aluno. Analise o código abaixo, na

definimos a classe Relatorio. Dentro da mesma, especificamos um método para

impressão de dados (linha 3). Este método recebe um aluno como parâmetro e

imprime seu nome (linha 4) e sua matrícula (linha 5). Até agora, não há qualquer

problema nisto.

Figura 7.4 – Alteração indevida de atributo do objeto aluno

A questão é a instrução da linha 7. Observe que, propositalmente, estamos

alterando o valor do nome do aluno, o que não poderia ocorrer. É um acesso

direto ao atributo nome de aluno e, por isto, não existe qualquer restrição,

qualquer verificação antes de o nome ser alterado. Isso é muito ruim. Se o objeto

aluno da classe Sistema (figura 7.3) fosse passado por parâmetro para o método

imprimir da classe Sistema, o aluno – de nome Fernando – teria ser nome

alterado para Maria – o que é uma inconsistência. Esse é o problema da falta de

encapsulamento. Precisamos, então, fazer com que a classe Aluno esteja

encapsulada. Para começar, vamos definir seus atributos nome e matrícula como

privados. Em seguida, temos que definir métodos para acesso aos mesmos.

Observe a figura abaixo.

Page 104: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

104

Figura 7.5 – Definição de atributo público

O código da figura acima é uma extensão do código da classe Aluno (figura

7.2). Continuamos com os atributos nome e matrícula, mas, agora, privados. A

maior diferença foi a especificação de métodos para acesso aos mesmos. Em

Java, métodos para acesso (leitura) aos atributos de uma classe, objetivando

captura de seus valores, são denominados GET. Já os métodos para alteração de

valores são denominados SET. Para cada atributo de uma classe, então, devem

ser definidos um método set e um método get – isso se o programador definir que

o atributo deve permitir tanto acesso quanto alteração por membros externos.

Observe a linha 6 do código acima. Por padrão, um método de captura de valor

possui o como tipo de retorno o mesmo tipo definido para o atributo, seguido do

termo get e do nome do atributo. Como este método é criado para obter valor de

atributo, ele apenas retorna o valor do mesmo (linha 7). Note o método da linha

10. Este é um método para alteração de valor de atributo. Por padrão, ele possui

tipo de retorno void (já que não precisa retornar nada, apenas alterar), seguido do

termo set e do nome do atributo. Além disso, se iremos alterar o valor do atributo,

temos que passar o nome valor por parâmetro - é o que está sendo realizado

entre parênteses na mesma linha 10. Como o atributo nome é do tipo String,

devemos passar um valor que também o seja. A linha 11 apenas atualiza o valor

Page 105: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

105

do atributo nome com o valor recebido por parâmetro. Observe que para o

atributo matricula, foram, igualmente, criados métodos get (linha 14) e set (linha

18). Podemos dizer, então, que os atributos da classe Aluno estão todos

encapsulados – ou seja, o acesso a eles não mais é permitido diretamente, mas

somente por meio da interface pública (dos métodos) da classe. Lembre-se que

métodos públicos podem ser acessados por qualquer outra classe do sistema.

Logo, métodos públicos correspondem à forma de ligação entre classes - são

serviços oferecidos por classes, que podem ser solicitados por outras classes.

Toda vez que uma classe X quiser acessar o atributo matrícula, seja para obter

seu valor ou alterá-lo, deverá fazer isto através dos métodos públicos de Aluno.

Note que se não haver métodos para acesso, o atributo jamais poderá ser

acessado – uma vez que ele está definido como privado. Vamos analisar como

fica a classe Sistema (figura 7.3) a partir da nova implementação da classe Aluno.

Antes, observe a figura abaixo.

Figura 7.6 – Erro ao tentar acessar atributo privado de Aluno

Como era de se esperar, Sistema não consegue mais acessar o atributo

nome de forma direta. Note o erro emitido pelo compilador, dizendo que nome

possui acesso privado em Aluno. Se quisermos acessar este atributo, passando o

valor Fernando para o mesmo, deveremos fazê-lo através dos métodos de Aluno,

se houver. No nosso caso, há. Observe a figura abaixo e veja a alteração do

código de Sistema.

Page 106: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

106

Figura 7.7 – Acesso a atributos de Aluno via métodos públicos

Note, pelo código acima, que o acesso aos atributos nome e matrícula é

realizado via os métodos públicos setNome e setMatricula (linhas 5 e 6). Após a

execução, o objeto aluno é criado, com os valores Fernando e 200222015,

respectivamente, para nome e matricula. Se quisermos verificar se estes valores

estão, de fato, inseridos em aluno, podemos utilizar os métodos de acesso get.

Observe a figura abaixo.

Figura 7.8 – Uso de métodos get de Aluno

No código acima, as linhas 8 e 9 imprimem os valores obtidos através dos

métodos getNome e getMatricula. Observe que ambos os métodos retornam um

string, que é impressa através da instrução de impressão de Java. Vamos

verificar o resultado da execução desta classe Sistema. Observe a figura abaixo.

Figura 7.9 – Resultado da execução

Page 107: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

107

A figura acima apresenta o resultado da execução da classe Sistema. Note

que tanto o nome quanto a matrícula foram impressas. Logo, os métodos

getNome e getMatricula executaram sem problemas. Suponha que seja

necessário alterar o valor da matrícula do aluno Fernando, de 200222015 para

200222001. Observe o código abaixo.

Figura 7.10 – Alteração de matricula de aluno

No código acima, em relação ao código da figura 7.8, apenas

acrescentamos as linhas 11, 13 e 14. Na linha 11, inserimos uma instrução para

alterar o valor do atributo matricula. As linhas 13 e 14 voltam a imprimir o nome e

a matricula do objeto aluno, para compararmos com os valores impressos pelas

linhas 8 e 9. Observe, abaixo, o resultado da execução desta classe.

Figura 7.11 – Resultado da execução após alteração de Sistema

As linhas 7 e 8 (figura 7.10) imprimem o nome Fernando e a matrícula

200222015. Já as linhas 13 e 14 imprimem o mesmo nome Fernando, mas um

novo valor para matrícula: 200222001. Note que, de fato, o valor de matrícula foi

alterado, conforme especificado na linha 11 da figura 7.8. Perceba, portanto, que

Page 108: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

108

os atributos de uma classe devem ser acessíveis somente através de métodos

públicos definidos pela própria classe. O que precisamos entender, agora, é: que

real vantagem existe ao se criar métodos set e get para os atributos das classes.

Observe que, na figura 7.8, embora não tenha sido feito um acesso direto, o

atributo matricula foi alterado. Então, podemos nos perguntar que diferença existe

entre acessar diretamente e acessar através de um método público. Para

começar, o acesso (para obter valor ou modifica-lo) só é possível se houver

métodos para isto. Logo, temos como prover segurança, pois se o desenvolvedor

entender que um atributo não pode ser alterado por membros externos, ele

simplesmente não irá implementar o método set para o mesmo. Da mesma forma,

se não for interessante permitir que o valor de um atributo seja acessado por

membros externos, basta não implementar o método get. Cabe ao programador,

com base nas necessidades do sistema, entender quais atributos terão ou não

métodos de modificação e acesso. Segundo, supondo que seja necessário

implementar tanto get quanto set - neste caso, podemos até achar que não existe

diferença, mas existe. Perceba pelo código da figura 7.4 (linha 7), que o acesso

direto não impõem qualquer restrição para a alteração – ou seja, bastou atribuir

um valor e a alteração é executada, pois o atributo matricula estava definido como

sendo público. Agora, considere que o mesmo atributo seja privado e que existam

métodos set e get para o mesmo, como ilustrado na figura 7.5. Note que a

alteração de matricula só pode ser executava através de um método set. Observe

a figura abaixo.

Figura 7.12 – Resultado da execução após alteração de Sistema

Page 109: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

109

A chave essencial do código acima está no método definido na linha 10.

Observe que este set não altera, simplesmente, o valor do atributo matricula.

Antes de executar a alteração, existe um código (não implementação – ilustrado

como comentário) para verificar se a mesma é possível. Em outras palavras, um

membro externo solicita a alteração do valor do atributo matrícula para um dado

objeto, mas antes de realizar essa alteração, o método set analise se ela, por

exemplo, não causaria inconsistências. O código entre as linhas 11 e 14 pode ser

definido conforme a necessidade do programador – que pode exigir uma série de

verificações antes de autorizar a mudança de valor de matricula. No caso do

acesso direto, não se realiza tais verificações, apenas altera-se o valor. Logo, há,

sim, muito sentido em se especificar get e seu em vez de possibilitar acesso

direto. Mesmo que, em princípio, o programador acredite que não haja

necessidade real de validação de dados, um bom projeto OO sempre preconiza o

planejamento – visando futuras modificações no código. Pode ser que, em

mudanças futuras, seja importante fazer alguma verificação antes de permitir

acesso e modificação de atributo. Para ficar seguro, é sempre viável exigir código

para capturar e modificar atributos, em vez de permitir acesso direto aos mesmos.

Fique Atento Cuidado com códigos que pareçam estar encapsulados, mas que, de fato, não estão. Em, primeiro lugar, o que define encapsulamento de atributos é o fato de estes estarem marcados como privados e não a simples presença de métodos get e set. Assim, de nada adianta a presença destes métodos se os atributos estiverem definidos como públicos. Sendo públicos, não há qualquer obrigação de acessá-los através dos métodos – podendo o acesso pode ser realizado diretamente.

Precisamos entender, então, que o encapsulamento dos atributos depende

de não se permitir o acesso direto aos mesmos. Assim, qualquer aplicação que

tente acessá-los, deverá fazê-lo mediantes métodos disponibilizados pela classe

em que se encontram. A figura abaixo ilustra esse cenário.

Figura 7.13 – Encapsulamento de atributos

Page 110: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

110

Apêndice A: Introdução à Tecnologia Java Java é uma linguagem de programação desenvolvida pela Sun

Microsystems e lançada em versão beta em 1995. Seu desenvolvimento foi

iniciado em 1991, visando o mercado de bens eletrônicos de consumo. Por esta

razão, foi projetada desde o início para ser independente de hardware, já que as

características arquiteturais dos equipamentos variam amplamente neste nicho de

desenvolvimento. Outro objetivo estabelecido desde sua concepção foi o de ser

uma linguagem segura. Segura tanto no sentido de evitar falhas comuns que os

programadores costumam cometer durante o desenvolvimento, como no sentido

de evitar ataques externos.

Estas características despertaram o interesse para utilização de Java em

outro ambiente que também necessitava de uma linguagem com este perfil: a

Internet. A Internet, também, é um ambiente constituído por equipamentos de

diferentes arquiteturas e necessita de linguagens que permitam a construção de

aplicativos seguros. Embora estas características possam ser encontradas em

outras linguagens, Java alcançou enorme sucesso entre os programadores. A

sintaxe semelhante às linguagens C e C++, sua filosofia de implementação, a

independência de plataforma, etc. são apenas algumas características que a

tornaram um das linguagens mais difundidas e utilizadas para desenvolvimento

de sistemas computacionais.

A.1. Máquina Virtual

Em uma linguagem de programação como C, dita ser linguagem

compilada, temos a seguinte figura quando vamos compilar um programa.

Figura A.1 – Processo de compilação

O código fonte (em programa C, por exemplo) é compilado para uma

plataforma e um sistema operacional específico. Não raro, o próprio código fonte

é desenvolvido visando uma única plataforma. O código executável (binário)

resultante será executado pelo sistema operacional e, por esta razão, é essencial

Page 111: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

111

que ele saiba “conversar” com o sistema em questão. Logo, temos um código

executável para cada sistema. Isso significa que se o programa for utilizado em

um sistema Windows, devemos compilar o mesmo para este sistema em

específico. Se seu uso será em um sistema Linux, outra compilação será

necessária. Em outras palavras, haverá uma compilação para cada tipo de

sistema. Além disso, na maioria das vezes, uma aplicação faz uso das bibliotecas

do sistema operacional (por exemplo, as bibliotecas de interface gráfica).

Acontece que as bibliotecas do Windows são diferentes daquelas especificadas

para Linux. Logo, o programador acaba tendo que reescrever o mesmo pedaço

de código da aplicação para diferentes sistemas operacionais, uma vez que eles

não são compatíveis.

Figura A.2 – Compilação para plataformas diferentes

Isso limita o desenvolvimento, além de torna-lo custoso. Já a linguagem

Java se utiliza do conceito de máquina virtual. Neste contexto, verifica-se a

existência de uma camada extra entre o sistema operacional e a aplicação, a qual

é responsável por traduzir o que a aplicação deseja fazer para as respectivas

chamadas do sistema operacional onde ela está rodando no momento.

Figura A.3 – Máquina virtual Java

Com a utilização de uma máquina virtual, ganhamos independência de

sistema operacional. Ou, melhor ainda, independência de plataforma em geral:

não é preciso se preocupar em qual sistema operacional a aplicação irá rodar,

nem em que tipo de máquina, configurações etc.

Page 112: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

112

Podemos dizer, por analogia, que uma máquina virtual é como um

computador, uma vez que é responsável por gerenciar memória, pilha de

execução, etc. A aplicação roda sem nenhum envolvimento com o sistema

operacional - sempre conversando apenas com a Java Virtual Machine (JVM).

Essa característica é interessante: como tudo passa pela JVM, ela pode tirar

métricas, decidir onde é melhor alocar a memória, entre outros. Uma JVM isola,

totalmente, a aplicação do sistema operacional. Se uma JVM termina

abruptamente, só as aplicações que estavam rodando nela irão terminar: isso não

afetará outras JVM que estejam rodando no mesmo computador, nem afetará o

sistema operacional.

A.2. Processo de Compilação e Interpretação

Em Java, o código fonte continua passando pelo processo de compilação.

Contudo, diferente de outras linguagens, o resultado deste processo não é um

código binário executável pelo SO, mas o que chamamos de “bytecode” (código

binário gerado pelo compilador Java). Bytecode é um formato de código

intermediário entre o código fonte (texto que o programador consegue manipular)

e o código de máquina (que o SO consegue executar). O curioso é perceber que

os bytecodes podem ser executados em diferentes SO, desde que haja uma JVM

instalada nestes SO. A JMV instalada em um SO irá receber os bytecodes e

traduzir os mesmos para serem usados naquele SO. Esta é a razão pela qual

Java é independente de plataforma. A figura abaixo ilustra o processo de

compilação e tradução (interpretação) relacionada à tecnologia Java.

Figura A.4 – Processo de compilação e interpretação

Resumidamente, o código fonte (escrito em Java) é compilado, gerando os

bytecodes – que, por sua vez, são entregues às maquinas virtuais instaladas nos

diferentes SO nos quais se deseja que o programa execute. Se precisarmos

executar os mesmos bytecodes em outro SO, basta instalar nele a JVM. Não é

Page 113: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

113

necessário compilar novamente o código fonte. Precisamos analisar, na prática,

como é este processo. Antes disso, entenda que existem dois processos

envolvidos para a execução de programas em Java: compilação e execução. A

compilação gera os arquivos com extensão class (bytecodes) e a execução,

realizada pela JVM, interpreta esses arquivos para instruções que são inteligíveis

para a máquina (SO). Em Java, para cada classe (unidade de programação

Java), é gerado um arquivo class. No ambiente de execução, alguns arquivos

class são utilizados pela JVM para execução do programa. Esses arquivos fazem

parte da Java API (biblioteca Java). A figura abaixo ilustra o processo completo de

execução de um programa Java.

Figura A.5 – Processo de compilação e interpretação

Observe os arquivos gerados após o processo de compilação. Estes são

os arquivos de classe (possuem extensão “.class”). O arquivo de classe contém

apenas a conversão das instruções que o programador escreveu. Na verdade,

isso não é suficiente para executar um programa. Para certas ações, como exibir

um texto em uma janela, é necessário o uso de arquivos de classe já

disponibilizados pela linguagem (ou escritos por outros programadores). No caso

dos arquivos da linguagem, as ações necessárias para executar algumas

operações (como imprimir um texto) foram implementadas e compiladas. Os

arquivos de classes (bytecodes) foram, então, organizados em uma biblioteca –

um conjunto de códigos, que podem, sempre, que necessário, ser utilizados.

Assim, para a execução de um programa, o interpretador Java carrega o bytecode

do programa escrito pelo programador, inicializa o mesmo e carrega os arquivos

necessários da biblioteca, à medida que são requeridos. O não uso dos arquivos

Page 114: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

114

para a execução de certas ações gera erro de compilação. É interessante o erro

ser informado em tempo de compilação, e não de execução, pois permite que o

programador acerte o problema, especificando as bibliotecas necessárias para o

bom funcionamento do seu programa antes que o mesmo seja colocado em

execução.

A.3. Execução de Programas - Exemplo

Vamos, agora, apresentar um exemplo completo de execução de um

programa simples em Java. Entenda que o desenvolvimento de sistemas em Java

é feito através de ambientes apropriados, chamados de IDE (Ambiente de

Desenvolvimento Integrado). Estes permitem que todo o programa seja compilado

e executado, sem que seja necessário realizar o exemplo a seguir. A proposta,

aqui, é apenas ilustrar como ocorrer a execução de um programa escrito em

linguagem Java. Não seria nada produtivo se tivéssemos que realizar o

procedimento deste exemplo para todos os nossos programas. Para começar,

observe a figura abaixo. Ela ilustra o código fonte de um simples programa em

Java.

Figura A.6 – Exemplo de programa em Java

A figura acima ilustra um programa simples, que imprime a mensagem “Oi

Mundo” ao ser executado. Não entraremos em detalhes sobre o código em si.

Nosso propósito, agora, é apenas ilustrar a geração do arquivo de bytecode e a

execução do mesmo. O programa acima foi salvo com o nome “HelloWord.java”.

Agora que temos o nosso arquivo com o código fonte, precisamos (i) compilá-lo e

(ii) executá-lo. Para realizar a compilação e a execução, iremos utilizar o prompt

de comando do Windows - o propósito é ilustrar as instruções de comando para

ser realizar ambos os procedimentos. Observe a figura abaixo.

Page 115: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

115

Figura A.7 – Compilação do programa Java

A figura acima ilustra o procedimento para compilação do arquivo

HelloWord.java. Usamos o termo javac para chamar o compilador e compilar

nosso programa. Note que precisamos especificar a localização do arquivo. Após

este procedimento, no arquivo está compilado. No mesmo local onde o arquivo se

encontra, há , agora, um arquivo denominado HelloWord.class – que é o arquivo

com os bytecodes. Em seguida, precisamos executar o nosso programa. Para

isto, usamos a JVM. Observe a figura abaixo.

Figura A.8 – Execução do programa Java

A figura acima ilustra a execução do programa. Observe a primeira

instrução mostrada no prompt. Utilizamos o termo java, seguido do nome do

programa (mas sem a extensão java). Esta instrução executa o programa, cujo

resultado (o texto Oi Mundo) pode ser visto na linha abaixo. Com isto, terminamos

a execução do nosso programa. A figura A.8 ilustra todo o processo que

acabamos de executar.

Page 116: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

116

Figura A.9 – Processo completo de execução do programa Java

Alguns programadores iniciantes em tecnologia Java - mas acostumados

com programas em C, por exemplo - acreditam que Java possui baixa

produtividade. Essa percepção é fomentada pelo fato de, segundo eles, ser mais

simples criar os pequenos programas utilizados no início do aprendizado nas

linguagens que já utilizam. É importante, entretanto, deixar claro que a premissa

do Java não é a de criar sistemas pequenos. O foco da plataforma é outro:

aplicações de médio a grande porte. Certamente, criar a primeira versão de uma

aplicação usando Java, mesmo utilizando IDE, é mais trabalhoso do que utilizar

outra linguagem, mas simples. Porém, com uma linguagem orientada a objetos e

madura como o Java, é extremamente mais fácil e rápido realizar alterações no

sistema - desde as boas práticas e recomendações sejam, de fato, seguidas.

Além disso, a quantidade enorme de bibliotecas gratuitas para realizar os mais

diversos trabalhos (tais como relatórios, gráficos, sistemas de busca, geração de

código de barra, manipulação de XML, tocadores de vídeo, manipuladores de

texto, persistência, impressão, etc.) é um ponto fortíssimo para adoção do Java.

Cada linguagem tem seu espaço e seu melhor uso. O uso do Java é interessante,

por exemplo, em aplicações que virão a crescer, em que a legibilidade do código

é importante, onde temos muita conectividade e onde há a necessidade de

integração/comunicação entre diferentes plataformas.

Page 117: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

117

Apêndice B: Método Main Quando aplicativos Java são desenvolvidos, deve haver um único ponto de

partida a partir do qual sua execução começa. Em Java, este ponto de partida é

um método, chamado de main (principal).

B.1. Método main

Para um aplicativo Java, exatamente um dos métodos deve ser chamado

de main e ser definido conforme a figura abaixo. Caso contrário, a JVM não

executará o aplicativo. O método main do Java é como se fosse a função main no

C e no C++. Quando a aplicação é executada pelo interpretador Java, o método

main é o primeiro método a ser chamado. Então, o método main chama os

demais métodos necessários para a execução completa da aplicação. A figura

abaixo apresenta a assinatura deste método.

Figura B.1– Método main ou principal

Podemos, pela figura, acima, observar a estrutura do método main. A

assinatura do método é formada pelo modificador de acesso public, seguido pela

palavra static, o tipo de retorno void e seu nome (main). Note que main é um

método. Logo, o seu nome é seguido de parênteses. No caso deste método, há

um argumento – especificamente, um vetor – do tipo String. A assinatura sempre

segue este padrão. O método main deve ser public para que seja possível invocá-

lo externamente. Ele precisa ser estático (static), pois não há nenhum objeto

criado no sistema quando o iniciamos (note que toda aplicação começa a

execução a partir deste método e, antes de se iniciar a execução do mesmo, não

Page 118: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

118

há objetos instanciados). O tipo de retorno é void, uma vez que o método não

retorna valor. O nome main é fixo (ou seja, deve possuir este nome). O

parâmetro, como dito, é um vetor String, que permite que os usuários passem

argumentos necessários para iniciar o método. Essa passagem é opcional – ou

seja, deve-se passar algum argumento se for necessário. Caso não seja, não

passamos e o vetor fica com comprimento zero. O corpo do método main,

teoricamente, pode conter quaisquer instruções. Contudo, um bom estilo dita que

o comprimento do método (a quantidade de linhas que o mesmo possui) deve ser

mantido em um mínimo possível. Especificamente, ele não deve conter nada que

faça parte da lógica da aplicação. Ele deve ser um método apenas para iniciar a

aplicação. Quando realizamos uma chamada para execução de um programa em

Java (por exemplo, no prompt de comando: Java NomedaClasse), o comando

java inicia a JVM. O sistema, então, procura um método na classe exatamente

com a assinatura da linha 5 (figura B.1). Sobre a passagem de parâmetro para a

execução do main, convém destacar que estamos trabalhando com um vetor do

tipo String. Assim, somente elementos desse tipo são passados. Entretanto, Java

fornece meios de transformarmos um numeral do tipo String em um numeral

inteiro, por exemplo. Lembre-se que é perfeitamente possível uma variável do tipo

String receber um valor “5”. As aspas são utilizadas para informar ao compilador

que 5 é uma String e não um valor inteiro. Observe o código abaixo

Figura B.2– Método main recebendo parâmetro

No código acima, temos a especificação da classe Exemplo, que possui um

método main. Note que este método, como dizemos, recebe um vetor de Strings,

nomeado como args. Na linha 4, estamos realizando quatro ações: (i) estamos

acessando o valor da posição 0 do vetor args, (ii) convertendo este valor em um

inteiro, (iii) multiplicando este valor, já convertido, por 10 e (iv) imprimindo o

resultado dessa multiplicação. Estamos considerando o fato de que, para

Page 119: Este material foi elaborado por Edmar Welington Oliveira, …professorfabriciomendonca.com.br/gallery/apostila... · 2020. 3. 10. · atuação da Engenharia de Software. Diversas

EADDCC031 – Linguagem de Programação II

119

executar o programa acima, temos que passar uma String que representa um

número inteiro. Observe a figura abaixo

Figura B.2– Método main recebendo parâmetro

Note que estamos realizando uma chamada ao programa Exemplo,

passando o valor 5 como parâmetro. O resultado, como esperado, será o valor

50. Cada palavra depois do nome da classe na primeira linha de comando na

figura acima será lida como uma String separada e passada para o método main

como um elemento do vetor de strings. Nesse nosso exemplo, o vetor args

contém um único elemento, 5 (String). Convém ressaltarmos que os parâmetros

de linha de comando não são frequentemente utilizados em Java.