Explorar E-books
Categorias
Explorar Audiolivros
Categorias
Explorar Revistas
Categorias
Explorar Documentos
Categorias
Genealogia das principais linguagens de programação de alto nível (SEBESTA, 2011: 58).
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.
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.
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.
É 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.”
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.
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.
• 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.
• 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.
• 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
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.
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.
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.
• 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.
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.
• 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.
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.
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
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.
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
• 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.
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.
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.
• 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).
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.
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.
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.
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.
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.
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.
• 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.
• 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 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.
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.
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.
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
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).
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.
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
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.
• 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
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.
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
2
https://www.timetoast.com/timelines/linha-do-tempo-java--2
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 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;
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.
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.
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.
• 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
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.
Para instalar o JDK no Windows, você já deve ter baixado o arquivo no site da Oracle.
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.
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).
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:
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.
• 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.
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 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 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
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.
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.
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.
• 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
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.
• 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.
• 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.
• 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).
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.
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:
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.
• 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.
✓ 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).
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.
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.
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.
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.
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.
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:
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.
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.
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 { }.
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.
• 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).
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.
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:
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
multiplataforma da linguagem Java (WORA – Write Once, Run Anywhere (Escreva uma vez, execute
em qualquer lugar)).
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.
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.
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.
O uso de aspas faz com que uma String seja um conjunto de caracteres (vide figura acima).
• 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.
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.
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.
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 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.
• 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.
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).
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.
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 “\”:
Através dos exemplos acima, podemos observar que as conversões em Java ocorrem da seguinte
maneira, sem perda de informação:
• 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: (/** */).
• 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
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.
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
(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.
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:
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.
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.
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.
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.
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.
Se quisermos criar uma nova instância de um objecto de uma classe, podemos utilizar o new2, por
exemplo:
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).
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 ();
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.
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.
ponto (.), em que este sinal gráfico concatena a referência e o atributo, como mostra o exemplo a
seguir:
Estes endereços de memória, dos objectos Java, não nos interessa, ele interessa apenas ao Garbage
Collector.
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:
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:
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.
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.
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
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.
• 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.
• 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:
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.
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).
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.
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.
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, ...
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.
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.
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:
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;
• 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.
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).
• 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.
^ (Xor) binário
Operando A Operando B Operando A ^ Operando B
Verdadeiro Verdadeiro Falso
Verdadeiro Falso Verdadeiro
Falso Verdadeiro Verdadeiro
! (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.
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.
• 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.
• 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.
1
Expressão booleana.
| (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.
É 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:
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ó:
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.
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.
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.
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;
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.
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
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
• 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 (%).
1
Resto da divisão
Podemos também, utilizar o operador aritmético de adição (+) para concatenar Strings.
• Relacionais
Os resultados dos operadores relacionais são do tipo boolean (true or false).
• Lógicos
• && (and)
• || (ou)
• ^ (ou exclusivo)
• ! (negação)
• Igualdade
• Incremento/decremento
• Condicional
• Bitwise
Também conhecido como operadores bit a bit, porque os operandos são comparados no nível dos
seus bits.
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.
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.
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.
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
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.
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
• ~ (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
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:
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:
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).
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.
• 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).
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).
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.
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.
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
• 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.
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:
• 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.
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).
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 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.
• 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.
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:
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.
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.
Subclasses OutputStream
• 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.
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
• 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.
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
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.
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 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,
É 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.
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.
• 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.
É possível também aninhar estruturas de decisão, para construir algoritmos mais complexos, como
ilustra a figura abaixo.
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 é:
Dentro da estrutura if, podemos, opcionalmente, controlar os desvios de fluxo usando else. A sintaxe
para utilização do else é a seguinte:
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).
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
(ii) Programa que calcula e imprime o maior de três números diferentes em ordem crescente.
• 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.
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.
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
(ii) Programa que lê dois operandos e efectua a operação aritmética que o utilizador escolher.
O programa deve também ler a opção escolhida pelo utilizador e indicar o procedimento respectivo.
Saída:
• 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.
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
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;
}
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.
• 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.
• 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.
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.
É 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:
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).
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:
• 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.