Você está na página 1de 268

1

Unidade Temática HC HEI


1. Visão geral de linguagens e Paradigmas de Programação 6 3

• Evolução das principais Linguagens de Programação

Genealogia das principais linguagens de programação de alto nível (SEBESTA, 2011: 58).

• Visão geral das Linguagens de Programação


A realização de determinadas operações em linguagem de máquina pode ser extremamente complexa.
Operações que necessitem de interface com os dispositivos de entrada e saída, como gravação ou
leitura de dados em disco, são um exemplo. Esse tipo de operação não é importante apenas em
linguagens de programação. Este é um dos motivos pelos quais os sistemas operativos são
construídos.

Na realidade, os sistemas operativos constroem uma camada entre hardware e software, facilitando a
utilização dos recursos da máquina real por meio da criação de uma máquina virtual. Essa máquina
2

virtual é muito mais simples de ser utilizada e provê uma série de mecanismos ao utilizador que facilitam
a utilização dos recursos do computador.

Um exemplo são os arquivos, que permitem a manipulação de dados em disco sem que seja necessário
o conhecimento dos detalhes do funcionamento dos discos (como trilhas, sectores, velocidade da
rotação, posicionamento dos dados no disco e outros mais). Além disso, o sistema operativo gere os
recursos da máquina, evitando a má utilização e até mesmo a danificação de seus componentes por
um programador.

Além de os sistemas operativos implementarem operações complexas e extremamente úteis ao uso do


computador, também fornecem uma interface para que outros programas possam utilizá-las. Essas
interfaces são conhecidas como chamadas de sistema, ou System calls, e o conjunto de operações
disponibilizadas são as bibliotecas do sistema operativo. Os compiladores utilizam essas interfaces
em vez de implementar as operações complexas por si mesmos. Quando o compilador encontra
chamadas ao sistema operativo no código-fonte em linguagem de alto nível, transforma-as em
referências “não resolvidas” no código-objecto em linguagem de máquina. Para que esse código possa
ser executado, precisa ser ligado ao código do sistema operativo que efectivamente implementa a
operação. Esta é a segunda etapa da compilação. O processo é conhecido como ligação ou linking.
A figura a seguir apresenta as etapas da transformação de um código em linguagem de alto nível até
que este possa ser executado.

Etapas para a execução de um programa em linguagem de alto nível.

A integração dos compiladores com os sistemas operativos permite que o código-objecto de um


programa seja portável para qualquer máquina que utilize o mesmo sistema operativo, tornando o
código-objecto dependente do sistema operativo e não mais da plataforma do processador, visto que o
sistema operativo deixa transparente as diferenças entre plataformas diversas. Porém, um código-
objecto compilado em um sistema operativo não poderá ser executado em um outro sistema operativo.

A linguagem de programação Java tem uma arquitectura diferente, o que fornece a seus programas
mais portabilidade que outras linguagens de programação de alto nível. A compilação do código Java
não gera código executável em nenhum sistema operativo. Em vez disso, gera um código “pseudo-
executável”, chamado bytecode. Esse código é uma espécie de código de baixo nível que, porém, não
é uma linguagem de máquina de algum processador, nem faz interface específica com algum
determinado sistema operativo. Para que esse código possa ser executado em algum (e ao mesmo
3

tempo em qualquer) sistema operativo, é necessário que mais uma camada de software esteja aí
instalada.

A camada funciona como um sistema operativo genérico, deixando transparentes as particularidades


do sistema operativo real. Essa camada de software é chamada de Máquina Java Virtual, ou Java
Virtual Machine (JVM). A JVM faz a tradução dos bytecode para código executável naquele sistema
operativo. Isto faz com que um programa escrito em Java e que seja compilado para bytecode possa
ser executado em qualquer máquina que tenha a JVM adequada instalada. Durante a execução, esse
código será traduzido em tempo real para a linguagem proprietária e executado. A tradução em tempo
real pode ser entendida como uma espécie de interpretação. Portanto, é difícil afirmar que a linguagem
de programação Java seja uma linguagem puramente compilada ou interpretada.

Essa característica da linguagem Java faz com que esta seja extremamente interessante para o uso
na Internet, visto que o código Java que esteja presente em uma página web poderá ser executado em
qualquer plataforma que contenha a JVM instalada. Normalmente, os browsers (navegadores) já
contém uma JVM para que possam trabalhar com páginas web que contenham programas Java.

Processo de execução de programas Java.

Em suma, as linguagens de programação caracterizam-se por: (i) gramática e significado bem


definidos; (ii) implementável (executável) com eficiência “aceitável”; (iii) universal (deve ser possível
expressar todo problema computável); e, (iv) natural (para expressar problemas em um certo domínio
de aplicação).

É importante referenciar alguns aspectos do estudo de linguagens, a saber: (i) léxico (palavras); (ii)
sintaxe (gramática/forma); (iii) semântica (significado); (iv) pragmática (metodologias); e, (v)
processadores (compiladores, interpretadores, editores, ambientes visuais, entre outros).
Existem tantas linguagens de programação, porém, cada uma, foi desenvolvida para atender um (i)
propósito diferente/específico; (ii) avanços tecnológicos; (iii) interesses comerciais e (iv) cultura e
background científico.
4

• Paradigma Imperativo
“Eu realmente odeio esta maldita máquina; eu queria que eles
vendessem. Ela não faz o que quero que faça, só o que lhe digo para
fazer.”

A programação imperativa é o paradigma de programação mais antigo e bem desenvolvido. Este


paradigma surgiu com os primeiros computadores, na década de 1940, e seus elementos espelham
directamente as características arquitecturais dos computadores modernos também.

Em meados da década de 1940, John Von Neumann e outros reconheceram que tanto um programa
quanto os seus dados poderiam residir na memória principal de um computador, uma ideia implícita no
trabalho inicial de Turing (Turing, 1936). Os primeiros computadores armazenavam seus programas
fora da memória, geralmente usando um painel de ligações com fios. A ideia de armazenar um
programa na memória do computador levou a um aumento enorme do poder e da versatilidade
potenciais de um computador.

A arquitectura do assim chamado modelo de Von Neumann-Eckert é a base para o paradigma da


programação imperativa. A memória da máquina contém tanto instruções de programas (o
armazenamento de programa) quanto valores de dados (o armazenamento de dados). No Kernel
(coração) dessa arquitectura está a ideia de atribuição – alterar o valor de um local de memória e
destruir seu valor anterior.

Já que elas surgiram do modelo de Von Neumann-Eckert, todas as linguagens imperativas incluem a
atribuição como um elemento central. Além disso, elas suportam declarações de variáveis, expressões,
comandos condicionais, laços e abstração procedural. As declarações atribuem nomes a locais de
memória e associam tipos aos valores armazenados. As expressões são interpretadas por meio da
recuperação dos valores correntes das variáveis com nomes a partir das suas respectivas localizações
na memória e pelo cálculo de um resultado a partir desses valores. Dada uma referência a uma variável
x, a memória retorna o valor corrente no local associado a x.

Nesse paradigma, os programas são centrados no conceito de um estado (modelado por variáveis) e
acções (comandos) que manipulam o estado. Este paradigma também é denominado de procedural,
por incluir sub-rotinas ou procedimentos como mecanismo de estruturação.

Algumas vantagens do modelo imperativo: (i) eficiência (embute o modelo de John Von Neumann); (ii)
modelagem “natural” de aplicações do mundo real e, (iii) paradigma dominante e bem estabelecido.

Algumas desvantagens: (i) relacionamento indirecto entre E/S resulta em: difícil legibilidade; erros
introduzidos durante a manutenção; e, descrições demasiadamente operacionais focalizam o como e
não o que.

As linguagens imperativas predominantes incluem o COBOL, FORTRAN, C, Ada e Perl.

Modelo computacional do Paradigma Imperativo.


5

• Paradigma Funcional
“É melhor fazer cem funções operarem sobre uma estrutura de dados
do que dez funções operarem sobre dez estruturas de dados.”
Atribuído a Alan Perlis

A programação funcional emergiu como um paradigma distinto no início da década de 1960. Sua
criação foi motivada pela necessidade dos pesquisadores no desenvolvimento de inteligência artificial
e em seus subcampos – computação simbólica, prova de teoremas, sistemas baseados em regras e
processamento de linguagem natural. Essas necessidades não eram particularmente bem atendidas
pelas linguagens imperativas da época.

A programação funcional modela um problema computacional como uma colecção de funções


matemáticas, cada uma com um domínio de entrada e um resultado. As funções interagem e combinam
entre si usando composição funcional, condições e recursão.

As linguagens importantes da programação funcional são o LISP, Scheme, Haskell e ML.

• Paradigma Lógico
“P: Quantas patas tem um cachorro, se chamarmos sua cauda de
pata?
R: Quatro. Chamar uma cauda de pata não a transforma em uma
pata.”
Abraham Lincoln

A programação lógica (declarativa) surgiu como um paradigma distinto nos anos 70. A programação
lógica é diferente dos outros paradigmas porque ela requer que o programador declare os objectivos
da computação, em vez dos algoritmos detalhados por meio dos quais esses objectivos podem ser
alcançados.

A programação lógica permite a um programa modelar um problema declarando qual resultado o


programa deve obter, em vez de como ele deve ser obtido. Outrora, são chamadas linguagens
baseadas em regras, pois as declarações do programa se parecem mais com um conjunto de regras
ou restrições sobre o problema, ao invés de uma sequência imperativa de instruções.
A principal linguagem lógica é o Prolog.

• Programas de computador
Neste ponto, veremos o processo necessário para se criar um programa e executá-lo. Primeiramente
introduziremos os principais conceitos para a melhor compreensão de como um programa é visto pelo
computador.

• O que é um programa
Os computadores das mais variadas arquitecturas têm funcionamento similar. A figura a seguir
apresenta a arquitectura simplificada de um computador.
6

Arquitectura simplificada de um computador.

A parte física do computador é chamada de hardware, que é formado basicamente por uma Unidade
Central de Processamento (CPU), pela memória e pelos dispositivos de entrada e saída. O barramento
faz a ligação desses componentes.

A CPU (ou simplesmente, processador) contém um conjunto relativamente pequeno de instruções que
é capaz de executar. Cada processador contém um conjunto diferente de instruções, apesar de
similares entre si. As instruções podem ser desde operações matemáticas a interações com os
dispositivos de entrada e saída. Chama-se de programa de computador um conjunto de instruções que
será executado pelo processador em uma determinada sequência. Esse programa leva o computador
a executar alguma tarefa.

Como pode-se perceber, um programa nada mais é que um tipo de algoritmo. Sua particularidade é
que suas operações são especificas para o computador e restritas ao conjunto de instruções que o
processador pode executar. Pode-se considerar esse conjunto de instruções como a primeira
linguagem de programação do computador, também chamada de linguagem máquina. Classificamos
as linguagens de programação segundo a sua proximidade com a linguagem de máquina. Quanto maior
a semelhança com a linguagem de máquina, mais baixo é o nível da linguagem. As linguagens de
programação mais semelhantes à linguagem de máquina são conhecidas como linguagens de baixo
nível. Analogamente, linguagens de programação “distantes” da linguagem de máquina são conhecidas
como linguagens de programação de alto nível. As linguagens de programação de alto nível são mais
próximas da linguagem natural e guardam pouca similaridade com a linguagem da máquina em que
serão executadas.

A linguagem de programação que um computador é capaz de compreender é composta apenas de


números (sistema binário, 0 e 1). Assim, fazer algoritmos na linguagem de programação do computador
ou em sua linguagem de máquina é um processo extremamente complicado para nós, seres humanos,
acostumados com a nossa própria linguagem. Por isso, para facilitar a programação de computadores,
foi necessária a criação de um código que relacionasse a linguagem de máquina a uma linguagem mais
fácil de ser compreendida. A linguagem de montagem (ou assembly) é um código que tem uma
instrução alfanumérica (ou mnemónica1) para cada instrução numérica em linguagem de máquina.

1
É um auxiliar de memória. São tipicamente, verbais, e utilizados para memorizar listas ou fórmulas, e baseiam-
se em formas simples de memorizar maiores construções, baseados no princípio de que a mente humana tem mais
facilidade de memorizar dados quando estes são associados a informação pessoal, espacial ou de carácter
relativamente importante.

Note-se, além disso, que na linguagem de programação assembly, são designadas como mnemónicas as palavras
reservadas da linguagem, que constituem e especificam uma determinada sintaxe.
7

Para que um programa escrito em linguagem de montagem possa ser executado pelo computador, é
necessário que seu código seja traduzido para o código de máquina. Isto é feito por meio de um
programa chamado assembler. A figura a seguir apresenta o esquema de tradução feita por um
assembler.

Tradução para a linguagem de máquina.

A linguagem de montagem é muito próxima da linguagem de máquina e, por isso, é uma linguagem de
programação de baixo nível. Para cada processador, há uma linguagem de montagem já que há uma
relação directa entre as instruções em linguagem de montagem e em linguagem de máquina. Isto faz
com que o código tenha que ser refeito se quisermos executar um programa em um processador não
compatível com o qual ele foi escrito inicialmente. Neste caso, diz-se que o código não é portável.

A implementação de programas nesse tipo de linguagem ainda é muito complexa e dependente do


conhecimento das instruções do processador. Para aumentar a produtividade dos programadores e a
portabilidade dos programas, foram criadas as linguagens de programação de alto nível. Essas
linguagens são independentes do processador em que serão executadas. Suas características
principais são que seu código é mais elaborado, contemplando operações mais complexas e mais
próximas da “lógica humana”. Para que possam ser processadas por um computador, os comandos da
linguagem precisarão ser traduzidos para a linguagem de máquina. Essa tradução é feita por meio de
um compilador ou de um interpretador, dependendo do caso.

• Executando um programa
O compilador, a partir do código em linguagem de alto nível, chamado de código-fonte, gera um arquivo
com o código em linguagem de máquina, conhecido como código-objecto. Esse código-objecto fica em
disco e só é carregado em memória no momento da execução. O interpretador faz o mesmo trabalho,
porém não gera o arquivo em código-objecto. As instruções são traduzidas para linguagem de máquina
em tempo de execução, instrução a instrução. A figura a seguir mostra os passos para a execução de
um código em linguagem de alto nível por meio da compilação.

Compilação de um programa.

A figura abaixo apresenta o esquema similar para a interpretação.


8

Interpretação de um programa.

Pode-se notar que programar um computador tornou-se muito mais fácil do que anteriormente. A
introdução de linguagens de alto nível como Pascal, C, C++, Cobol, Java, entre outras, aumentou a
produtividade dos programadores, permitindo que programas mais elaborados fossem feitos,
atendendo a demandas mais complexas. Além disso, os códigos são portáteis, ou seja, independentes
da plataforma mediante nova compilação do código-fonte.

Na verdade, os computadores não executam código escrito em linguagem de programação. Esse


código que é denominado código-fonte deve ser traduzido para código em linguagem de máquina. Essa
tradução é realizada por programas especiais chamados compiladores.

Processo de compilação e execução de um programa.

• Conceito de Algoritmo
Para Forbellone (1999), é uma sequência de passos que visa atingir um objectivo bem definido.

Segundo Ascencio (1999), é a descrição de uma sequência de passos que deve ser seguida para a
realização de uma tarefa.

De acordo com Salvetti (1999), é uma sequência finita de instruções ou operações cuja execução, em
tempo finito, resolve um problema computacional, qualquer que seja sua instância.

Para Manzano (1997), são regras formais para a obtenção de um resultado ou da solução de um
problema, englobando fórmulas de expressões aritméticas.

Na óptica de Farrer (1999), acção é um acontecimento que, a partir de um estado inicial, após um
período de tempo finito, produz um estado final previsível e bem-definido. Portanto, um algoritmo é a
descrição de um conjunto de comandos que, obedecidos, resultam numa sucessão finita de acções.
9

Em suma, um algoritmo é uma sequência ordenada, e não ambígua2, de instruções (passos) que é
executada até que determinada condição se verifique. No entanto, um algoritmo pode repetir passos
(fazer iterações) ou necessitar de decisões (tais como, comparações ou lógica) até que a tarefa seja
completada.

De salientar que um algoritmo não representa, necessariamente, um programa de computador, e sim


os passos necessários para realizar uma tarefa. A sua implementação pode ser feita por um
computador, por outro tipo de autómato3 ou mesmo por um ser humano. As etapas para o
desenvolvimento de um algoritmo: análise (estudo do problema e definição dos dados de entrada, processamento e
dados de saída), algoritmo (solução do problema e pode ser descrita em fluxograma, pseudocódigo ou português estruturado)
e codificação (transformação do algoritmo em uma linguagem de programação).

• Conceitos de Linguagem de Programação


É o conjunto de palavras e símbolos utilizada para escrever cada parte de um programa. Em outras
palavras, é um método padronizado para comunicar instruções para um computador. Pode também ser
considerado, um conjunto de regras sintáticas4 e semânticas5 usadas para definir um programa de
computador.

As linguagens de programação permitem que um programador especifique precisamente sobre quais


dados um computador vai actuar, como estes dados serão armazenados ou transmitidos e quais acções
devem ser tomadas sob várias circunstâncias. As linguagens de programação podem ser usadas para
expressar algoritmos com precisão. O conjunto de palavras (Tokens), compostos de acordo com essas
regras, constituem o código-fonte de um software. Esse código-fonte é depois traduzido para código de
máquina, que é executado pelo processador.

Uma das principais metas das linguagens de programação é permitir que programadores tenham uma
maior produtividade, permitindo expressar suas intenções mais facilmente do que quando comparado
com a linguagem que um computador entende nativamente (código de máquina). Assim, linguagens de
programação são projectadas para adoptar uma sintaxe de nível mais alto, que pode ser mais
facilmente entendida por programadores humanos. Linguagens de programação são ferramentas
importantes para que programadores e engenheiro de software possam escrever programas mais
organizados e com maior rapidez.

São inúmeras as linguagens de programação com as quais um programador consegue implementar


um algoritmo. Eis, algumas linguagens de programação: Java, C++, C#, C, Ruby, Python, JavaScript,
PHP, HTML5, Matlab, Visual Basic .NET, R, Perl, Swift, Delphi, Object Pascal, Visual Basic, Assembly
Language, Objective-C, Scratch, Go, PL/SQL, Lisp, Fortran, etc.

2
(adjectivo) define algo que tem ou pode ter mais do que um sentido, ou seja, alguma coisa que pode ter múltiplos
significados. Por exemplo: eu vi um homem na montanha com um binóculo. Pergunta-se: quem estava com o
binóculo, eu ou o homem?).
3
Aquele que não age nem pensa por conta própria. Mero repetidor de acções. Em outras palavras, é uma máquina
ou robô que se opera de maneira automática.
4
conjunto de regras formais para a composição de um texto na linguagem (programa) a partir do agrupamento de
letras, dígitos e/ou outros caracteres (alfabeto da linguagem). A sintaxe é um conjunto de regras formais para a
escrita do programa.
5
diz respeito à significação. A semântica é o estudo do sentido dos significantes. Em programação a semântica
diz respeito ao significado do programa sintaticamente válido. O que pode ocorrer é que um programa seja
sintaticamente válido sem, no entanto, ter um significado lógico coerente. É parte do domínio da semântica
verificar esta coerência em termos de significado linguístico e não em termos de lógica de programa.
10

• Propriedades desejáveis de uma Linguagem de Programação


 Legibilidade – está relacionada com a facilidade de leitura dos códigos;
 Redigibilidade - relaciona-se com a facilidade em escrever programas;
 Confiabilidade - está relacionada com os mecanismos fornecidos pela Linguagem de
Programação para incentivar a construção de programas confiáveis;
 Eficiência - de acordo com as demandas por recursos de um tipo de aplicação, certas
Linguagens de Programação são mais recomendadas, e outras não devem ser usadas;
 Facilidade de aprendizado - o programador deve ser capaz de aprender a linguagem com
facilidade. Linguagens de Programação com muitas características e múltiplas maneiras de
realizar a mesma funcionalidade tendem a ser mais difíceis de aprender;
 Ortogonalidade - diz respeito a capacidade de a Linguagem de Programação permitir ao
programador combinar seus conceitos básicos sem que se produzam efeitos irregulares nessa
combinação. Assim uma Linguagem de Programação é tão mais ortogonal quanto menor for o
número de excepções aos seus padrões regulares;
 Reusabilidade - possibilita a reutilização do mesmo código para diversas aplicações. Quanto
mais reusável for um código, maior será a produtividade de programação, uma vez que, na
construção de novos programas, bastará adaptar códigos escritos anteriormente sem a
necessidade de reconstruí-los;
 Modificabilidade - refere-se às facilidades oferecidas pela Linguagem de Programação para
possibilitar ao programador alterar o programa em função de novos requisitos, sem que tais
modificações impliquem mudanças em outras partes do programa;
 Portabilidade - é altamente desejável que programas escritos em uma Linguagem de
Programação se comportem da mesma maneira independentes da ferramenta utilizada para
traduzi-los para a linguagem de máquina ou da arquitectura computacional (hardware ou
sistema operativo) sobre a qual estão sendo executados.

Dessa forma, não existe a melhor ou a pior Linguagem de Programação, isso não depende somente
da linguagem e sim para que tipo de projecto será aplicado.

• Níveis das Linguagens de Programação

Linguagem de É uma linguagem mais próxima da Linguagem Máquina, ou seja, mais


Baixo Nível próxima do hardware.

Linguagem de É suficientemente próxima do hardware e ao mesmo tempo prevê a


Nível Médio utilização de recursos de alto nível.

São linguagens, cada vez mais, afastadas da Linguagem Máquina, através do uso de
Linguagem de abstracções cada vez mais complexas. Estas abstracções se dão em função da
Alto Nível adopção de tipos de dados, palavras reservadas, funções, procedimentos automáticos.
11

Unidade Temática HC HEI


2. Princípios fundamentais de Programação Orientada a Objectos 8 4

• Introdução
Embora as linguagens orientadas a objectos já existam desde a década de 1960, o objectivo da
Programação Orientada a Objectos é aproximar o mundo digital do mundo real. Nas décadas 40 e 50,
a programação era feita em baixo nível, o programador não tinha nenhum tipo de programação e,
nenhum tipo de comandos, todas as instruções eram dadas aos computadores da maneira que ele
compreendia. Então, se o computador era binário, o programador deveria dar instruções em binário,
se era em decimal, o programador deveria dar instruções em decimal. Esse tipo de linguagem é de
difícil programação.

Depois disso, começaram a surgir as linguagens de alto nível, porém, elas surgiram modestamente. E
a programação se tornou linear, isto é, os comandos já eram compreensíveis pelos programadores,
mas, não era possível fazer muita coisa, porque era linear (de cima para baixo) sem muitos desvios,
sem rotinas internas. Na programação linear, pressupõe a criação de programas que, na sua
execução, obedeçam a uma sequência de passos executados consecutivamente, com início e fim
específicos. A desvantagem da programação linear é a sua complexidade. Os programas lineares
extensos são difíceis de ser desenvolvidos e até compreendidos.

A evolução da programação linear deu origem a programação estruturada. A programação estruturada


permitia pequenos pedaços de programação linear, só que organizados de maneira que elas
pudessem ser executadas fora da ordem natural das coisas, isso deu origem aos sistemas. A coisa
parou de ser pequenos programinhas e, começaram a dar origem a sistemas.

Usando o velho provérbio “dividir para conquistar”, pode-se afirmar que, para a consecução de um
objectivo, é melhor e bem mais fácil dividir as tarefas a serem realizadas em etapas, executando-as
uma por vez, até que todo o trabalho tenha sido realizado. Na programação estruturada, a divisão de
tarefas é um processo chamado de modularização. Nesse processo, divide-se o programa em partes
ou módulos que executam tarefas específicas.

Com o crescimento dos sistemas, a programação estruturada começou a falhar em alguns conceitos
e aí precisou-se de criar outro tipo de linguagem, uma linguagem um pouco mais evoluída, chamada
de programação modular.

A programação modular permitia que o programador cria-se pequenos módulos estruturados,


valorizando dados e funcionalidades e colocar eles em pequenas cápsulas protegidas que podiam
compor sistemas mais complexos. A programação modular teve uma vida um tanto quanto reduzida,
isso porque, surgiu logo um outro paradigma que ampliava ainda mais os conceitos da programação
modular.
A programação Orientada a Objectos representa uma mudança no enfoque da programação, na forma
como os sistemas eram vistos até então. Representa uma quebra de paradigma1, revolucionando
todos os conceitos de projecto e desenvolvimento de sistemas existentes anteriormente.

1
Paradigma é um conjunto de regras que estabelecem fronteiras e descrevem como resolver os problemas dentro
dessas fronteiras. Os paradigmas influenciam nossa percepção; ajudam-nos a organizar e a coordenar a maneira
como olhamos para o mundo.

Elaborado por: Bruno Alfredo Gamito, MSc.


12

• Objectos
Segundo Puga e Rissetti (2004, 36), um dos conceitos básicos da Orientação a Objectos é o do próprio
objecto. Um objecto é uma extensão do conceito de objecto do mundo real, em que se podem ter
coisas tangíveis, um incidente (evento ou ocorrência) ou uma interacção (transacção ou contrato).

Fonte: Puga e Rissetti (2004: 36).

Por outras palavras, é uma representação abstracta de coisas do mundo real, possui atributos
(características) e métodos (funções/acções), facilitam a compreensão do mundo real e oferecem
uma base real para implementação em computador.

Por exemplo, em um sistema académico em que o fulano x é um aluno (objecto) e fulano y é um


docente (objecto) que lecciona aulas (objecto) da disciplina (objecto) programação orientada a
objectos, para que o fulano x possa assistir às aulas da disciplina do professor fulano y, ele precisa
fazer uma matrícula (objecto) no curso (objecto) de Informática.

Têm-se as ocorrências de objectos citados:


Tangíveis Fulano x e Fulano y
Incidente Curso, aulas e disciplina
Interacção Matrícula

A identificação dos objectos em um sistema depende do nível de abstracção 2 de quem faz a


modelagem, podendo ocorrer a identificação de diferentes tipos de objectos e diferentes tipos de
classificação desses objectos. Não existe um modelo definitivamente correcto; isso vai depender de

2
Abstracção consiste em se concentrar nos aspectos essenciais, próprios, de uma entidade e em ignorar suas
propriedades acidentais. Isso significa concentrar-se no que um objecto é e faz, antes de decidir como ele deve
ser implementado em uma linguagem de programação.

Elaborado por: Bruno Alfredo Gamito, MSc.


13

quem faz a modelagem e de processos sucessivos de refinamento, até que se possa encontrar um
modelo adequado a sua aplicação.

• Classes
O conceito de classes é muito importante para o entendimento da Orientação a Objectos. Uma classe
é um conjunto ou colecção de objectos que podem ser descritos por um conjunto básico de atributos
(características) e possuem operações (métodos) semelhantes.

Quando um objecto é identificado com atributos e métodos semelhantes em nosso sistema, diz-se
que pode ser agrupado em uma classe. Esse processo é chamado de generalização. Por outro lado,
pode ocorrer que um objecto, ao ser identificado, constitua-se, na verdade, de uma classe de objectos,
visto que dele podem se derivar outros objectos. Já esse processo é chamado de especialização.

Fonte: Puga e Rissetti (2004: 38).

Na figura acima, partindo-se de uma classe veículo, observa-se a existência de vários tipos de veículos
e a criação de especializações como: os utilitários, os veículos de exporte, os de passeio e os de
transporte de passageiros. Porém, se os veículos utilitários, exporte, de passeio e de transporte de
passageiros são considerados tipos de veículos, pode-se generalizar criando-se a classe veiculo.

Em suma, o mundo da Orientação a Objectos (OO) é totalmente semelhante ao mundo a nossa volta,
onde tudo é tratado como objecto. Então, não existe mais a palavra “Programa” para definir um
conjunto de instruções que o computador deverá executar, mas sim, uma “Classe”.

Por exemplo, imaginemos uma casa: a planta que deu origem a esta é uma classe, e a casa pronta,
que se pode habitar e colocar os móveis, é o objecto. Ou seja, uma classe é a implementação lógica,
a ideia original, enquanto o objecto é a implementação física.

• Abstracção
A programação orientada a objectos (POO), é onde o programador tem de começar a pensar fora da
“caixa”, a imaginar uma forma aonde será preciso recorrer ao mundo real para o desenvolvimento dos
programas, pois hoje toda a programação em Java é orientada a objectos.

Elaborado por: Bruno Alfredo Gamito, MSc.


14

Para obter esse entendimento, é necessário conhecer alguns dos pilares da Orientação a Objectos
que são: abstracção, encapsulamento, herança e polimorfismo.

A abstracção é utilizada para a definição de entidades do mundo real. Sendo onde são criadas as
classes. Essas entidades são consideradas tudo que é real, tendo como consideração as suas
características e acções.

Entidade Características Acções


Carro, Moto Tamanho, cor, peso, altura, … Acelerar, parar, ligar, desligar
Elevador Tamanho, peso máximo, … Subir, descer, escolher o andar
Conta do banco Saldo, limite de levamento, número da conta Depositar, levantar, ver extracto

Em suma, o poder de abstracção é inerente ao ser humano. Desde cedo aprendemos a arte de abstrair
ou – em termos mais, digamos abstractos – simplificar. Em outras palavras, devemos nos importar
com os aspectos relevantes do problema em questão.

• Encapsulamento
É a técnica utilizada para esconder/ocultar uma ideia, ou seja, não expor detalhes internos para o
utilizador, tornando partes do sistema mais independentes possível.

Em um processo de encapsulamento os atributos (características) das classes são do tipo private.


Para aceder esses tipos de modificadores, é necessário criar os métodos setters e getters. Por
entendimento os métodos setters servem para alterar a informação de uma propriedade de um
objecto. E os métodos getters para retornar o valor dessa propriedade.

• Herança
Na POO, o significado de herança tem o mesmo significado para o mundo real. Assim como um filho
pode herdar alguma característica do pai, na Orientação a Objectos é permitido que uma classe herde
atributos e métodos da outra, tendo apenas uma restrição para a herança. Os modificadores de
acessos das classes, métodos e atributos só podem estar com visibilidade public e protected para
que sejam herdados.

Uma das grandes vantagens de usar o recurso da herança é na reutilização do código. Esse
reaproveitamento pode ser accionado quando se identifica que o atributo ou método de uma classe
será igual para as outras. Para efectuar uma herança de uma classe é utilizada a palavra reservada
chamada extends.

A herança nada mais é do que a implementação da generalização; é o compartilhamento de atributos


e operações (métodos) entre classes com base em um relacionamento hierárquico. Quando se cria
uma nova instância de um objecto, dizemos, em Orientação a Objectos, que esse novo objecto herda
os atributos e operações de sua classe.

O conceito de herança é importantíssimo na orientação a objectos pois, a programação fica bastante


facilitada. Os códigos escritos na definição da classe, de seus atributos e operações são aproveitados
por suas subclasses e instâncias de objecto, o que reduz o número de linhas de programação, gera
maior qualidade e facilita a programação, a verificação de erros e futuras correcções.

Elaborado por: Bruno Alfredo Gamito, MSc.


15

• Polimorfismo
O polimorfismo (significa a capacidade de assumir muitas formas), pois permite que um objecto
assuma um comportamento diferente daquele definido em sua classe. Pode parecer um pouco
abstracto sobre expressar “muitos comportamentos diferentes”. Por exemplo, pense no termo abrir.
Podemos abrir uma porta, uma caixa, uma janela, uma conta no banco, um caderno, etc. A palavra
abrir pode ser aplicada a muitos objectos diferentes no mundo real. Cada objecto interpreta “abrir” de
sua própria maneira. Entretanto, em cada caso, podemos simplesmente dizer “abrir”, para descrever
a acção.

• Ligação dinâmica
Na programação orientada a objectos, ligação dinâmica significa determinar a exacta implementação
de uma requisição com base no nome da requisição (operação) e no objecto que executa a operação
no tempo de execução. Geralmente acontece quando uma função membro de uma classe derivada é
invocada usando um ponteiro para sua classe base. A implementação de uma classe derivada será
invocada ao invés da operação da classe base. Isto permite a substituição de uma implementação
particular usando a mesma interface, o que por sua vez habilita o uso de polimorfismo.

Em outras palavras, a vinculação/ligação dinâmica permite determinar dinamicamente qual método


será executado, para isso, deve-se declarar uma referência do tipo da superclasse e criado um objecto
da subclasse. A ligação dinâmica permite o polimorfismo, uma vez que, a chamada do mesmo método
pode assumir várias formas, dependendo do objecto criado em tempo de execução.

Em suma, quando referências de uma superclasse são utilizadas para referenciar instâncias de
subclasses, a compilação fará a checagem pelos tipos da referência. Na hora da execução, porém, o
que conta é a classe a qual pertence a instância referenciada. Esse mecanismo é que permite ao Java
decidir em tempo de execução qual o método que será activado em função da classe a qual pertence
a instância referenciada é chamado de ligação dinâmica.

O fato de Java implementar ligação dinâmica é que permite que o mesmo explore a característica do
polimorfismo. Explorando esta característica é possível construir algoritmos genéricos que trabalham
com as referências para uma superclasse, mas que se aplicam as subclasses sem a necessidade de
testes para determinar o tipo de instância que está sendo referenciada.

• Comunicação de Objectos
Na programação Orientada a Objectos, o programador é responsável por moldar o mundo dos
objectos, e explicar para estes (objectos) como eles devem interagir entre si. Os objectos “conversam”
uns com os outros através do envio de mensagens, e o papel principal do programador é especificar
quais serão as mensagens que cada objecto pode receber, e também qual a acção que aquele objecto
deve realizar ao receber aquela mensagem em específico.

Uma mensagem é um pequeno texto que os objectos conseguem entender e, por questões técnicas,
não pode conter espaços. Junto com algumas dessas mensagens ainda é possível passar algumas
informações para o objecto (parâmetros), dessa forma, dois objectos conseguem trocar informações
entre si facilmente.

• Benefícios da Programação Orientada a Objectos


A programação orientada a objectos define seis objectivos (benefícios) sobrepostos para o
desenvolvimento de programas, a saber:

Elaborado por: Bruno Alfredo Gamito, MSc.


16

 Natural – A POO produz um programa natural. Os programas naturais são mais inteligíveis,
ou seja, é mais fácil de entender. O programador se preocupa mais na funcionalidade do que
nos detalhes de implementação;
 Confiável – Para criar um programa útil, precisamos criar programa que seja tão confiável
quanto outros produtos. Em outras palavras, o isolamento entre as partes gera programa
seguro. Ao alterar uma parte, nenhuma outra é afectada;
 Oportuno – O ciclo de vida do projecto de um programa moderno é frequentemente medido
em semanas. Quando o programador divide um programa em vários objectos, o
desenvolvimento de cada parte pode ocorrer em paralelo;
 Manutenível – O ciclo de vida de um programa não termina quando o desenvolvedor o
distribui. Em vez disso, o desenvolvedor deve manter sua base de código;
 Extensível – Assim como um programador deve manter um programa, seus utilizadores
exigem o acréscimo de nova funcionalidade em seu sistema. Em outras palavras, o programa
não é estático. Ele deve crescer para permanecer útil;
 Reutilizável – Podemos usar objectos de um sistema que criamos em outro sistema futuro.

• Aplicações em Programação Orientada a Objectos


Durante anos, os programadores se dedicaram a construir aplicações muito parecidas que resolviam
uma vez ou outra, os mesmos problemas. Para conseguir que os esforços dos programadores possam
ser utilizados por outras pessoas foi criado a POO. Esta é uma série de normas de realizar as coisas
de maneira com que outras pessoas possam utilizá-las e adiantar seu trabalho, de maneira que
consigamos que o código possa se reutilizar.

A POO não é difícil, mas é uma forma especial de pensar, às vezes subjectiva de quem a programa,
de forma que a maneira de fazer as coisas possa ser diferente segundo o programador. Embora
possamos fazer os programas de formas distintas, nem todas elas são correctas, o difícil não é
programar orientado a objetos e sim, programar bem. Programar bem é importante porque assim
podemos aproveitar todas as vantagens da POO.

Outro aspecto a salientar é que nos desenvolvimentos de sistemas, existem alguns factores
importantes como: o entendimento do código, fácil manutenção, reaproveitamento entre outros. Para
isso, a Programação Orientada a Objetos (POO), tem a intenção de ajudar nesses factores, dando
tempo e agilidade no desenvolvimento de um sistema para o programador.

A Programação Orientada a Objetos foi criada por Alan Kay, autor da linguagem Smalltalk. Antes
mesmo da criação Orientada a Objectos, já existiam algumas aplicações, neste caso da linguagem
Simula 67, criada por Ole Johan Dahl e Kristen Nygaard em 1967.

Elaborado por: Bruno Alfredo Gamito, MSc.


17

Unidade Temática HC HEI


3. Evolução da Linguagem Java 2 2

• A linha do tempo de Java


O Java está relacionado ao C++, que é descendente directo do C. Grande parte dos caracteres Java
é herdada destas duas linguagens. Do C, o Java tirou sua sintaxe. Muitas das características
orientadas a objecto do Java foram influenciadas pelo C++. Na verdade, muitas das características
que definem o Java vieram – ou são respostas – de seus predecessores. Além disso, a criação do
Java foi profundamente enraizada no processo de refinamento e adaptação que vem ocorrendo nas
linguagens nas últimas décadas. Cada inovação no design da linguagem foi orientada pela
necessidade de solucionar um problema fundamental que as linguagens anteriores não podiam
resolver. O Java não é excepção.

• O Nascimento da Programação Moderna: C


A linguagem C sacudiu o mundo da informática. Seu impacto não deveria ser subestimado, pois esta
linguagem mudou a forma como a programação era abordada e pensada. A criação do C foi o
resultado directo da necessidade de uma linguagem estruturada, eficiente e de alto nível que pudesse
substituir o código de montagem na criação de programas para sistemas. É sabido de antemão,
quando uma linguagem é projectada, geralmente ocorrem trocas como as seguintes:
• Facilidade de uso versus potência;
• Segurança versus eficiência;
• Rigidez versus extensibilidade.

Antes do C, os programadores geralmente tinham de escolher entre linguagens que optimizavam um


ou outro conjunto de características. Por exemplo: embora o FORTRAN pudesse ser usado para
escrever programas razoavelmente eficientes para aplicativos científicos, ele não era muito bom para
escrever código de sistema.

E, enquanto o BASIC era fácil de aprender, ele não era muito potente, e sua falta de estrutura tornou
sua utilidade questionável para programas maiores. A linguagem Assembly pode ser usada para gerar
programas altamente eficientes, mas não é tão fácil de ser aprendida ou não pode ser usada de
maneira eficiente. Além disso, depurar um código Assembly pode ser bem difícil. Outro problema era
que as primeiras linguagens, como BASIC, COBOL e FORTRAN, não eram projectadas a partir de
princípios estruturados. Ao invés disso, elas dependiam do GOTO como seu principal meio de controlo
do programa. Como resultado, os programas escritos com estas linguagens tendiam a produzir um
código conhecido como “macarronada” – um emaranhado de saltos e ramificações condicionais que
tornam um programa virtualmente impossível de se compreender.

Ao mesmo tempo em que linguagens como Pascal eram estruturadas, elas não eram projectadas para
serem eficientes e não eram capazes de incluir determinadas características necessárias para que
pudessem ser aplicadas em uma grande variedade de programas. (Especificamente, com os dialetos
padrão para Pascal disponíveis na época, não era prático considerar o uso do Pascal para códigos
em nível de sistema).

Portanto, pouco antes da invenção do C, nenhuma outra linguagem havia reconciliado os atributos
conflitantes que levaram ao fracasso. Ao mesmo tempo, a necessidade por uma linguagem que
solucionasse estes conflitos era urgente. No início dos anos 1970, a revolução na informática já estava
começando a se estabelecer, e a demanda por software estava cada vez mais superando a
capacidade dos programadores de produzi-lo. Foi feito um grande esforço nos círculos académicos
em uma tentativa de criar uma linguagem melhor. Mas, e talvez o mais importante, uma força
secundária começava a ser sentida. O hardware finalmente se tornava comum o suficiente e um

Elaborado por: Bruno Alfredo Gamito, MSc.


18

número importante de pessoas estava sendo atingido. Os computadores não eram mais mantidos
atrás de portas trancadas. Pela primeira vez, os programadores obtinham acesso praticamente
ilimitado às suas máquinas. Isso lhes dava liberdade para experimentar e também permitia a eles
começarem a criar suas próprias ferramentas.

Às vésperas da criação do C, o palco estava pronto para um grande salto no que dizia respeito às
linguagens. Inventado e implementado pela primeira vez por Dennis Ritchie em um DEC PDP-11 com
sistema operativo UNIX, o C foi o resultado de um processo de desenvolvimento que começou com
uma linguagem mais antiga, chamada BCPL 1, desenvolvida por Martin Richards. O BCPL influenciou
uma linguagem, chamada B, inventada por Ken Thompson, que levou ao desenvolvimento do C nos
anos 1970.

Dennis M. Ritchie (de pé) e Ken L. Thompson (sentado), inventores da Unix, no Bell Labs, em frente a um
computador DEC PDP-11, em 1970 (Computer History Museum).

Durante muitos anos, o padrão de facto para C era fornecido pelo sistema operativo UNIX e descrito
em 1986, no livro C: A Linguagem de Programação, de Brian Kernighan e Dennis Ritchie. A linguagem

1
É uma linguagem de Programação, criada por Martin Richards, da Universidade de Cambridge em 1966. Anos
depois foi utilizada por Ken Thompson para desenvolver a linguagem B, que se tornaria a base para a linguagem
C. O nome BCPL é acrónimo para Basic Combined Programming Language (Linguagem de Programação Básica
Combinada).

Elaborado por: Bruno Alfredo Gamito, MSc.


19

C foi oficialmente padronizada em dezembro de 1989, quando o padrão ANSI (American National
Standards Institute) para C foi adoptado.

A criação do C é considerada por muitos como o marco do início da idade moderna das linguagens.
Ela sintetizava, de maneira bem-sucedida, os atributos conflitantes que tanto atormentavam as outras
linguagens. O resultado foi uma linguagem potente, eficiente e estruturada, e relativamente fácil de
usar. Ela também tinha um aspecto quase que intangível: era uma linguagem de programador. Antes
da invenção do C, as linguagens eram projectadas de maneira geral como exercícios académicos ou
por comités burocráticos. O C era diferente. A linguagem havia sido projectada, implementada e
desenvolvida por programadores de verdade, reflectindo o modo como eles abordavam a tarefa de
desenvolver. Seus recursos foram ajustados, testados, pensados e repensados pelas pessoas que
realmente usavam a linguagem. O resultado foi uma linguagem que os programadores gostavam de
usar. De facto, o C rapidamente atraiu muitos seguidores que tinham um zelo quase que religioso com
a linguagem. Sendo assim, o C encontrou uma aceitação ampla e rápida na comunidade de
programadores. Resumindo, o C é uma linguagem criada por e para programadores. Visto que, o Java
herdou uma parte desta linguagem.

• C++: O Passo Seguinte


No final dos anos 1970 e início dos anos 1980, o C era a linguagem dominante e ainda é bastante
utilizada actualmente. Como o C é uma linguagem bem-sucedida e útil, podemos nos estar se
perguntando por que havia a necessidade de algo mais. A resposta é: complexidade. Na história da
programação, a crescente complexidade dos programas orientou a necessidade por melhores
maneiras de gerir tal complexidade. O C++ é uma resposta a esta necessidade. Para entender melhor
por que gerir a complexidade de um programa foi fundamental para a criação do C++, considere o
seguinte:

As abordagens com relação à programação mudaram drasticamente desde a invenção do


computador. Por exemplo: quando os computadores foram inventados, a programação era feita
manualmente através de instruções de uma máquina binária no painel frontal. Desde que os
programas tivessem algumas centenas de instruções, esta abordagem funcionava. Com o crescimento
dos programas, a linguagem Assembly foi inventada para que o desenvolvedor pudesse lidar com
programas cada vez maiores e mais complexos, utilizando representações simbólicas das instruções
da máquina. Como os programas continuaram crescendo, as linguagens de alto nível foram
introduzidas e deram ao desenvolvedor mais ferramentas para lidar com a complexidade.

A primeira linguagem mais difundida, é claro, foi o FORTRAN. Ao mesmo tempo em que o FORTRAN
foi um primeiro passo impressionante, não se trata de uma linguagem que incentive programas claros
e fáceis de entender. Nos anos 1960 nasceu a programação estruturada. Este é o método de
programação preferido das linguagens como C. O uso de linguagens estruturadas permitiu aos
programadores escrever, de modo relativamente fácil, pela primeira vez, programas moderadamente
complexos. No entanto, mesmo com os métodos de programação estruturada, uma vez que um
projecto atinja um determinado tamanho, sua complexidade excede o que pode ser gerido pelo
programador. No início dos anos 1980, muitos projectos forçavam os limites da abordagem
estruturada. Para resolver este problema, uma nova maneira de programar foi inventada e chamada
de programação orientada a objectos (em inglês, object-oriented programming – OOP), visto que, é
uma metodologia de programação que ajuda a organizar programas complexos através do uso de
herança, encapsulamento e polimorfismo.

Na análise final, embora o C seja uma das maiores linguagens do mundo, existe um limite em sua
capacidade de lidar com a complexidade. Quando o tamanho de um programa excede um determinado
ponto, ele fica tão complexo que é difícil vê-lo na totalidade. Enquanto o tamanho preciso em que isso

Elaborado por: Bruno Alfredo Gamito, MSc.


20

ocorre difere dependendo da natureza do programa e do programador, sempre há um ponto em que


um programa passa a deixar de ser gerenciável. O C++ agregou recursos que permitiam que este
ponto fosse eliminado, possibilitando aos programadores compreenderem e gerissem programas
maiores.

O C++ foi inventado por Bjarne Stroustrup, em 1979, quando ele trabalhava na Bell Laboratories, em
Murray Hill, New Jersey. Stroustrup inicialmente chamou a linguagem de “C com Classes”. No entanto,
em 1983, o nome foi mudado para C++. O C++ estende o C, adicionando recursos orientados a
objectos. Como o C++ é construído com base em C, ele tem todos os recursos, atributos e benefícios
da linguagem C. Este é um motivo crucial para o sucesso do C++ como linguagem. A invenção do
C++ não foi uma tentativa de criar uma linguagem totalmente nova; ela foi uma melhoria de uma
linguagem que já era muito bem-sucedida.

• O Palco Está Pronto para o Java


No final dos anos 1980 e início dos anos 1990, a programação orientada a objectos usando o C++ era
dominante. Na verdade, por um breve momento, parecia que os programadores haviam finalmente
encontrado a linguagem perfeita. Como o C++ misturava os elementos altamente eficientes e o estilo
do C com o paradigma da orientação a objectos, era uma linguagem que podia ser usada para criar
uma grande variedade de programas. No entanto, assim como havia ocorrido no passado, havia forças
trabalhando para, mais uma vez, levar a evolução da linguagem um passo à frente. Em alguns anos,
a World Wide Web e a Internet atingiriam um público gigantesco. Este evento desencadearia outra
revolução na programação.

• A Criação do Java
O Java foi concebido por James Gosling, Patrick Naughton, Chris Warth, Ed Frank e Mike Sheridan,
na Sun Microsystems, Inc., em 1991. Levaram-se 18 meses para desenvolver a primeira versão que
funcionasse. Inicialmente, a linguagem se chamava “Oak”, mas recebeu o nome “Java”, em 1995.
Entre a implementação inicial do Oak, na primavera de 1992, e o anúncio público do Java, no outono
de 1995, muitas outras pessoas contribuíram para o projecto e evolução da linguagem. Arthur Van
Hoff, Jonathan Payne, Frank Yellin e Tim Lindholm foram pessoas muito importantes no
amadurecimento do protótipo original.

Surpreendentemente, o objectivo original do Java não era a internet! Ao invés disso, a principal
motivação era a necessidade de uma linguagem independente de plataforma (isto é, com arquitectura
neutra), que pudesse ser usada para criar software que pudesse ser inserido em vários dispositivos
electrónicos de consumo, como fornos de micro-ondas e controlos remotos. Supondo, que muitos tipos

Elaborado por: Bruno Alfredo Gamito, MSc.


21

diferentes de CPUs são usados como controladores. O problema com C e C++ (e a maioria das outras
linguagens) é que elas são projectadas para serem compiladas com um objectivo específico. Embora
seja possível compilar um programa em C++ para praticamente qualquer tipo de CPU, fazer isso exige
um compilador C++ totalmente dedicado àquela CPU. O problema é que os compiladores são caros e
consomem tempo para serem criados. Uma solução mais fácil – e mais eficiente em termos de custo
– era necessária. Em uma tentativa de encontrar esta solução, Gosling e outros começaram a trabalhar
em uma linguagem portátil, independente de plataforma e que tivesse possibilidade de ser usada para
gerar código que pudesse ser executado em diversas CPUs e em diferentes ambientes. Estes esforços
acabaram levando à criação do Java.

Quando os detalhes do Java estavam sendo trabalhados, um segundo factor, que acabou sendo mais
importante, surgia e teria um papel crucial no futuro do Java. Esta segunda força era, é claro, a World
Wide Web. Se a web não tivesse sido criada quase que ao mesmo tempo em que o Java estava sendo
implementado, o Java seria uma linguagem útil, porém obscura, para programar aparelhos
electrónicos. No entanto, com o surgimento da World Wide Web, o Java foi colocado em primeiro lugar
no projecto de linguagens, pois a web também demandava programas portáteis.

A maioria dos programadores aprende, no início de suas carreiras, que os programas portáteis são
tão ilusórios quanto desejáveis. Ao mesmo tempo em que a busca por uma forma de criar programas
eficientes e portáteis (independentes de plataforma) é quase tão antiga quanto a disciplina de
programação em si, ela acabou pegando carona em outros problemas mais urgentes. Além disso,
como (na época) grande parte do mundo da informática havia se dividido em três acampamentos de
competição entre Intel, Macintosh e UNIX, a maioria dos programadores permanecia em seus limites
protegidos, e a necessidade urgente por um código portátil foi reduzida. No entanto, com o advento da
web e da Internet, o antigo problema da portabilidade voltou com uma vingança. Afinal de contas, a
Internet é composta por um universo, diverso e distribuído, ocupado por vários tipos de computadores,
sistemas operativos e CPUs. Embora muitos tipos de plataformas estejam ligados à Internet, os
utilizadores gostariam que todas eles fossem capazes de executar o mesmo programa. O que uma
vez foi um problema irritante, mas de baixa prioridade, passara a ser uma grande necessidade.

Por volta de 1993, tornou-se óbvio para os membros da equipe do projecto Java que os problemas de
portabilidade, frequentemente encontrados durante a criação de código para controladores integrados,
também são encontrados quando se tenta criar código para a Internet. Na verdade, o mesmo problema
para o qual o Java foi inicialmente criado para solucionar em pequena escala também podia ser
aplicado à Internet, só que em larga escala. Esta percepção fez com que o foco do Java passasse dos
electrónicos de consumo à programação para Internet. Portanto, enquanto o desejo por uma
linguagem de arquitectura neutra foi a faísca inicial, a Internet acabou conduzindo o Java ao sucesso
em larga escala.

Como mencionado anteriormente, o Java tem muitas características do C e do C++. Isso é intencional.
Os criadores do Java sabiam que, ao usar a sintaxe familiar do C e ao reproduzir os recursos
orientados a objectos do C++, sua linguagem chamaria atenção das legiões de programadores
experientes de C e C++. Além das similaridades superficiais, o Java compartilha outros atributos que
ajudaram no sucesso do C e do C++. Primeiro: o Java foi projectado, testado e refinado por
programadores de verdade. É uma linguagem baseada nas necessidades e experiências das pessoas
que o criaram. Portanto, o Java é uma linguagem para desenvolvedores. Segundo: o Java é coeso e
logicamente consistente. Terceiro: com excepção das restrições impostas pelo ambiente da Internet,
o Java proporciona, ao programador, controlo total. Se alguém programar bem, seu programa reflectirá
isto. Se programar mal, o programa também reflectirá. Em outras palavras, o Java não é uma
linguagem com “rodinhas” de treinamento; é uma linguagem para programadores profissionais.

Elaborado por: Bruno Alfredo Gamito, MSc.


22

Devido às similaridades entre Java e C++, é tentador pensar no Java como a “versão para Internet do
C++”. No entanto, este é um grande erro. O Java tem diferenças práticas e filosóficas significativas. É
verdade que a linguagem foi influenciada pelo C++, mas não é uma versão melhorada do C++. Por
exemplo: o Java não pode ser comparado superior ou inferiormente com o C++. É claro que as
similaridades com o C++ são importantes e, se alguém é programador de C++, se sentirá em casa
com o Java. Outro ponto: o Java não foi criado para substituir o C++, e sim para solucionar um
determinado conjunto de problemas. O C++ foi desenvolvido para solucionar um conjunto diferente de
problemas. Ambos irão coexistir durante os muitos anos que virão.

As linguagens evoluem por dois motivos: para se adaptar às mudanças no ambiente e para
implementar avanços na arte da programação. A mudança ambiental que culminou na criação do Java
foi a necessidade de programas independentes de plataforma destinados à distribuição na Internet.
No entanto, o Java também incorpora mudanças no modo como as pessoas abordam a escrita dos
programas. Por exemplo: o Java melhorou e refinou o paradigma da programação orientada a objectos
usado pelo C++, acrescentou suporte integrado à programação multithread (em várias linhas de
execução) e forneceu uma biblioteca que simplifica o acesso à Internet. Na análise final, no entanto,
não foram as características individuais do Java que o tornaram tão marcante; foi a linguagem como
um todo. O Java foi a resposta perfeita para as demandas do então novo e altamente distribuído
universo da informática. O Java foi para a programação da Internet o que o C foi para a programação
de sistemas: uma força revolucionária que mudou o mundo.

James Gosling

• Linha do tempo Java (Evolução)2


1991 – Green Project, “O berço do java” (FEB 19, 1991)
Na Sun Microsystems, foi iniciado o Green Project, o berço do Java, uma linguagem de programação
orientada a objectos. Os mentores do projecto eram Patrick Naughton, Mike Sheridan, e James
Gosling. O objectivo do projecto não era a criação de uma nova linguagem de programação, mas
antecipar e planear. Eles acreditavam que, em algum tempo, haveria uma convergência dos
computadores com os equipamentos e electrodomésticos comumente usados pelas pessoas no seu
dia-a-dia.

2
https://www.timetoast.com/timelines/linha-do-tempo-java--2

Elaborado por: Bruno Alfredo Gamito, MSc.


23

1992 – StarSeven (JAN 1, 1992)


No verão de 1992 eles emergiram de um escritório de Sand Hill Road, no Menlo Park, com uma
demonstração funcional da ideia inicial. O protótipo se chamava *7 (lê-se “StarSeven”), um controlo
remoto com uma interface gráfica touchscreen. O trabalho do Duke no *7 era ser um guia virtual
ajudando e ensinando o utilizador a utilizar o equipamento. O *7 tinha a habilidade de controlar diversos
dispositivos e aplicações. James Gosling especificou uma nova linguagem de programação para o *7.

1993 – E chega a Internet (JAN 1, 1993)


Com o advento em 1993 do web browser Mosaic e das páginas estáticas HTML a vida das pessoas
sofreria uma mudança profunda, bem como a do projecto Green. Com o objectivo de tirar proveito
desse mercado o projecto Green sofre algumas mudanças e adaptações, se chamando Oak.

1994 – WebRunner (JAN 1, 1994)


Para demonstrar o potencial da linguagem, construíram em 1994, com a própria linguagem um
browser, que podia correr applets, e designaram-no por WebRunner.

1995 – Enfim Java (JAN 1, 1995)


Em janeiro de 1995, verificaram que “oak” era uma marca registada da “Oak Technologies” e
“WebRunner” da “Taligent”. Fizeram uma reunião do tipo “brainstorm”, e surgiram muitos nomes, um
dos quais Java por um dos Donos que estava a beber café Pete’s Java, este foi o escolhido para a
linguagem e HotJava para o browser com isso a Sun anuncia formalmente a linguagem Java, mas
esta linguagem só alcançou muita popularidade depois da Netscape a licenciar em agosto de 1995.

1996 – JDK é lançado (JAN 1, 1996)


O Release do JDK 1.0 é lançado, Java SE Development Kit (JDK) for Windows é que até hoje é um
Kit de Desenvolvimento Java, ou seja, um conjunto de utilitários que permitem criar sistemas de
software para plataforma Java, mais já em maio é realizado o primeiro JavaOne, conferência máxima
da tecnologia Java. Apresentados a tecnologia JavaBeans e Servlets. Já em outubro e dezembro é
anunciada a API Java Card e é lançado o release do JDK 1.1 Beta.

1997 – Java Development Kit (JDK ou Java 1.1)


Obteve muitas bibliotecas adicionadas das quais se destacaram o Java RMI, JavaBeans, novo modelo
de eventos, JDBC (driver para conexão com base de dados).

1998 – Java Standard Edition (J2SE 1.2 ou Java2)


Com o tempo surgiu a versão do Java 1.2, que obteve um grande aumento das classes na biblioteca
Java (API), ficando considerada a versão da mudança do nome para as versões do produto (JDK) e
também sendo optada pela divisão de 3 tipos de plataformas. O principal motivo para essa acção foi
que muitos desenvolvedores e utilizadores estavam confundindo a linguagem Java da linguagem
JavaScript, que são diferentes. A partir daqui todas as versões Java foram denominadas de Java 2
Standard Edition, que passaram a ter apelidos ou codinomes, esta versão ficou conhecida como
Playground da qual foi adicionado o Framework Collections entre outros.

2000 – Java Standard Edition - J2SE 1.3


Codinome Kestrel, inclusão das bibliotecas JNDI, JavaSound entre outros.

Elaborado por: Bruno Alfredo Gamito, MSc.


24

2002 – Java Standard Edition J2SE 1.4


Codinome Merlin, criada a palavra reservada “assert”, biblioteca NIO entre outros.

2004 – Java Standard Edition J2SE 5.0


A versão mais usada, sendo conhecida com o codinome Tiger. Apesar da versão ser 1.5, agora é
chamada apenas de 5. Adições importantes como: Enumeração, Autoboxing, Generics, for-each entre
outros estão nela.

2006 – Java Standard Edition JSE 6


Codinome Mustang, teve outras alterações que mudaram na nomenclatura (remoção do 2 - J2SE) e
melhora significativa na performance e na estabilidade tendo o surgimento do JIT.

2011 – Java Standard Edition JSE 7


Possuindo alguns aperfeiçoamentos que são: (i) suporte ao uso de Strings em condições do switch;
(ii) inferência na criação de objectos com tipos genéricos; (iii) simplificação na invocação de métodos
com parâmetros varargs e tipos genéricos; (iv) gestão automática de recursos, tais como conexões a
base de dados, I/O; (v) possibilidade de tratar diversas excepções em um mesmo catch (Multicatch)
entre outros;

2014 – Java Standard Edition JSE 8


A partir do Java 8, há uma nova API de datas disponíveis no pacote java.time. Essa API é uma
excelente adição às bibliotecas padrão do Java e já vinha sendo desenvolvida desde 2007. Para além,
da API de datas, contém API de stream e as lambdas, inclui a remoção do permgen e termina com um
bom conjunto de melhorias.

Elaborado por: Bruno Alfredo Gamito, MSc.


25

Unidade Temática HC HEI


4. Características e diferença entre Java – C++ | Internet 2 2

• Características e diferença entre:


• Java e C++
A maneira de se programar em Java é muito similar à programação em C++. Mas, existem algumas
diferenças importantes. Estas mudanças referem-se a:
• Compilador e Interpretador – O compilador pode ser invocado por uma linha de comando
com a sintaxe: javac nome.java

Este arquivo nome.java pode conter mais de uma classe, mas apenas uma poderá ter o
atributo public. O compilador gera vários arquivos com extensão .class, um para cada classe
do arquivo compilado.

A exemplo do compilador, o interpretador pode também ser chamado por meio de uma linha
de comando com a seguinte sintaxe: java nome (o arquivo nome.class deve ser referente a
classe que possui o método main).

Os compiladores em Java são muito mais rígidos que os compiladores em C++, esta
característica do compilador facilita a portabilidade do código entre as linguagens.

• Herança - Da mesma maneira que C++, Java suporta herança de objectos, mas não suporta
herança múltipla. Em seu lugar Java admite uma nova construção chamada "interface". As
interfaces especificam o comportamento de um objecto sem definir a sua implementação. Java
suporta a herança múltipla de interfaces com o que se ganha muitos dos benefícios da herança
múltipla de classes sem seus inconvenientes. A Interface é uma colecção de definições de
métodos (sem implementação) e constantes.

Na declaração de uma classe pode constar que ela implementa uma ou mais interfaces.
Algumas características das interfaces são: (i) não é possível herdar variáveis; (ii) não é
possível herdar implementação de métodos; (iii) a hierarquia de Interfaces é independente da
hierarquia de Classes. Duas classes que implementam a mesma Interface não são
necessariamente relacionadas na hierarquia de Classes.

• Ponteiros – Neste ponto estão as grandes diferenças: o Java não tem ponteiros e não possui
destrutores. Os criadores da linguagem Java retiraram o tipo ponteiro. A princípio, isto pode
parecer uma grande dificuldade para aqueles que fazem grande uso deste recurso em outras
linguagens, mas é muito simples fazer esta mudança.

O que permitiu a eliminação dos ponteiros sem prejuízo para os programadores foi o facto de
que todas as variáveis são manipuladas por referência. Portanto onde se usava ponteiro,
passamos a usar simplesmente a variável. É importante ressaltar o problema de cópia de
objectos.

• O Java não define as funções de pré-processador #. As directivas de #if e #ifdef são menos
necessárias em Java. Em geral, servem para marcar código em linguagem específica.

• O Java não trabalha com os #includes, visto que, os compiladores tanto do Java, quanto do
C++ trabalham de maneira muito diferente nesta questão;

• O import do Java é absolutamente diferente do #include do C++;

Elaborado por: Bruno Alfredo Gamito, MSc.


26

• O compilador do C++ somente abrirá os arquivos específicos por completo no include;

• O compilador Java abrirá o arquivo que for necessário, bastando indicar onde procurar;

• O import é apenas uma questão de conveniência. Pode-se evitar por completo o import,
enquanto que, o include não;

• O Java organiza as classes em pacotes (packages), para tornar o projecto mais organizado;

• O Java importa os pacotes (packages) necessários para cada uma de suas classes;

• Os dados em Java não precisam ser destruídos. Não é necessário se preocupar com
vazamentos de memória, visto que, o Java possui um colector de lixo 1 (garbage collector)
implementado. Fazendo uma variável igual a null o programador pode forçar o dado a ser
recolhido antes do fim do escopo;

• No quesito, modificadores, no C++ os dados e métodos podem ser declarados como public,
protected e private; o processo de dynamic cast permanece inalterado; o modificador static
permanece inalterado;

• Não é necessário declarar um método em Java como virtual (este é seu comportamento
padrão);

• Pode-se prevenir a herança declarando métodos e classes como final; classes finais não
podem gerar herdeiras, enquanto que, os métodos finais não podem ser sobrecarregados pela
herança;

• O Java não permite herança múltipla. Em Java, uma classe pode ter uma única classe mãe
(superclasse). Esta exclusão foi proposital pois a herança múltipla torna os compiladores muito
complexos (C++) ou ineficientes (Eiffel). Para contornar o problema da herança múltipla foram
criadas as interfaces;

• O Java tem implementação fácil de multithread.

A simplicidade é uma de suas mais importantes características. É isso que possibilita que a sua
aprendizagem possa ocorrer sem necessidade de treinamento intensos ou larga experiência anterior.
Programadores com conhecimento das linguagens C e C++ encontrarão muitas semelhanças destas
com o Java e o assimilarão de forma ainda mais rápida. Além disso, o código escrito com o Java é
muito mais limpo do que aquele escrito em C ou C++.

Java é orientado a objectos e, com excepção dos tipos primitivos, tudo é representado na forma de
objectos. Até mesmo os tipos primitivos podem ser encapsulados em objectos sempre que isso for
necessário. Os programas são compostos por classes, que representam categorias de objectos e

1
O colector de lixo é um processo que roda em segundo plano e é responsável pela liberação de memória alocada
por variáveis que não mais serão utilizadas pela aplicação. Na linguagem de programação Java a responsabilidade
pela gestão da memória é do Colector de lixo (Garbage Collector), desta forma, programadores Java ficam livres
da preocupação de (des)alocação da memória.

Elaborado por: Bruno Alfredo Gamito, MSc.


27

podem herdar atributos e métodos de outras classes. A ausência de herança múltipla é compensada
com uma solução muito melhor: o uso de interfaces, onde uma classe pode herdar características de
uma superclasse e ainda implementar métodos de uma ou mais interfaces. Toda a variável ou método
pertence a uma classe ou objecto e só pode ser invocada através dessa classe ou objecto. Isso reforça
seu forte carácter orientado a objecto.

O Java também garante a confiabilidade dos programas produzidos. O processo de compilação


elimina uma gama enorme de possíveis problemas e uma checagem dinâmica (realizada em tempo
de execução) contorna muitas situações que poderiam gerar erros.

A confiabilidade dos programas escritos com o Java também é incrementada com um mecanismo
eficiente para contornar situações inesperadas que podem ocorrer em tempo de execução. Essas
condições excepcionais, são chamadas de excepções, podem ser devidamente tratadas para evitar
que o programa aborte, mesmo frente a situações de erro.

A segurança é outro ponto muito forte do Java. Um programa sempre é verificado antes de ser
executado. Essa verificação também é realizada nos navegadores e visa impedir que os applets
possam provocar quaisquer danos ao computador do utilizador. Ademais, como o Java não permite
acesso directo à memória, impede seu uso para desenvolvimento de vírus.

O Java também, é mais dinâmico que o C/C++. Ele foi projectado para se adaptar facilmente a
ambientes em constante evolução (como a Internet). A inclusão de novos métodos e atributos a
classes existentes pode ser feita livremente e o tipo de objecto pode ser pesquisado em tempo de
execução.

Como se não bastasse tudo isso, o Java contém recursos que permitem o desenvolvimento de
sistemas extremamente robustos. Dentre estes recursos, destaca-se o suporte a multiprocessamento
(multithread), que possibilita a um programa a realização de mais de uma tarefa ao mesmo tempo. O
resultado disso é o aumento da sensibilidade interactiva dos programas e seu comportamento em
tempo real.

Além de todas as vantagens anteriores, o Java ainda oferece facilidades para programação de
sistemas cliente-servidor e sistemas distribuídos. Sua API contém uma biblioteca de classes e
interfaces muito rica para se trabalhar com sockets, TCP/IP, RMI 2 (Remote Method Invocation) e muitos
outros recursos correlatos.

Devido às diversas características que o Java possui, a sua tecnologia tem provocado mudanças
significativas nos processos de engenharia de software e tende a alterar até o modo como os
programas são produzidos, distribuídos e utilizados.

• Java e Internet
A Internet ajudou o Java a liderar a Internet e o Java influenciou a Internet, simplificando a
programação web e o uso dos applets. Os applets expandiram o escopo da Internet, não só, como
também, o Java também abordou duas outras questões importantes da Internet, que são: a segurança
e a portabilidade.

2
Remote Method Invocation (RMI) é um protocolo usado pelo Java para comunicação entre processos (programas)
diferentes.

Elaborado por: Bruno Alfredo Gamito, MSc.


28

• Alguns conceitos
Java Applets - Um applet é um tipo especial de programa que é transmitido pela Internet e executado
automaticamente pelo navegador web compatível com Java. Os Applets geralmente são pequenos
programas e estes ajudaram a mover alguns programas interactivos do utilizador de um servidor para
outro, melhorando assim a usabilidade do aplicativo da web.

Segurança - Uma vez que os applets são baixados e executados automaticamente na máquina do
cliente, existem restrições sobre o que pode ser feito nos Applets. Eles podem usar apenas um
subconjunto de todas as funções suportadas por Java. Por exemplo, eles não podem aceder o sistema
de arquivos local ou não podem iniciar outros programas no sistema cliente. O Java conseguiu essa
protecção restringindo um applet ao ambiente de execução Java e não permitindo o acesso a outras
partes do computador. Em outras palavras, um programa sempre é verificado antes de ser executado.
Essa verificação também é realizada nos navegadores e visa impedir que os applets possam provocar
quaisquer danos ao computador do utilizador. Ademais, como o Java não permite acesso directo à
memória, impede seu uso para desenvolvimento de vírus.

Portabilidade - Uma vez que a Internet é composta por muitos tipos diferentes de computadores e
sistemas operativos, é importante que os programas sejam executados em todos esses sistemas. Ou
temos que ter um programa separado para cada sistema operativo ou escrever um programa portátil
(ou independente de plataforma), como Java Applet, que funciona em todos os lugares. Essa
portabilidade é alcançada usando bytecode do Java.

A tecnologia Java tinha sido projectada para se mover por meio das redes de dispositivos
heterogêneos, redes como a Internet. Agora aplicações poderiam ser executadas dentro dos
navegadores nos Applets Java e tudo seria disponibilizado pela Internet instantaneamente. Foi o
estático HTML dos navegadores que promoveu a rápida disseminação da dinâmica tecnologia Java.
A velocidade dos acontecimentos seguintes foi assustadora, o número de utilizadores cresceu
rapidamente, grandes fornecedores de tecnologia, como a IBM anunciaram suporte para a tecnologia
Java.

Resumindo, quando surgiu nos anos 90 do século XX, o Java introduziu na Web a possibilidade de
integrar verdadeira interactividade em páginas WWW (World Wide Web, ou simplesmente Web). As
pequenas aplicações Java, eram denominadas applets, e podiam ser directamente integradas no
código HTML (HyperTexT Markup Language) das páginas e, assim, tornar acessível a uma página

Elaborado por: Bruno Alfredo Gamito, MSc.


29

web tudo aquilo que usualmente se conseguia obter através de um programa, desenvolvido numa
qualquer linguagem de programação.

Assim, a popularidade do Java deve-se, sobretudo, à sua utilização no âmbito da World Wide Web.
Desde o aparecimento dos primeiros browsers com capacidades de interpretação Java que a
comunidade Internet aderiu de forma entusiástica a tudo o que se relacione com a linguagem Java.

A Java EE (Java Platform, Enterprise Edition) é uma plataforma padrão para desenvolver aplicações
Java de grande porte e/ou para a Internet, que inclui bibliotecas e funcionalidades para implementar
software Java distribuído, baseado em componentes modulares que executam em servidores de
aplicações e que suportam escalabilidade, segurança, integridade e outros requisitos de aplicações
corporativas ou de grande porte.

A plataforma Java EE possui uma série de especificações (tecnologias) com objectivos distintos, por
isso é considerada uma plataforma guarda-chuva. Entre as especificações da Java EE, as mais
conhecidas são:

• Servlets: são componentes Java executados no servidor para gerar conteúdo dinâmico para
a web, como HTML e XML.

• JSP (Java Server Pages): uma especialização de Servlets que permite que aplicações web
desenvolvidas em Java sejam mais fáceis de manter. É similar às tecnologias como ASP e
PHP, porém mais robusta por ter todas as facilidades da plataforma Java.

• JSF (Java Server Faces): é um framework web baseado em Java que tem como objectivo
simplificar o desenvolvimento de interfaces (telas) de sistemas para a web, através de um
modelo de componentes reutilizáveis. A proposta é que os sistemas sejam desenvolvidos com
a mesma facilidade e produtividade que se desenvolve sistemas desktop (até mesmo com
ferramentas que suportam clicar-e-arrastar componentes).

• JPA (Java Persistence API): é uma API padrão do Java para persistência de dados, que usa
um conceito de mapeamento objecto-relacional. Essa tecnologia traz alta produtividade para
o desenvolvimento de sistemas que necessitam de integração com base de dados. Só para
citar, essa API possibilita que você desenvolva aplicações usando base de dados sem precisar
escrever uma linha sequer de SQL.

• EJB (Enterprise JavaBeans): são componentes que executam em servidores de aplicação e


possuem como principais objectivos, fornecer facilidade e produtividade no desenvolvimento
de componentes distribuídos, transacionados, seguros e portáveis.

• O ambiente Java (JDK e JRE)


• JDK (Java Development Kit)
Para desenvolver um programa em Java, precisamos baixar e instalar o JDK (Java Development Kit)
da plataforma Java SE. Um kit de desenvolvimento Java possui aplicativos para compilar e debugar
seus códigos-fonte, além de diversas outras ferramentas úteis a desenvolvedores de sistemas. O JDK
também possui uma JRE (Java Runtime Environment).

Elaborado por: Bruno Alfredo Gamito, MSc.


30

• Algumas ferramentas do Java Development Kit (JDK)


O JDK é composto por inúmeras ferramentas (por ai, 54 na versão 8), contudo, iremos citar algumas,
às mais usuais: javac (compilador da linguagem Java); java (interpretador Java); jdb (debugador Java); java -prof
(interpretador com opção para gerar estatísticas sobre o uso dos métodos) ; javadoc (gerador de documentação); jar
(ferramenta que comprime, lista e expande); appletviewer (permite a execução e debug de applets sem browser); javap
(permite ler a interface pública das classes) e, extcheck (detecta conflitos em arquivos Jar).

• JRE (Java Runtime Environment)


O JRE é o ambiente de execução da plataforma Java. Os utilizadores de sistemas desenvolvidos em
Java precisam instalar apenas o JRE, pois ele possui uma JVM e bibliotecas básicas do Java, como
o próprio nome diz é o ambiente de execução Java.

Visão geral do Java Runtime Environment

Elaborado por: Bruno Alfredo Gamito, MSc.


31

• Requisitos de Hardware e Software


Neste ponto, os requisitos aplicam-se a Java version(s): 7.0 e 8.0.
Java 8
Windows Mac OS X Linux
• Windows 10 (8u51 e versões • Mac baseado em Intel executando o • Oracle Linux 5.5+1
posteriores) Mac OS X 10.8.3+, 10.9+ • Oracle Linux 6.x (32 bits), 6.x (64
• Windows 8.x (Desktop) • Privilégios de administrador para bits)2
• Windows 7 SP1 instalação • Oracle Linux 7.x (64 bits)2 (8u20 e
• Windows Vista SP2 • Browser de 64 bits versões posteriores)
• Windows Server 2008 R2 SP1 (64 • Red Hat Enterprise Linux 5.5+1,
bits) Um browser de 64 bits (Safari, por 6.x (32 bits), 6.x (64 bits)2
• Windows Server 2012 e 2012 R2 (64 exemplo) é necessário para executar o • Red Hat Enterprise Linux 7.x (64
bits) Oracle Java no Mac. bits)2 (8u20 e versões posteriores)
• RAM: 128 MB • Suse Linux Enterprise Server 10
• Espaço em disco: 124 MB para JRE; SP2+, 11.x
2 MB para Java Update • Suse Linux Enterprise Server 12.x
• Processador: no mínimo, um (64 bits)2 (8u31 e versões
processador Pentium 2 de 266 MHz posteriores)
• Browsers: Internet Explorer 9 e • Ubuntu Linux 12.04 LTS, 13.x
versão mais recente, Firefox • Ubuntu Linux 14.x (8u25 e versões
posteriores)
• Ubuntu Linux 15.04 (8u45 e
versões posteriores)
• Ubuntu Linux 15.10 (8u65 e
versões posteriores)
• Browsers: Firefox
Java 7
• Windows 10 (7u85 e versões • Mac baseado em Intel executando o • Oracle Linux 5.5+
posteriores) Mac OS X 10.7.3+, 10.8.3+, 10.9+ • Oracle Linux 6.x (32 bits), 6.x (64
• Windows 8.x (Desktop) • Privilégios de administrador para bits)3
• Windows 7 SP1 instalação • Oracle Linux 7.x (64 bits)3 (7u67 e
• Windows Vista SP2 • Browser de 64 bits versões posteriores)
• Windows Server 2008 SP2 e 2008 • Red Hat Enterprise Linux 5.5+, 6.x
R2 SP1 (64 bits) Um browser de 64 bits (Safari, por (32 bits), 6.x (64 bits)3
• Windows Server 2012 (64 bits) e exemplo) é necessário para executar o • Red Hat Enterprise Linux 7.x (64
2012 R2 (64 bits) Oracle Java no Mac. bits)3 (7u67 e versões posteriores)
• RAM: 128 MB; 64 MB para Windows • Suse Linux Enterprise Server 10
XP (32 bits) SP2, 11.x
• Espaço em disco: 124 MB • Suse Linux Enterprise Server 12.x
• Browsers: Internet Explorer 7.0 e (7u75 e versões posteriores)
versão mais recente, Firefox 3.6 e • Ubuntu Linux 10.04 e versões
versão mais recente posteriores
• Browsers: Firefox 3.6 e versões
posteriores

• Instalando o JDK no Windows


Para desenvolver em Java, precisamos baixar e instalar o JDK (Java Development Kit) da plataforma
Java SE. Um kit de desenvolvimento Java possui aplicativos para compilar e debugar seus códigos-
fonte, além de diversas outras ferramentas úteis a desenvolvedores de sistemas. O JDK também
possui uma JRE (Java Runtime Environment).

Para instalar o JDK no Windows, você já deve ter baixado o arquivo no site da Oracle.

Elaborado por: Bruno Alfredo Gamito, MSc.


32

A próxima tela permite seleccionar outros recursos que podem ser instalados junto com as ferramentas
de desenvolvimento. Também será solicitado o caminho onde o JDK deve ser instalado. Anote o
caminho (precisaremos mais adiante), clique em Next e aguarde o processo de instalação.

A ferramenta de instalação perguntará onde você quer instalar o JRE. Clique em Next e aguarde o
término da instalação, depois, clique em Finish.

Agora nós precisamos configurar algumas variáveis de ambiente para que as ferramentas de
desenvolvimento funcionem adequadamente.

No Windows 7, localize a opção Computador, e clique com o botão direito sobre ela. Clique sobre a
opção Propriedades.

Clique no menu Configurações avançadas do sistema, no menu lateral direito, depois, clique no
botão Variáveis de ambiente.

Elaborado por: Bruno Alfredo Gamito, MSc.


33

Crie uma nova variável do sistema. No campo Nome da variável, digite JAVA_HOME, e no campo
Valor da variável, digite o caminho onde você instalou o JDK, por exemplo, C:\Program
Files\Java\jdk1.8.0_144. Clique no botão OK para finalizar a inclusão dessa variável.

Repita esse processo de criação de variável, porém agora com o nome da variável igual a CLASSPATH
e valor igual a . (ponto).

Elaborado por: Bruno Alfredo Gamito, MSc.


34

Por último, encontre uma variável já existente chamada PATH e clique no botão Editar. Cuidado para
não apagar o conteúdo dessa variável, pois ela é usada pelo sistema operativo. Inclua
;%JAVA_HOME%\bin ao final dela. Preste atenção, pois você não pode deixar de incluir o ponto e
vírgula.

Para ter a certeza que o JDK já se encontra instalado, abra o command Prompt (cmd) e digite:

Elaborado por: Bruno Alfredo Gamito, MSc.


35

Unidade Temática HC HEI


5. Visão geral da linguagem Java 4 2

• Um pouco de história do Java


Em 1991, a Sun MicroSystems financiou uma pesquisa corporativa interna com o codinome Green,
acreditando que a próxima área importante seria os dispositivos electrónicos inteligentes destinados
ao consumidor final.

O projecto resultou no desenvolvimento de uma linguagem baseada em C e C++ que seu criador,
James Gosling, chamou de Oak (carvalho), em homenagem a uma árvore que dava para a janela do
seu escritório na Sun.

Descobriu-se mais tarde que já havia uma linguagem de computador chamada Oak, e quando uma
equipe da Sun visitou uma cafeteria local, o nome Java (cidade de origem de um tipo de café
importado) foi sugerido e pegou.

O projecto Green demonstrou a linguagem através de um controlo interactivo voltado para TV a cabo.
Era muita inovação para a época, e o mercado para dispositivos electrónicos inteligentes destinados
ao consumidor final não estava se desenvolvendo tão rapidamente como a Sun tinha previsto. Pior
ainda, um contrato importante pelo qual a Sun competia fora concedido a outra empresa. Então, o
projecto estava em risco de cancelamento.

Por pura sorte, a World Wide Web explodiu em popularidade em 1993 e as pessoas da Sun viram o
imediato potencial de utilizar Java para criar páginas da Web com o chamado conteúdo dinâmico
(applets). Isso deu nova vida ao projecto.

Em maio de 1995, a Sun anunciou Java formalmente em uma conferência importante. Normalmente,
um evento como esse não teria gerado muita atenção. Entretanto, Java gerou interesse imediato na
comunidade comercial por causa do fenomenal interesse pela World Wide Web.

No ano de 2009, durante uma forte crise financeira, a Oracle comprou a Sun, com promessas de
continuar inovando e investindo no Java e demais produtos da Sun. Actualmente, Java está presente
em mais de 4,5 bilhões de dispositivos. A plataforma é utilizada para criar páginas da web com
conteúdo interactivo e dinâmico, para desenvolver aplicativos corporativos de grande porte, aprimorar
a funcionalidade de servidores da World Wide Web, fornecer aplicativos para dispositivos destinados
ao consumidor final e para muitas outras finalidades.

1991 Iniciativa: Sun MicroSystems


Fundação do Projecto Green
Projecto conduzido por: Mike Sheridan (negócio), Patrick Naughton (Sistema gráfico) e
• Coordenação: James Gosling (linguagem de programação);
Foco: desenvolvimento de programas para pequenos dispositivos electrónicos;
Primeiros artefactos construídos: um novo sistema operativo (GreenOS), uma nova
linguagem de programação (Oak) e uma interface gráfica padronizada.

O objectivo do projecto: descobrir a “nova onda” da computação. Eles chegaram a


conclusão de que uma das ondas seria a convergência entre dispositivos electrónicos
“inteligentes” e computadores.
• O projecto vislumbrou a criação de um ambiente de execução flexível, capaz de
rodar nos mais diversos tipos de dispositivos. Primeiramente em pequenos
dispositivos (móveis ou não);

Elaborado por: Bruno Alfredo Gamito, MSc.


36

• A implementação inicial do Runtime era em C++ (James Gosling viu que C++ não
era adequada para o projecto e suas extensões e modificações ao C++ foram os
primeiros passos para o desenvolvimento de uma linguagem independente
apropriada ao projecto).
• A linguagem foi inicialmente chamada Oak, mas o nome foi mudado devido à
existência de uma patente já registada para outra linguagem de programação.

Para dispositivos com pouca memória era necessária uma linguagem pequena com
código bastante optimizado para poupar espaço. Como os fabricantes de dispositivos
poderiam usar diferentes CPUs era importante não ficar preso a nenhuma arquitectura
em particular.

Os requisitos de código pequeno e independente de plataforma fez a equipe ressuscitar


o modelo de algumas implementações do Pascal dos primeiros dias do PC, baseadas em
um código intermediário para uma máquina virtual.

A inspiração do nome Java surgiu em uma cafeteria local, cujo conteúdo do café vinha de
uma ilha da Indonésia chamada Java. O projecto Green ainda criou o personagem Duke,
que seria o ajudante usado na interface gráfica.
1993 Primeira aplicação comercial: Star7 (*7)
Natureza: um avançado PDA
Primeira aplicação: TV a cabo interactiva
Resultado: concorrência perdida (razão: o produto certo na época errada)
Consequência: projecto Green em risco
A salvação: Internet
Mudança de nome: de Oak para Java.

O projecto estava à beira do cancelamento. Não conseguiu vender nada em 93 e até


metade de 94. O mercado de dispositivos “inteligentes” não estava se desenvolvendo
como a Sun previa.

O rápido crescimento da Internet por volta de 1994 deu um novo impulso ao projecto. A
Sun voltou seus esforços para acelerar a conclusão da sua linguagem.

O objectivo era aproveitar a flexibilidade da linguagem para prover maior dinamismo na


Web. Não havia nenhum concorrente no horizonte na nova era digital que se abria com a
Web.
1994 Criação do WebRunner
• Responsáveis: Jonathan Payne e Patrick Naughton
• Função especial: suporte aos applets

Na metade de 94 a equipe percebeu que um browser independente de arquitectura,


confiável e seguro seria importante e, então, construiu um browser.

O browser HotJava foi feito para mostrar o poder do Java, mas os idealizadores também
tinham em mente o poder do que chamamos applets. Assim, fizeram um browser capaz
de executar código Java em páginas web.
1995 • WebRunner apresentado como HotJava;
• Suporte aos applets se estende a outros navegadores;
• Disponibilizado o primeiro JDK (Java Development Kit);
Em 23 de maio de 1995 o browser com suporte a applets foi apresentado na SunWorld.
1996 O Netscape decide dar suporte a Java ao seu browser Navigator

Elaborado por: Bruno Alfredo Gamito, MSc.


37

Disponibilização da primeira release JDK 1.0 (Java Development Kit)


JDK disponibilizado gratuitamente
1997 Lançamento do JDK 1.1 (ampliação dos recursos para aplicações gráficas e distribuídas)
1998 Java Community Process (JCP) especificação da tecnologia Java passa a ser conduzida
através de processo aberto formado pela Sun e utilizadores Java
1999 Lançamento do JDK 1.2 (nova ampliação da API (inclusão do Swing))
Ramificação da plataforma: J2SE, J2ME e J2EE
Dezembro: primeira release J2EE e J2SE para Linux
2005 Acréscimo de mecanismos à linguagem
Mudança na nomenclatura de Java 1.5 para Java 5
2009 Oracle adquire a Sun MicroSystems

• O caminho para a Aprendizagem


Os elementos envolvidos na aprendizagem do Java (linguagem de programação, ambiente de
desenvolvimento e API)
Linguagem de programação: conjunto de palavras e
símbolos; utilizada para escrever cada parte de um programa.
Além disso, ela também define regras de sintaxe que
precisam ser observadas. No Java, a linguagem é utilizada
para produzir diversos elementos de um programa, tais como:
classes, interfaces, atributos, métodos, variáveis, constantes,
estruturas de decisão e estruturas de repetição.

Ambiente de desenvolvimento: ferramentas utilizadas para


a construção de programas (IDEs – NetBeans, Eclipse, etc.;
compilador (javac); interpretador (java); visualizador de
applets (appletviewer); gerador de documentação (javadoc)).

API (Applications Programming Interface) conjunto de


componentes prontos (classes/interfaces). Benefício:
produtividade.

Porém os softwares desenvolvidos em Java não resultam somente da junção de um ambiente de


desenvolvimento e de uma linguagem de programação. A isso se soma um extenso conjunto de
componentes, que formam sua API. O Java contém dois tipos fundamentais de componentes em sua
API: as classes e as interfaces, uma pessoa que queira estudar Java, deverá passar a maior parte do
seu tempo estudando a sua API do que aprender a lidar com a linguagem e com um ambiente de
desenvolvimento.

O termo interface não deve ser confundido com “Interface gráfica”. Do mesmo modo que uma classe,
uma interface pode abrigar atributos e métodos. Para evitar mal-entendidos, as referências à “Interface
Gráfica” são feitas através da sigla GUI (Graphic User Interface – Interface Gráfica de Usuário).

Como a API Java contém diversas classes e interfaces para o desenvolvimento de software, o
desenvolvedor poderá se concentrar apenas na construção dos componentes que atenderão às
necessidades do sistema que está sendo desenvolvido.

Sendo assim, pode-se dizer que existem três partes distintas para se aprender acerca do Java: A
linguagem de programação, a segunda diz respeito ao funcionamento das ferramentas que compõem
o seu ambiente de desenvolvimento, e a terceira é o extenso conjunto de classes e interfaces que
compõem a sua API.

Elaborado por: Bruno Alfredo Gamito, MSc.


38

Pode-se perceber claramente que “linguagem Java” é uma expressão que não representa todo o
caminho a ser trilhado. Para se tornar um desenvolvedor Java, é preciso estudar tanto a linguagem
quanto sua API e um ambiente de desenvolvimento. Assim, o mais adequado é assumir o conceito de
Java como um conjunto de tecnologias que podem ser aplicadas ao desenvolvimento de software e
não apenas como uma linguagem.

Ao tratar do Java tão somente como uma linguagem de programação, ignora-se o segredo de seu
sucesso: a produtividade possibilitada por sua extensa API e um ambiente de execução que permite
que seus programas sejam executados nos principais sistemas operativos.

• Características da linguagem Java


• Orientado a Objectos
O Java é uma linguagem orientada a objectos. Podemos usar o Java para desenvolver aplicações em
termos de dados e de métodos (functions) que operam os dados. No Java, uma classe é um conjunto
de dados e de métodos que descrevem um objecto com o qual a aplicação funciona. Por agora,
podemos pensar que um objecto é algo como uma imagem gráfica ou uma dialog box ou mesmo um
ficheiro. Desde o início do seu desenvolvimento esta linguagem foi projectada para ser orientada a
objectos. Em outras palavras, o Java é uma linguagem totalmente orientada a objectos, permitindo a
herança e a reutilização de códigos de forma dinâmica e estática.

Em outras palavras, a linguagem Java foi criada seguindo o paradigma da orientação a objectos e, por
isso, traz de forma nativa a possibilidade de o programador usar os conceitos de herança, polimorfismo
e encapsulamento. O paradigma da orientação a objectos existe desde a década de 70, mas somente
após o sucesso da linguagem Java é que o paradigma ganhou credibilidade. O paradigma de
orientação a objectos traz um enfoque diferente da programação estruturada, no sentido de adoptar
formas mais próximas do mecanismo humano para gerir a complexidade de um sistema. Nesse
paradigma, o mundo real é visto como sendo constituído de objectos autónomos, concorrentes, que
interagem entre si, e cada objecto tem seu próprio estado (atributos) e comportamento (métodos),
semelhante a seu correspondente no mundo real.

Quando desenvolvemos programas orientados a objectos e estruturados temos dois paradigmas


totalmente diferentes. A forma de pensar e escrever o código são diferentes. É importante observar
que muitos programadores usam a linguagem Java, mas continuam pensando no formato estruturado.
Essa má prática de programação é muito frequente em estudantes e profissionais que utilizam Java
diariamente.

• Robusta
Quando as pessoas falam se o código é robusto, na maior parte das vezes referem-se se este é ou
não legível. Apesar de o Java não ter eliminado a possibilidade de existir código ilegível, fez um
considerável esforço no sentido de permitir escrever software de alta qualidade e de fácil leitura. Ela
foi pensada para o desenvolvimento de softwares confiáveis, provendo verificações tanto em tempo
de execução quanto em tempo de compilação, o colector de lixo responsabiliza-se pela limpeza da
memória quando houver necessidade.

Em outras palavras, a linguagem Java foi projectada para gerar sistemas confiáveis, pois fornece já
em tempo de compilação, por exemplo, uma checagem para identificar código não-alcançável.
Entenda-se como código não-alcançável uma linha de código que por algum motivo na lógica de
programação nunca será executada. Como exemplo podemos ter um comando return e logo abaixo
a impressão de uma String. Nesse caso, a String nunca seria impressa, por isso o compilador Java
gera um erro. A linguagem Java também oferece uma checagem para identificar variáveis que foram

Elaborado por: Bruno Alfredo Gamito, MSc.


39

definidas, porém não foram inicializadas. O modelo de gestão de memória é extremamente simples,
sendo que após a alocação de memória por meio do operador new não é necessário que o
programador libere esse espaço alocado, pois o Garbage Collector realiza essa actividade.

Outros factores que contribuem para o rótulo de linguagem robusta são:


✓ Sistema de tipo rígido que, em tempo de compilação, verifica erros comuns, como if (idade =
1). Nesse caso estamos atribuindo um valor, e não realizando a comparação. O correcto seria
apresentar o comando if (idade == 1).
✓ Inicialização das variáveis e atributos inteiros com o valor 0 automaticamente ou com vazio no
caso de variáveis ou atributos do tipo String.
✓ Manipulação de excepções é parte integrante da linguagem.

• Fácil de aprender e prático


Caso esteja já familiarizado com o C/C++, verificará que o Java é uma linguagem de fácil
aprendizagem, uma vez que a sintaxe é muito similar à linguagem C++. O ambiente retira do
programador a responsabilidade de gerir a memória e os ponteiros.

• Segura
As aplicações Java são executadas em ambiente próprio (JRE) o que inviabiliza a intrusão de código
malicioso. A linguagem Java foi criada para operar em ambientes distribuídos, o que significa que
segurança é de extrema importância. Com as características projectadas na linguagem Java, e
principalmente na JVM, podemos garantir que em um ambiente de rede nenhum programa Java
permitirá que outro programa escrito em qualquer outra linguagem possa se esconder em um código
Java a fim de se instalar automaticamente.

• Desempenho ao mais alto nível


Suporta vários recursos de alto desempenho, como multithreading, compilação Just-in-Time (JIT) e
utilização de código nativo.

• Portabilidade
Independência de plataforma. Aqui entra o slogan da Sun, “Write Once, Run Anywhere” (WORA) –
escreva uma vez, execute em qualquer lugar. Em outras palavras, Java pode rodar em qualquer
plataforma o que permite sua alta portabilidade. Isto se deve ao facto de que seu compilador não gera
instruções específicas a uma plataforma, mas sim um programa em um código intermediário. Esse
código intermediário é chamado de bytecode.

• Recursos de Rede
Possui extensa biblioteca de rotinas que facilitam a cooperação com protocolos TCP/IP, como HTTP
e FTP.

• Processamento distribuído
As chamadas a funções de acesso remoto (sockets) e os protocolos Internet mais comuns (HTTP,
FTP, Telnet, etc.) são suportadas em Java, de forma que a elaboração de aplicativos baseados em
arquitecturas cliente-servidor é facilmente obtida.

Elaborado por: Bruno Alfredo Gamito, MSc.


40

• Interpretada
A linguagem Java é interpretada, ou seja, após a compilação é gerado um arquivo intermediário (nem
texto nem executável) no formato bytecode, que poderá ser executado em qualquer arquitectura
(Windows, Linux, Mac e Unix) que tenha uma máquina virtual Java instalada. A “linkedição” do
programa no formato bytecode é realizada no momento de sua execução de forma simples e
totalmente gerida pela JVM (Java Virtual Machine).

• Multithreading
A maior parte dos sistemas operativos hoje no mercado dão suporte à multitarefa, como o Linux,
Windows, Unix, OS/2, ou seja, o computador é capaz de executar diversas tarefas ao mesmo tempo.
O Java tem o suporte a multitarefa embutido na linguagem: um programa Java pode possuir mais de
uma linha de execução (thread).

Resumindo, a plataforma Java permite a criação de programas que implementam o conceito


multithread, incluindo sofisticados mecanismos de sincronização entre processos. O multithreading é
uma técnica de programação concorrente, que permite projectar e implementar aplicações paralelas
de forma eficiente.

• Java como plataforma


Uma plataforma é uma estrutura que possibilita a execução de softwares. Basicamente, o que você
precisa para rodar um aplicativo são um computador e um sistema operativo instalado nele. Mas os
sistemas operativos são concebidos para determinadas arquitecturas de computadores e são
incompatíveis com todas as demais. Por isso, os próprios sistemas operativos são utilizados para
identificar as plataformas.

Do mesmo modo que os sistemas operativos são compatíveis apenas com determinado tipo de
computador, os programas compilados com as tecnologias tradicionais somente são compatíveis com
um único sistema operativo, e isso tira o sono de um desenvolvedor quando ele escreve um programa
e compila em C++ para executar no Windows, e o mesmo não será possível executar em outra
plataforma.

O Java supera essa dificuldade, o programa escrito em Java pode ser executado em qualquer sistema
operativo e, por conseguinte, em qualquer arquitectura de computador. E é exactamente isso que se
refere a expressão adoptada pela Sun MicroSystems: “Escreva uma vez. Execute em qualquer lugar.”

Para entender como é possível, basta realizar uma análise comparativa entre os processos de
desenvolvimento, compilação e execução de programas com as tecnologias tradicionais e com o Java.

Elaborado por: Bruno Alfredo Gamito, MSc.


41

Note que o processo de compilação do Java gera uma representação intermediária do código
(bytecodes), que pode ser interpretada por qualquer sistema operativo que tenha uma JVM (Java
Virtual Machine – Máquina Virtual Java), e é essa JVM que garante que o que foi escrito em Java seja
executado em qualquer sistema operativo.

Dessa forma e de outras particularidades, a plataforma Java se distingue claramente das tecnologias
de desenvolvimento de software tradicionais.

Mas apesar de ser simples com relação à sua estrutura, Java não é fácil de aprender se o
programador não possuir sólidos conhecimentos da tecnologia de Orientação a Objectos; por outro
lado, trabalhar com Java traz alguns benefícios:

• Extensa biblioteca de rotinas para se trabalhar com protocolos para a Internet;


• Comunicação entre objectos remotos;
• Muito empregada em servidores;
• Empregada em dispositivos portáteis de maneira quase obrigatória;
• Excelente gestor de memória;
• Os bytecodes gerados constituem um arquivo que pode ser interpretado por vários tipos de
máquina virtual;
• Tendo capacidade de multithreading, o programa pode executar mais de um trecho de
instruções ao mesmo tempo;
• Definição de threads muito eficiente.

Por ser orientada a objectos, a linguagem Java sempre faz menção a isto, introduzindo sempre o
conceito de classe nos programas. Diferentemente de outras linguagens, Java tem entre suas
peculiaridades o facto de ser sensível às letras maiúsculas e minúsculas.

Elaborado por: Bruno Alfredo Gamito, MSc.


42

• Palavras reservadas
As palavras-chave são palavras especiais, reservadas em Java, que têm significados para o
compilador, que as usa para determinar o que seu código-fonte está tentando fazer. Você não poderá
usar as palavras-chave como identificadores (nomes) de classes, métodos ou variáveis. As palavras-
chave reservadas estão listadas a seguir:

Você aprenderá a maioria das palavras-chave da listagem acima no momento que for adequado, por
isso, não se preocupe em memorizá-las.

Apesar de goto e const serem palavras-chave reservadas, elas não possuem significado para o
compilador. Na verdade, se você tentar usá-las, receberá um erro ao compilar seu código-fonte.

• Conceitos errados sobre o Java


Alguns conceitos errados que cercam a tecnologia Java, os quais muitas vezes causam uma
resistência infundada em relação ao seu aprendizado. A seguir, os principais mitos em torno do Java:

✓ Trata-se de uma extensão do HTML (Falso. Na realidade, o Java é uma linguagem completa,
derivada do SmallTalk e do C++);
✓ É apenas uma linguagem como outra qualquer (Falso. O Java possui uma linguagem única,
que permite construir componentes para todos os ambientes);
✓ Todos os programas Java rodam em páginas Web (Falso. Existem três ambientes distintos
(J2SE, J2EE e J2ME) de execução para programas Java);
✓ O JavaScript é uma versão light do Java (Falso. A Netscape aproveitou a onda de marketing
e baptizou sua tecnologia, LiveScript, como JavaScript);
✓ A linguagem Java é interpretada, sendo muito lenta para aplicações sérias (Falso. A
linguagem Java realmente exige interpretação, mas inclui também compilação. A forma como
a dupla compilador/interpretador trata os programas garante um desempenho muitas vezes
equivalente ao do C++ (com a vantagem de se tratar de uma linguagem bem mais simples do
que esta));
✓ É difícil programar em Java (Falso. A programação em Java em si é relativamente simples.
A única possível dificuldade inicial é a assimilação dos conceitos de Orientação a Objectos).

Elaborado por: Bruno Alfredo Gamito, MSc.


43

• Entendendo a portabilidade do Java


O Java usa as duas formas computacionais de execução de software – compilação (javac – java
compiler) e interpretação (JRE) – reunindo as vantagens de ambas.

As linguagens que utilizam apenas compilação necessitam de instruções adicionais da plataforma


operacional, o que compromete a portabilidade. Por outro lado, as linguagens que se apoiam apenas
sobre a interpretação geralmente são muito lentas. A coordenação dos processos de compilação e
interpretação permite a agilização do processo de desenvolvimento do software ao mesmo tempo em
que garante a portabilidade das aplicações criadas.

Vejamos como funciona a combinação das formas de execução no Java: em um primeiro momento, o
código é compilado, ou seja, transformado em instruções compreensíveis pela Java Virtual Machine
(bytecode), como mostra a figura a seguir:

Esse bytecode, por sua vez, pode ser interpretado em várias plataformas operacionais, bastando que
haja um interpretador Java (incluído em uma JRE) instalado. Os fabricantes de hardware e sistemas
operativos, em conjunto com a JavaSoft, desenvolveram JREs para vários ambientes operacionais.
Por isso, aplicações totalmente desenvolvidas em Java são 100% portáveis.

Elaborado por: Bruno Alfredo Gamito, MSc.


44

• Por trás da Java Runtime Environment (JRE)


Como vimos, a JRE executa (interpreta) bytecodes em Java, sendo um elemento central no trabalho
com a tecnologia Java. Portanto, é interessante conhecermos mais sobre sua estrutura e seu
funcionamento.

A JRE é um pacote de software executado como um aplicativo do sistema operativo que executa
programas Java de forma escalável, segura e com alto desempenho. A figura abaixo mostra os
principais componentes da JRE.

Principais componentes da JRE:

class loader – responsável por carregar as


classes Java (bytecode) que farão parte da
execução dos aplicativos de um sistema;

bytecode verifier – verifica se as classes


carregadas são realmente classes Java e se o
código é seguro e não viola nenhuma
especificação;

interpreter e Runtime – interpretam uma


porção do bytecode, transformando a em
chamadas do sistema operativo;

JIT (Just-in-Time Compiler) – adquirido da


Symantec em 1997, é responsável por compilar
uma parte do bytecode Java, transformando-a
directamente em chamadas do sistema
operativo.

Além dos componentes descritos acima, a JRE ainda conta com o Garbage Collector, um mecanismo
responsável por remover da memória objectos e dados que não estiverem mais sendo usados. O
Garbage Collector tem seus próprios algoritmos e ciclo de execução, de modo que o programador não
precisa se preocupar com alocação e desalocação de memória ao lidar com objectos. No Java, quando
um objecto não é mais necessário, basta anular sua referência apontando-a ao Garbage Collector, e
este realiza o restante da tarefa, garantindo o bom desempenho da memória em que a aplicação está
sendo executada. Essa é uma facilidade que não existe, por exemplo, no C ou no C++, nos quais a
responsabilidade sobre a gestão de memória recai sobre o programador.

• Localização de classes e CLASSPATH


Antes que possamos executar nossa aplicação, a JVM precisa fazer o carregamento das classes
(class loader). O class loader é um mecanismo usado para carregar classes a partir de arquivos nos
directórios ou arquivos compactados (zip ou jar), especificados na propriedade de sistema
java.class.path. Esta propriedade é configurada quando a variável de ambiente CLASSPATH está
definida. As classes são localizadas sequencialmente através dos directórios e arquivos jar (ou zip)
que estejam especificados na propriedade java.class.path. A configuração padrão para propriedade
java.class.path é o subdirectório jre/lib dentro do directório onde foi instalado o JDK (ou JRE).

Elaborado por: Bruno Alfredo Gamito, MSc.


45

Podemos usar java -verbose nome_da_classe, para ver a sequência da carga das classes, ao
executar nossa aplicação.

Quando surge o erro NoClassDefFoundError, significa que o JVM não conseguiu localizar sua classe
ou alguma classe da qual ela depende. Devemos informar à JVM onde procurar pelos pacotes,
definindo a variável CLASSPATH.

Elaborado por: Bruno Alfredo Gamito, MSc.


46

Unidade Temática HC HEI


6. Simples programa em Java, estrutura de um programa, Java
Tokens, Máquina Virtual Java (JVM), Argumentos da linha de 4 2
comando.

• Simples Programa em Java


Quando você aprende qualquer linguagem de programação, normalmente o primeiro programa que
desenvolve é o mais simples possível. Na maioria das vezes ele é chamado de "Olá mundo" ou "Hello
World". Para fazer um pouco diferente, chamamos nosso primeiro programa de "Oi Mundo, bem-vindo
a Programação Orientada a Objectos".

O primeiro programa é importante para aprendermos a estrutura básica que usaremos nos próximos
exemplos, o processo de compilação e execução e, também para resolver erros comuns de iniciantes.

A seguir, veja abaixo o código-fonte de um programa simples que imprime na saída padrão (console)
a mensagem "Oi Mundo, bem-vindo a Programação Orientada a Objectos".

Para começar a programar em Java, devemos criar uma pasta para colocar todos os exemplos e
exercícios referentes a disciplina de Programação Orientada a Objectos (visto que futuramente,
facilitará o entendimento de (package) pacotes, no java). Devemos escolher um editor de texto1 e
digitar o código do exemplo acima.

Por enquanto, não se preocupe com o significado do que escrevemos, pois você aprenderá cada coisa
na hora certa. Você deve digitar o código exactamente como foi apresentado no exemplo, inclusive
letras maiúsculas e minúsculas, pois a linguagem a Java é case-sensitive.

Quando terminar de digitar tudo, salve o arquivo com o nome Exemplo.java. Preste atenção
novamente às letras maiúsculas e minúsculas do nome do arquivo e também na extensão, que deve
ser .java.

• Compilando e executando
O código-fonte contém instruções de alto nível, isto é, instruções em uma linguagem que o homem é
capaz de compreender, mas a máquina não. Por isso, o código-fonte precisa ser compilado.

1
Não devemos começar a programar usando IDEs (por exemplo, Eclipse, NetBeans, JCreator, JBlue, entre outros),
pois, as IDEs mascaram muita coisa essencial para o aprendizado, por isso apenas mude para essas ferramentas
depois de alguns meses de prática. No momento, precisamos apenas de um bom editor de textos para ajudar na
sintaxe.

Elaborado por: Bruno Alfredo Gamito, MSc.


47

Agora vamos compilar nosso primeiro programa. O processo de compilação é o que transforma o
código-fonte em bytecode (aquele que só a JVM consegue interpretar). Para tal, entre no prompt de
comando do Windows, Linux ou Mac, acede a pasta onde salvou o arquivo Exemplo.java e digite:

javac Exemplo.java

O javac é o programa do JDK responsável por compilar um arquivo com código-fonte Java. Se
funcionar, o programa ficará silencioso (não aparecerá nenhuma mensagem de sucesso). Se der
alguma coisa de errado, você ficará sabendo, pois podem surgir várias mensagens estranhas no
terminal (quando você aprender melhor, não será mais tão estranha assim).

Para confirmar se o arquivo foi compilado, você pode listar todos os arquivos da pasta usando o
comando dir se estiver usando Windows, ou ls se Linux ou Mac. Se um novo arquivo chamado
Exemplo.class aparecer, é porque você teve sucesso ao compilar seu primeiro programa.

O arquivo com extensão .class é o bytecode gerado (executável). Só a JVM consegue interpretá-lo.

Uma vez que o programa já esta compilado, para executá-lo, digite o comando:

Elaborado por: Bruno Alfredo Gamito, MSc.


48

java Exemplo

Se funcionar, você deve ver a mensagem "Oi Mundo, bem-vindo a Programação Orientada a Objectos " no
seu terminal, como na imagem abaixo.

Apenas para ter certeza que você entendeu, o que acabamos de executar no último passo foi o arquivo
Exemplo.class, mas veja que não podemos colocar a extensão dele quando vamos executá-lo. Você
poderia ter apagado ou movido o arquivo Exemplo.java para outro lugar, e mesmo assim, a execução
teria sucesso, pois nesse momento apenas o bytecode é lido e executado.

• Ambiente Java de compilação (Processo de Compilação e Interpretação)

Elaborado por: Bruno Alfredo Gamito, MSc.


49

Durante o processo de compilação, os arquivos são compilados à medida que são convertidos para
bytecodes. Em tempo de execução, os bytecodes são carregados, verificados e executados. Durante
a execução, são feitas as chamadas ao hardware subjacente.

O carregamento de classe é responsável pela carga de todas as classes necessárias à execução do


programa.
O interpretador verifica se foi gerado algum código ilegal, com a finalidade de verificar se o bytecode
adere às especificações JVM e não viola a integridade e a segurança do sistema.

Na geração de código, os bytecodes são compilados no código de máquina nativo antes da execução,
isto é, o código é interpretado para a plataforma em questão.

Para desenvolver programas, será necessário utilizar o JDK (Java Development Kit), um ambiente de
desenvolvimento que, além das ferramentas de desenvolvimento, contém todos os elementos da
plataforma Java.

• Estrutura de um programa
Um arquivo de código-fonte (com a extensão .java) contém
uma definição de classe. A classe representa uma parte de
seu programa, embora uma aplicação muito pequena possa
precisar apenas de uma classe. A classe deve ficar dentro de
um par de chavetas { }.

Uma classe tem um ou mais métodos. Este(s) método(s)


conterá instruções de como o objecto deve agir. Seus métodos
devem ser declarados dentro de uma classe (em outras
palavras, dentro das chavetas da classe).

Dentro das chavetas de um método, escreva as instruções de


como ele deve ser executado.

Elaborado por: Bruno Alfredo Gamito, MSc.


50

O código do método é basicamente um conjunto de instruções, e por enquanto você pode considerar
o método como se fosse uma função ou procedimento.

• Anatomia de uma classe


Quando a JVM começar a ser executada, procurará a classe que você forneceu na linha de comando.
Em seguida, começará a procurar um método especialmente escrito que se pareça exactamente com:
public static void main (String args [ ]) {
// o código entra aqui
}
Depois a JVM executará tudo que estiver entre as chavetas { } de seu método principal. Toda aplicação
Java precisa ter pelo menos uma classe e um método main (não um método main por classe, apenas
um por aplicação).

Os principais “actores” em um programa Java são os objectos. Os objectos armazenam dados e


fornecem os métodos (acções) para aceder e modificar esses dados. Todo objecto é instância de uma
classe que define o tipo do objecto, bem como os tipos de operações que executa. Os membros
críticos de uma classe Java são os seguintes (classes também podem conter definições de classes
aninhadas):
• Dados de objectos Java são armazenados em variáveis de instância (também chamadas de
campos). Por essa razão, se um objecto de uma classe deve armazenar dados, então sua
classe deve especificar variáveis de instância para esse fim. As variáveis de instância podem
ser de tipos básicos (tais como inteiros, números de ponto flutuante ou booleanos) ou podem
se referir a objectos de outras classes.
• As operações que podem actuar sobre os dados e que expressam as “mensagens” às quais
os objectos respondem são chamadas de métodos, e estes consistem de construtores,
subprogramas e funções. Eles definem o comportamento dos objectos daquela classe.

Elaborado por: Bruno Alfredo Gamito, MSc.


51

• Java Tokens
Basicamente os Tokens, também são conhecidos como tokenização e servem para se fraccionar
grandes quantidades de dados em outras de menor tamanho para que posteriormente e possivelmente
sejam encapsulados em objectos como Arrays.

Para sincronizarmos os termos definiremos “Tokens” como sendo os pedaços dos dados e
“delimitadores” como as expressões que separam os Tokens uns dos outros.

Quando falamos de Tokens, logo falamos também de delimitadores. Eles caminham juntos pois um é
complemento do outro. Quando trabalhamos com arquivos, ao lermos os dados procuramos um
caractere que delimita as informações. Os mais comuns são a tecla de espaço e o ponto e vírgula,
mas é possível termos qualquer caractere como um delimitador.

Como exemplo temos: "Java; HTML; JavaScript; Python; C++". Nisso, podemos verificar na String do
exemplo, os Tokens: "Java HTML JavaScript Python C++", que estão separadas pelos delimitadores
";". O processo de “partir” esses Tokens é o que chamamos de tokenização.

E na classe String temos o método split que realiza essa quebra desses dados, ou seja, o método
split() é usado para dividir uma String em um array de Strings. É passado como parâmetro qual o
caractere ou caracteres que delimitam a divisão. A sua sintaxe é:

nome-da-string.split(caractere);

Na linha [4], criamos a nossa String com os nossos dados; para quebramos cada um dos Tokens pelo
delimitador “;” devemos usar o método split que recebe uma expressão regular (linha 5) esse método
retornará um array de Strings contendo todos nossos Tokens. Agora podemos saber quantos
elementos temos, acedendo a propriedade length (linha 6) dos nossos Tokens.

Podemos também imprimir esses Tokens percorrendo os elementos desse array (pegando cada token
dentro do nosso array de Tokens, e imprimir cada um deles numa linha separada). Para isso, devemos
acrescentar a estrutura de repetição for (linhas 7 – 9).

Elaborado por: Bruno Alfredo Gamito, MSc.


52

Os delimitadores podem ser qualquer coisa, por exemplo, a figura acima mostra nosso delimitador
como sendo “;”. Mas, a figura abaixo mostra nosso delimitador sendo um espaço “ “ definido na linha
4.

• Máquina Virtual Java (JVM)


Imagine que você queira escrever um discurso para uma reunião que você vai participar com
representantes do Japão, Estados Unidos e Espanha. Esses representantes não sabem falar
português, por isso, você terá que aprender a língua deles para se comunicar. É um trabalho possível,
porém trabalhoso.

Elaborado por: Bruno Alfredo Gamito, MSc.


53

Escreve

É lido por

O que você poderia fazer para optimizar seu trabalho? O que acha de contratar tradutores que podem
falar o seu idioma, mais japonês, inglês ou espanhol? Se você fizer isso, estará economizando muito
do seu tempo, pois precisará focar apenas no seu texto em português, sem se preocupar com
traduções e detalhes de cada língua. É como se fosse uma terceirização do trabalho de tradução para
idiomas específicos.

Escreve

É lido por

Traduz para

É lido por

O funcionamento de software funciona quase assim. Quando você desenvolve e compila um projecto
usando a linguagem C, por exemplo, temos o seguinte cenário para um mesmo sistema que deve
funcionar em vários sistemas operativos:

Elaborado por: Bruno Alfredo Gamito, MSc.


54

Nessas linguagens de programação, o código-fonte é compilado em código de máquina, que é


específico para um SO (sistema operativo) e arquitectura de processador. Isso quer dizer que o binário
gerado conhece todos os detalhes do SO para fazer o software funcionar.

Se quisermos que o software seja executado em múltiplos sistemas operativos ou com tipos de
processadores diferentes, precisamos compilar novamente nosso código-fonte. Parece simples, mas
não é só isso! Muitas vezes os programadores usam recursos específicos do SO dentro do código-
fonte, como bibliotecas nativas de interfaces gráficas ou de gestão de memória, por exemplo. Se este
for o caso, uma simples compilação não resolve o problema, precisando ter múltiplos projectos com
códigos-fonte para cada SO.

Com Java, você não precisa se preocupar com nada disso! Graças a JVM (Java Virtual Machine ou
Máquina Virtual Java), você pode codificar uma única vez e rodar em diferentes sistemas operativos,
em outras palavras, utilizando o slogan da Sun MicroSystems para exemplificar os benefícios

Elaborado por: Bruno Alfredo Gamito, MSc.


55

multiplataforma da linguagem Java (WORA – Write Once, Run Anywhere (Escreva uma vez, execute
em qualquer lugar)).

Quando compilamos um código-fonte Java, um arquivo chamado de bytecode é gerado. Ele é um


arquivo intermediário que não é legível aos programadores e também não é executável por si só (o
SO não sabe do que se trata esse arquivo).

Apenas a JVM consegue entender o bytecode e traduzir as instruções contidas dentro dele em
instruções nativas do sistema operativo.

Dessa forma, um único arquivo compilado (bytecode) pode ser executado em qualquer sistema
operativo, desde que você possua a JVM compatível para ele. Por exemplo, se você quiser executar
um sistema desenvolvido no Windows, basta ter uma JVM para Windows instalada em seu
computador, mas se tiver um cliente que use Linux, simplesmente instale uma JVM para Linux. Você
não precisa fazer mais nada além disso.

Na verdade, a JVM é muito mais que um simples tradutor. Como o próprio nome diz, é uma máquina
virtual, ou seja, uma imitação de um computador que consegue gerir a pilha de execução, uso de
memória, processamento, segurança, etc.

Ao contrário do que se parece, a JVM não faz as aplicações desenvolvidas em Java ficarem lentas,
mas “turbinam” elas para que fiquem, em alguns casos, até mais rápidas que aplicações nativas
desenvolvidas em C.
Isso é possível graças ao Hotspot, uma tecnologia de optimização dinâmica que trabalha para
aumentar a performance da JVM em tempo de execução.

Elaborado por: Bruno Alfredo Gamito, MSc.


56

Quando codificamos um programa na linguagem C, por exemplo, as optimizações são realizadas em


tempo de compilação, enquanto em Java isso é feito Just in Time (JIT), ou seja, no exacto momento
que é necessário. Isso é vantajoso, pois a JVM consegue fazer estatísticas de execução do código e
optimizar a execução de pontos isolados enquanto a aplicação roda.

Em suma, a máquina virtual java (JVM) é uma máquina imaginária que emula uma aplicação em uma
máquina real. É a JVM que permite a portabilidade do código Java, isto ocorre porque todo código
Java é compilada para um formato intermediário, bytecode, este formato é então interpretado pela
JVM.

Existem diversas JVMs cada uma delas destinada a um tipo de sistema operativo (Windows, Linux,
Mac e etc.), desta forma sendo o código da aplicação Java, bytecode, um código interpretado pela
JVM, podemos desenvolver uma aplicação sem nos preocuparmos onde ela será executada pois
sabemos que existindo a JVM instalada nosso código será executável.

• Argumentos da linha de comando


Qualquer número de argumentos pode ser passado para um programa Java através da linha de
comando. Ao executar o programa todas as coisas escritas após o nome da classe na linha de
comando são argumentos. Os argumentos são limitados pelo espaço.

No entanto, a palavra reservada main (serve para indicar ao compilador que este método será o
executável) e este vector args que é passado como parâmetro para o método main. Frisar que o
vector de String args passado como argumento para este método main pode ou não ser utilizado.
Para passar argumentos (parâmetros) para o método main basta fazer como a figura abaixo.

Tratando-se de um array de Strings, podemos colocar a posição a que pretendemos mostrar.

Elaborado por: Bruno Alfredo Gamito, MSc.


57

O uso de aspas faz com que uma String seja um conjunto de caracteres (vide figura acima).

Elaborado por: Bruno Alfredo Gamito, MSc.


58

Unidade Temática HC HEI


7. Constantes, variáveis e tipos de dados 4 2

• Constantes
Entendemos que um dado é constante quando não sofre nenhuma variação no decorrer do tempo, ou
seja, seu valor é constante desde o início até o fim da execução do algoritmo, assim como é constante
para execuções diferentes no tempo. Por exemplo, o valor 3.1415… é atribuído à constante PI e
permanecerá fixo até o final da execução.

Pi  3.1415…, em Java, uma constante é uma variável declarada com o modificador 1 final. As
constantes devem ser declaradas como variáveis cujo valor atribuído permanecerá inalterado ao longo
do programa. Por isso, são também chamadas de variáveis somente de leitura.

A sua sintaxe: final <tipo> <identificador> = <valor>;

Em suma, uma constante é declarada quando precisamos lidar com dados que não devem ser
alterados durante a execução do programa. Para isso, utilizamos a palavra reservada final para que
a variável seja inicializada uma única vez. Por exemplo, a figura abaixo dará um erro no processo de
compilação, isto porque, declaramos a constante com a palavra reservada final:

Caso não declaremos com a palavra reservada final, a mesma constante pode sofrer alteração ao
longo do programa.

1
Modificadores são utilizados para modificar a atribuição de classes, variáveis ou métodos.

Elaborado por: Bruno Alfredo Gamito, MSc.


59

Em Java, uma constante é uma variável declarada com o modificador 2 final. Usando a classe Math
nos proporciona uma série de operações e constantes que são acedidas estaticamente. A classe Math
possui suas constantes que são: o PI (Math.PI) e a constante de Euler (Math.E) usada em bases
naturais e neperianos.

Por convenção, usamos letras maiúsculas para declarar constantes e assim distingui-las das variáveis.
Também, usamos o underline (_) para separar nomes compostos das constantes.

• Variáveis
Em um computador, manipulamos informações que, durante a execução de um programa, ficam
armazenadas temporariamente em memória. Essas informações, num nível maior de abstracção, são
os elementos pertencentes aos tipos. Para que tais valores possam ser manipulados na linguagem de

2
Modificadores são utilizados para modificar a atribuição de classes, variáveis ou métodos.

Elaborado por: Bruno Alfredo Gamito, MSc.


60

programação, é preciso que haja algum identificador informando em que local da memória (endereço)
esse elemento se encontra.

Uma analogia útil seria entender esta porção de memória onde o elemento está armazenado como
uma garagem que tenha uma placa de identificação na frente. Assim, supondo que tivéssemos vários
carros para escolher quando fossemos viajar, encontraríamos o que procuramos pela placa de
identificação.

Variáveis e constantes são repositórios de elementos pertencentes aos tipos. A diferença é que o
elemento armazenado em uma constante é definido no início do programa e não é mais modificado,
enquanto o da variável pode ser alterado durante a execução do programa. Tanto variáveis como
constantes têm algumas características em comum (ambas são identificadas por um nome). Nisso,
podemos compreender variáveis e constantes como as garagens da analogia. Os nomes devem
identificar o objecto que ali está guardado, porém não há garantia de que o objecto armazenado seja
o esperado. Digamos que se tentarmos colocar um Toyota na garagem em que se encontra a placa
de Isuzu, conseguiremos. No entanto, quando quisermos dar uma volta de Isuzu, acederemos a
garagem do Isuzu e teremos uma surpresa ao nos depararmos com o Toyota. Portanto, convém ao
desenvolvedor dar/atribuir nomes significativos às variáveis e constantes de acordo com os
elementos (ou valores) que armazenarão. Normalmente utilizamos mnemónicos, ou seja, nomes ou
abreviaturas que lembram o uso da variável no algoritmo.

Variáveis são referências a dados. As variáveis devem ser sempre declaradas, isso porque cada uma
pode conter um tipo de dados, primitivos ou objectos.

A sintaxe das variáveis é composta por: <tipo de dados> <nome da variável (identificador)> e
<escopo>. Pois, a quantidade de memória ocupada: depende do tipo.

Eis alguns exemplos: byte b; / short s; / int i; / long l; / float f; / double d; / char c; / Boolean b;

A sua inicialização é atribuição de um valor a uma variável. E o símbolo de igualdade é o operador de


atribuição.

A figura acima mostra o tipo da variável, o nome e o valor atribuído na sua inicialização. Enquanto que,
a figura abaixo mostra o tipo da variável e o nome e, depois o valor atribuído na sua inicialização.

Elaborado por: Bruno Alfredo Gamito, MSc.


61

A nomenclatura para a criação de variáveis consoante as convenções da Sun MicroSystem são as


seguintes: os nomes das variáveis podem começar com qualquer letra e os caracteres $, & ou _,
porém não podem começar com números. Caso o nome de um atributo (variável) seja composto por
mais de uma palavra, a primeira letra de cada palavra deve ser em maiúscula.

• Tipos de dados
Tipos primitivos são os tipos básicos de dados da linguagem e, são capazes de armazenar um único
valor. O tipo de dados é importante para que o compilador saiba quantos bytes de memória devem ser
reservados à variável. Os tipos de dados podem ser primitivos, como os encontrados na maioria das
linguagens existentes.

• Inteiros
Existem quatro diferentes tipos de dados inteiros e podem armazenar valores dentro dos seguintes
intervalos numéricos:

Armazenamento
Tipo Extensão (Escopo)
Bits Bytes
byte -128 a 127 8 1
short -32.768 a 32.767 16 2
int -2.147.483.648 a 2.147.483.647 32 4
long -9.223.372.036.854.775.808L a 9.223.372.036.854.775.807L 64 8

Em Java, os tipos inteiros são sempre capazes de armazenar tanto valores positivos como negativos.

Elaborado por: Bruno Alfredo Gamito, MSc.


62

Para entendermos o comportamento dos tipos de dados (inteiros, neste caso) vamos analisá-los em
um programa. No entanto, vamos atribuir o valor 100 à variável b (que é do tipo byte), uma vez, que
esse valor “cabe” em um short e em um int, não há problema nenhum quando atribuímos o valor de b
à variável s (short) e o valor de s à variável i (int).

Elaborado por: Bruno Alfredo Gamito, MSc.


63

Agora vamos multiplicar i por 10 e atribuímos o total dessa multiplicação ao próprio i.

Agora, vamos inserir o valor do int em um long que é o dobro de um int.

• Tipos de dados em Ponto Flutuante


Existem dois tipos de representações para valores numéricos em ponto flutuante que se diferenciam
pela precisão oferecida. Os tipos de ponto flutuante servem para representar números com casas
decimais, tanto negativos quanto positivos. Todos números com ponto flutuante são por padrão do tipo
double, mas é possível especificar o tipo do valor durante a criação, para float utilize f ou F e se quiser
pode especificar para double usando d ou D.

Armazenamento
Tipo Extensão (Escopo)
Bits Bytes
Aproximadamente ± 3.40282347𝐸 − 38𝐹 (6-7 dígitos
float 32 4
decimais significativos).
Aproximadamente ± 1.79769313486231570𝐸 + 308 (15
double 64 8
dígitos decimais significativos).

O nome double refere-se ao facto de ter os números duas vezes mais precisão que o tipo float.

Elaborado por: Bruno Alfredo Gamito, MSc.


64

Atribuímos à variável “d” o seguinte valor:


100.12f. Dessa vez estamos inserindo um float
num double (float é 32bits e o double 64bits).
Mas, existirá perda de precisão.

Trabalhando com o float. No primeiro caso já é


necessário fazer um casting (lembre-se, o tipo
padrão de um número literal em ponto flutuante
é double).

No segundo caso nada é preciso.

No terceiro caso, novamente a presença no


casting já que estamos informando
explicitamente que o literal é do tipo double.

É bom lembrar que ao converter de double para


float pode haver perda de bits.

• Tipo de dados de Caractere


O tipo de dados char denota caracteres segundo o padrão Unicode de representação. Enquanto uma
String é representada por valores entre aspas duplas (exemplo, "valor"); o tipo char é representado
por valores entre aspas simples (apóstrofo, exemplo: 'valor'). Como o padrão Unicode foi projectado
para lidar com todos os tipos de caracteres em todos os idiomas, ele tem um código de 2 bytes
(ocupando 16 bits), sem sinal, o que lhe permite representar até 32.768 caracteres diferentes.

Alguns caracteres são considerados especiais pois não possuem uma representação visual, sendo
que em sua maioria são caracteres de controlo e outros são de uso reservado pela linguagem Java.
Esses caracteres podem ser especificados dentro dos programas como indicado na tabela a seguir,
precedidos pela barra invertida “\”:

Elaborado por: Bruno Alfredo Gamito, MSc.


65

Caractere Nome Significado


\b Backspace Retrocesso
\t Tab Tabulação
\n Linefeed or NewLine Nova linha
\r Carriage return Retorno de carro
\’ Single quote Apóstrofo
\” Double quote Aspas
\\ Backslash Barra invertida

• Tipo de dados lógico


Em Java o tipo lógico é o boolean, capaz de assumir valores como false (falso) ou true (verdadeiro).

Elaborado por: Bruno Alfredo Gamito, MSc.


66

Através dos exemplos acima, podemos observar que as conversões em Java ocorrem da seguinte
maneira, sem perda de informação:

Tipo Primitivo Pode ser convertido em…


byte short, int, long, float ou double
short int, long, float ou double
char int, long, float ou double
int long, float ou double
long float ou double
float double

Elaborado por: Bruno Alfredo Gamito, MSc.


67

As conversões explicitas, o casting, é permitido em todos os tipos (excepto o boolean), mas o


programador deve estar ciente que poderá haver perda de bits.

• Comentários
Como toda linguagem de programação, você pode fazer comentários em seu código-fonte tornando
assim uma forma de documentar os códigos deixados, que poderá ser utilizado por outra pessoa, pela
equipe de desenvolvimento ou até mesmo por você, afinal, se você levar muito tempo para rever um
código desenvolvido, pode ser que você nem se lembre de como foi desenvolvido.

O Java aceita três tipos de comentários: de linha, representado por duas barras (//), o de múltiplas
linhas, representado por barra e asterisco (/*) e finalizado por asterisco e barra (*/), ficando assim, (/*
*/), e um terceiro que se assemelha ao comentário de múltiplas linhas, que tem o propósito de
documentar o programa: (/** */).

O comentário de documentação é posicionado imediatamente antes do elemento a ser documentado


e tem seu conteúdo extraído automaticamente pelo utilitário javadoc fornecido juntamente com o JDK.

Elaborado por: Bruno Alfredo Gamito, MSc.


68

Unidade Temática HC HEI


8. Tipos de constantes, tipos de dados, os vários tipos de dados
em Java, declaração de variáveis, atribuindo valores a variáveis, 8 2
escopo das variáveis, conversão de variáveis.

• Tipos de constantes
Você pode definir constantes em Java utilizando a palavra reservada final. Essa palavra indica que
você definiu o valor de uma variável e que esse valor não pode ser modificado. Normalmente as
constantes são definidas em caixa alta, como no exemplo:

As constantes em Java são criadas marcando-se variáveis com os modificadores static e final, ou por
meio de enums. Elas devem ser nomeadas usando-se letras maiúsculas com caracteres underscore
(_) como separadores. A linguagem Java não tem suporte embutido para constantes, mas os
modificadores de variáveis estáticas e finais pode ser usado para criar efectivamente uma constante.

• Tipos Enumerados
Quando você precisar criar uma constante, você utilizará definição de variáveis estática e final. Toda
vez que usamos a palavra-chave final estamos informando que algo não pode mudar. Com classes,
final significa que não podemos mais fazer herança, com métodos não podemos mais sobrepô-lo e
com variáveis significa que após a inicialização o valor da variável não pode mais ser alterado,
exemplo:

Saída: 1

Elaborado por: Bruno Alfredo Gamito, MSc.


69

Elaborado por: Bruno Alfredo Gamito, MSc.


70

Para declarar uma constante,


simplesmente precisamos declarar
o atributo com o modificador final,
ou seja, vai ser um valor constante
que não pode mudar.

Saída:

Os enumeradores são simplesmente muito mais que um tipo de constantes, eles possuem
funcionalidades extras que podem deixar o nosso projecto mais elegante, legível e com melhor
controlo para o programador.

Elaborado por: Bruno Alfredo Gamito, MSc.


71

Saída:

• Tipos de dados
A linguagem Java oferece diversos tipos de dados com os quais podemos trabalhar. Na verdade, há
basicamente duas categorias em que se encaixam os tipos de dados: tipos primitivos e tipos de
referências.

Os tipos primitivos correspondem a dados mais simples ou escalares, enquanto que, os tipos de
referências consistem em arrays, classes e interfaces. Em Java, são oferecidos tipos literais primitivos

Elaborado por: Bruno Alfredo Gamito, MSc.


72

(não objectos) para representar valores booleanos (verdadeiro ou falso), caracteres (char), valores
numéricos inteiros (byte, short, int e long) e valores numéricos em ponto flutuante (float e double).

• Tipos primitivos
Na linguagem Java, o tipo de dado boolean (booleano) é o mais simples encontrado. Uma variável
booleana pode assumir apenas um entre dois valores: true ou false. Os tipos de dados primitivos byte,
short, int e long constituem tipos de dados inteiros. Isso porque variáveis desses tipos podem conter
um valor numérico inteiro dentro da faixa estabelecida para cada tipo individual. Por exemplo, um byte
pode conter um inteiro entre -128 e 127, enquanto um short pode conter um valor entre -32.768 e
32.767.

Há diversas razoes para se utilizar um ou outro dos tipos inteiros em uma aplicação. Em geral, não é
sensato declarar todas as variáveis inteiras do programa como long. Raramente os programas
necessitam trabalhar com dados inteiros que permitam fazer uso da máxima capacidade de
armazenagem de um long. Além disso, variáveis grandes consomem mais memória do que variáveis
menores, como short.

Uma variável do tipo char armazena um caractere Unicode, que é um caractere de 8bits, sendo que
de 0 a 255 correspondem aos caracteres do código ASCII1. Em Java existem duas categorias de
variáveis de ponto flutuante: float que armazena valores numéricos em ponto flutuante de precisão
simples e double de precisão dupla (ambas seguem a norma: IEEE Standard for Binary Floating Point
Arithmetic, ANSI/IEEE Standard 754-1985 (IEEE, New York)). O facto de obedecer a essa norma é
que torna os tipos de dados aceitos pela linguagem Java tão portáveis. Esses dados serão aceitos por
qualquer plataforma, independendo do tipo de sistema operativo e do fabricante do computador. Na
sequência, é exibida uma tabela (vide tabela abaixo) que demonstra as capacidades de cada um dos
tipos primitivos.

1
A tabela ASCII é uma tabela padronizada internacionalmente de associações entre caractere e a sua representação
numérica no computador.

Elaborado por: Bruno Alfredo Gamito, MSc.


73

A variável do tipo booleano pode assumir os valores true ou false. Uma variável do tipo booleano ocupa
um bit de armazenamento.

Uma variável do tipo char contém um caractere Unicode, ocupando 16bits de armazenamento em
memória. O valor padrão de um atributo de classe do tipo char, se não especificado, é o caractere
NULL. Um valor literal do tipo caractere é representado entre aspas simples, como em:

Como podemos notar, a variável ou atributo c recebe o caractere C, correspondente ao código


hexadecimal 0043 ou ao valor decimal 67. Os valores literais de caracteres também podem ser
representados por sequências de escape, como em ‘\n’ (nova linha). As sequências de escape são
combinações de caracteres que consistem de uma contra barra (ou barra invertida) seguida por uma
letra ou por combinações de dígitos. Eis alguns exemplos de sequências de escape:

Elaborado por: Bruno Alfredo Gamito, MSc.


74

As sequências de escape Unicode (precedidas por \u) são processadas antes das anteriores, podendo
aparecer não apenas em variáveis caracteres, ou Strings (como as outras sequências), mas também
em identificadores da linguagem Java.

Em Java, valores numéricos inteiros podem ser representados por variáveis do tipo byte, short, int ou
long. Todos os tipos possuem valores inteiros com sinal, com representação interna em complemento
de dois. O valor padrão para atributos desses tipos é 0.

Há um espaço de armazenamento definido na especificação da linguagem para cada um desses tipos


de dados, não sendo dependente de diferentes implementações:

Constantes literais do tipo long podem ser identificadas em código Java por meio do sufixo l ou L,
como em:

Em Java, não há valores inteiros sem sinal (unsigned), adicionalmente, as combinações long int ou
short int são inválidas em Java.

As variáveis de tipo float ou double podem representar valores reais, com apresentação em ponto
flutuante. Em qualquer situação, a representação interna desses valores segue o padrão IEEE 754,
sendo 0.0 o valor padrão para tais atributos.

Como para valores inteiros, o espaço de armazenamento e, consequentemente, a precisão de valores


associados a esses tipos de dados são definidos na especificação da linguagem:

Elaborado por: Bruno Alfredo Gamito, MSc.


75

O tipo float possui nove dígitos significativos de precisão. As variáveis do tipo double possuem 18
dígitos significativos de precisão. As constantes literais do tipo float podem ser identificadas no código
Java pelo sufixo f, ou F; do tipo double, pelo sufixo d, ou D.

• Identificadores
Definimos identificadores como sequências de caracteres Unicode, que devem obedecer às seguintes
regras:
✓ Um nome pode ser composto por letras, por dígitos e pelos símbolos ‘_’ e ‘$’;
✓ Um nome não pode ser iniciado por um dígito (0 a 9);
✓ Letras maiúsculas são diferenciadas de letras minúsculas;
✓ Uma palavra-chave da linguagem Java não pode ser um identificador.

O uso de convenção padrão para identificadores Java torna a codificação mais uniforme e pode facilitar
o entendimento de um código. Embora não seja obrigatório, o conhecimento e uso da seguinte
convenção padrão para atribuir nomes em Java pode facilitar bastante a manutenção de um programa:
✓ Nomes de classes são iniciados por letras maiúsculas;
✓ Nomes de métodos, atributos e variáveis são iniciados por letras minúsculas;
✓ Em nomes compostos, cada palavra do nome é iniciada por letra maiúscula. As palavras não
são separadas por nenhum símbolo.

Por padrão, as variáveis do tipo char, byte, short, int, long, float e double são inicializadas com valor
0. Já a variável do tipo boolean é inicializada com valor false.

Cabe ao programador entender o melhor tipo de dado para utilizar em cada variável do seu programa.
As variáveis de tipos de dados primitivos não podem referenciar objectos ou invocar métodos.

• Tipos de dados de referência


Os tipos de dados por referência são compostos por arrays, Strings e qualquer outro tipo de classe
instanciável.

Os dados por referência são utilizados para armazenar as localizações dos objectos da memória do
computador. Dentro desses objectos podem existir variáveis de instância e métodos.

Elaborado por: Bruno Alfredo Gamito, MSc.


76

Quando um objecto é atribuído para uma variável, aquela variável está na verdade associada a uma
referência do objecto, e não ao objecto em si. Essa referência é o endereço de memória em que aquele
objecto está localizado.

Por exemplo, temos a seguinte classe:

Para declarar uma variável utilizando um tipo de referência,


basta colocar o nome da classe como tipo de dado:
Pessoa a;

Aqui, estamos criando uma variável a, que pode referenciar


objectos criados da classe Pessoa.

Se quisermos criar uma nova instância de um objecto de uma classe, podemos utilizar o new2, por
exemplo:

Falando em tipos de dados de referência, é importante citar um outro exemplo: O array.

Arrays são objectos que possuem valores chamados "elementos". O array permite armazenar valores
de um mesmo tipo em alocações de memória contínuas. Os elementos de um array são identificados
por um índice de números inteiros, que começa do 0 e vai até n-1, onde n é o número de elementos
disponíveis no array.

Todos os elementos do array devem conter o mesmo tipo de dado, por exemplo: Um array "int" deve
conter apenas valores do tipo "int".

O array é considerado um tipo de dado de referência pois ele é uma classe do java (está contido em
java.lang.Object).

A sintaxe para declaração de array: TipoDeDado [ ] nome = new TipoDeDado [TamanhoDoArray]; ou


TipoDeDado nome [ ] = {elemento1, elemento2, …, elemento n};

Por exemplo:

Neste exemplo criamos um array chamado nome, declarado como tipo de dados “String”. O tamanho
do array criado é de acordo com o número de elementos declarados no momento da criação. No caso,
esse array possui 3 (três) elementos.

2
Com o operador new, podemos fazer a criação de objectos. Dada uma classe nomeClasse, é possível (em
princípio) criar um objecto dessa classe usando esse operador: nomeClasse objecto = new nomeClasse ();

O método à direita do operador new é um construtor da classe nomeClasse. Um construtor é um pseudométodo


especial, definido para cada classe. O corpo desse método determina as actividades associadas à inicialização de
cada objecto criado. Assim, o construtor é apenas invocado no momento da criação do objecto por meio do
operador new.

Elaborado por: Bruno Alfredo Gamito, MSc.


77

Neste exemplo criamos um array chamado número, que está declarado como tipo de dados “int”. O
índice do array começa em número[0] e acaba em número [2]. Cada posição do índice pode conter
um valor diferente.

Para simplificar podemos usar


o método length para indicar o
tamanho do array.

Elaborado por: Bruno Alfredo Gamito, MSc.


78

Em outras palavras, os tipos referência facilitam a manipulação dos objectos em memória, agilizando
a construção do código, a gestão de memória pelo Garbage Collector e a navegação entre os
objectos do sistema.

Uma variável referência permite o acesso a instâncias de objectos em memória, pois a ela é atribuído
o endereço de memória de um objecto. Essa associação é que recebe o nome de referência.
Diferentemente, os tipos primitivos são armazenados directamente em áreas de memória dentro dos
objectos que os contém.

Por exemplo, podemos criar, dentro de um programa, um ou mais objectos Conta e manipulá-los.

Saída:

Neste exemplo, é possível perceber que as variáveis conta1 e conta2 são do tipo referência, por duas
razões: (i) não são de tipo primitivo e, (ii) estão associadas ao operador new.

• A notação do . (ponto) e o uso de uma referência


Para aceder o número da Conta 1, precisamos usar a sua referência, conta1, indicando o atributo em
que estamos interessados (no caso, o número de conta). Para isso, devemos utilizar a notação do

Elaborado por: Bruno Alfredo Gamito, MSc.


79

ponto (.), em que este sinal gráfico concatena a referência e o atributo, como mostra o exemplo a
seguir:

Executando essas linhas de código, obtém-se o resultado apresentado a seguir:

Acesso aos atributos.

• O endereçamento de memória da JVM para um objecto


Os endereços de memória, impressos com @, são resultantes das linhas:
System.out.println (conta1);
System.out.println (conta2);

Estes endereços de memória, dos objectos Java, não nos interessa, ele interessa apenas ao Garbage
Collector.

• Usando variáveis referência como atributos de objecto


Um objecto também pode ter como atributos tipos referência (não apenas primitivos). Veja, por
exemplo, a classe Produto, que poderia ser usada para modelar um sistema de controlo para uma
livraria:

Elaborado por: Bruno Alfredo Gamito, MSc.


80

A classe Produto possui atributos que permitem manipular vários tipos de produtos. Em nosso sistema
de livraria, todos os produtos terão os atributos definidos no código. Dentro dessa classe, podemos
identificar dois atributos não primitivos:

public String código;


public String descrição;

Podemos, também, detectar dois atributos primitivos:

double preço;
int quantidade;

Tendo montado o sistema, vamos inserir nele alguns objectos do tipo Produto e solicitar que seus
atributos sejam exibidos no prompt:

Elaborado por: Bruno Alfredo Gamito, MSc.


81

Saída:

A variável livro1, do tipo Produto, é do tipo referência. Ela permite armazenar outras duas referências
do tipo String dentro dela (código e descrição). O tipo String permite armazenar cadeias de caracteres,
ou seja, palavras, frases, etc.

Podemos usar uma variável do tipo String de duas formas:

Ou

A primeira forma é mais intuitiva, sendo preferida por programadores experientes. A segunda forma,
mais clássica e fácil de entender, não deixa dúvidas de que a variável descrição da classe Produto
é do tipo String.

• Os vários tipos de dados em Java


Os tipos primitivos do Java são a base de construção de outros tipos de dados. O Java herdou os tipos
primitivos das linguagens C e C++ e por essa razão que falamos que o Java é uma linguagem
fortemente tipada.

No Java, existem algumas palavras reservadas para a representação dos tipos de dados básicos que
precisam ser manipulados para a construção de programas. Estes tipos de dados são conhecidos
como tipos primitivos.

Pode-se dividir os tipos primitivos suportados pelo Java em função da natureza de seu conteúdo. Há
quatro tipos primitivos para a representação de números inteiros, dois tipos primitivos para a
representação de números fraccionários, um tipo primitivo para representação de caracteres e um tipo
primitivo para representação dos valores booleanos.

Existem milhares de classes disponíveis na API do Java e todas são tipos de dados, porém uma classe
pode armazenar diversos dados ao mesmo tempo em seus atributos, e realizar tarefas através de seus

Elaborado por: Bruno Alfredo Gamito, MSc.


82

métodos. Um tipo primitivo por outro lado, só armazena um único dado e não contém quaisquer
métodos para realizar tarefas.

Já para representar textos o Java não possui um tipo primitivo, ele possui uma classe chamada String,
que serve para esse propósito, essa classe pode ser usada de modo semelhante a um tipo primitivo e
ainda conta com diversos métodos disponíveis nessa classe para realizar diversas operações com o
dado armazenado.

Também existem classes para representar cada um dos tipos primitivos. Sempre que for preciso
realizar uma operação mais complexa com algum dado, você poderá armazená-la em um objecto da
classe correspondente ao invés de utilizar um tipo primitivo. Assim, poderá fazer uso dos métodos
disponíveis nessa classe para realizar diversas operações com o dado armazenado.

• Dados numéricos

Números que podem conter partes fraccionárias podem ser representados por dois tipos:

Apesar de o tipo float ocupar metade da memória consumida por um tipo double, ele é menos utilizado.
Ele sofre de uma limitação que compromete seu uso em determinadas situações: somente mantém
uma precisão decimal entre 6 e 7 dígitos.

Obs.: A declaração de um número como um float, deve ser feito utilizando a letra F como sufixo.

• Dados textuais
É possível representar dois tipos de elementos textuais em Java: caracteres e textos. A representação
de um caractere solitário é feita pelo tipo char e a representação de textos é feita pela classe String.

Enquanto o tipo de char representa apenas um caractere, a representação de textos deverá ser feita
pela classe String. Essa classe pode ser utilizada de forma similar aos tipos primitivos, mais os valores
literais desse tipo são transcritos entre aspas e não entre apóstrofos.

Elaborado por: Bruno Alfredo Gamito, MSc.


83

• Dados lógicos
O tipo lógico é representado, em Java, pelo tipo booleano. Este tipo pode armazenar um de dois
valores possíveis: true ou false. Ele é empregado para realizar testes lógicos em conjunto com
operadores relacionais e dentro de estruturas de decisão e repetição.

Regra básica: em tipos primitivos inteiros, int é default. Para tipos primitivos ponto-flutuante, double
é default.

Os tipos primitivos em Java trabalham com bits em si, o que seria:

No caso, o tipo int tem 32bits, na figura somente estão representados


8bits porque antes dele será adicionado somente 0s para chegar em
32bits, ou seja, 0000 0000 0000 0000 0000 0000 0001 01000, enquanto
que, na figura ao lado estão somente 0001 0100.

• Declaração de variáveis
A declaração de variáveis em Java, como em várias outras linguagens, exige que o tipo da variável
seja declarado. Você inicia a declaração indicando o tipo da variável e o nome desejado, como no
exemplo a seguir:

Note que todas as declarações terminam com o ponto-e-vírgula. Os nomes das variáveis devem ser
iniciados com qualquer letra, seguidas por uma sequência de letras ou dígitos. O tamanho do nome
da variável não tem limites.

É possível declarar várias variáveis em uma linha, bem como atribuir valores a elas na declaração,
como no exemplo abaixo:

Elaborado por: Bruno Alfredo Gamito, MSc.


84

Dentro de um bloco, podemos declarar variáveis e usá-las. Em Java, toda variável tem um tipo que
não pode ser mudado, uma vez que declarado: tipoDaVariavel nomeDaVariavel;

Por exemplo, é possível ter um contacto que guarda um número inteiro (de telefone): int contacto;

Com isso, declaramos a variável contacto, que passa a existir a partir daquela linha. Ela é do tipo int,
que guarda um número inteiro. A partir daí, podemos usá-la, primeiramente atribuindo valores.

• Atribuindo valores a variáveis


Como vimos, há vários tipos de dados em Java, cada um com um consumo de memória determinado
que afecta directamente o seu alcance. Neste ponto, veremos como atribuir valores a esses endereços
de memória (atributo ou variável).

Para atribuirmos valor a uma variável ou atributo usamos o operador “=”. Neste caso, o sinal de igual
não significa igualdade, mas que um valor será atribuído. A regra básica é que sempre o que estiver
à esquerda do “=” receberá o valor que estiver à direita.

Por exemplo, se expressarmos var1 = 10 queremos dizer que a variável var1 receberá o número 10,
agora, temos o número 10 gravado na memória. Podemos também atribuir valores contidos em outras
variáveis. Eis um exemplo:

A variável var1 recebe o número 10, depois a variável var2 recebe o valor de var1 (que é 10). Agora
temos duas variáveis guardando o mesmo número (10).

• Escopo das variáveis


O escopo de uma variável é o bloco de código dentro do qual ela é acessível e determina quando a
variável é criada e destruída. Basicamente, o que define o escopo de uma variável é o bloco onde ela

Elaborado por: Bruno Alfredo Gamito, MSc.


85

se encontra. O bloco é determinado por “{“ e “}”. A imagem abaixo ilustra o escopo de variáveis no
Java:

Escopo de
parâmetros do
método

Em outras palavras, no Java, podemos declarar variáveis a qualquer momento. Porém, dependendo
de onde você as declarou, ela vai valer de um determinado ponto a outro.

O escopo da variável é o nome dado ao trecho de código em que aquela variável existe e onde é
possível acede-la. Quando abrimos um novo bloco com as chaves “{ e }”, as variáveis declaradas ali
dentro só valem até o fim daquele bloco.

Elaborado por: Bruno Alfredo Gamito, MSc.


86

No bloco acima, a variável j para de existir quando termina o bloco onde ela foi declarada. Se você
tentar aceder uma variável fora de seu escopo, ocorrerá um erro de compilação.

Escopo da variável b

Frisar que um bloco nada mais é uma série de linhas de código situadas entre um abre e fecha de
chaves “{ e }”. Podemos criar blocos dentro de blocos. Pois, dentro de um bloco temos um determinado
escopo, que determina a visibilidade e tempo de vida de variáveis e nomes. Por exemplo:

Escopo da variável z

• Conversão de variáveis
Quando lidamos com linguagens de programação fortemente tipadas como Java, nos confrontamos
muitas vezes com a necessidade de variar de um tipo de dado para outro.

Há casos, também, em que até mesmo o compilador não compreende que tipo de dado estamos
atribuindo a uma variável.

Em Java, nós podemos fazer uso do que chamamos de indução de tipo ou typecast. O typecast dita
ao compilador como tal dado deve ser interpretado e manipulado. Essa indução de tipo ou typecast
pode ser implícita ou explícita.

Elaborado por: Bruno Alfredo Gamito, MSc.


87

O typecast implícito é feito automaticamente pelo compilador quando um tipo de dado pode ser
facilmente manipulado no lugar de outro tipo de dado. O typecast explícito é feito directamente no
algoritmo para indicar ao compilador qual a forma de interpretação de um dado quando ele entende
que há ambiguidade ou formatos incompatíveis.

O typecast explícito é dado sempre dentro de parênteses que sempre vem antes do dado a ser
induzido. Ex.: (int) var1, (float) var2, (Object) var3, ...

• Typecast de dados primitivos


O typecast de dados primitivos é dado basicamente em questão de seu consumo de memória. Se
tentamos designar um tipo de dado que consome menos memória para um que consome mais
memória, o typecast é realizado implicitamente. No exemplo abaixo, atribuímos um dado inteiro (varInt)
a uma variável do tipo float (varFloat).

O contrário não se aplica. Tentar atribuir um tipo de dado maior para um tipo de dado menor irá resultar
em um erro de tipos incompatíveis (type mismatch).

Para demonstrar isso, usaremos dois tipos de dados inteiros. Porém, iremos atribuir um inteiro longo
(que consome mais memória) a um dado inteiro que consome menos. Nesse caso, somos obrigados
a usar typecast explícito.

Elaborado por: Bruno Alfredo Gamito, MSc.


88

• Conversões entre tipos


O Java não tem problemas para atribuir um tipo int para um double (ele vai tratar o valor como double).
Assim sempre que uma atribuição for efectuada o tipo mais representativo será utilizado. Entretanto,
existem ocasiões onde queremos representar o valor inteiro de um tipo double, por exemplo. Assim,
torna-se necessário converter o tipo, em uma operação chamada de cast. Essa conversão nada mais
é do que indicar o tipo desejado, como no exemplo:

Note que a variável y terá como valor o número 9.

As conversões permitidas sem cast são: byte  short  int  long  float  double e char  int

Resumindo, para transformar um tipo primitivo em outro (por exemplo, int em double), devemos
utilizar o conceito de conversão de tipos. Ele se divide em dois tipos: casting e promotion.

O procedimento de casting é usado para diminuir a precisão de uma variável, enquanto o promotion
realiza o caminho inverso.

Elaborado por: Bruno Alfredo Gamito, MSc.


89

Em Java, o compilador não nos obriga a explicitar no código a operação de promotion, pois ele realiza
auto-promotion dos tipos primitivos. Veja um exemplo de auto-promotion em que a precisão do tipo é
aumentada sem problemas:

tipo_menor identificador2 = valorLiteral;


tipo_maior identificador = identificador2;

Para o casting, no entanto, não existe procedimento automático, de modo que ele deve ser explicitado
no código:
tipo_maior identificador = valorLiteral;
tipo_menor identificador2 = (tipo_menor) identificador;

Eis, alguns exemplos de promotion e casting:


int x = 200;
byte b = x; // falha na compilação
byte c = (byte) x; // typecast
float f = x; // auto-promotion

// lembre-se que os tipos preferidos são int e double


int z = b + c;
double d = f + b;

Elaborado por: Bruno Alfredo Gamito, MSc.


90

Unidade Temática HC HEI


9. Operadores e Expressões 2 2

• Operadores
Todos os tipos comuns de operadores existentes nas linguagens de programação estruturadas estão
presentes em Java. Veja quais são eles, suas principais características e formas de sua utilização.

• Operadores aritméticos
Operadores aritméticos são aqueles utilizados para efectuar operações matemáticas.

Operador Sintaxe Função


* a*b Multiplica a por b
/ a/b Divide a por b
% a%b Resto da divisão de a por b
- a–b Subtrai a de b
+ a+b Soma

Esses operadores são aplicados a tipos numéricos, que possuem tamanhos e características
diferentes, conforme seu tipo. Assim, caso você opere com duas variáveis numéricas de tipos
diferentes, por exemplo, um int multiplicado por um double, o resultado será sempre do maior tipo;
neste caso, um double.

• Operadores Relacionais
Operadores Relacionais comparam duas variáveis e determinam seu relacionamento com um
resultado lógico, ou seja, true (para verdadeiro) ou false (para falso).

Operador Sintaxe Função


Avalia se a variável a é maior que a variável b (e retorna true ou false,
> a>b
dependendo dos valores de a e b)

Elaborado por: Bruno Alfredo Gamito, MSc.


91

Avalia se a variável a é maior ou igual à variável b (e retorna true ou false,


>= a >= b
dependendo dos valores de a e b)
Avalia se a variável a é menor que a variável b (e retorna true ou false,
< a<b
dependendo dos valores de a e b)
Avalia se a variável a é menor ou igual à variável b (e retorna true ou false,
<= a <= b
dependendo dos valores de a e b)
Avalia se a variável a é igual à variável b (e retorna true ou false,
== a == b
dependendo dos valores de a e b)
Avalia se a variável a é diferente da variável b (e retorna true ou false,
!= a != b
dependendo dos valores de a e b)

• Operadores Lógicos
Operadores Lógicos avaliam um ou mais operandos lógicos que geram um único valor lógico (true ou
false) como resultado final da expressão avaliada.

Existem seis Operadores Lógicos:


• &&: and (operador e) lógico;
• &: and (operador e) binário;
• ||: or (operador ou) lógico;
• |: or (operador ou) binário;
• ^: ou exclusivo binário;
• !: operador de negação.

Os Operadores Lógicos podem se aplicar a expressões, variáveis ou constantes.

&& (and) lógico e & (and) binário


Operando A Operando B Operando A && Operando B
Verdadeiro Verdadeiro Verdadeiro
Verdadeiro Falso Falso

Elaborado por: Bruno Alfredo Gamito, MSc.


92

Falso Verdadeiro Falso


Falso Falso Falso

II (or) lógico e I (or) binário


Operando A Operando B Operando A II Operando B
Verdadeiro Verdadeiro Verdadeiro
Verdadeiro Falso Verdadeiro
Falso Verdadeiro Verdadeiro
Falso Falso Falso

^ (Xor) binário
Operando A Operando B Operando A ^ Operando B
Verdadeiro Verdadeiro Falso
Verdadeiro Falso Verdadeiro
Falso Verdadeiro Verdadeiro

Elaborado por: Bruno Alfredo Gamito, MSc.


93

Falso Falso Falso

! (not)
Operando A ! Operando A
Verdadeiro Falso
Falso Verdadeiro

• Operadores de igualdade
Os operadores de igualdade verificam se o valor ou resultado da expressão lógica à esquerda é igual
(==) ou diferente (!=) ao da direita, retornando um valor booleano.

Elaborado por: Bruno Alfredo Gamito, MSc.


94

• Operadores de Incremento e Decremento


Operadores de Incremento e Decremento têm a função de aumentar ou diminuir em uma unidade o
valor de uma variável.

Deve ser dada atenção especial à posição do operador, pois, dependendo dela, pode-se efectuar a
operação desejada antes ou depois de uma avaliação numa determinada expressão ou condição.

Operador Sintaxe Função


++ i ++ Incrementa a variável em 1; avalia a expressão antes do incremento
++ ++ i Incrementa a variável em 1; o incremento está antes de avaliar a expressão
-- i -- Decrementa a variável em 1; avalia a expressão antes do decremento
-- -- i Decrementa a variável em 1, antes de avaliar a expressão

Elaborado por: Bruno Alfredo Gamito, MSc.


95

• Operadores condicionais
Esses operadores são aplicados sobre no mínimo duas expressões booleanas, para ajudar em
conjunções (expressões AND) ou em disjunções (expressões OR).

Operador Descrição
&& Operador condicional para expressões do tipo AND
|| Operador condicional para expressões do tipo OR

Também, o operador condicional é do tipo ternário, pois envolve três operandos. O símbolo (?) é
utilizado para fazer uma condição if/else de forma simplificada.

A sua sintaxe: <operando11> ? <operando2> : <operando3>

Se o valor do operando1 for true, então o resultado da condicional é o operando2, se o valor do


operando1 for false, então o resultado da condicional é o operando3.

• Operadores bitwise
Os operadores bitwise são utilizados quando precisamos realizar operações a nível de bits com
números inteiros, ou seja, trabalhar com sua representação binária.

Caso ambos os operandos sejam Strings, esses operadores irão trabalhar com os valores ASCII de
seus caracteres.

Os operadores bitwise disponíveis em Java são:


& (AND): executa uma operação E entre os bits. Esta operação resulta em 1 somente se todos os
valores forem 1:

1
Expressão booleana.

Elaborado por: Bruno Alfredo Gamito, MSc.


96

| (OR): executa uma operação OU entre os bits. O resultado obtido por esta operação será 1 sempre
que qualquer um dos valores for 1:

~ (NOT): operador unário que obtém o complemento dos bits, ou seja, inverte todos os bits. Isto
significa que o bit que possui valor 1 passará a valer 0 e vice-versa:

• Operadores especiais
Os operadores especiais são: (i) operador ternário (?:); (ii) separação de expressões (,); (iii) chamada
de método () e, (iv) (cast) coerção unária.

(i) Operador ternário


O operador ternário como o próprio nome diz, ele aceita três operandos. O operador ternário é um
modo de fazer um teste condicional simples substituído o if.

É usado para avaliar expressões booleanas de modo semelhante à instrução if, excepto por: em vez
de executar um bloco de código se o resultado do teste for true, ele atribui um valor a uma variável.
Em outras palavras, o objectivo do operador condicional (ternário) é decidir qual dos dois valores
atribuir a uma variável. Sua estrutura é mostrada abaixo:

<expressão booleana> ? <operando1> : <operando2>;

(ii) Separação de expressões


Quando as variáveis são do mesmo tipo, podemos usar o operador vírgula para separar elas. Sua
sintaxe:

Elaborado por: Bruno Alfredo Gamito, MSc.


97

<tipo de dado primitivo> <variável1> = <valor> , <variável2> = <valor>;

(iii) Chamada de métodos


As variáveis associadas a objectos, que são referências aos mesmos, precisam ser declaradas. Os
objectos por sua vez precisam ser criados. Declaração e criação podem sem realizadas por comandos
separados:

NomeDaClasse nomeDoObjecto;
nomeDoObjecto = new NomeDaClasse();

A primeira linha informa que a variável nomeDoObjecto vai designar um objecto que é uma instância
da classe NomeDaClasse. A segunda linha cria um novo objecto pertencente àquela classe e atribui
a este objecto a referência nomeDoObjecto. A parte NomeDaClasse() do comando é na verdade uma
chamada ao método construtor da classe. Em muitos casos, este método pode receber parâmetros
que são incluídos nos parênteses e servem para especificar a inicialização do objecto. As duas linhas
acima podem ser combinadas numa só:

NomeDaClasse nomeDoObjecto = new NomeDaClasse();

O escopo de uma variável, ou seja, a região do programa na qual ela está definida, é limitado ao bloco
no qual ela foi declarada.
(iv) Coerção unária
O casting é um recurso do Java, que possibilita mudar (moldar) um valor de tipo para outro. Às vezes
isso é feito “automaticamente”, o que chamamos de casting implícito e também pode ser feito por
você mesmo, que o casting explícito.

Um exemplo de casting explícito:


int num1 = 20;
short num2;
num2 = (int) num1; //aqui o casting explicito funcionando

Olhando acima, o Java não nos permite fazer num2 = num1 directamente, porque, num1 é do tipo int
(32bits), que é maior que short (16bits) à nível de armazenamento.

Algumas observações sobre tipos primitivos…


boolean – não pode ser moldado para nenhum outro tipo, tanto implicitamente quanto explicitamente;
char – nenhum outro tipo pode ser moldado para char;
long – é semelhante a um inteiro, mas com uma possibilidade maior de números.

Elaborado por: Bruno Alfredo Gamito, MSc.


98

Com relação a casting implícito (automático), o seu funcionamento é o seguinte:


i) todo tipo pode ser moldado para um superior a ele, excepto float  long, pois possuem
características diferentes;
ii) nenhum tipo pode ser convertido para outro com tamanho inferior a ele. O Java faz isso como
medida de segurança para que não haja risco de perda de precisão do valor;
iii) um tipo pode ser moldado para um outro de mesmo tamanho (bits), quando possuírem
características semelhantes. Os tipos primitivos de mesmo tamanho são: char e short (16bits);
int e float (32bits); double e long (64bits).

Legenda:
- -> : ocorre casting implícito;
-/-> : não ocorre casting implícito.

char e short
char -/-> short (não ocorre casting implícito (moldar) de um char para short)
short -/-> char

float e int
float --> int (ocorre casting implícito de float para int, pois o float possui também as características de um int)
int -/-> float

long e double
long --> double (ocorre casting implícito, pois o double possui também as características de um long)
double -/-> long (não se esqueça que um long é bem-parecido com um int ou seja bem diferente de um valor
quebrado, representado com pontos flutuantes)

• Expressões
Expressões são sentenças da linguagem Java terminadas pelo símbolo ‘;’. Essas sentenças podem
denotar expressões envolvendo uma operação aritmética, uma operação lógica inteira, uma operação
lógica booleana, uma avaliação de condições, uma atribuição, um retorno de método, ou ainda
operações sobre objectos.

Elaborado por: Bruno Alfredo Gamito, MSc.


99

• Expressões retornando valores numéricos


Expressões aritméticas envolvem atributos, variáveis e/ou constantes numéricas (inteiras ou reais).
Os operadores aritméticos definidos em Java incluem:

soma, operador binário denotado pelo símbolo +;

subtração, operador binário denotado pelo símbolo -. O mesmo símbolo pode ser utilizado como
operador unário prefixo, denotando a complementação (subtração de 0) do valor;

multiplicação, operador binário denotado pelo símbolo *;

divisão, operador binário denotado pelo símbolo /;

resto da divisão, operador binário denotado pelo símbolo % e que pode ser aplicado apenas para
operandos inteiros;

incremento, operador unário denotado pelo símbolo ++ que é definido apenas para operandos
inteiros, podendo ocorrer na forma prefixa (pré-incremento) ou pós-fixa (pós-incremento); e

decremento, operador unário denotado pelo símbolo -- que também é definido apenas para
operandos inteiros, podendo ocorrer na forma prefixa (pré-decremento) ou pós fixa (pós-decremento).

As operações lógicas sobre valores inteiros actuam sobre a representação binária do valor
armazenado, operando internamente bit a bit. Operadores desse tipo são:

complemento, operador unário prefixo denotado pelo símbolo ~ que complementa cada bit na
representação interna do valor;

OR bit-a-bit, operador binário denotado pelo símbolo | que resulta no bit 1 se pelo menos um dos bits
na posição correspondente na representação interna dos operandos era 1;

AND bit-a-bit, operador binário denotado pelo símbolo & que resulta no bit 0 se pelo menos um dos
bits na posição correspondente na representação interna dos operandos era 0;
XOR bit-a-bit, operador binário denotado pelo símbolo ^ que resulta no bit 1 se os bits na posição
correspondente na representação interna dos operandos eram diferentes;
deslocamento à esquerda, operador binário denotado pelo símbolo << que desloca a representação
interna do primeiro operando para a esquerda pelo número de posições indicado pelo segundo
operando;

deslocamento à direita, operador binário denotado pelo símbolo >> que desloca a representação
interna do primeiro operando para a direita pelo número de posições indicado pelo segundo operando.
Os bits inseridos à esquerda terão o mesmo valor do bit mais significativo da representação interna;

deslocamento à direita com extensão 0, operador binário denotado pelo símbolo >>> que desloca
a representação interna do primeiro operando para a direita pelo número de posições indicado pelo
segundo operando. Os bits inseridos à esquerda terão o valor 0.

• Expressões retornando valores booleanos


As operações lógicas booleanas operam sobre valores booleanos. Operadores booleanos incluem:

Elaborado por: Bruno Alfredo Gamito, MSc.


100

complemento lógico, operador unário prefixo denotado pelo símbolo (!) que retorna true se o
argumento é false ou retorna false se o argumento é true;

OR lógico booleano, operador binário denotado pelo símbolo | que retorna true se pelo menos um
dos dois argumentos é true;

OR lógico condicional, operador binário denotado pelo símbolo || que opera como o correspondente
booleano; porém, se o primeiro argumento já é true, o segundo argumento não é nem avaliado;

AND lógico booleano, operador binário denotado pelo símbolo & que retorna false se pelo menos um
dos dois argumentos é false;

AND lógico condicional, operador binário infixo denotado pelo símbolo && que opera como o
correspondente booleano; porém, se o primeiro argumento já é false, o segundo argumento não é nem
avaliado;

XOR booleano, operador binário denotado pelo símbolo ^ que retorna true quando os dois argumentos
têm valores lógicos distintos.

Condições permitem realizar testes baseados nas comparações entre valores numéricos. Operadores
condicionais incluem:

maior, operador binário denotado pelo símbolo > que retorna true se o primeiro valor for
exclusivamente maior que o segundo;

maior ou igual, operador binário denotado pelo símbolo >= que retorna true se o primeiro valor for
maior que ou igual ao segundo;

menor, operador binário denotado pelo símbolo < que retorna true se o primeiro valor for
exclusivamente menor que o segundo;

menor ou igual, operador binário denotado pelo símbolo <= que retorna true se o primeiro valor for
menor que ou igual ao segundo;
igual, operador binário denotado pelo símbolo == que retorna true se o primeiro valor for igual ao
segundo;

diferente, operador binário infixo denotado pelo símbolo (!=) que retorna true se o primeiro valor não
for igual ao segundo.

• Precedência de operadores
Uma vez que os operadores aritméticos buscam reproduzir as operações matemáticas fundamentais,
é natural que eles mantenham as suas regras de precedência, que podem ser manipuladas pelo
programador com o uso de parênteses.

Por exemplo, a expressão 1 + 1 * 2, quando analisada pelo compilador, vai retornar o valor 3, porque
a multiplicação será resolvida antes da adição. Usando parênteses, a expressão (1 + 1) * 2 retornará
o valor 4, pois a adição, por estar dentro dos parênteses, será resolvida primeiro.

Nisso, a precedência de operadores serve para indicar a ordem na qual o compilador interpretará os
diferentes tipos de operadores, para que ele sempre tenha como saída um resultado consistente com

Elaborado por: Bruno Alfredo Gamito, MSc.


101

a regra que quer ser aplicada pelo programador, evitando ambiguidades e inconsistências de
operações.

Uma vez que, é essencial saber como Java interpreta os símbolos que você usa para executar
operações específicas e em que ordem ele interpreta-os. Caso contrário, você poderia escrever um
programa com um resultado em mente e receber um resultado completamente diferente. Sempre que
tiver uma dúvida sobre como Java irá interpretar um símbolo que você usa, você pode confiar na
informação nesta tabela para ajudá-lo.

Prioridade Operador(es)
( ) parênteses
1
[ ] colchetes
++ pós-incremento
2
-- pós-decremento
++ pré-incremento
3
-- pré-decremento
4 ! negação lógica
* multiplicação
5
/ divisão
6 % resto da divisão (mod)
+ soma
7
- subtracção
< menor que
<= menor ou igual
8
> maior que
>= maior ou igual
== igual
9
!= diferente
10 & (and) binário
11 | (or) binário
12 ^ (ou exclusivo) binário
13 && (and) lógico
14 || (or) lógico
15 = operador de atribuição

Elaborado por: Bruno Alfredo Gamito, MSc.


102

Unidade Temática HC HEI


10. Operadores – aritméticos, relacionais, lógicos, igualdade,
incremento/decremento, condicionais, bitwise, especiais.
4 2
Expressões – aritméticas, precedência, conversões, Entrada e
Saída de Dados (DataInputStream/Scanner Class)

• Operadores
• Aritméticos
Existem 5 operadores aritméticos em Java que podemos usar para efectuar cálculos matemáticos.
Uma operação pode ser de adição (+), subtracção (-), multiplicação (*), divisão (/) ou módulo1 (%).

Operação aritmética de Adição sem uso de


variáveis e com uso de variáveis.

Operação aritmética de Subtracção sem uso de


variáveis e com uso de variáveis.

Operação aritmética de Multiplicação sem uso


de variáveis e com uso de variáveis.

1
Resto da divisão

Elaborado por: Bruno Alfredo Gamito, MSc.


103

Operação aritmética de Divisão sem uso de


variáveis e com uso de variáveis.

Operação aritmética de Módulo (Resto da


Divisão) sem uso de variáveis e com uso de
variáveis.

Podemos também, utilizar o operador aritmético de adição (+) para concatenar Strings.

Elaborado por: Bruno Alfredo Gamito, MSc.


104

• Relacionais
Os resultados dos operadores relacionais são do tipo boolean (true or false).

Avalia se o número 6 é menor que 4, uma vez,


que não é, retorna false.

Avalia se a variável b (20) é maior que a (10) e,


retorna true.

Avalia se o número 6 é menor ou igual a 4, uma


vez, que não é, retorna false.

Avalia se a variável b (20) é maior ou igual à a


(10) e, retorna true.

Avalia se o número 6 é igual a 6 e, retorna true.

Avalia se a variável b (20) é igual à variável a


(10) e retorna false.

Avalia se o número 6 é diferente de 6 e, retorna


false.

Avalia se a variável b (20) é diferente da variável


a (10) e retorna true.

Elaborado por: Bruno Alfredo Gamito, MSc.


105

• Lógicos
• && (and)

• || (ou)

Elaborado por: Bruno Alfredo Gamito, MSc.


106

• ^ (ou exclusivo)

• ! (negação)

Elaborado por: Bruno Alfredo Gamito, MSc.


107

• Igualdade

Elaborado por: Bruno Alfredo Gamito, MSc.


108

• Incremento/decremento

A variável valorInicial é atribuída o valor 10,


depois tem um pós-incremento (passando para
11) e na sua saída recebe o pré-incremento
(ficando 12).

A variável valorInicial é atribuída o valor 10,


depois tem um pré-incremento (passando para
11) e na sua saída recebe o pós-incremento
(ficando 11), mas na verdade, o valor final é 12.

Elaborado por: Bruno Alfredo Gamito, MSc.


109

A variável valorInicial é atribuída o valor 10,


depois tem um pré-decremento (passando para
9) e na sua saída recebe o pós-decremento
(ficando 9), mas na verdade, o seu valor final é
8.

• Condicional

Elaborado por: Bruno Alfredo Gamito, MSc.


110

• Bitwise
Também conhecido como operadores bit a bit, porque os operandos são comparados no nível dos
seus bits.

• & (and) binário


Para este operador não importa se algum dos operando tem o valor false, ele vai verificar todos os
operados que houver na expressão. Sua sintaxe: <operando 1> & <operando 2>

Se usarmos valores inteiros, ele irá comparar cada bit, exemplo:

Elaborado por: Bruno Alfredo Gamito, MSc.


111

Olhando para o resultado, foi impresso o valor 8, porque o operador & vai comparar cada bit na base
binária e depois a mesma é convertida para a base decimal.

Lembrando que um tipo primitivo int tem 16bits, então:

A variável a tem o valor 40, que em binário será: 0000 0000 0010 1000
A variável b tem o valor 10, que em binário será: 0000 0000 0000 1010
O resultado tem o valor 8, que em binário será: 0000 0000 0000 1000

Tabela verdade:
Operando 1 Operando 2 Resultado
1 1 1
1 0 0
0 1 0
0 0 0

• | (or) binário
Para este operador não importa se algum dos operando tem o valor true, ele vai verificar todos os
operandos que tiver na expressão. Sua sintaxe: <operando 1> | <operando 2>

O valor impresso será true, e o operador | irá comparar o valor de todas variáveis.

Se usarmos valores inteiros, ele irá comparar cada bit, exemplo:

Neste caso será impresso o valor 58, porque o operador | vai comparar cada bit na base binária e
depois a mesma é convertida para a base decimal.

Elaborado por: Bruno Alfredo Gamito, MSc.


112

Lembrando que um inteiro (int) tem 16bits, então:

A variável a tem o valor 50, que em binário será: 0000 0000 0011 0010
A variável b tem o valor 10, que em binário será: 0000 0000 0000 1010
O resultado tem o valor 58, que em binário será: 0000 0000 0011 1010

Tabela verdade:
Operando 1 Operando 2 Resultado
1 1 1
1 0 1
0 1 1
0 0 0

• ^ (ou exclusivo) binário


O operador ^ (XOR) executa uma operação OU exclusivo entre os bits. Esta operação resultará em
1 quando apenas um dos valores for 1. Tem como sintaxe: <operando 1> ^ <operando 2>.

Se usarmos valores inteiros, por exemplo ele irá comparar cada bit, exemplo:

Neste caso será impresso o valor 56, porque o operador ^ vai comparar cada bit na base binária e
depois a mesma é convertida para a base decimal.

Lembrando que um inteiro (int) tem 16bits, então:

A variável a tem o valor 50, que em binário será: 0000 0000 0011 0010
A variável b tem o valor 10, que em binário será: 0000 0000 0000 1010
O resultado tem o valor 56, que em binário será: 0000 0000 0011 1000

Tabela verdade:
Operando 1 Operando 2 Resultado
1 1 0
1 0 1
0 1 1
0 0 0

Elaborado por: Bruno Alfredo Gamito, MSc.


113

• ~ (NOT) binário
O operador ~ (unário) que obtém o complemento dos bits, ou seja, inverte todos os bits. Isto significa
que o bit que possui valor 1 passará a valer 0 e vice-versa.

• Manipulação de bits
Este ponto apresenta uma extensa discussão sobre os operadores de manipulação de bits e também
sobre a classe BitSet que permite a criação de objectos do tipo array de bits para configurar e obter
valores de bits individuais. O Java fornece várias capacidades de manipulação de bits para
programadores que precisam descer ao nível dos “bits e bytes”. Os sistemas operativos, software de
equipamento de teste, software de rede e muitos outros tipos de software exigem que o programador
se comunique “directamente com o hardware”. Agora, veremos as capacidades de manipulação de
bits do Java e os operadores de bits.

Os computadores representam todos os dados internamente como sequências de bits. Cada bit pode
assumir o valor 0 ou o valor 1. Na maioria dos sistemas, uma sequência de oito bits forma um byte, a
unidade de armazenamento padrão para uma variável do tipo byte. Outros tipos são armazenados em
números maiores de bytes. Os operadores de bits podem manipular os bits de operandos integrais
(operações do tipo byte, char, short, int e long), mas não os operandos de ponto flutuante (float e
double).Os operadores de bits são E sobre bits (&), OU inclusivo sobre bits (|), OU exclusivo sobre bits
(^), deslocamento para a esquerda (<<), deslocamento para a direita com sinal (>>), deslocamento
para a direita sem sinal (>>>) e complemento de bits (~).

Os operadores E sobre bits, OU inclusivo sobre bits e OU exclusivo sobre bits comparam seus dois
operandos bit a bit. O operador E (&) sobre bits configura cada bit no resultado como 1 se e somente
se o bit correspondente nos dois operandos for 1. O operador OU inclusivo sobre bits (|) configura
cada bit no resultado como 1 se o bit correspondente em qualquer (ou ambos os) operando(s) for 1.
O operador OU exclusivo sobre bits configura cada bit no resultado como 1 se o bit correspondente
em exactamente um operando for 1.

O operador de deslocamento para a esquerda (<<) desloca os bits de seu operando esquerdo para a
esquerda, pelo número de bits especificado em seu operando direito. O operador de deslocamento

Elaborado por: Bruno Alfredo Gamito, MSc.


114

para a direita com sinal (>>) muda os bits em seu operando esquerdo para a direita pelo número de
bits especificado em seu operando direito; se o operando esquerdo for negativo, 1s são deslocados
da esquerda; caso contrário, 0s são deslocados da esquerda.

O operador de deslocamento para a direita sem sinal (>>>) desloca os bits no seu operando esquerdo
para a direita de acordo com o número de bits especificado no seu operando à direita – 0s são
deslocados a partir da esquerda. O operador de complemento de bits configura todos os bits 0 em seu
operando como 1 no resultado e configura todos os bits 1 em seu operando como 0 no resultado. Um
exemplo:

Primeiro, peguemos o número em binário:


0010 0100 – que corresponde ao número 36. Andamos então 2 bits para trás, ou se você prefere para
a direita, e como resultado teremos  0000 1001 – que corresponde ao número 9.

Novamente, quanto ao número 36 andamos 1 bit para a frente, ou se você prefere para a esquerda, e
como resultado teremos: 0100 1000 – que corresponde ao número 72.

Note que, em ambos os casos, ficam espaços vazios com essas “andanças”.
0010 0100 – corresponde ao número 36;
001001 – deslocando 2 bits à direita (>> 2);
01001000 – deslocando 1 bit à esquerda (<< 1).

No caso do operador <<, esses espaços serão preenchidos com 0. No caso do operador >>,
preencheremos com 0 se por acaso o número for positivo e com o 1 caso o número seja negativo. No
operador >>>, o preenchimento será sempre com 0, e como resultado verificamos que, em números
negativos, qualquer valor deslocado acarretará sempre uma mudança no sinal, a menos em caso de
“voltas completas”, conforme o exemplo abaixo:

Elaborado por: Bruno Alfredo Gamito, MSc.


115

Uma grande dica: tome cuidado, pois o resultado retornado sempre será como tipo primitivo int (a
menos que seja usado explicitamente um long – como visto na terceira instrução), ou seja, ao
fazermos:

O resultado 144, em binário corresponde a: 1001 0000, entretanto, como o número resultante é do tipo
primitivo int, com 32bits ele é representado desta maneira: 00000000 00000000 000000000 10010000
Então note que o primeiro bit de sinal permanece desligado. Agora, se você deseja o seu valor em
byte, teremos que proceder um “casting” da seguinte maneira:

E como resultado, teremos o esperado -112. Como o resultado é sempre um tipo primitivo int, acontece
um fenómeno a cada 32bits, que é conhecido por “volta completa”

• Especiais
Os operadores especiais são: operador ternário (?:); separação de expressões (,); chamada de método
() e coerção unária (cast).

• Operador condicional ternário (? :)


O operador condicional ternário (? :) usa o valor booleano de uma expressão para decidir qual das
outras duas expressões serão avaliadas e utilizadas.

O operador de atribuição condicional é sintaticamente associativo à direita, sendo avaliado do extremo


à direita para a esquerda, por exemplo: a?b : c?d : e?f : g significa o mesmo que a?b : (c?d : (e?f:g)).

O operador de atribuição condicional possui três operandos, o operador ? aparece entre a primeira e
a segunda expressão e o : aparece entre a segunda e terceira expressão.

No momento da execução, o primeiro operando da expressão condicional é avaliado inicialmente,


efectuando uma conversão para booleano se necessário, utilizando o valor resultante boolean para
escolher um dos demais operandos como segue:
• se o valor do primeiro operando for true, então o segundo operando é escolhido;
• se o valor do primeiro operando for false, então o terceiro operando é escolhido.

Elaborado por: Bruno Alfredo Gamito, MSc.


116

• Separação de expressões (,)


Quando criamos várias variáveis do mesmo tipo podemos utilizar o operador (,) para executar as
variáveis expressões na mesma linha. Assim, suprimimos o tipo de dado.

• Chamada de método ()
Quando definimos um objecto num programa orientado a objectos, implementamos todo o
comportamento desse objecto em um ou mais métodos. Um método em Java é equivalente a uma
função, sub-rotina ou procedimento em outras linguagens de programação.

Um método entra em acção no momento em que é chamado. Isto pode ocorrer explicitamente ou
implicitamente. A chamada explícita se dá por ordem do programador através da execução de um
comando ou expressão contendo o nome do método. Em nossos programas fizemos uma chamada
explícita do método System.out.println para mostrar um texto na tela do computador. As chamadas
implícitas ocorrem quando o interpretador Java chama um método por sua própria deliberação. A
chamada do método main é um exemplo de chamada implícita. O interpretador Java chama esse
método para dar início à execução do programa.

No exemplo abaixo, chamamos o método void dormir() da classe Pessoa na nossa classe Principal.
Note que ambas classes se encontram no mesmo pacote (package chamada).

Elaborado por: Bruno Alfredo Gamito, MSc.


117

• Coerção unária (cast)


A coerção é definida como uma conversão de tipo implícita iniciada pelo compilador. As conversões
de tipo explícitas solicitadas pelo programador são referidas como conversões explícitas (casts), não
como coerções.

• Conversão de tipo explícita


A maioria das linguagens fornece alguma capacidade para a realização de conversões explícitas, tanto
de alargamento quanto de estreitamento. Em alguns casos, mensagens de aviso são produzidas
quando uma conversão de estreitamento explícita resulta em uma mudança significativa para o valor
do objeto que está sendo convertido.

As conversões de tipo explícitas são chamadas de casts. Para especificar um cast, o tipo desejado é
colocado entre parênteses imediatamente antes da expressão a ser convertida, como em (int)
Math.pow (2, 4).

As conversões de tipos são de estreitamento ou alargamento. Uma conversão de estreitamento


converte um valor para um tipo que não pode armazenar aproximações equivalentes a todos os valores
do tipo original. Por exemplo, converter um double para um float em Java é uma conversão de
estreitamento, porque a faixa do tipo double é muito maior do que a de float. Uma conversão de
alargamento converte um valor para um tipo que pode incluir ao menos aproximações de todos os
valores do tipo original. Por exemplo, converter um int para um float em Java é uma conversão de
alargamento.

As conversões de alargamento são quase sempre seguras, mantendo a magnitude do valor


convertido. Conversões de estreitamento nem sempre são seguras – algumas vezes, a magnitude do
valor convertido é modificada no processo.

Por exemplo, se o valor de ponto flutuante 1.3E25 é convertido para um inteiro em um programa Java,
o resultado será apenas distantemente relacionado com o valor original.

Elaborado por: Bruno Alfredo Gamito, MSc.


118

Apesar de as conversões de alargamento serem normalmente seguras, elas podem resultar em uma
precisão reduzida. Em muitas implementações de linguagens, apesar de as conversões de inteiro para
ponto flutuante serem conversões de alargamento, alguma precisão pode ser perdida. Por exemplo,
em muitos casos, inteiros são armazenados em 32 bits, o que permite ao menos nove dígitos decimais
de precisão. Mas os valores de ponto flutuante são também armazenados em 32 bits, com apenas
cerca de sete dígitos de precisão (por causa do espaço usado para o expoente). Então, alargamentos
de inteiro para ponto flutuante podem resultar na perda de dois dígitos de precisão.

Às vezes, precisamos que um número quebrado seja arredondado e armazenado em um número


inteiro. Para fazer isso sem que haja o erro de compilação, é preciso ordenar que o número quebrado
seja moldado (cast) como um número inteiro. Esse processo recebe o nome de casting. Um exemplo,
para elucidar:

Não irá compilar. Porque o compilador vai


alertar para incompatibilidade de tipos
(double  int). Para tal devemos fazer um
casting.

O casting foi feito para moldar a variável x como


um int. O valor de i agora é 4. O mesmo ocorre
entre valores int e long.

Não compila, pois pode estar perdendo


informação. E, se quisermos realmente fazer
isso, fazemos o casting.

O casting foi feito para moldar a variável x como


um int.

• Literais numéricos e promoção numérica


Alguns castings aparecem também quando atribuímos valores literais numéricos, por exemplo: float
x = 0.0; // o código não compila pois todos os literais ponto flutuante são considerados double pelo

Elaborado por: Bruno Alfredo Gamito, MSc.


119

Java. E float não pode receber um double sem perda de informação, para fazer isso funcionar
podemos escrever o seguinte: float x = 0.0f; // a letra f, pode ser maiúscula ou minúscula e, indica que
aquele literal deve ser tratado como float.

long l = 0.0L; // a letra L, que também pode ser maiúscula ou minúscula, indica que aquele literal deve
ser tratado como long. Outro caso, que é mais comum:

Você precisa do casting porque o Java faz as contas e vai promovendo o resultado intermediário
sempre para o maior tipo que apareceu durante as operações, no caso o double. Em caso de tipos
menores como byte e short o Java os promove para int na hora de fazer as contas.

Até casting com variáveis do tipo char podem ocorrer. O único tipo primitivo que não pode ser atribuído
a nenhum outro tipo é o boolean.

• Operadores de atribuição
Existem ao todo 12 operadores de atribuição, sendo todos associativos à direita, ou seja, são avaliados
da direita para a esquerda. Por exemplo: a = b = c significa a = (b = c). Os operadores de atribuição
são divididos em dois grupos, atribuição simples com o único operador (=), e as atribuições compostas
formadas pelos demais operadores no formato op2=.

Os operadores de atribuição são: =, *=, /=, %=, +=, -=, <<=, >>=, >>>=, &=, ^=, |=

Uso de atalhos:

a *= a; // a = a * a;
a /= a; // a = a / a;
a += a; // a = a + a;
a -= a; // a = a - a;
a %= a; // a = a % a;

2
Operador

Elaborado por: Bruno Alfredo Gamito, MSc.


120

• Expressões aritméticas
Em Java, cada variável deve possuir um nome, um tamanho e um valor. toda vez que um valor é
colocado em uma posição de memória, o valor substitui o anterior, que é perdido. Quando um valor é
somente lido, ele continua armazenado, podendo ser reutilizado sempre que necessário. A maior parte
dos programas realiza cálculos aritméticos. Java, assim como a maior parte das linguagens,
disponibiliza cinco operadores para tal tipo de cálculo: multiplicação, divisão e resto (que são avaliados
antes, da esquerda para a direita), além dos operadores de adição e subtracção (avaliados em
seguida, também da esquerda para a direita).

A divisão de inteiros produz um resultado inteiro. Qualquer parte fracionária da divisão é simplesmente
truncada, ou seja, nenhum arredondamento acontece. Há ainda o operador de resto (%), que fornece
o resto depois da divisão. Este operador é normalmente usado com operandos inteiros, mas também
pode ser usado com outros tipos de aritmética. A utilização de parêntesis em expressões aritméticas
complexas, mesmo não sendo necessários, tornam as expressões mais legíveis.

• Operadores de igualdade e operadores relacionais


Vamos primeiro definir o que é condição. Uma condição é uma expressão que pode ser avaliada
somente para dois valores: verdadeiro ou falso. Nesse ponto analisamos uma versão simples do
operador condicional if. Se a condição em uma estrutura if for verdadeira, o corpo da estrutura if é
executado. Se a condição for falsa, o corpo não será executado. É extremamente comum nos
depararmos com operadores de igualdade (== e !=), além de operadores relacionais (>, <, >= e <=)
compondo condições mais complexas.

Os operadores de igualdade têm o mesmo nível de precedência entre si e um nível mais baixo de
precedência na comparação com operadores relacionais que, por sua vez, possuem o mesmo nível
de precedência entre si. Tanto operadores de igualdade como os relacionais são associados da
esquerda para a direita.

Confundir o operador de igualdade (==), com o operador de atribuição (=), pode causar erros de lógica
ou de sintaxe. O operador de igualdade deve ser lido como “igual a”, e o operador de atribuição deve
ser lido como “obtém” ou “obtém o valor de”.

• Precedência de operadores
É preciso entender como o Java lida com as expressões matemáticas, porque senão, o Java pode
apresentar resultados menos esperados. Para melhor entendimento, no quesito de precedência,
vamos entender o programa abaixo:

Elaborado por: Bruno Alfredo Gamito, MSc.


121

• Conversões
As conversões implícitas ocorrem geralmente na aplicação de alguns operadores: atribuição, por
exemplo. As conversões explícitas ocorrem por meio de um mecanismo conhecido como “casting” e
as mudanças de tipo implícitas são conhecidas apenas por “conversão”. Portanto, a partir daqui,
chamaremos conversão ou casting para designar, respectivamente, as mudanças de tipo implícitas e
explícitas.

As conversões de tipos primitivos são tratadas em tempo de compilação. Toda a informação


necessária para efectuar a mudança de tipo já está disponível para o compilador Java no momento
em que ele efectua o seu trabalho. A conversão de primitivos ocorrem em três situações: atribuições,
chamada de métodos e operações aritméticas. A conversão, durante a atribuição, ocorre quando
atribui-se um valor a uma variável de um tipo diferente do valor original. Por exemplo:

A variável d é do tipo double e, portanto,


armazena somente variáveis deste tipo. Perceba
que ocorre uma conversão na linha 6. O valor
que está armazenado na variável i, que é 10
(32bits), é convertido para um valor com
capacidade de armazenamento de 64bits
(double).

Agora analisemos outro exemplo:


Este código não compila. Na verdade, o
compilador verifica que o programa está
tentando atribuir um valor double a uma variável
int, ou seja, armazenar em uma variável um
valor que pode ser maior do que o suportado. Ao
tentar compilar o código ao lado, o compilador
exibirá um erro do tipo “possible loss of
precission” ou “incompatible types for =”
(dependendo do compilador utilizado). A regra é
que a conversão só ocorre se não houver perda
de precisão. Por essa razão, alguns autores
chamam a conversão de promoção. Nesses
casos, a mudança de tipo deve ser feita de forma
explícita através de Casting.

Existem alguns detalhes peculiares na conversão de tipos primitivos, sobretudo quando a conversão
é feita a partir de valores literais. Por exemplo: float f = 9.999;

O fragmento de código acima não permite que a classe que o contenha seja compilada porque os
números literais em Java ou são double ou int (por padrão). Dessa forma, o programa tenta atribuir
um valor double a uma variável float. Para evitar o erro de compilação há três possibilidades: (i) ou se
troca o tipo primitivo para double, (ii) ou se sufixa o literal com “f ou F”, (iii) ou se realiza uma operação
de casting, prefixando o literal com o operador (float).

Elaborado por: Bruno Alfredo Gamito, MSc.


122

A partir da informação anterior de que o fragmento de código não compila é comum pensarmos ou
chegar à conclusão de que o próximo exemplo apresenta o mesmo tipo de erro, ou seja, atribuir a
variáveis byte, short e char o valor de um literal int, que é o padrão. Contudo, esse fragmento de
programa, ao contrário do anterior, executa sem nenhum problema.

A razão é que Java resolve a conversão durante a atribuição apenas quando um literal do tipo int é
atribuído a uma variável “menor” (byte, short ou char). É importante saber, ainda, que para a conversão
ser efectuada é necessário que o valor do literal esteja dentro da capacidade da variável.

Outro exemplo:
O fragmento de código possui dois erros. O
primeiro deles é o da linha 3: o valor que está
sendo atribuído à variável do tipo byte é maior
do que sua capacidade; o segundo erro aparece
na linha 5: a conversão do tipo int para tipos
menores só funciona para valores literais e não
para variáveis.

A conversão de tipos primitivos durante a chamada de um método ocorre quando se passa um valor
de um tipo como argumento para um método que espera um tipo diferente. Por exemplo, a maioria
dos métodos da classe java.lang.Math recebe parâmetros do tipo double. Porém, ao acionar tais
métodos fornecendo variáveis “menores”, elas são automaticamente convertidas para double. Por
exemplo:

O valor da variável int é automaticamente


convertido para double na chamada do método
da linha 4. As regras para conversão de tipos
primitivos na chamada de métodos são as
mesmas para conversão para atribuição.

O programa Java pode executar uma série de operações matemáticas, algumas delas tratando
variáveis de tipos diferentes. Resta ao compilador efectuar as devidas conversões antes de executar
a operação.

Elaborado por: Bruno Alfredo Gamito, MSc.


123

Na linha 6 o programa executa três operações.


Para isso executa também conversões de tipos
primitivos: na primeira operação (multiplicação)
a variável byte é convertida para int. Na segunda
operação (subtracção) o resultado da operação
anterior que está representado pelo tipo int é
convertido para float. Por fim, a operação de
atribuição converte o resultado da operação, que
é do tipo float, para double. Há necessidade de
compatibilidade integral entre tipos, levando-se
em consideração ambos os lados da atribuição.
A regra geral é a seguinte: caso os operandos
sejam de tipos diferentes, o operando de menor
tipo será convertido para o tipo do maior. No
caso dos tipos menores do que int, todos eles
são convertidos para int.

Observe o fragmento de código a seguir:


Um programador desatento diria que o código ao
lado compilaria sem problemas, contudo, ambos
os operandos b e b1, são convertidos para o tipo
int (conversão automática em expressões
aritméticas). Como o resultado da operação da
linha 5 é do tipo int, a atribuição não é permitida
e o compilador identifica o erro na compilação.

• Castings
As mudanças de tipos primitivos que foram explicados anteriormente são executadas
automaticamente. Não é necessário informar ao compilador quando a conversão deve ser feita. O
casting, por sua vez, representa uma mudança de tipo forçada (coerção), que ocorre quando o
compilador é informado por meio do uso de um prefixo, indicando o novo tipo. Após a execução do
casting pode haver ou não perda de informação. O exemplo a seguir efectua o cast do tipo int para
byte.

O código executa dois castings. O primeiro deles


(linha 5) é desnecessário já que a mudança de
tipo é feita de um tipo menor para um maior (int
para double). Já o segundo cast é exigido para
efectuar a mudança de tipo (int para byte). Os
castings são necessários quando a mudança
ocorrer de um tipo maior para um menor. Na
verdade o casting informa o compilador de que a
mudança pode ser feita mesmo que haja o risco
de perda de informação.

Elaborado por: Bruno Alfredo Gamito, MSc.


124

• Entrada e Saída de Dados (DataInputStream/Scanner Class)


A linguagem Java é capaz de processar arquivos que requisitam grandes quantidades de dados
persistentes. Esses são os dados que ficam armazenados dentro dos arquivos, sendo que a sua
duração vai além da finalização de um programa. Esse processamento de arquivos é uma capacidade
que o Java oferece para ler e gravar dados na memória, arquivos e conexões de rede.

Quando existe um dispositivo de entrada de dados activo, para obter dados deste dispositivo é usada
uma acção conhecida como read(). E para enviar um dado para um dispositivo de saída é utilizado o
método write().

Para trabalhar com entrada e saída de dados são utilizadas as classes que estão dentro do java.io.
Essas classes oferecem algumas funcionalidades como:

• Manipulação de entrada e saída de bytes – transferência de dados binários;


• Manipulação de entrada e saída de caracteres – transferência de textos;
• Buffers - melhoram a eficiência da velocidade de leitura e escrita;
• Conversão de formatos – texto e formato interno de dados binários.

As entradas e saídas de dados são manipuladas como sequência de bytes, através das classes
InputStream3 e OutputStream4 e as classes dependentes.

Hierarquia da classe InputStream

Então as principais subclasses de InputStream são:

• ByteArrayInputStream – Valores são originários de um arranjo de bytes;


• FileInputStream – Bytes com originalidade de um arquivo.
• FilterInputStream – Filtra os dados de um InputStream.

3
É uma classe abstracta que oferece a funcionalidade básica para a leitura de um byte ou de uma sequência de
bytes a partir de alguma fonte.
4
É uma classe abstracta que transfere sequencialmente os bytes para algum destino. Os métodos write(), tem a
função de escrever em forma de bytes para o destino onde vai enviar.

Elaborado por: Bruno Alfredo Gamito, MSc.


125

• BufferedInputStream – Faz a leitura de grandes volumes de bytes que armazena em um buffer


interno.
• DataInputStream – Permite a leitura de representações binárias dos tipos primitivos de Java.
• ObjectInputStream – Oferece o método readObject para a leitura de objectos que foram
serializados para um ObjectOutputStream.
• PipedInputStream – Faz a leitura de um pipe de bytes cuja origem está associada a um objeto
PipedOutputStream.

Subclasses OutputStream

Objecto de saída em Java (System.out)


O objecto System.out é a saída padrão, que permite exibir as Strings no console (terminal) de comando
quando o aplicativo de Java é executado. Dentro desse objecto existem métodos para gerar saídas de
Strings, entre elas são: println, print e o printf.

Elaborado por: Bruno Alfredo Gamito, MSc.


126

• O método System.out.println()
A instrução System.out.println(), gera uma saída de texto entre aspas duplas significando uma String,
criando uma nova linha e posicionando o cursor na linha abaixo, o que é identificado pela terminação
“ln”.

• O método System.out.print()
O método com print, se for observado não possui o “ln”, por isso exibe uma String sem criar uma nova
linha, deixando o seu cursor na mesma linha.

• Exibindo Texto com printf


Para exibir dados formatados, um novo recurso foi introduzido na versão Java 5. Trata-se do método
printf de System.out. Aliás, o f – de printf – significa formatted, ou seja, exibe os dados formatados.

A sua sintaxe: System.out.printf (formato, dados de saída);

O formato obviamente descreve a saída, as saídas são separadas por vírgula. Os formatos sempre
começam com “%”, e sua saída são separadas por vírgulas.

%s = representa String
%d = representa números Inteiros
%f = número com ponto flutuante. Na verdade, o “f” representa a vírgula
%2f = representa números doubles
%b = representa valores booleanos
%c = representa valores char
\t = tabulação
\n = salto de linha

Elaborado por: Bruno Alfredo Gamito, MSc.


127

• Classe Scanner
Quando se começa a conhecer os princípios da programação, com o tempo surge a vontade do
desenvolvedor iniciante a trabalhar com programas no modo texto (console). Com esse princípio,
muitos começam a usar a classe Scanner, pois tem justamente a finalidade de facilitar a entrada de
dados no modo Console. Essa classe apareceu a partir do Java 5, antes dessa versão era complicado
criar programas que recebiam valores de variáveis no modo Console.

O significado da classe Scanner para muitos no começo é um pouco complicado de entender, mas
com o tempo o programador acaba se acostumando com a sua definição. Um Scanner de texto simples
pode analisar os tipos primitivos e Strings usando expressões regulares.

A classe Scanner tem como objectivo separar a entrada dos textos em blocos, gerando os conhecidos
Tokens, que são sequências de caracteres separados por delimitadores que por padrão correspondem
aos espaços em branco, tabulações e mudança de linha.

Com essa classe podem ser convertidos textos para tipos primitivos, sendo que esses textos podem
ser considerados como objectos do tipo String, InputStream e arquivos.

Antes de tudo, é necessário saber algumas funções e aspectos que essa classe tem para exercer o
funcionamento dentro do esperado. Quando invocada a classe Scanner, o compilador pedirá para
fazer a seguinte importação: import java.util.Scanner;

Essa declaração tem por objectivo permitir que o compilador localize a classe Scanner, que será usada
nesse programa. Importa frisar, que um dos maiores destaques da linguagem Java é a extraordinária
quantidade de classes predefinidas disponíveis aos programadores e que podem ser importadas. Em
geral, esquecer de incluir uma declaração import para uma classe utilizada no seu programa resulta
em um erro de compilação contendo uma mensagem como “cannot find symbol” dependendo do
compilador. Quando isso ocorre, verifique se você forneceu as declarações import adequadas e se
os nomes nas declarações import estão escritos correctamente, incluindo a utilização adequada de
letras maiúsculas e minúsculas.

Essas classes são agrupadas em pacotes e os conjuntos de pacotes são chamados de bibliotecas ou
APIs (Application Programming Interface). A classe Scanner, presente nos nossos exemplos, pertence
ao pacote java.util. É fundamental que você saiba que todas as declarações de import devem ser
posicionadas antes da primeira declaração de classe do arquivo. Uma variável é uma posição de
memória do computador em que um valor pode ser armazenado para utilização posterior em um
programa. O nome de uma variável pode ser qualquer identificador válido. O tipo de uma variável
define o tipo de informação que ela pode armazenar.

Assim como acontece com outras instruções, a declaração de uma variável é finalizada com um ponto-
e-vírgula. A instrução Scanner nome_variável = new Scanner (System.in) especifica que a variável
nome_variável seja do tipo Scanner. Um Scanner permite a um programador ler os dados para
utilização em um programa. Os dados podem ser provenientes de várias origens, como um arquivo,
ou serem digitados pelo utilizador, por exemplo. Antes de utilizar um Scanner, o programa deve criá-
lo e especificar a origem dos dados. O sinal de igual (=) indica que a variável nome_variável deve ser
inicializada (isto é, preparada para ser utilizada no programa) na sua declaração, com o resultado da
expressão new Scanner (System.in) à direita do sinal de igual. Esse sinal significa atribuição. A
expressão completa cria um objecto Scanner, denominado nome_variável, que lê o dado digitado
pelo utilizador.

O objecto de entrada padrão (System.in), análogo ao System.out, permite que as informações


digitadas pelo utilizador sejam lidas. As instruções int num1, num2, produto (vide o primeiro exemplo

Elaborado por: Bruno Alfredo Gamito, MSc.


128

abaixo) declaram três variáveis que são do tipo int. Tais variáveis conterão valores inteiros e ainda
não foram inicializadas.

Por convenção, identificadores de nomes de variáveis iniciam com uma letra minúscula e cada palavra
no nome, depois da primeira palavra, inicia com uma letra maiúscula.

O System é uma classe (começa, por convenção, com letra maiúscula). Tal classe faz parte do pacote
java.lang. Esse pacote é importado, por padrão, em todos os programas Java. Ele é o único pacote
da API que não exige uma declaração de import. A instrução num1 = Entrada.nextInt(); utiliza o
método nextInt() para obter um inteiro digitado pelo utilizador. Nesse momento o programa aguarda o
utilizador digitar um número e pressionar a tecla Enter para submeter o número ao programa. Em
teoria, o utilizador pode digitar qualquer tipo como valor de entrada. O programa supõe que o utilizador
entre com um valor inteiro válido, conforme solicitado. Caso um valor não inteiro seja digitado, ocorrerá
um erro em tempo de execução (runtime) e o programa será finalizado

Alguns exemplos práticos usando a classe Scanner.

1. Programa que solicita ao utilizador dois números inteiros, calcula e exibe, na janela de
comando, o produto de ambos. Esses números devem ser armazenados para que o produto
seja apresentado. O armazenamento é realizado por meio do uso de variáveis.

Os operadores de igualdade têm o mesmo nível de precedência entre si e um nível mais baixo de
precedência na comparação com operadores relacionais que, por sua vez, possuem o mesmo nível
de precedência entre si. Tanto operadores de igualdade como os relacionais são associados da
esquerda para a direita.

O exemplo 2 utiliza seis condições if para comparar duas entradas de inteiros pelo utilizador. Sendo
uma das condições verdadeira, a instrução associada será executada.

Elaborado por: Bruno Alfredo Gamito, MSc.


129

2. O programa utiliza a classe Scanner para inserir os dois inteiros do utilizador e os armazena
em duas variáveis. O programa então compara os números e exibe o resultado das
comparações que são verdadeiras.

Comparação com dois


valores diferentes

Comparação com
dois valores iguais

Note que não há ponto-e-vírgula no final da primeira linha de cada condição if. Estando presente, esse
ponto-e-vírgula resultaria em um erro de lógica. Por exemplo,

Elaborado por: Bruno Alfredo Gamito, MSc.


130

É um if vazio, que não possui sentido algum. A instrução será executada independentemente do teste,
uma vez que não estão vinculadas.

3. Escreva um programa que solicite ao utilizador dois inteiros e exiba a soma, o produto e a
divisão desses dois números.

4. Escreva um programa que solicite ao utilizador cinco inteiros e imprima o maior e o menor do
grupo.

Elaborado por: Bruno Alfredo Gamito, MSc.


131

5. Escreva um programa que insira um número consistindo em cinco dígitos, separe em seus
dígitos individuais e imprima os dígitos separados por três espaços cada. Por exemplo, se o
utilizador digitar 54321, o programa deve imprimir: 5 4 3 2 1.

Elaborado por: Bruno Alfredo Gamito, MSc.


132

Unidade Temática HC HEI


11. Estruturas de decisão e Estruturas de repetição 2 2

• Estruturas de decisão
As estruturas de decisão em Java, também podem ser designadas como estruturas de selecção. As
estruturas de selecção efectuam um desvio no processamento das informações, permitindo a criação
de fluxos alternativos ao principal. Aliando estruturas de repetição e selecção, podemos criar
algoritmos de execução de funções ou rotinas para resolver problemas computacionais simples ou
complexos.

As estruturas de selecção funcionam por meio de um processo de comparação: se a variável que


determina o caminho a ser seguido apresentar determinado valor, o programa segue um fluxo; caso
contrário, segue outro. A figura a seguir ilustra esse procedimento.

É possível também aninhar estruturas de decisão, para construir algoritmos mais complexos, como
ilustra a figura abaixo.

Elaborado por: Bruno Alfredo Gamito, MSc.


133

No Java, existem duas estruturas de selecção: if e switch. Adiante veremos como funciona cada uma
delas.

• if
O if (se) é usado para comparar expressões, variáveis e condições booleanas. A sua sintaxe é:

Caso contrário, não irá executar, conforme a figura abaixo:

Dentro da estrutura if, podemos, opcionalmente, controlar os desvios de fluxo usando else. A sintaxe
para utilização do else é a seguinte:

Elaborado por: Bruno Alfredo Gamito, MSc.


134

Se for true ela executa o que esta entre as primeiras chaves.


Se for false ela executa o que esta entre else (caso contrário)

Para comparar múltiplas condições, o ideal é usar o else if, que tem esta sintaxe:

Dentro da expressão booleana, podemos usar os operadores lógicos OR (||) e AND (&&) para testar
condições compostas, facilitando a construção da estrutura if. Esses operadores são chamados
tecnicamente de short-circuit operators (operadores de curto-circuito).

Elaborado por: Bruno Alfredo Gamito, MSc.


135

IF é um dos comandos mais comuns e um dos mais importantes na programação, este comando é
responsável pelas tomadas de decisões, existe praticamente em todas as linguagens, antigamente
este comando era conhecido como “se-então-senão” ou “if-then-else” mas as linguagens mais

Elaborado por: Bruno Alfredo Gamito, MSc.


136

modernas substituirão o “então” pelo símbolo “{ }” (colchetes/chaves), se a condição puder ser


executada por apenas uma linha de comando então não é necessário usar o colchete/chave.

• Alguns programas utilizando a instrução if / if…else

(i) Programa que calcule e imprima o maior de três números diferentes.

(ii) Programa que calcula e imprime o maior de três números diferentes em ordem crescente.

Elaborado por: Bruno Alfredo Gamito, MSc.


137

• switch
O switch funciona por meio da verificação de condições diversas, as quais determinarão diversos
casos. Ele realiza um único teste, verificando condições numéricas (não booleanas) e, a partir daí,
executa todos os casos.

Ao usar o switch, é necessário observar algumas condições:


✓ O parâmetro a ser comparado (que aparece no início da estrutura switch) somente pode ser
dos tipos byte, short, char ou int;
✓ Os casos para comparação obrigatoriamente devem ser literais inteiros ou constantes;
✓ É possível determinar um parâmetro default para incluir os valores que não se encaixem nos
casos predefinidos. O uso do default é opcional.

A sua sintaxe é a seguinte:

Elaborado por: Bruno Alfredo Gamito, MSc.


138

Podemos controlar o fluxo de execução do switch (ou de qualquer bloco de selecção ou repetição)
usando o break, que interrompe a execução saindo do bloco.

Em outras palavras, o Java fornece a instrução de selecção múltipla switch para realizar diferentes
acções baseadas nos possíveis valores de uma variável inteira, ou expressão, ou seja, o comando
switch serve para simplificar certas situações onde existem vários valores a serem testados. Assim,
identificamos a variável a ser testada, e colocamos uma linha case para cada possível valor que a
variável pode assumir. No final, nos é permitido colocar uma linha default para o caso de a variável
não assumir nenhum dos valores previstos. O break no final de cada comando serve para evitar
comparações inúteis depois de encontrado o valor correcto. Se for necessário mais de um comando,
é necessário colocar o bloco das instruções entre “{ }”.

É importante compreender o funcionamento do switch para não cometer enganos. O comando switch
testa linha a linha dos cases encontrados, e a partir do momento que encontra um valor igual ao da
variável testada, passa a executar todos os comandos seguintes, mesmo os que fazem parte de outro
teste, até o fim do bloco. Por isso usa-se o comando break, quebrando o fluxo e fazendo com que o
código seja executado da maneira desejada.

Elaborado por: Bruno Alfredo Gamito, MSc.


139

• Alguns programas utilizando a instrução switch case


(i) Programa que, a partir da leitura da categoria profissional, imprima o respectivo salário base. Os
salários base dos funcionários da função pública estão tabelados segundo a categoria profissional:

CATEGORIA SALÁRIO
Professor auxiliar (A)
Professor associado (B) 50.000 Mt
Professor catedrático (C)
Investigador/pesquisador (D) 30.000 Mt
Assistente estagiário (E)
20.000 Mt
Assistente (F)
Outra 10.000 Mt

Elaborado por: Bruno Alfredo Gamito, MSc.


140

(ii) Programa que lê dois operandos e efectua a operação aritmética que o utilizador escolher.

Elaborado por: Bruno Alfredo Gamito, MSc.


141

(iii) Programa que afixe o seguinte menu de opções:


1. Levantamento
2. Depósito
3. Pagamento de serviços
4. Fim

O programa deve também ler a opção escolhida pelo utilizador e indicar o procedimento respectivo.

Elaborado por: Bruno Alfredo Gamito, MSc.


142

Saída:

Elaborado por: Bruno Alfredo Gamito, MSc.


143

• Estruturas de repetição
Neste ponto você aprenderá a utilizar as estruturas de repetição, ou loop. Há diversos algoritmos que
necessitam que uma acção, ou conjunto de acções, ocorra mais de uma vez para que uma
determinada tarefa se complete. É só lembrar de quantas vezes usamos a palavra “enquanto” na
nossa vida quotidiana. Por exemplo: enquanto não estive vestido, não posso ir para o trabalho;
enquanto estiver no posto de abastecimento a encher o tanque do carro, tenho que esperar. Essas
frases remetem ao loop while.

Outra forma de descobrirmos acções que exigem repetições em nossas vidas é por meio de frases
como: até que tenha terminado o almoço não posso levantar da mesa (questão de educação) e vou
ficar até que a prova tenha finalizado. Essas frases remetem ao loop do…while. Existe, ainda, mais
uma instrução de loop, que consiste em controlar um número predeterminado de execuções. Dessa
forma, frases do tipo: vou fazer vinte flexões, ou estou muito nervoso e, para me acalmar, vou contar
até dez, remetem ao loop for.

Iremos demonstrar as instruções de repetição: while, for e do…while, por meio de diversos pequenos
exemplos, para melhor elucidar a teoria.

• Instrução de repetição while


A instrução de repetição permite ao desenvolvedor especificar que um programa deve repetir uma
acção ou conjunto de acções, enquanto alguma condição permanecer verdadeira. Para exemplificar,
pense na seguinte frase: enquanto houver mais provas para corrigir, vou para a próxima e corrijo. Ao
final, a média da turma deve ser calculada. A condição “enquanto houver mais provas para corrigir”
pode ser verdadeira ou falsa. Essa acção será repetida enquanto a condição permanecer verdadeira.
As instruções contidas na instrução de repetição while constituem o corpo da instrução de repetição,
que normalmente é um bloco.

Por fim, a condição se tornará falsa quando a última prova for corrigida. Nesse ponto a repetição
termina e a primeira instrução posterior à instrução de repetição será executada. A sintaxe da instrução
while, em Java. Considere um fragmento de programa projectado para encontrar a primeira potência
de 5 maior que 200.

Quando essa instrução while inicia a execução, o valor da variável potência é 5. Cada iteração da
instrução while multiplica potência por 5 (cinco) e guarda o resultado em potência. Dessa forma,
potência assume os valores 25, 125 e 625. No momento em que a variável potência é 625. Nesse
ponto a execução do programa continua na próxima instrução depois do while. Não fornecer, no corpo

Elaborado por: Bruno Alfredo Gamito, MSc.


144

de uma instrução while, uma acção que consequentemente faz com que a condição na instrução while
torne-se falsa em geral resulta em um erro de lógica chamado loop infinito, no qual o loop nunca
termina.

Nos exemplos acima podemos ver, existe a variável x que é iniciada valendo 0, a cada loop executado
(repetição) é somado 1 a variável x. Perceba que o while irá manter a repetição enquanto a variável
x for menor que 4.

Em outras palavras, a instrução while serve para fazermos uma sequência de comandos serem
executados uma determinada quantidade de vezes até que uma condição seja falsa.

Note que é feito o teste condicional primeiro, e se a condição for verdadeira, entra no loop.

while (condição) {
comandos;
}

Elaborado por: Bruno Alfredo Gamito, MSc.


145

Note que dentro da estrutura de repetição tem o comando “i++” que serve para aumentar em um o
valor da variável “i”, do contrário o programa entraria em loop infinito travando o programa. Importa
frisar, que cada ciclo de repetição é chamado de iteração.

• Alguns exemplos
O programa a seguir mostra os termos “an” de uma PA (Progressão Aritmética), de termo inicial, “inicial”
e razão, “razão”, até um valor máximo “valor_max”. Os termos de uma PA crescem através da adição
da razão ao termo anterior. Em programação, poderíamos representar isso assim: a n = an + razão.
Porém, em Java podemos fazer isso: an += razão.

O programa a seguir calcula os termos “gn” de uma PG (Progressão Geométrica), de termo inicial,
“inicial” e quociente, “quociente”, até um valor máximo “valor_max”.

Os produtos de uma progressão geométrica, crescem através do produto do termo anterior com o
quociente. Em programação, poderíamos representar isso assim: gn = gn * razão.

Porém, em Java podemos fazer o seguinte: gn *= razão.

Elaborado por: Bruno Alfredo Gamito, MSc.


146

• Comando do…while
Esse comando é parecido com o “while”, a diferença é que no while o teste condicional é feito no início
da estrutura e o do…while o teste é feito no final. Na prática isso significa que se o teste no while for
falso, os comandos não serão executados nenhuma vez. Com o do…while o teste é feito no final,
sendo assim, mesmo que a condição for falsa os comandos serão executados pelo menos uma vez.

do {

} while (condição)

Note que o teste é feito no final, dessa forma pelo menos uma vez a mensagem será executada mesmo
que a condição seja falsa. Para testar essa hipótese mudemos a condição para i > 10, executemos o
programa e vejamos que aparecerá uma mensagem na tela comprovando a execução da mensagem.

Sendo assim, com o do…while, sempre é efectuada pelo menos uma execução.

Elaborado por: Bruno Alfredo Gamito, MSc.


147

• Alguns exemplos utilizando a instrução do…while


(i) Programa que imprime os números impares no intervalo [A, B]. Atendendo que o limite inferior do
intervalo deve ser inferior ou igual ao limite superior, ou seja, A <= B.

Elaborado por: Bruno Alfredo Gamito, MSc.


148

• Instrução for
O for executa uma declaração que indica o valor inicial de uma variável e testa uma condição inicial.
Dependendo do resultado do teste, é executado o bloco de código que está dentro do laço e, depois,
uma expressão envolvendo a variável. Esse procedimento é realizado até que a variável atinja o valor
determinado pela condição. A sintaxe do for é a seguinte:

Conhecendo a sintaxe do for, vamos aplicá-lo ao exemplo a seguir, que exibe os números em ordem
crescente, partindo de 1 até chegar a 60.

Elaborado por: Bruno Alfredo Gamito, MSc.


149

No código acima, a linha na qual tem início a estrutura for solicita ao interpretador que execute o bloco
de código contido no laço enquanto i for menor ou igual a 60 e incremente i em 1 unidade antes de
começar o próximo laço.

• Exemplos de uso de estruturas de controlo


As estruturas de controlo permitem construir uma infinidade de algoritmos. Suponhamos que queremos
imprimir a tabela de tabuada de um determinado número, por exemplo. O algoritmo para esse
procedimento, em linhas gerais, é o seguinte: deve-se exibir o número cuja tabela de tabuada queremos
mostrar e, depois, o valor desse número multiplicado pelos números de 1 a 10. Para isso, podemos
usar, como factor de multiplicação, um contador iniciando com o valor 1 e sendo incrementado até
chegar a 10, como mostra o exemplo a seguir.

Elaborado por: Bruno Alfredo Gamito, MSc.


150

Elaborado por: Bruno Alfredo Gamito, MSc.


151

É possível, ainda, combinar o uso de estruturas de repetição para realizar rotinas complexas. O código
aninhados mostra um exemplo de algoritmo que utiliza estruturas de controlo aninhadas:

Elaborado por: Bruno Alfredo Gamito, MSc.


152

Em outras palavras, o loop for é uma estrutura de controlo iterativa alternativa que é particularmente
apropriada quando: (i) desejamos executar algumas instruções um número fixo de vezes; (ii)
precisamos de uma variável dentro do loop cujo valor muda por uma quantidade fixa (em geral
acrescido por 1, a cada iteração).

for (inicialização; condição; acção pós-corpo) {


instruções a serem repetidas
}

• Mais exemplos com a instrução for


(i) Programa que imprime dez vezes “Bem-vindo a Programação Orientada a Objectos com Java”.

Elaborado por: Bruno Alfredo Gamito, MSc.


153

(ii) Programa que calcula a soma dos N primeiros inteiros positivos.

(iii) Programa que imprime os 10 primeiros números de Fibonacci.


Declarando as variáveis: (i) fCorrente (int) é o número de Fibonacci que vamos calcular; (ii) fPenultimo
(int) é o penúltimo número de Fibonacci que calculámos; (iii) fUltimo (int) é o último número de
Fibonacci que calculámos e; (iv) i (int) são contadores de números de Fibonacci.

Elaborado por: Bruno Alfredo Gamito, MSc.


154

(iv) Programa que imprime os seguintes triângulos rectângulos:

Elaborado por: Bruno Alfredo Gamito, MSc.


155

O mesmo programa trocando a instrução x += c; para x += cont; irá substituir os “asteriscos “*” para
sequência de números, veja o código abaixo:

Elaborado por: Bruno Alfredo Gamito, MSc.


156

Unidade Temática HC HEI


12. Classes, Objectos e Métodos 6 4

• Classes
Abstracções são o fundamento utilizado por nós, seres humanos, a fim de lidar com a complexidade.
Uma abstracção demonstra o comportamento e propriedades essenciais de um objecto, que o
diferencia de outro qualquer. Por exemplo, ao utilizar um carro, você não pensa nele como um conjunto
de dezenas de milhares de peças individuais. Pensamos em um carro como um único objecto sobre
qual podemos realizar um determinado conjunto de operações. Essa abstracção permite que as
pessoas utilizem um carro sem nenhum conhecimento sobre mecânica de automóveis.