Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Índice de Matérias
Nota: Ao longo da leitura deste documento, sempre que não entender o significado de determinado termo, por favor
dirija-se ao glossário que é apresentado em anexo para descobrir uma possível definição para o mesmo.
Caso não esteja lá definida a palavra que procura, não hesite em questionar os colegas ou professores para melhor
ficar esclarecido.
Ao notificar o professor estará também a contribuir para melhorias futuras no documento, pois novas palavras poderão
ser adicionadas ao glossário que possivelmente esclarecerão outros colegas com dúvidas semelhantes à sua.
Este documento, para além de ter sido elaborado pelos professores já mencionados, conta com a participação dos
docentes da cadeira de IP dos anos lectivos de 2000/2001 até 2004/2005, visto terem sido utilizados os respectivos
acetatos como referência para alguns dos conteúdos aqui utilizados. A sebenta do ano lectivo 2004/2005 foi
reestruturada e melhorada.
1.1 - Motivação
Nas sociedades modernas todos os sectores de actividade dependem directa ou indirectamente do apoio dos
computadores.
Um algoritmo pode ser definido como um conjunto finito de instruções bem definidas e não ambíguas, que
executadas por uma determinada ordem resolvem um problema.
Para que se possa encontrar uma boa solução para um problema, é necessário começar pela sua compreensão. Em
seguida, o problema deve ser analisado no sentido de procurar soluções para o mesmo. Quando se faz a análise de
um problema é importante considerar três componentes:
estado inicial, isto é quais os dados de entrada do problema;
a transformação, que especifica os passos necessários para transformar o estado inicial no estado final;
estado final, ou seja, o que se pretende obter como resultado.
Muitos dos erros dos programadores são causa de uma deficiente compreensão e análise do problema.
A resolução de problemas é uma tarefa para a qual não há receitas nem fórmulas, pois cada problema é um caso
diferente, tal como podem ser inúmeras as soluções que o resolvem. Umas serão mais correctas ou terão melhor
desempenho que outras, umas ainda serão mais ou menos exaustivas; cabe-nos a nós, como programadores, saber
averiguar qual das soluções encontradas se adequa melhor às necessidades da tarefa que temos em mãos.
Exemplo:
Vamos supor que temos como tarefa resolver o problema da confecção de um bolo. Uma receita não é mais do que
uma descrição de passos ou acções que fazem a combinação de um conjunto de ingredientes com vista a obter um
produto final (bolo).
1 Sintaxe de uma linguagem é o conjunto de regras que define as relações válidas entre componentes da linguagem.
2 Semântica de uma linguagem define o significado de cada frase da linguagem.
Analisando tudo o que é necessário para cumprir essa tarefa e tendo em conta os passos atrás mencionados, quais
são os passos a seguir?
Afinal não é só na programação de computadores que podemos aplicar algum método ou tipo de raciocínio para
resolver problemas!
Com este exemplo vimos aplicada a um caso da vida quotidiana exactamente a mesma ordem de pensamento que
iremos seguir daqui por diante na implementação de programas de computadores.
Existe também a abordagem contrária, apelidade de Bottom-Up que parte da base para o topo, ou seja, partindo de
problemas pequenos, visualizar a solução para algo mais amplo e mais genérico.
Pode até utilizar-se um misto destas duas abordagens, desde que no fim consigamos atingir uma boa solução para o
problema.
Em qualquer uma delas está inerente um conceito que é a base de resolução de qualquer problema: a divisão em
elementos de solução mais simples, que trabalhando em conjunto nos fazem chegar à solução que nos interessa.
Independentemente do sentido em que procuramos a solução, estamos sempre a utilizar uma técnica que se chama
“Divide and Conquer” , ou seja, Dividir para Conquistar. Ninguém ganha uma corrida só porque decide ganhar, tem de
correr as etapas todas uma por uma com o objectivo de no fim ter conseguido uma média de tempos inferior à dos
outros corredores!
Exemplo:
Aplicando o método anteriormente descrito, significa que temos de dividir um problema em partes menores (ou
subproblemas) de modo a que seja mais fácil a sua resolução
Algoritmo:
Lavar a laranja
Partir a laranja ao meio
Espremer a laranja
Filtrar o sumo
Servir o sumo
O que se pode destacar com este exemplo é que cada passo é completado antes que o próximo comece. Como por
exemplo, é impossível ouvir rádio sem primeiro o ligar.
Compreender o problema
Identificar os dados de entrada
Identificar os dados de saída
Determinar o que é necessário para transformar dados de entrada em dados de saída, isto é, deve-se
descrever todas as transformações necessárias para resolver o problema, fazendo-o em termos que
possam ser executáveis pelo computador
- usar a estratégia de dividir em problemas mais pequenos
- observar regras e limitações
- identificar todas as acções a realizar
- eliminar ambiguidades (qualquer pessoa que o execute obtém os mesmos resultados)
Construir o algoritmo, tendo em consideração que:
-Para um mesmo conjunto de dados deve produzir sempre os mesmos resultados (se com os
mesmos ingredientes, nas mesmas quantidades tivéssemos bolos diferentes algo de errado se tinha
passado)
- Produzir resultados correctos, qualquer que seja o conjunto de dados legítimos utilizado.
Testar o algoritmo
Executar o algoritmo
No entanto, quando se concebem algoritmos para serem transformados em programas é importante exprimi-los
utilizando acções que o computador seja capaz de executar:
Outro aspecto importante é que os nossos algoritmos vão ter que manipular valores, estes têm que ser guardados em
algum lugar. Para tal utilizam-se variáveis. Uma variável é um lugar na memória do computador onde podemos
colocar um valor, ou consultar o valor que lá está. Cada variável é identificada por um nome.
Exemplo:
Elaborar um algoritmo que calcule a área total das paredes de uma caixa de secção rectangular a partir das suas
dimensões.
Algoritmo:
1. Introdução dos valores iniciais
1.1. Pedir comprimento da caixa
1.2. Ler comp
1.3. Pedir largura da caixa
1.4. Ler largura
1.5. Pedir altura da caixa
1.6. Ler altura
2. Transformação
2.1. area_base = comp * largura
2.2. area_lateral1 = comp * altura
2.3. area_lateral2 = largura * altura
2.4. area_caixa = 2 * area_base + 2 * area_lateral1 + 2 * area_lateral2
3. Apresentação dos resultados
3.1. Escrever area_caixa
Existem várias técnicas para a descrição de algoritmos. Uma maneira de descrever algoritmos é usando linguagem
natural, em que os algoritmos são expressos directamente em linguagem natural (isto é, o português como no
exemplo do bolo). Outra maneira é usando pseudocódigo (exemplo calculo da área). O pseudocódigo é um código
de escrita em que se utilizam termos convencionais para indicar as instruções do programa; esses termos são
geralmente um misto de palavras da nossa linguagem natural com palavras e notações típicas das linguagens de
programação. A utilização de pseudocódigo permite ao programador expressar as suas ideias sem ter de se
preocupar com a sintaxe da linguagem de programação. Os algoritmos podem também ser descritos utilizando
fluxogramas. Os fluxogramas são diagramas representativos do fluxo de acções de um programa, através de
símbolos, que representam os diferentes tipos de acções e seu encadeamento na sequência do programa (ver figura
abaixo).
Processamento em geral
Leitura/Escrita de Dados
Início/Fim de processamento
Linha de fluxo
Decisão condicional
Escolha múltipla
Subprograma
Actualmente os fluxogramas não são efectivamente muito utilizados. No entanto, talvez possam ser úteis para ajudar
a visualizar melhor determinadas estruturas típicas de programação. Mas, para representar programas de média e
grande extensão, tornam-se difíceis de concretizar.
Exemplo
Pseudocódigo
Início
Pedir valor de a
Ler a
Calcular quadrado de a
result = (a*a)
Escrever “O quadrado é:” result
Fim
Fluxograma
Início
Pedir a
Ler a
result = a*a
Escrever “O
quadrado é:”
result
Fim
Numa sequência é processado um conjunto de acções (ou instruções) em série. O conjunto de acções é executado
exactamente pela ordem em que as instruções estão indicadas, não havendo qualquer possibilidade de alterar a
ordem de processamento dessas acções.
Uma estrutura de selecção (ou decisão) é uma estrutura que permite, com base numa condição, decidir sobre a
execução ou não de determinada acção ou optar entre duas alternativas expressas. Ou seja, o processo segue por
uma de duas vias, dependendo do valor lógico (verdadeiro ou falso) da expressão que é avaliada no início da
estrutura.
Exemplo
Pseudocódigo
Início
Pedir valor a
Ler a
Pedir valor b
Ler b
Se a ≠ 0 então
Calcular o valor de x (ax+b=0)
Escrever valor de x
Senão
Escrever “Não há zero”
Fim
Fluxograma
Início
Pedir
Ler a
Ler ab
Pedir
Ler ab
Ler b
Não
a≠0
Sim
x= - b/a
Fim
Exemplo
Pseudocódigo:
Início
Pedir “Introduza dois números”
Ler valor 1
Ler valor 2
Se valor1 > valor 2 então
Escreve valor1 “é maior”
Senão
Se valor1 < valor 2 então
Escreve valor2 “é maior”
Senão
Escreve “valores iguais”
Fim_Se
Fim_Se
Fim
Fluxograma:
Início
Pedir
Ler dois
a valores
Ler valor 1
Ler valor 2
Não Não
valor 1 > valor 2 valor 1 < valor 2
Sim Sim
Fim
Por último falta falar das estruturas de repetição (também conhecidas por ciclos). Neste caso também é necessário
tomar a decisão com base no valor lógico de uma expressão. No entanto, a mesma acção é expressa repetidamente
enquanto o resultado da expressão lógica se mantiver verdadeiro.
Nas estruturas de repetição o teste da expressão lógica pode preceder a acção ou pode suceder a acção, isto é na
primeira situação a acção só é realizada se a condição tiver o valor lógico verdadeira, no segundo caso a acção é
sempre realizada qualquer que seja o valor lógico da expressão pois este só é verificado depois da acção ter sido
realizada.
Expressão
Verdadeiro
Falso Expressão
Verdadeiro
Falso
Expressão
Verdadeiro
Falso
Esta última estrutura de repetição é idêntica às duas anteriores. A diferença é que logo à partida é especificado o
número de ciclos (ou iterações) que serão efectuados, isto é o número de vezes que a acção é realizada.
Estas estruturas apresentam grandes vantagens para a programação em termos de economia de escrita de código,
uma vez que, com um determinado número de instruções, pode repetir-se um conjunto de acções um determinado
número de vezes ou enquanto se quiser, mediante uma expressão de controlo.
Exemplo
Elaborar um algoritmo em que um utilizador introduz um número e outro utilizador vai tentar advinhar esse número,
introduzindo números até a acertar. O segundo utilizador só para de introduzir número quando acertar.
Algoritmo Fluxograma
Início
Pedir a Início
Ler a (número a acertar)
Repetir
Pedir b
Ler b
Até que a = 0 Pedir a
Escrever “Acertou”
Fim
Ler a
Pedir b
Ler b
Não
a=b
Sim
Escrever
“Acertou”
Fim
Não existe consenso entre os especialistas sobre qual é a melhor maneira de representar um algoritmo. Actualmente
a maneira mais comum de representar é através de pseudocódigo. A utilização de pseudocódigo tem a vantagem de
podermos utilizar um texto escrito dessa forma como base para a escrita de um programa, não apenas numa
determinada linguagem de programação, mas em qualquer outra, sem ter de se elaborar um novo algoritmo.
Outra vantagem da escrita de algoritmos em pseudocódigo é que esta forma de abordagem presta-se a uma
aproximação sucessiva à versão final do programa, ou seja, pode-se ir progredindo por fases, revendo o
pseudocódigo e substituindo-o progressivamente por termos e sinais próprios da linguagem de programação.
Do sistema decimal para o sistema binário, basta fazer divisões sucessivas por dois e aproveitar o resto da divisão
inteira:
7 / 2 = 3 e o resto da divisão é 1
3 / 2 = 1 e o resto da divisão é 1
1 / 2 = 0 e o resto da divisão é 1
Existem outros sistemas de numeração para além do decimal e do binário, tais como o octal e o hexadecimal, por
exemplo.
Periféricos Unidade
Central de Processamento Periféricos
De Entrada De Saída
(CPU)
Memória Memória
Principal Secundária
Outro elemento fundamental é a memória central, também conhecida por RAM (Random Access Memory). É uma
área de armazenamento de informação que apresenta as seguintes características:
Perde o conteúdo quando se desliga o computador – memória volátil;
Pode ser imaginada como sucessão de células (ver figura abaixo);
Cada célula de memória armazena um único valor;
Quando se escreve um novo valor numa célula de memória, perde-se o valor que anteriormente lá estava;
A capacidade da memória é expressa no número de bytes que ela consegue armazenar: kilobytes (KB –
1024 bytes), megabytes (MB – 1024 KB ou 1024000 bytes), gigabytes (GB – 1024 MB ou 1024000000
bytes)
Supondo que tínhamos uma memória RAM de 1MB, a sua representação gráfica poderia ser a seguinte (em que os
números abaixo da tabela de células representam os índices que identificam cada célula. Célula número 1, 2, ... 256,
etc):
z a 12 x
0 1 2 3 ... 256 ... 512 ... 1024
Para além da CPU e da memória central, existem diversos tipos de dispositivos que permitem a comunicação entre o
computador e o utilizador, como por exemplo:
Estes dispositivos de armazenamento secundário persistente são os Discos rígidos, disquetes, CD-Roms, DVDs,
FlashDrives, cartões de memória usados nas máquinas fotográficas digitais, entre outros.
Há diversos níveis de linguagem de acordo com as afinidades que apresentam com as linguagens humanas:
Linguagens Máquina: são utilizadas para comandar directamente as acções do computador. As instruções
são constituídas por 0s e 1s, e manipulam directamente entidades dentro do computador.
Linguagens Assembly: são linguagens com características semelhantes às das de máquina, diferindo
apenas por usarem nomes simbólicos em vez de sequências de 0s e 1s.
Linguagens de Alto Nível: são linguagens mais próximas das linguagens humanas e, por isso, são mais
fáceis de utilizar do que as outras.
Há muitas linguagens que se podem incluir nesta categoria: C, Pascal, Lisp, Prolog, C++ e Java.
compiladores. Os compiladores são programas que fazem a tradução do código fonte (escrito numa linguagem de
alto nível) para código executável (em linguagem máquina).
A primeira função dos compiladores é verificar se o código fonte que lhe é fornecido cumpre as regras sintácticas da
linguagem em causa e assinalar os erros que eventualmente existam, denominados por erros de compilação. Aos
erros que ocorrem durante a execução do programa e que não podem ser detectados durante a compilação dá-se o
nome de erros de execução.
Um programa sem erros de compilação não significa necessariamente que funcione como era desejado. Pode ter
erros semânticos (ou seja, erros lógicos, derivados de um raciocínio errado que levou a uma solução também
incorrecta). Neste caso, teremos de proceder a uma revisão do código e novos testes para que os erros sejam
resolvidos.
Este problema fica resolvido considerando um computador virtual que se imagina capaz de executar um código
constituído por instruções simples. O compilador Java não gera código executável mais sim bytecode, um código
que é independente do computador em que seja executado. Esse código tem de ser interpretado pela chamada
Máquina Virtual Java, que é um programa em execução no computador e que é responsável por executar programas
Java.
Existem versões diferentes desta Máquina Virtual para diferentes sistemas operativos (Windows, Unix, Linux, Solaris,
MacOS, etc).
Execução
A utilização do código intermédio tem a vantagem de permitir aos computadores criar programas que podem ser
executados em qualquer tipo de computador, sem que tenham que se preocupar com as suas especificações. Desde
que esse computador tenha a máquina virtual correspondente instalada, qualquer programa Java poderá ser lá
executado.
Para criar programas em Java é necessário ter instalado no computador, pelo menos o Java Standard Edition
Software Development Kit (J2SE SDK) instalado no computador.
Ola Mundo!
Exerc. 3: Calcular a nota de uma disciplina que é composta por 20% para o trabalho e 80% para o exame. O
utilizador introduz as duas nota e é apresentada a nota final.
Exerc. 4:
a) Calcular o salário de um funcionário obtendo o valor e o número de horas semanais sabendo que se o número de
horas for superior a 40, as 40 horas são pagas pelo preço normal e as restantes pelo dobro do valor.
b) Altere o algoritmo anterior de forma a incluir um valor diferente para o número de horas superior a 60. Isto é:
Até 40 horas – preço da hora
Das 40 às 60 horas – dobro do preço da hora
A partir das 60 horas – triplo da hora.
Exerc. 5:
a) Calcular a média da nota de três exames introduzidos pelo utilizador.
b) Altere o algoritmo anterior de forma a receber só notas compreendidas entre 0 e 20 valores.
c) Alterar o algoritmo de forma a indicar se o aluno está aprovado (média superior a 10 valores) ou reprovado.
Exerc. 6: Dado um valor de temperatura, em graus Celsius, Farenheit ou Kelvin, converter esse valor para as
restantes unidades. (Nota: ºC = (ºF-32)*5/9; ºK = ºC+273.15)
Exerc. 7: Fazer uma chamada de um telefone público. Deve considerar casos tais como “o telefone está ocupado”, “o
telefone está avariado”, “o número desejado está interrompido”, etc.
Exerc. 9: Receber uma seqência de números e determinar se um dado número (também recebido) aparece ou não
na sequência.
Exerc. 10: Determinar o resultado de uma expressão matemática que utilize os operadores: + e -
Exerc. 11: Calcular o perímetro e a área de uma figura geométrica, sabendo que a figura só pode ser um rectângulo
ou uma circunferência.
Exerc. 12: Procurar uma nota de um aluno numa pauta, através do seu número
Exerc. 13: Descobrir um número entre 0 e 1000, pensado por um colega. Após ter acertado informar o colega do
número de tentativas efectuadas.
Exerc. 15: Receber uma sequência ordenada de números e determinar se um dados número aparece ou não na
sequência. Qual a principal diferenção entre este algoritmo e o exercício 9.
Ao consultarmos um dicionário constatamos que a definição de conceito é “ Tudo o que o espírito e a alma concebem
ou entendem”, mais terra a terra, “ é uma ideia ou noção que aplicamos às coisas, ou objectos da nossa consciência.
Por exemplo, para dizermos que temos o conceito de barco apenas precisamos da capacidade de identificar uma
instância de um barco.
Tendo por base, a definição de conceito e as abstracções que realizamos no nosso dia a dia, é nos possível definir
classes. Uma classe não é mais que um grupo de pessoas, animais ou coisas. Mas como os agrupamos? Se
pensarmos bem, chegamos à conclusão que ao definir por exemplo um grupo de animais, o fazemos através dos seus
atributos, das suas características. Vejamos, no reino animal podemos definir vários grupos, tais como carnívoros vs
herbívoros, ovíparos vs mamíferos, entre outras.
A linguagem de programação Java diz-se orientada aos objectos. Num paradigma de objectos, ao contrário do que se
possa pensar, não se procura uma nova forma de programar, mas sim orientarmo-nos para uma forma de pensar
abstracta relativamente a qualquer problema, utilizando para isso conceitos do mundo real.
Por exemplo, peguemos num conceito que todos nós conhecemos, o de aluno. Aluno é todo aquele que é educado
por um segundo elemento, normalmente numa instituição. Ao conceito de Aluno chamamos de classe, em
programação orientada por objectos. Mais, de acordo com a definição de classe mencionada em cima, podemos
afirmar que são várias as características de um aluno – nome, número, curso em que está inscrito, turma, entre
outras. A estas características chamamos atributos.
Atributo define uma característica das futuras instâncias de uma classe. Têm um nome único dentro da mesma
classe.
Curso - EI
Turma - A
Ano – 1º
Por outro lado, sabemos ainda que, o aluno pode pagar propinas, pode transitar de ano, pode inscrever-se numa
determinada turma, pode mudar de turma. Estes são alguns dos comportamentos que um aluno pode desempenhar,
definindo aquilo que em orientação a objectos designamos de métodos. Os métodos representam funcionalidades
específicas da classe.
Uma classe é uma descrição de grupos de objectos com propriedades, comportamentos e relações comuns. Define,
assim uma categoria de objectos.
Temos vários elementos a serem identificados com as mesmas características, nome, número, curso e turma.
Aproveitando a classe descrita anteriormente, podemos afirmar que estamos na presença de vários alunos, ou seja,
debaixo do mesmo conceito de Aluno. Estamos perante aquilo a que em programação orientada por objectos
chamamos de objectos.
Um objecto é um elemento de uma classe e possui as mesmas características e modo de funcionamento que os
outros elementos (objectos) da mesma classe.
Podemos dizer que o objecto é uma instância de uma classe . Aluno1 é uma instância da classe Aluno.
Os objectos colaboram com outros objectos através da troca de mensagens (invocação de métodos) para produzir
resultados relevantes para o domínio do problema.
Os objectos são normalmente descritos (através das classes) recorrendo a nomes, pronomes, adjectivos ou
advérbios.
É possível dizer neste momento que as classes não são mais do que uma espécie de moldes a partir dos quais
criamos instâncias (objectos).
Imaginemos que um dos alunos pretende mudar de turma, como faze-lo? Basta recorrer aos métodos existentes na
classe, que foram previamente definidos. Vejamos:
Método define uma funcionalidade das futuras instâncias de uma classe. São normalmente descritos recorrendo a
verbos, por indicarem algum tipo de acção. Permitem modificar ou interrogar os objectos relativamente ao seu estado
actual.
• Definição - definir o que e que um método sabe fazer no âmbito do objecto a que pertence.
• Invocação - processo de enviar mensagens a um objecto para que determinado método do seu
comportamento seja executado.
Supondo que uma nova aluna ingressa na escola, será necessário registar um novo aluno. Como?
• Criar uma instância da classe Aluno - objecto Aluno5.
• Que informação será necessária para criar essa instância? - Nome, número, curso, turma
• Deve-se invocar um método que devolva o número do aluno. Esse método deve ser implementado na classe
Aluno.
E que desejamos consultar a turma de um aluno .
• Deve-se invocar um método que devolva a turma do aluno. Esse método deve ser implementado na classe
Aluno.
No mundo que nos rodeia lidamos com vários grupos de pessoas, animais ou coisas, ou seja, várias classes que
coexistem para algum fim. Em programação, podemos criar também várias classes. Imaginemos o seguinte: No
domínio Escola que outro conceito podemos encontrar ? O de Professor.
Categoria - Adjunto
Departamento - DSI
Podem verificar que as duas classes possuem atributos com a mesma designação. Isto acontece porque para
caracterizar tanto o aluno como o professor precisamos de em ambos definir o atributo nome, por exemplo.
Aluno Professor
Nome Nome
Numero Numero de Identificação
Ano Fiscal
Turma Departamento
Curso Ordenado
Valor da Propina Categoria
ValoPago de Propina
2.2 - Exercícios
Grupo I
1. Uma classe e um objecto são a mesma coisa? Se não são, então qual (ou quais) as diferenças e relações
entre estes dois conceitos?
2. No seu ponto de vista o que são atributos, métodos e mensagens?
Grupo II
1. Pretende-se desenvolver uma aplicação para gestão de um empresa de aluguer de automóveis. Deverá ser
possível consultar todos os automóveis existentes na empresa e verficar se um determinado está disponível
ou não para aluguer. Quando um automóvel é alugado, o sistema regista este automóvel como ficando
indisponível.
a. Quais as classes que a aplicação deve conter?
b. Identifique os atributos de cada classe
c. Identifique os métodos que cada uma das classes deve ter.
2. Pretende-se desenvolver uma aplicação para desenho. A aplicação permite definir pontos cartesianos e
linhas que se definem dando dois pontos.
a. Quais as classes que a aplicação deve conter?
b. Identifique os atributos de cada classe
c. Identifique os métodos que cada uma das classes deve ter.
3. Pretende-se desenvolver uma aplicação para gestão de uma biblioteca. A aplicação permite consultar os
livros que existem na biblioteca, assim como fazer reserva de livros. Para cada livro, a aplicação guarda a
seguinte informação : Autor, titulo, editora. Um utilizador para fazer a reserva de um livro tem que dar os
seguintes dados: nome , numero do BI e morada.
a. Quais as classes que a aplicação deve conter?
b. Identifique os atributos de cada classe
c. Identifique os métodos que cada uma das classes deve ter.
4. Pretende-se desenvolver uma aplicação para jogar o totoloto. A aplicação deve permitir preencher um
boletim realizar o sorteio da chave premiada e realizar a selecção dos boletins premiados.
a. Quais as classes que a aplicação deve conter?
b. Identifique os atributos de cada classe
c. Identifique os métodos que cada uma das classes deve ter.
3.1 - Identificadores
Os identificadores são nomes atribuídos aos diferentes elementos de um programa (classes, objectos, métodos,
variáveis, constantes e outras entidades que este manipula), os quais em java seguem as seguintes regras:
Devem ser iniciados por uma letra, underscore (_) ou cifrão ($)
Podem ter outros caracteres (letras, dígitos, underscore ou cifrão)
Não podem conter espaços!
Contrariamente ao que acontece noutras linguagens, o Java distingue entre maiúsculas e minúsculas, ou seja:
A ≠ a
B ≠ b
public static void ≠ PUBLIC STATIC VOID
Ainda que as convenções não sejam obrigatórias e o compilador não verifique se são cumpridas, é aconselhável a
sua utilização, pois facilitam a compreensão dos programas por programadores a elas habituados. Também para o
Java é comum usar convenções para os identificadores a criar quando se desenvolve um programa:
Exemplo
Quais dos seguintes identificadores são válidos em Java e quais não são?
Operadores: +, -, * e =
Sinais de pontuação: {, }, (, ) e ;
Tal como as palavras reservadas, os símbolos têm um significado bem definido e não podem ser utilizados para outro
fim.
Os Valores são dados explícitos que podem ser manipulados pelo programa, isto é são todos os valores aceites pela
linguagem.
Por exemplo:
3.4 - Variáveis
Uma variável é uma localização em memória na qual podemos guardar um valor de um dado tipo. A cada variável
estão associados um tipo e um nome.
O valor de uma variável pode variar durante a execução do programa. No entanto, em cada momento uma variável
pode apenas conter um valor. Como podemos verificar na figura seguinte, o armazenamento de um novo valor numa
variável destrói o valor anterior dessa mesma variável.
A E
Célula de Célula de
memória memória
1002 1002
Como vimos em 3.1 os atributos definem as características das futuras instâncias de uma classe. Têm um nome único
dentro da mesma classe. Para referenciar um atributo de um objecto devemos indicar o nome do objecto e o nome do
atributo. Classes diferentes podem ter atributos com o mesmo nome.
Mais concretamente os atributos são variáveis que guardam os valores que caracterizam os objectos. Mas para cada
característica teremos que indicar qual o tipo de dados a que pertence.
Ao definir uma classe em Java que represente as características de um aluno teremos os seguintes atributos:
int numero;
String nome;
int ano;
String curso;
char turma;
double valorPropina;
double valorPago;
Tipos de Dados Primitivos: são utilizados para guardar números, caracteres e valores booleanos. Armazenam
directamente os valores.
Tipos de Dados Referenciados: não guardam os dados directamente, mas são utilizados para guardar os endereços
de memória onde se encontram estes dados.
Exemplo:
memória
numero 2133 20A20
nome 40B40 30A40
referência
“Pedro” 40B40
Figura 11.
Figura 12.Representação gráfica da possível localização em memória das variáveis declaradas nos dois
exemplos anteriores
Um tipo de dados é definido pela gama de valores que pode representar e pelas operações que sobre eles se
podem efectuar.
Por agora vamo-nos focar apenas nos tipos de dados primitivos. A linguagem Java coloca oito tipos de dados
primitivos à nossa disposição:
3.5.1.1 - Inteiros
Tipo Tama-
Inteiro nho Menor Valor Maior Valor
byte 1 byte −128 127
(8 bits)
short 2 bytes −32,768 32,767
(16 bits)
int 4 bytes −2,147,483,648 2,147,483,647
(32 bits)
long 8 bytes −9,223,372,036,854,775,808 9,223,372,036,854,775,807
(64 bits)
Figura 13. Diferentes tipos de inteiros oferecidos pela linguagem Java. Nesta tabela podemos ver o espaço ocupado
por uma
Figura 14. variável de cada um destes tipos, bem como o maior e menor valor que essa variável pode ter.
Como exemplos de valores típicos a guardar em variáveis de cada um dos tipos inteiro podem ser apresentados:
byte: 40
short: -22 500
int: 1 500 000
long: 4 000 000 000
3.5.1.2 - Reais
Um número real pode ser representado usando a notação decimal, tem um ponto decimal ou a notação exponencial,
tem um expoente (E) Ex: 5.0, 12.34, 0.0, -45.8, 12. , 5.6E27
Tipo Tamanho
float 4 bytes (32 bits)
double 8 bytes (64 bits)
Figura 16. Maior e menor valor representáveis pelos diferentes tipos de dados reais do Java
3.5.1.3 - Caracteres
O armazenamento de caracteres em Java é conseguido pelo tipo char. Na realidade, as variáveis do tipo char
armazenam números que identificam os caracteres de acordo com um código denominado UNICODE. Este código é
um standard internacional de representação de caracteres que contém símbolos e caracteres dos alfabetos ocidentais
e orientais.
Exemplos:
// 'a' 'X' '7' '$' ',' '\n'
// são valores do tipo char
3.5.1.4 - Lógico
O armazenamento de valores lógicos em Java é conseguido pelo tipo boolean, que, evidentemente, apenas pode
assumir um de dois valores: verdadeiro, ou falso.
As palavras reservadas true e false são os únicos valores válidos para um tipo Lógico
Exemplo:
Para usar uma variável é necessário fazer a sua declaração previamente, isto é, a especificação do seu nome
(identificador) e da gama de valores que pode conter (tipo de dados). A declaração é terminada com um ponto e
vírgula (;). Este é o símbolo que se usa em Java para indicar o final de qualquer declaração ou instrução.
char turma
Não estamos a falar de um valor qualquer, mas sim do valor inicial da variável, que é guardado no espaço
reservado.
Exemplo:
int sum = 0;
int base = 32, max = 149;
É de notar que a declaração de uma variável apenas provoca a reserva de espaço de memória necessário e a sua
identificação pelo nome declarado e não define um valor. É na inicialização que a variável fica com um valor definido,
podendo a partir de agora ser utilizada.
Quando uma variável é referenciada num programa, é utilizado o valor actual (ver código abaixo).
String nome;
// a variável nome é uma referencia do tipo String
Uma referência é uma variável que guarda o endereço do local onde se encontra um objecto de uma dada classe.
Para atribuir valor a uma referencia, na maioria dos casos, deve ser executado o comando new seguido de nome da
classe utilizada na declaração dessa variável. (este comando será explicado em 3.7.1 - )
String nome;
nome = new String(“Pedro”);
O Java tem um conjunto de classes já elaboradas que permite aceder a um conjunto de funcionalidades e
propriedades já definidas. Uma destas classes é a classe String que já foi referida aquando da definição dos atributos
nome e curso da classe Aluno.
É uma classe bastante utilizada e que serve para manipular cadeias de caracteres.
Quando é declarada uma variável do tipo String ( String nome) é reservado um espaço em memória para guardar um
endereço da futura cadeia de caracteres. Enquanto essa variével não está inicializada, o valor da variável nome é
NULL ( isto é a referência não tem valor) . A inicialização pode ser efectuada pela instrução: nome = “Rita”;
Uma String serve para guardar valores constantes, isto é, uma vez que seja atribuido um valor a uma String
este não pode ser alterado. Assim, qualquer operação sobre uma String devolve uma nova String (String
modificada)
frase
“Introdução à Programação”
nome
“Rita”
“Pedro”
Para além de referenciar os objectos das classes já existentes em Java, pode-se referenciar também os objectos de
classes por nós criadas, como veremos mais à frente.
+= x += y x=x+y
-= x -= y x=x-y
*= x *= y x=x*y
/= x /= y x=x/y
%= x %= y x=x%y
Ao símbolo = chama-se operador de atribuição. A instrução de atribuição permite armazenar um valor numa variável.
A expressão da direita é calculada em primeiro lugar, sendo o resultado armazenado na variável da esquerda. Neste
contexto, o sinal = deve ler-se “toma o valor de”.
Só pode ser atribuído um valor a uma variável que seja consistente com o tipo de dados declarado para essa variável.
A mesma variável, pode estar no lado esquerdo e direito da instrução de atribuição. Considere-se o exemplo:
int conta ;
conta = 25; conta Å 25
sum = conta; sum Å 25
conta = sum + 15; conta Å 40
conta = conta + 1; conta Å 41
Figura 19. Conjunto de operações que podem ser efectuadas sobre valores do tipo inteiro
A precedência de operadores determina a ordem de execução das operações. Por exemplo considere-se a
instrução: System.out.println(30 + 10 / 2);
30 + 10 e 10
= 20 30 + = 35
2 2
errado! correcto!
Grau de Operação
Precedência
Alto ()
Médio -, + (unários)
*, /, %
Baixo +, -
Figura 20.
Figura 21. Interpretações possíveis da ordem dos operadores e tabela com as regras de precedência correctas
Grau de Operação
Precedência
Alto ()
Médio -, + (unários)
*, /
Baixo +, -
! NOT
&& AND
|| OR
O NOT é um operador unário (tem apenas um operando), enquanto que o AND e o OR são operadores binários (têm
dois operandos)
A b a && b a || b a ! a
true true true true true false
true false false true true false
false true false true false true
false false false false false true
Figura 25.
Figura 26. Tabelas de verdade dos operadores &&, || e !
Figura 27.
Operador Designação
> Maior que
>= Maior ou igual a
< Menor que
<= Menor ou igual a
== Igual a
!= Diferente de
3.6.5 - Expressões
Uma expressão é uma sequência de operadores e de valores. As expressões podem ser calculadas, obtendo-se um
novo valor.
Podemos ter:
5 * ( 2 + 3 ) + ( 7 – 3 ) / 2 Resultado: 27
As expressões aritméticas são calculadas levando em conta a prioridade dos diferentes operadores envolvidos
Expressões Lógicas – têm como resultado um valor lógico e são construídas com operadores relacionais e
operadores lógicos
Exemplos:
Exemplo1: Determine o valor das seguintes expressões, indicando também o tipo do valor obtido
Expressão Resultado
3-4*2+1 -4 int
2*(12%5)-(8-3)/2 2 int
2*12%5-8-3/2 -5 int
2.5+6%4*2.1 6.7 double
2*((20/6)+(3*(2-1.5)))% (2.5+1-1.5) 1.0 double
Expressão Resultado
3+5/2) >= (1-4%2) true
x=3 == 4; x fica false
8-1 != 3 + 4 false
‘a’ < ‘b’ || ‘c’ < ‘a’ true
(!a && b)|| (a || !b) true para a=true e b=false
3.7 - Métodos
Definem funcionalidade das futuras instâncias de uma classe. São normalmente descritos recorrendo a verbos, por
indicarem algum tipo de acção e permitem modificar ou interrogar os objectos relativamente ao seu estado actual.
tipoRetorno nomeMetodo(listaParametros)
{
declaracoes;
instruções;
return (se for o caso);
}
Nota:
Por convenção, em Java, os nomes de classes começam por letras maiúsculas (ex: NomeClasse), ao passo que os
nomes de objectos começam com minúsculas (ex: nomeObjecto).
Os nomes dos métodos começam também por minúsculas (ex: nomeMetodo).
Os parâmetros são os dados de entrada . O objecto para executar uma determinada operação pode precisar de
dados. Essa informação é passada através dos parâmetros.
Exemplo:
• O aluno para pagar uma prestação da propina precisa de indicar o valor que vai pagar.
• O aluno para mudar de turma precisa de indicar qual a nova turma.
Parâmetros
São variáveis que referenciam diferentes tipos de dados dos quais o método necessita para levar a cabo a sua tarefa.
return <expressão>;
Nota :
Dois métodos têm a mesma assinatura quando o nome, o valor de retorno e a lista de parametros são idênticos em
ambos. Não é possível na mesma classe a existência de métodos com a mesma assinatura.
O construtor é um método especial utilizado apenas na criação e inicialização de objectos de uma determinada classe.
O seu único e exclusivo objectivo é inicializar os atributos, ou seja, dar um estado inicial ao objecto.
Podem existir construtores com parâmetros e construtores sem parâmetros. Neste sub-capítulo apenas nos vamos
debruçar sobre os construtores sem parâmetros.
class Relogio
{
private int hora, segundo, minutos;
class Relogio
{
private int hora, segundo, minutos;
public Relógio()
{
hora = 24;
minutos = 0;
segundo = 0;
}
}
Quando não se define nenhum construtor na classe que especifica o objecto, a máquina virtual encarrega-se de criar
um em tempo de execução, equivalente a:
<nome da classe>()
{
//…….
}
Este construtor inicializa os atributos do objecto com valores default, que correspondem a:
• 0 se o atributo for de tipo numérico (short, byte, int, long, double,float, char)
• false se o atributo for de tipo boolean
Nota: null representa uma referência nula, não existente, para um objecto pertencente a uma qualquer classe.
Instanciação é o processo de criar instâncias (objectos) de uma determinada classe. É uma operação feita sobre a
classe e não sobre os objectos (dado que os objectos já são instâncias não faz sentido criar instâncias a partir deles).
Para criar uma instância de uma classe utilizamos o operador new, que quando é executado aloca a memória
necessária para guardar o objecto em causa na memória.
Teríamos um objecto denominado rel e outro denominado despertador, caracterizados por três atributos: hora,
minuto e segundo, cujos valores respectivamente são, 24, 0 e 0.
Poderão estes dois objectos existir ao mesmo tempo ? SIM!!!
nome_objecto.nome_método(<lista de Parâmetros>)
Vejamos um exemplo:
class Teste
{
public void metodoOla ()
{
System.out.println(“ola”);
}
}
class Programa
{
public static void main(String args[])
{
Teste obj = new Teste();
obj.metodoOla();//invocação do método
Como foi possível ver o objecto obj invocou um método da sua classe. Será possível um objecto invocar um método
de outra classe ? Veja-se o próximo exemplo:
public PrimeiraClasse()
{
System.out.println("Sou o construtor por default da
PrimeiraClasse");
}
public SegundaClasse()
{
System.out.println("Sou o construtor por default da
SegundaClasse");
}
public void metodoSegundaClasse()
{
System.out.println("Sou o metodoSegundaClasse");
}
}
segunda.metodoPrimeiraClasse();
segunda.metodoSegundaClasse();
}
}
Ao analisarmos o código verificamos que a instrução segunda.metodoPrimeiraClasse( ) não está correcta. Porquê?
Porque o método metodoPrimeiraClasse() não faz parte da classe SegundaClasse.
Importante: Todo o objecto só pode invocar métodos da sua classe, tal como podemos ver no exemplo seguinte:
Passar parâmetros a um método significa chamar esse método dando-lhe como entrada os dados que ele necessita.
Estes dados são chamados parâmetros actuais. Na chamada é efectuada a ligação entre parâmetros formais
(definidos no cabeçalho do método) e parâmetros actuais. A lista de parâmetros formais e actuais deve ter
correspondência. Cada parâmetro de lista de parâmetros actuais deve corresponder, pelo tipo e pela sua posição na
lista, a um parâmetro formal definido no cabeçalho do método.
class Teste
{
public void passagem ( int var)
{
var = var * 2 ;
}
}
class Programa
{
public static void main(String args[])
{
int variavel = 7;
Teste obj = new Teste();
obj.passagem(variavel);
System.out.println(variavel);
}
}
O código apresentado ilusta a invocação de um método por parte de um objecto da classe Teste e a passagem de
parâmetros por valor. O parametro formal é var e parametro actual é variável.
No quadro seguinte podemos seguir o conteúdo das variáveis definidas no programa anterior :
Variáveis
Linha de código
variavel var
int variável = 7; 7 ---------
Obj.passagem(variavel); 7 7
var = var * 2 ; --------- 14
System.out.println(variável); 7 ---------
No exemplo acima as variáveis que estavam envolvidas na passagem de parâmetros (variável e var) eram
variáveis do tipo primitivo int. Na passagem de tipos referenciados, quando se está a lidar com variáveis do tipo
referenciado, o comportamento é diferente. Este ponto será explicado detalhadamente no capitulo 4 – Tópicos
Avançados de POO.
Para terminarmos este capítulo, iremos ver o retorno dos métodos. No início do capítulo foi dito que através da
instrução return é determinada qual o valor que o método devolve.
tipoRetorno nomeMetodo(listaParametros)
{
declaracoes;
instruções;
return (se for o caso);
}
Se o método não devolve valor, a instrução return não é especificada e no cabeçalho do método no lugar do
tipoRetorno deve se indicar void.
class Calculo
{
class Programa
{
public static void main(String args[])
{
O método somaValores é um método que retorna o valor que foi atribuído à variável resultado. Ao analisarmos o
método main podemos ver a seguinte instrução
valorRetornado = resultado.somaValores();
Reparem que tipo da variável valorRetornado é igual ao tipo da variável resultado. Isto é muito importante, pois
devemos sempre garantir a correspondência dos tipos.
class Calculo
{
class Programa
{
public static void main(String args[])
{
Calculo resultado = new Calculo();
int x = 10, y = 5;
valorRetornado = resultado.somaValores(x,y);
System.out.println(valorRetornado);
}
}
• Da primeira vez que a variável valorRetornado guarda o valor retornado, qual o seu valor ? 7
• Da segunda vez que a variável valorRetornado guarda o valor retornado, qual o seu valor ? 15
public Aluno(String nom, int num, String curs, int an, char turm)
{…}
Em seguida para consolidar os conhecimentos até aqui adquiridos, será apresentado o código da classe Aluno
referente à definição de atributos e definição do construtor:
{
//ATRIBUTOS
private String nome;
private int numero;
private int ano;
private String curso;
private char turma;
private double valorPropina;
private double valorPago;
//CONSTRUTORES
public Aluno(String nom, int num, String curs, int an, char turm)
{
nome = nom;
numero = num;
ano = an;
curso =curs;
turma = turm;
valorPropina = 0;
valorPago=0;
}
}
A criação de objectos neste exemplo, será criada numa outra classe, na qual está definido o main:
class Programa
{
public static void main(String args[])
{
Aluno aluno2 = new Aluno(“João”, 2133,”EI”, 1, ‘A’);
Aluno aluno3 = new Aluno(“Ana”, 2131,”EI”, 1, ‘A’);
...
}
valorPropina=0;
valorPago=0;
No caso das variáveis valorPropina e ValorPago o valor é fornecido directamente. Contudo o valor atribuído pode
resultar do cálculo de uma expressão, tal como:
class Programa
{
public static void main(String args[])
{
int ano = 2005;
int anoNascimento = 1971
int idade = ano - anoNascimento
O método getNumero( )
• devolve o valor da variável numero
• o nome do método é getNumero
• o tipo de retorno é do tipo inteiro (Como numero é uma variável do tipo inteiro o tipo de retorno também
é do tipo inteiro)
aluno2.getNumero( );
Estará correcto? Não. Trata-se de um método que devolve um valor, logo devemos recebe-lo de alguma forma.
Chamam-se métodos modificadores aqueles que servem para alterar/modificar o valor de um atributo.
aluno2.setCurso(“EA”);
Em Java, uma aplicação é constituída por uma ou mais classes. Uma (e só uma) dessas classes contém um método
chamado main que é o programa principal. Este utiliza então métodos que podem existir na mesma classe ou
noutras.
Existem três regras importantes na estruturação de um programa (livro FCA – Fundamentos de Programação em
Java2):
• Um programa contém:
o Declarações de variáveis;
o Um método principal chamado main();
o Um conjunto de métodos definidos pelo programador.
• Os métodos contêm:
o Declarações de variáveis;
o Instruções;
Os detalhes de implementação de um método são irrelevantes, desde que se saiba para que é que ele serve, que
parâmetros recebe e que tipo de resultado deve produzir (é como se fosse uma caixa negra!).
Uma variável declarada dentro de um subprograma é chamada de variável local a esse subprograma. Uma variável
local é criada sempre que o subprograma é activado e destruída quando este termina a sua execução.
Ainda que haja outro subprograma que contenha uma variável declarada com o mesmo nome, estamos a falar de
variáveis diferentes, que existem em espaços de memória separados, por períodos de tempo diferentes e visibilidades
diferentes, pois existem em subprogramas distintos.
Uma variável global é aquela que é declarada no início do programa e subsiste até que o programa termine, sendo
visível dentro de qualquer subprograma que o constitui. Para além de serem visíveis dentro de qualquer subprograma
que constitui a aplicação, as variáveis globais podem também ser utilizadas por esses subprogramas.
Não é no entanto aconselhado o uso abusivo deste tipo de variáveis dadas as limitações que causam na legibilidade
do código e a facilidade com que provocam o aparecimento de erros difíceis de detectar.
Para além disso não contribuem em nada para a reutilização de código pois tornam o código desenvolvido
dependente do contexto de utilização.
class VisibilidadeVariaveis
{
private int valor1;
// variável global
public static int valor2 = 10;
A interacção da aplicação com o utilizador é efectuada através de entrada e saída de dados. Para pedirmos
informação ao utilizador temos que ser capazes de ler do teclado. Para mostrar informação ao utilizador temos que
ser capazes de escrever para o ecran.
class Programa
{
public static void main(String args[])
{
Aluno aluno2 = new Aluno(“João”, 2133,”EI”, 1, ‘A’);
Aluno aluno3 = new Aluno(“Ana”, 2131,”EI”, 1, ‘A’);
aluno3.setTurma(‘E’);
int i= aluno3.getNumero();
char t= aluno3.getTurma();
System.out.println(“Numero” + i);
System.out.println(“Turma” + t);
}
No exemplo de código apresentado, a variável i e a variável t são do tipo de dados primitivo.
System.out.println(aluno3);
Sim, mas para essa instrução imprimir informação sobre o objecto aluno3, é necessário definir na classe Aluno o
método toString().
Voltando ao nosso exemplo do Aluno, para este exemplo ser interactivo, os dados sobre o aluno (nome, numero etc.)
devem ser pedidos ao utilizador. A entrada de dados para o programa é feita através da leitura de dados do teclado. .
A escrita de informação para o ecran designa-se por saída de dados.
Como já temos visto ao longo dos exemplos anteriores, quando queremos escrever para o ecran utilizamos a
instrução em Java
System.out.println( x); // Onde x é a variável que se pretende mostrar no ecran.
Nota: x pode ser uma variável de um tipo de dados primitivo, ou se x é de um tipo referenciado, então a classe que
instancia tem que ter definido o método toString().
class Programa
{
public static void main(String args[])
{
Aluno aluno2 = new Aluno(“João”, 2133,”EI”, 1, ‘A’);
Aluno aluno3 = new Aluno(“Ana”, 2131,”EI”, 1, ‘A’);
aluno3.setTurma(‘E’);
int i= aluno3.getNumero();
System.out.println(aluno3);
}
}
A classe Scanner, introduzida com a versão J2SE 5.0, é uma classe que permite converter o texto para tipos
primitivos.
Para ler de forma confortável texto do canal de entrada padrão, é preciso criar primeiro um Scanner sobre canal
System.in que esta associado ao teclado
Scanner sc = new Scanner(System.in);
Para cada um dos tipos primitivos há um método correspondente com a assinatura nextXxx() que retorne um valor
desse tipo.
Exemplo
import java.util.*;
class ExScanner
{
public static void main(String[] args)throws InputMismatchException
{
Scanner scanTeclado = new Scanner(System.in);
System.out.println ("Introduza um inteiro");
int num = scanTeclado.nextInt();
System.out.println ("Numero introduzido:" + num);
}
class Programa
{
public static void main(String[] args)throws InputMismatchException
{
Scanner scanTeclado = new Scanner(System.in);
System.out.println ("Introduza o numero do Aluno ");
int num = scanTeclado.nextInt();
System.out.println ("Introduza o nome do Aluno ");
int nome = scanTeclado.next ();
System.out.println ("Introduza o curso do Aluno ");
int curso = scanTeclado.next ();
System.out.println ("Introduza o ano de inscrição");
int ano = scanTeclado.nextInt ();
System.out.println ("Introduza a turma de inscrição ");
char turma = scanTeclado.nextChar ();
Sumariando os passos que tem sido dados para a construção da classe exemplificativa Aluno. Para se definir uma
classe deve obedecer-se à seguinte estrutura:
Como já foi referido anteriormente o Java tem um conjunto de classes já elaboradas que permite aceder a um
conjunto de funcionalidades e propriedades já definidas. Estas classes estão guardadas em pastas compactadas.
Para podermos utilizar num programa uma dessas classes é necessário importa-las.
A instrução em Java é:
import <nome_do_pacote.nome_da_classe>
Método Descrição
String() Cria um objecto do tipo String
char charAt(int index) Devolve o caracter da posição index da string
int compareTo(String anotherString) Compara duas
Strings
int indexOf(String str) Determina a localização da primeira ocorrência de uma
cadeia de caracteres dentro de uma cadeia de
caracteres
String toUpperCase() Transforma em maiúsculas a cadeia de caracteres
int lenght() Retorna o comprimento da string
Nota: Para mais informações sobre os métodos disponibilizados pela String consultar a documentação do Java.
A classe String pertence ao package java.lang , este package é importado automaticamente sem
necessidade de inclusão específica no programa.
A classe Math pertence ao package java.lang , este package é importado automaticamente sem
necessidade de inclusão específica no programa.
Para utilizar classes de outros pacotes é necessário importar (import) explicitamente a classe.
Exemplo:
Calcular a potência de um número elevado a outro.
import java.util.Random;
Quando é necessário utilizar uma classe de um package mas não queremos usar a directiva import, pode ser utilizado
o seu nome completo.
Método Descrição
Random() Cria gerador de números aleatórios
int next(int bits) Gera o número aleatório seguinte
int nextInt() Retorna um número inteiro aleatório
int nextInt(int n) Retorna um número inteiro aleatório de um intervalo de 0 a n
double nextDouble() Retorna um número real aleatório, entre 0.0 e 1.0
A desvantagem é que o nome que temos a escrever se torna demasiado extenso, pelo que nem sempre compensa
utilizar esta abordagem.
A outra abordagem possível, consiste em utilizar de forma explícita a directiva import:
import java.util.Random;
3.8.6 - Comentários
Os comentários de um programa documentam as instruções existentes. Devem ser incluídos de forma explicar o
propósito do programa e descrever passos do processamento. Não influenciam o funcionamento do programa pois
são ignorados pelo compilador.
Um programa Java pode ser formatado de múltiplas formas. Deve ser utilizada a paragrafação, para assegurar a
legibilidade dos programas
package meuteste;
1. Relativamente à classe Aluno do exemplo deste capítulo implemente em Java as seguintes alterações
a. Defina dois novos atributos relacionados com as notas de entrada para a escola. Nota da prova
especifica de matemática e valor da média do 12º ano.
b. Defina um novo construtor para a classe aluno que também inicialize estes dois novos atributos
c. Implemente um método que retorne a media aritmética entre a nota da prova específica e a nota do 12º
ano
d. Implemente na classe programa a criação de um novo aluno com os seguintes dados
e. Implemente na classe programa a criação de um novo aluno com dados pedidos ao utilizador.
f. Implemente na classe programa a funcionalidade da mudança de turma. Deverá pedir ao utilizador a
nova turma. E no final imprimir os novos dados do aluno, através do uso implícito do método toString.
5. Implemente a classe Moeda que tem como atributos o valor monetário da moeda e tipo deMoeda (ex, dólar,
euro, Franco suisso).
a. Implemente o método construtor
b. Implemente os métodos
i. Verificar se duas moedas são iguais.
ii. Adicionar moeda
c. Implemente a classe programa onde testa o construtor e os métodos anteriormente implementados..
4- Instruções de Controlo
Até aqui, todos os exemplos de código que vimos seguiam uma estrutura a que se dá o nome de sequencial. Não
tinham nenhuma instrução que de alguma forma fizesse mudar o fluxo de execução da aplicação. Ou seja, as
instruções são executadas uma após a outra de acordo com a ordem por que aparecem. Esta filosofia limita em muito
a utilidade dos programas que se possam vir a produzir.
Resolver problemas mais complexos exige um maior poder de decisão sobre a forma como o programa é executado
(fluxo de execução), o que nos é possibilitado pelas instruções de controlo. O que estas trazem de novo é a
capacidade de controlar a execução de uma aplicação mediante o estabelecimento de condições lógicas.
Selecção: permitem determinar o caminho a seguir de acordo com o valor de uma expressão;
Repetição: permitem controlar o número de vezes que um determinado bloco de instruções é executado (são os
chamados ciclos).
4.1 - Selecção
Ao implementar aplicações podemos ter que tomar decisões com base em valores de expressões, dados fornecidos
pelo utilizador, resultados calculados, etc.
Deste tipo de necessidades surgiram as instruções de selecção: para impôr a execução condicional de instruções.
Simples: Com base na condição a avaliar executa um bloco de instruções ou então não executa;
Em alternativa: Tem duas possibilidades de blocos de instruções a executar. Com base na condição, ou executa um
ou executa outro.
Múltipla: As possibilidades são várias e as condições a verificar também, o bloco a executar é escolhido com base na
condição cuja verificação tenha sido positiva.
if/else
switch - case
Com estas instruções podemos concretizar cada um dos três tipos de selecções acima mencionados.
Verdadeiro
Expressão Instruções...
Falso
Começa por ser avaliada uma expressão lógica. Se o seu resultado for verdadeiro, então é executado um
determinado bloco de instruções. Caso contrário, a execução do programa prossegue sendo esse bloco ignorado.
Em Java, este tipo de selecção concretiza-se recorrendo à instrução if, cuja sintaxe é a seguinte:
if ( expressão ) instrução
if (condição) {
instruções;
}
Exemplo 1:
Exemplo 2:
import java.util.Random;
if (n < 0)
System.out.println(“O número é negativo”);
System.out.println(“Adeus.”);
}
}
Exemplo 3:
Relativamente à classe Aluno, do exemplo que temos vindo a acompanhar, implemente um método que retorne falso
se o valor da propina é igual a zero.
Recapitulando: Se a expressão for verdadeira, é executado o conjunto de instruções A. Se for falsa, é executado em
alternativa o conjunto de instruções B. O programa segue depois com as instruções que tiver para executar a seguir.
if ( expressão ) instrução
else instrução
Exemplo 1:
// Resultado da avaliação
if (nota < 10)
System.out.println(“Nota negativa”);
else
System.out.println(“Nota positiva”);
}
}
Exemplo 2:
As instruções if podem ser representadas em paralelo ou encadeadas, caso haja várias alternativas a estudar e a
representar.
Exemplo 3:
Relativamente à classe Aluno, implemente um método que retorne falso se o valor da propina é igual a zero e
verdadeiro se o valor for diferente de zero.
Exemplo 4:
// Determinar a ordem de grandeza
// dos três números (a, b, c) usando if’s encadeados.
a<b<c
sim
a<c<b
sim (b < c) ? sim c<a<b
não
(a < c) ?
(a < b) ? não b<a<c
b<c<a
sim
c<b<a
não (a < c) ? sim
não
(b < c) ?
não
// código:
if (a < b)
if (b < c)
System.out.println("a < b < c");
else
if (a < c)
System.out.println("a < c < b");
else
System.out.println("c < a < b");
else
if (a < c)
System.out.println("b < a < c");
else
if (b < c)
System.out.println("b < c < a");
else
System.out.println("c < b < a");
// código:
if (a < b && b < c) System.out.println("a < b < c");
if (a < c && c < b) System.out.println("a < c < b");
if (b < a && a < c) System.out.println("b < a < c");
if (b < c && c < a) System.out.println("b < c < a");
if (c < a && a < b) System.out.println("c < a < b");
if (c < b && b < a) System.out.println("c < b < a");
Essencialmente, ao tomar a decisão de usar if’s de forma encadeada ou em paralelo estamos apenas a ter em
conta o facto de cada condição a verificar depender ou não das anteriores.
Em relação ao uso específico da alternativa else, há uma nota importante a fazer. Há casos em que o else pode,
em termos semânticos, tornar-se ambíguo, ou seja, ser susceptível de interpretações diferentes numa mesma
situação.
Exemplo 5:
Da forma como está escrito o código, parece ser dado a entender que o else representa a alternativa à condição
numeroAluno > 1200....
Mas na realidade o código tem um erro de indentação e o else representa uma alternativa à condição
numeroAluno < 2000.
Não podem existir ambiguidades quanto ao caminho a seguir na execução de instruções. Se não tivesse sido
estipulada à partida uma regra para o Java operar nestes casos, o interpretador não saberia o que fazer quando lhe
surgisse um conjunto de instruções deste género.
Em Java, a semântica da instrução if/else dita que um else "pertence" sempre ao if que foi deixado em aberto
em último lugar. Ou seja, ao último if sem else.
Na situação anterior, para forçar a que o else estivesse associado ao primeiro if, teríamos de usar delimitadores:
Neste caso, dizemos que o segundo if e a mensagem que este envia para o canal de saída (caso a condição seja
verdadeira), fazem parte do bloco de instruções a executar quando numeroAluno > 1200 tem o valor true.
Tornámo-las numa instrução composta.
Nota: Pode haver if’s sem else’s, mas o contrário não se aplica.
Exemplo 6:
Aplicando a regra de que um else pertence ao último if deixado em aberto, o else pertence ao if onde se
verifica se pontos > 95!
Em termos sintácticos seria assim que a máquina virtual Java iria interpretar, pois não há margem para ambiguidades.
A máquina tem de saber sempre qual é a próxima instrução a executar.
Em termos semânticos, claramente teríamos aqui um erro de interpretação do problema e os resultados não seriam os
pedidos pelo enunciado.
V1 V2 ... Vn outro
Expressão
I1 I2 In Id
Começa por calcular o valor da expressão que tem de ter um resultado de tipo inteiro ou caracter. Depois, compara o
valor da expressão com os valores v1 a vn. Se for igual ao de algum deles, executa o bloco de instruções
correspondente.
Se o valor da expressão a executar não for igual a nenhum dos valores v1... vn, pode suceder uma de duas situações:
não há mais nenhuma alternativa a considerar e portanto nenhum bloco de instruções a executar;
há uma alternativa pronta a ser executada quando mais nenhuma serve e é essa a escolhida (no fluxograma está
representada pelo valor outro).
Em Java, a instrução que se usa para representar situações de selecção múltipla é a instrução switch. A instrução
switch é também uma instrução de selecção que, com base na avaliação de uma variável ou expressão de valor
inteiro (ou caracter) escolhe, entre várias alternativas possíveis, uma instrução ou conjunto de instruções a executar.
switch (expressão) {
case
constante
A instrução break após cada case serve para evitar que os próximos cases sejam executados. Se há um case
que tem o mesmo valor da expressão o seu bloco de instruções deve ser executado e o break faz com que a
instrução switch seja terminada.
O default é o caso que deve ser executado quando mais nenhum serve (segundo o fluxograma é a opção outro).
Os cases que não sejam explicitamente programados num switch sem default são ignorados. A existência do
default foca o programador na necessidade de processar condições excepcionais. Há, no entanto, situações em
que a utilização do default não é necessária.
Nota:
Esquecer um break onde ele deve ser usado, conduz a erros lógicos ou semânticos.
Embora a ordem dos cases e do default não esteja estipulada à partida, é considerada boa prática colocar o
default em último lugar.
O break após a última instrução case (ou default, se esta for a última), não é necessário. Muitos
programadores colocam-no apenas por uma questão de coerência e simetria para com as anteriores.
Exemplo 1:
public class Pontos
{
public static void main(String args[])
{
int pontos;
Scanner scanTeclado = new Scanner(System.in);
System.out.print (“Digite um inteiro entre 1 e 100:> “);
pontos = scanTeclado.nextInt();
switch (pontos/10)
{
case 10:
case 9: System.out.println("A - Excelente!");
break; // por exemplo, se o break nao estivesse aqui
// e pontos/10 = 9, o bloco seguinte (do case 8)
// era executado na mesma, conduzindo a um erro!
Exemplo 2:
switch (ch)
{
case ‘0’: case ’1’: case ’2’: case ’3’: case ’4’:
case ’5’: case ’6’: case ’7’: case ’8’: case ’9’:
return (ch – ‘0’);
case ‘a’: case ‘b’: case ‘c’: case ‘d’: case ‘e’:
case ‘f’:
return (ch – ‘a’) + 10;
case ‘A’: case ‘B’: case ‘C’: case ‘D’: case ‘E’:
case ‘F’:
return (ch – ‘A’) + 10;
default:
System.out.print(“Adeus.”);
}
}
}
// Não há breaks porque cada expressão tem um return que
// sai do bloco antes que se tente sequer avançar para o próximo!
If Versus Switch
4.2 - Repetição
É fundamental que uma linguagem de programação dê suporte à repetição de tarefas, não só por uma questão de
eficiência mas também de produtividade.
Se há uma tarefa que deve ser executada 500 vezes, não vamos escrever 500 vezes o mesmo bloco de código. Não
só deixa de ser produtivo, é cansativo e desmotivante, mas ainda por cima torna o código mais susceptível a erros
que para serem corrigidos nos obrigam a percorrer cada um dos blocos de instruções que programámos
repetidamente.
As instruções que nos permitem resolver este tipo de problemas de uma forma mais eficaz são as instruções de
repetição, também conhecidas por ciclos.
Um ciclo é uma sequência de instruções executada de forma repetitiva e sujeita a uma condição de término. É
normalmente composto por:
Corpo: conjunto de instruções a repetir
Uma condição ou outra estrutura que controla a execução do corpo, especificando de alguma forma a quantidade de
vezes que este deve ser executado (número de iterações).
Quando um ciclo não tem fim, ou seja, repete as instruções do corpo indefinidamente, diz-se que se trata de um Ciclo
Infinito.
Este tipo de situações é absolutamente indesejável pois se um ciclo não termina o programa não avança e,
consequentemente, a sua execução também não terá fim nem produzirá resultados. Para além disso, corremos o
risco de esgotar os recursos do computador rapidamente se durante esse ciclo interminável houver operações
exigentes em termos de requisitos de memória, por exemplo.
Em Java ( e na maioria das linguagens de programação de alto nível), há três tipos de instruções de repetição:
while
do-while
for
De acordo com o que nos mostra o fluxograma seguinte, o que vemos é que enquanto a condição tiver o valor
verdadeiro (true), o corpo continua a ser executado.
Expressão Instruções
Verdadeiro
Falso
while(condição){
instruções;
}
Após uma pequena análise dos diagramas e sintaxes apresentados acima, há algumas pequenas notas a fazer:
A avaliação da expressão ou condição tem que ter obrigatoriamente como resultado um valor lógico (true ou
false).
O corpo do ciclo pode ser uma instrução simples ou composta. Isto é, pode haver uma ou mais instruções a repetir.
Essas instruções são repetidas enquanto a condição a avaliar em cada passagem for verdadeira.
Se a condição a verificar tiver à partida o valor false, o ciclo não chegará a ser executado.
Para que o ciclo não se torne infinito, temos de garantir de alguma forma que a expressão avaliada a cada passagem
chegará a ter o valor false. Isso consegue-se fazendo com que uma das instruções do corpo do ciclo se encarregue
de mudar o valor da condição no momento apropriado.
Exemplo 1:
while (i>0)
{
a-=10;
b-=5;
i--;
System.out.println(“A é “ + a + “B é “ + b);
}
}
} // qual é o resultado produzido?
Exemplo 2:
int d = 2;
Random random = new Random();
int x = random.nextInt(100)+1;
boolean primo = (x > 1);
if (primo)
System.out.println(x + " e primo.");
else
System.out.println(x + " não é primo.");
}
}
Exemplo 3:
Por avaliar primeiro a condição, um while pode nunca chegar a executar o corpo, se a condição for falsa logo à
partida. Diz-se que o while executa 0 ou mais vezes.
Por avaliar a condição no fim, um do-while executa sempre o corpo pelo menos uma vez. O do-while
executa 1 ou mais vezes.
Instruções
Expressão
Verdadeiro
Falso
do instrução while ( )
expressão-lógica
Figura 36. Instrução de repetição do-while
Exemplo 1:
do
{
++a;
++i;
System.out.println(“A é “ + a);
}
while (i<3);
}
} // qual é o resultado produzido?
Exemplo 2:
import java.util.Random;
do
primo = (n % d++ != 0);
while (primo && d < n);
if (primo)
System.out.println(n + " é primo.");
else
System.out.println(n + " não é primo.");
}
}
Recapitulando:
instrução
for(expressão1;expressão2;expressão3){
instruções
}
Em termos de fluxograma, um for poder-se-ia representar da seguinte forma (seguindo literalmente a especificação
do ciclo):
Verdadeiro
Expressão Instruções
Falso
Aplica Experssão 3 a Experssão 1
Notas:
Num ciclo for, devemos ter em atenção que, a variável de controlo, ao ser declarada no cabeçalho do ciclo, só é
conhecida dentro do seu corpo. Se tentarmos usá-la fora do ciclo obtemos um erro de sintaxe. Para conseguir esse
efeito devemos declará-la antes do cabeçalho do ciclo (a inicialização pode, ou não, ser feita nesse cabeçalho).
Um erro comum de programação consiste em usar virgulas, ao invés de pontos e virgula, para separar as expressões
do cabeçalho do for.
Normalmente geram-se erros semânticos/lógicos se colocarmos um ; imediatamente a seguir ao ) do cabeçalho do
for, isto porque assim o ciclo não tem corpo e passa a executar uma sucessão de instruções vazias.
Exemplo 1:
import java.util.Random;
Se omitirmos a expressão2, o Java assume que a condição de continuação do ciclo é true, gerando portanto
um ciclo infinito.
Podemos omitir a expressão1 se inicializarmos a variável de controlo antes do ciclo.
Podemos omitir a expressão3 se o programa calcular o incremento ou decremento com instruções dentro do
corpo do ciclo.
Ciclos Encadeados
Tal como podemos ter instruções de selecção encadeadas, também podemos ter ciclos encadeados, ou seja, ter
ciclos dentro de ciclos.
A regra dita apenas que o ciclo mais interior é o último a ser executado. Desde que o exterior reúna as condições para
prosseguir, o interior também terá a sua chance de realizar trabalho.
Exemplo 3:
Não é aconselhável encadear muitos ciclos uns nos outros. Torna muito mais difícil a manutenção do código e a
resolução de problemas, ou seja, aumentamos toda a complexidade da solução.
Uma métrica razoável seria nunca utilizar mais do que três ciclos encadeados.
Após sabermos como funciona cada tipo de instrução de repetição reside ainda uma questão por resolver: qual delas
deve ser usada em cada situação?
Um ciclo for, usa-se essencialmente quando o número de repetições é conhecido antes do início da sua execução.
Em Java, o for é flexível o suficiente para que possa ser utilizado em situações onde o número de repetições seja
também controlado por uma condição lógica e em que o número de repetições não seja conhecido de antemão. No
entanto, apesar de tal ser possível e de produzir um código mais compacto, por norma também é mais difícil de ler e
compreender caso tenhamos de corrigir erros.
Quando o número de repetições não é conhecido à partida, estando a execução do bloco de instruções dependente
apenas de uma condição lógica, devemos então utilizar o while.
Devemos usar um ciclo do-while em situações em que o bloco de instruções a repetir, para além de estar
dependente da avaliação de uma condição lógica, deva ser executado pelo menos uma vez. Aliás, é esse o ponto que
marca a diferença entre um do-while e os outros dois tipos de instruções de repetição.
Grupo II
Exerc. 1: Para os exercícios seguintes indique o output que espera como resultado, tendo em conta a declaração e
inicialização das seguintes variáveis:
Alínea A
if (num3 >= LIMIT)
System.out.println ("maçã");
System.out.println ("laranja");
System.out.println ("pera");
Alínea B
if (num2 == MAX)
{
System.out.println ("maçã");
System.out.println ("laranja");
}
System.out.println ("pera");
Alínea C
if (LIMIT+num3 <= 150)
{
System.out.println ("maçã");
System.out.println ("laranja");
}
else
System.out.println ("pera");
Alínea D
if (num1 < MAX)
if (LIMIT >= num2)
System.out.println ("maçã");
System.out.println ("laranja");
Alínea E
if (LIMIT%num1 + 4 == num1 + (MAX-num2))
{
System.out.println ("maçã");
System.out.println ("laranja");
}
else
{
System.out.println ("pera");
System.out.println ("banana");
}
Alínea F
if (num2 > 18)
if (num1 < 0)
System.out.println ("maçã");
else
System.out.println ("laranja");
System.out.println ("pera");
Alínea G
if (num3 >= MAX)
{
if (MAX/num2 == 1)
System.out.println ("maçã");
System.out.println ("laranja");
if (LIMIT-num3 > num1+2)
System.out.println ("pera");
else
{
System.out.println ("banana");
System.out.println ("kiwi");
}
}
else
if (num2*2 == MAX*2)
System.out.println ("uva");
Alínea H
if (LIMIT >= 4*num2)
if (MAX == 25)
System.out.println ("maçã");
else
System.out.println ("laranja");
else
System.out.println ("pera");.
Exerc. 2: Para os exercícios seguintes escreva segmentos de código que sirvam para executar as acções indicadas:
Alínea A
Imprimir “Hurra!” se a variável sum for divisível por count
Alínea B
Imprimir “Num é zero”, “Num é positivo”, “Num é negativo”, consoante o valor de num.
Alínea C
Atribuir o valor mais pequeno de dois inteiros à variável smallest utilizando:
If-else
Operador condicional
Alínea D
Imprimir “Igual” se dois valores do tipo float guardados em val1 e val2 forem exactamente iguais e
“Essencialmente igual” se esses dois valores estiverem a uma distância inferior ou igual a 0.0001 um do outro.
Imprimir “Diferente” se não forem de todo iguais ou semelhantes.:
Exerc. 3: Nos seguintes exercícios indicar o output que irá ser produzido tendo em conta o valor de base das
seguintes variáveis:
final int MIN = 10, MAX = 20;
int num = 15;
Alínea A
while (num < MAX)
{
System.out.println (num);
num = num + 1;
}
Alínea B
do
{
num = num + 1;
System.out.println (num);
}
while (num <= MAX);
Alínea C
for (int count1=1; count1 <= 5; count1++)
{
for (int count2=1; count2 <= 5; count2++)
System.out.print (count1*count2 + " ");
System.out.println();
}
Alínea D
while (num > MIN)
{
System.out.println (num);
num = num - 1;
}
Alínea E
while (num < MAX)
{
if (num%2 == 0)
System.out.println (num);
num++;
}
Alínea F
for (int value=7; value < 0; value--)
System.out.println (value);
Alínea G
do
{
num = num + 1;
if (num*2 > MAX+num)
System.out.println (num);
}
while (num <= MAX);
Alínea H
for (int value=num; value <= MAX; value++)
if (value%4 != 0)
System.out.println(value);
Grupo III
Exerc. 1: Implemente um método que receba um número e indique se o número é par ou ímpar.
Exerc. 2: Implemente um método que receba um caracter e determinar se este corresponde a um dígito.
Exerc. 3: Implemente um método que receba um determinado número e informe o utilizador se o mesmo é divisível
por 5.
Exerc. 4: Implemente um método que receba um número inteiro e verifique se é divisível por 2, 3, 5, 6, 10, 15 ou 30.
Exerc. 5: Implemente um método chamado, calcIRS, que receba o ordenado de um trabalhador e devolva o
imposto a pagar de acordo com a tabela 2:
Ordenado Taxa
<100.000 5%
>= 100 000 e < 300 000 15%
> 300 000 25%
Tabela 1
Exerc. 6: Observe o código da figura seguinte e indique, caso existam, os erros existentes:
switch(n)
{
case 1:
a=11;
b=22;
break;
case 2:
c=33;
break;
d=44;
}
Exerc. 7: Crie uma classe, chamada Utilitaria, constituída pelos seguintes métodos:
a) gerar um número aleatório;
b) receber dois números obtidos aleatoriamente e retornar o resultado da operação matemática entre ambos,
dependendo do operador escolhido (+, -, *,/)
Crie outra classe, chamada Principal, que permita testar os métodos desenvolvidos na classe Utilitaria.
Exerc. 8: Implemente um método que receba três valores reais, que correspondem a três lados de um triângulo e que
classifique o triângulo como equilátero, isósceles ou escaleno.
Exerc. 9: Crie uma classe, chamada Utilitaria, constituída pelos seguintes métodos:
a) ler um número inteiro;
b) Calcular a soma de um conjunto de números. A quantidade de números é desconhecida à partida. A sequência de
entrada só termina quando for introduzido um número negativo.
Crie outra classe, chamada Principal, que permita testar os métodos desenvolvidos na classe Utilitaria.
Exerc. 10: Implemente um método que recebe um valor n e calcule a soma de n primeiros números positivos.
while (i<100)
System.out.println(i,“ao quadrado é igual a ”,Math.sqr(i));
Exerc. 12: Observe o código da figura seguinte e indique, caso existam, os erros existentes:
Exerc. 13: Um banco venda moeda estrangeira (Dólares, Francos, Libras, etc.) e leva uma comissão de 2% do valor
transaccionado mais 1 Euro. Escreva um programa para indicar qual o valor a pagar por uma certa venda de moeda.
O programa recebe a quantia e tipo de moeda a adquirir. As taxas de câmbio existem no programa como constantes.
Exerc. 14: Uma certa empresa fabrica motores com potências compreendidas entre 1 e 99cv. Ao processar
informação sobre um destes motores, um programa usa um de três procedimentos, de acordo com a potência do
motor. Partindo do princípio que o programa está escrito em Java e de que a variável inteira Pot representa a potência
do motor a ser fabricado, escreva uma instrução if que execute o procedimento correspondente de acordo com a
tabela seguinte:
Exerc. 15: Escreva um programa em Java que converta notas quantitativas de 0 a 20 em notas qualitativas de mau a
excelente, utilizando uma estrutura de if’s encadeados e que verifique o limite superior das gamas de cada uma das
notas qualitativas.
Considere que:
0-4 : Mau
5-9 : Medíocre
10-13 : Suficiente
14-17 : Bom
18-20 : Muito Bom
Exerc. 16: Escreva um programa em Java para calcular os juros e o novo saldo de uma conta bancária, tendo em
atenção que se o saldo for inferior a 1.000 a conta não recebe juros e paga uma taxa ao banco (multa). Caso a conta
atinja um saldo negativo é cancelada. O programa deverá ter a multa representada por uma constante. Deverá ler o
saldo anterior, o número de meses ao fim do qual queremos calcular o saldo, e a taxa de juros anual. Parta do
princípio de que não há movimentos na conta e de que os juros só são calculados no fim do número de meses
especificado.
Exerc. 17: Relativamente à classe Aluno, criada no capítulo 3, implemente os seguintes métodos:
retornar falso se a nota da prova específica de matemática foi inferior a 12 valores;
verificar se a nota da prova específica foi inferior a 12 valores (invoque o método que calcula a média e o
implementado na alínea anterior).
imprimir um menu que permita ter as seguintes opções: inserir; listar; apagar e procurar aluno e sair do programa.
tratar as opções do menu anterior.
Como referido no livro “Fundamentos de Programação em Java2, FCA”, numa linguagem orientada a objectos os
programas funcionam através da criação de objectos, dos comportamentos que estes são capazes de apresentar e da
comunicação entre eles. Um objecto pode solicitar a outro um determinado comportamento, de entre os que estão
definidos. Antes de criar um objecto é necessário ter um “molde” que defina os seus atributos e comportamentos, o
qual se denomina por classe. Antes de se criar um objecto é necessário criar a respectiva classe. Podem ser criados
diversos objectos a partir de uma classe, cada um com atributos próprios mas com os mesmos comportamentos.
As linguagens de programação orientadas a objecto incluem diversas classes predefinidas a partir das quais se
podem criar objectos. Contudo, para a maioria dos programas é necessário definir novas classes de forma a poder
criar objectos que satisfaçam as necessidades dos programadores.
No JAVA, a definição de classes segue a seguinte sintaxe:
// Construtores
// Métodos
}
As variáveis ou atributos, os construtores e os métodos de uma classe são genericamente denominados membros da
classe.
As variáveis de instância são as variáveis definidas para armazenar os atributos específicos de cada objecto, por
exemplo a cor, o peso, etc. Cada objecto da classe definida tem a cor e o peso como atributo embora os seus valores
possam ser diferentes para cada objecto.
As variáveis de classe não existem nos objectos. A declaração de uma variável de classe provoca a criação de uma
variável em memória, a qual será partilhada por todos os objectos que forem criados a partir da classe. Assim, as
alterações efectuadas por um objecto ao valor de uma variável de classe manifestam-se quando a mesma variável é
acedida por qualquer objecto da mesma classe. Este tipo de variáveis utiliza-se quando é necessário partilhar
informação entre objectos de uma classe ou aglutinar informação produzida pelos mesmos.
Como exemplo de uma variável de classe, podemos considerar uma situação em que se tenha interesse em saber
quantos alunos existem. A solução passa pela criação de uma variável de classe da classe Aluno que seja
incrementada quando for criado um novo objecto:
A palavra reservada static indica ao compilador que deve criar uma variável que é partilhada por todos os
objectos da classe e como tal não deve criar uma cópia da variável sempre que for criado um novo objecto.
Da mesma forma que existem variáveis estáticas também existem métodos estáticos. Os métodos estáticos podem
ser invocados sobre a classe, não é necessário criar nenhuma instância da classe (i.e. um objecto) em que foram
declarados para os podermos utilizar (um bom exemplo disto são os métodos definidos na classe Math).
Os métodos estáticos apenas podem manipular variáveis locais ou então atributos estáticos pois as variáveis de
instância só são criadas quando são criadas instâncias da classe.
A figura seguinte apresenta um exemplo de utilização de atributos e métodos estáticos.
// VARIÁVEIS DE CLASSE
static private int numeroDeAlunos = 0;
// VARIÁVEIS DE INSTÂNCIA
private String nome;
private int numero;
//CONSTRUTORES
public Aluno( String nom)
{
nome = nom;
numero = this.numeroDeAlunos++ +1;
}
// selectores
public int getNumero()
{
return numero;
}
public String getNome()
{
return nome;
}
}
System.out.println();
System.out.print("Número de Alunos existentes :");
}
}
Figura 39. Demonstração de utilização de atributos e métodos estáticos utilizando uma versão simplificada da classe
Aluno
5.3 - Encapsulamento
O conceito de encapsulamento representa o princípio que cada objecto deve ser responsável pelos seus próprios
dados e deve controlar o seu funcionamento. Um objecto deve funcionar como uma caixa negra que é capaz de
apresentar um conjunto de comportamentos se for solicitado. Para o exterior basta dar a conhecer os comportamentos
possíveis e os seus efeitos, mas não a forma como são implementados
Um objecto pode ser considerado sob duas perspectivas: a interna e a externa. A perspectiva interna tem a ver com a
estrutura dos seus dados e com os algoritmos utilizados para implementar os seus comportamentos. Esta perspectiva
apenas interessa ao programador que criou a classe, pois só este necessita de conhecer os seus detalhes internos.
Numa perspectiva externa, um objecto é visto como uma entidade encapsulada, que apenas responde a um
determinado conjunto de pedidos que lhe podem ser feitos por outros objectos. Estes pedidos correspondem ao
conjunto de comportamentos definidos como públicos na classe do objecto, i.e., acessíveis do exterior.
Esta perspectiva permite a reutilização do código do objecto uma vez que os detalhes internos de um objecto são
independentes do contexto da sua utilização.
Para obter este nível de independência, um objecto deve ser auto-contido, ou seja, qualquer alteração do seu estado
(das suas variáveis) deve ser provocada apenas pelos seus métodos e não deve permitir que outro objecto altere o
seu estado.
//CONSTRUTORES
Neste caso o operador this ajuda ainda a resolver ambiguidades. Caso contrário como saberíamos de que nome,
numero, ano, curso ou turma se estava a falar? Quando se utiliza this.nome estamos a referenciar a
variável de instância nome pertencente ao objecto que está a ser criado, evitando deste modo a confusão com o
parâmetro nome do método construtor Aluno.
System.out.println( aluno.getNome());
}
Figura 41. Exemplo de passagem por parâmetros referenciados
A sobrecarga de métodos é bastante utilizada no caso dos construtores. Como exemplo podemos ver a figura
seguinte onde são definidos dois construtores para aluno, os quais podem ser utilizados conforme a informação
disponível na altura da criação do objecto da classe Aluno.
Objectivos da Série:
1. Diga o que entende por variável de classe e variável de instância. Mencione as diferenças entre ambas e
justifique a resposta.
2. Como pode criar e utilizar variáveis de classe? Isto é, quais são os passos a dar? Justifique e dê exemplos.
3. Para utilizar uma variável e/ou um método estático de uma classe necessita de instanciar a classe?
Justifique.
4. Métodos estáticos podem referenciar atributos que não sejam estáticos? Justifique.
5. O que é e para que serve o operador this?
Exerc. 1: Indique qual o resultado visualizado no ecrã após a execução do seguinte programa:
Exerc. 2: No exercício anterior, indique onde se encontra uma variável de classe e uma variável de instância.
Exerc. 3: Crie um programa que determine a partir das coordenadas de 3 pontos, fornecidas pelo utilizador, o tipo de
triângulo que estas representam (equilátero, isósceles ou escaleno). Deve também mostrar se se trata ou não de um
triângulo rectângulo.
Nota:
1. Triângulo equilátero tem os 3 lados iguais.
2. Triângulo isóscele tem 2 lados iguais.
3. Triângulo escaleno tem os 3 lados diferentes.
4. Um triângulo é rectângulo quando verifica a propriedade dada pelo teorema de Pitágoras: o quadrado da
hipotenusa é igual à soma dos quadrados dos catetos.
Deve implementar:
1. A classe ponto para representar as coordenadas e
2. A classe triângulo para representar triângulos.
3. Em separado terá a classe que contém o programa principal e faz uso das outras duas para cumprir os
objectivos pretendidos.
6- Tabelas
6.1 - Motivação
Um programa que manipule vários valores, armazena estes valores em algumas variávei, faz o respectivo
processamento e envia o resultado para o exterior (fig.-1).
Programa
Valor 1 Variável 1
Valor 3 Variável 3
Nem sempre os programas manipulam tão poucos valores, podendo necessitar de manipular vários valores. Estes
podem ser lidos e processados um de cada vez(fig.-2).
Valor 1 Programa
Valor 2
Valor 4
Valor n
...
Imaginemos agora um programa que irá ler vários valores e imprimi-los pela ordem inversa. O programa deve
processá-los “quase todos ao mesmo tempo” para produzir a saída desejada (fig. 3).
Programa
Valor 1 Variável 1
Valor 2 Variável 2
Valor 3 Variável 3
Saída
Valor 4 Variável 4
Valor 5 Variável 5
... ...
Valor n Variável n
Figura 45. Leitura de vários valores e sua visualização pela ordem inversa
A alternativa para este tipo de situações é o uso de Tabelas, que não são mais do que uma lista de valores. Assim,
para o exemplo da fig. 3 poderiamos armazenar os valores numa variável do tipo tabela e aceder aos valores pela
ordem desejada (fig.4).
Valor 1
Programa
Valor 2
Valor 3
Saída
Variável
Valor 4 Tabela
Valor 5
...
Valor n
Exemplos :
• armazenam um só valor.
• são criadas quando da sua declaração.
• armazenam tipos primitivos, que são manipulados por valor.
As tabelas:
Os passos para utilizar uma tabela são três, declaração, instanciação e referenciação:
1) Declaração :
Na declaração de uma tabela cria-se uma referência para um determinado tipo de dados. No exemplo que
se segue cria-se uma referência para uma tabela de inteiros com qualquer dimensão, sendo a mesma
iniciada com null.
int [] tab;
tab null
2) Instanciação :
Na instanciação de uma tabela é definida a capacidade da mesma. No exemplo seguinte cria-se uma tabela
com o nome tab, com uma capacidade para cinco inteiros.
tab
A instanciação (criação) de uma tabela pode ser feita no momento de declaração, se se indicar o conteúdo.
tab 1 1 2 3 5 8 13 34
3) Referenciação :
tab 1 12 2 3 5 8 13 34
Exemplo 1
Neste exemplo pretende-se calcular o valor médio da pluvisiosidade em 18 anos. Para tal, cria-se uma tabela que
guarde 18 valores e preenchesse a tabela com os valores da pluvisiosidade:
Depois efectua-se a soma dos valores, que pode ser feita por duas formas :
ou
Nota :
relembre-se que o índice do vector começa em 0 e pode ir até length-1
Com o valor da soma efectuado, podemos calcular a média e imprimir para o ecran o valor:
Exemplo 2
O exemplo seguinte tem como objectivo pedir ao utilizador o valor de cinco inteiros, imprimir no ecran os valores
introduzidos, calcular a soma, a média e o valor máximo destes valores.
import Le;
//Ler array
//Mostrar array
for(int i=0;i<5; i++)
System.out.println("i["+i+"]="+n1[i]);
//Mostrar resultados
System.out.println("Total:"+soma);
System.out.println("Media:"+(float)soma/n1.length);
System.out.println("Maximo:"+max);
0 1 1 1
1 3 3
2 10
11
3
4 2
índices
Considere o exemplo 2, do ponto 1.2.4, no qual é criado e preenchido um array de cinco elementos. Caso se tentasse
aceder a um elemento inexistente, durante a execução do programa seria detectado um erro:
Java.lang.ArrayIndexOutOfBoundsException:19
At Teste.main(Test.java)
Na sequência da classe Aluno, imaginemos que se pretendia gerir uma turma de alunos, com funcionalidades como
inserir, listar, procurar e apagar alunos. A solução seria criar uma tabela do tipo Aluno.
Aluno [] tabelaAlunos ;
Uma vez que se pretende gerir uma turma de alunos, deve-se criar uma classe Turma, que como atributo terá a tabela
de alunos:
Quando se define uma classe, deve-se implementar os construtores da mesma. No caso da classe Turma serão
definidos dois construtores.
public Turma()
{
tabelaAlunos = new Aluno[MAXIMO_ALUNOS];
numAlunos=0;
}
No primeiro construtor, define-se que a tabela de alunos terá como dimensão o valor da constante
MAXIMO_ALUNOS. No segundo construtor a dimensão da tabela de alunos será passada por argumento. Em ambos
os construtores a variável numAlunos é inicializada a zero por não existirem alunos inscritos no início.
Outra forma de implementar este método sem a passagem de um objecto do tipo Aluno seria:
{
if(tabelaAlunos.length>numAlunos)
{
Aluno aluno = new Aluno(nome, num,curso, ano, turma);
//inserir na tabela
tabelaAlunos[numAlunos++]=aluno;
return true;
}
return false;
}
Outra das funcionalidades requeridas é listar a informação contida na tabela, ou seja, lista os alunos inscritos na
turma:
É de notar que quando se está a correr uma tabela de objectos, é necessário verificar se a posição corrente tem ou
não conteúdo. tabelaAlunos[i]!=null é a expressão que verifica se a posição i da tabela de alunos é
diferente de null.
if(this.tabelaAlunos[i].getNumero()==num)
return tabelaAlunos[i];
return null;
}
Na procura por número de aluno tem que se percorrer a tabela e para cada posição da mesma verificar se o número
do aluno é igual ao num passado no argumento do método. Como num é do tipo primitivo inteiro usa-se o operador
==.
Na procura por nome de aluno tem que se percorrer a tabela e para cada posição da mesma verificar se o nomedo
aluno é igual ao nome passado no argumento do método. Como estamos perante variáveis do tipo String usa-se o
método equals.
str1.equals(str2) verifica se a str1 1 é igual à str2.
A funcionalidade de apagar aluno é mais complexa de implementar. O algoritmo deste método é dividido em duas
partes:
boolean flag=true;
int i,j;
for(i=0;i<numAlunos && flag;i++)
if(tabelaAlunos[i].getNumero()==num)
{
flag =false;
}
if( flag)
return false;
else
{
for(j=i-1; j<numAlunos-1;j++)
tabelaAlunos[j]=tabelaAlunos[j+1];
tabelaAlunos[j]=null;
}
return true;
}
Para testar o funcionamento dos métodos pode-se utilizar a seguinte classe com o método main:
System.out.println(tEIA.listar());
tEIA.insereAluno(aluno2);
System.out.println(tEIA.listar());
tEIA.apagarAluno(2133);
System.out.println(tEIA.listar());
tEIA.apagarAluno(2131);
System.out.println(tEIA.listar());
tEIA.apagarAluno(2138);
System.out.println(tEIA.listar());
}
}
O Output seria:
Exemplos:
Tabela de Strings
No exemplo em seguida apresentado é declarada uma tabela de Strings que não é mais do que uma tabela em que
cada elemento da tabela é uma referência a uma String.
// Declaração e instanciação
String[] nome = new String [NUM_NOMES ] ;
// Inicialização ou Atribuição
nome[0] = "Smith";
nome[1] = "Jones";
nome[2] = "Miller";
nome[3] = "Luis";
nome[4] = "Gonzales";
// Saída
System.out.println ("Resultado:");
for (int indice = 0; indice < nome.length; indice ++)
System.out.println (“nome[“ + indice + "]: " + nome[indice]);
class Pessoa
{
String nome;
int idade;
String BI;
Em seguida apresenta-se o código onde se cria uma tabela de objectos da classe pessoa, que é uma tabela em que
cada elemento é uma referência a um objecto da classe pessoa.
//Saída
System.out.println("Conteúdos de cada elemento da tabela:");
for (int pessoa = 0; pessoa < 4; pessoa ++)
System.out.println((pessoa + 1) + "ª Pessoa do Grupo: " +
grupo[pessoa].toString );
}
}
Figura 48. Exemplo gráfico dos resultados obtidos com o código anterior
Tabela dinâmica
No exemplo apresentado em seguida será criada uma tabela dinâmica, lida uma lista de números inteiros terminada
por 0 e depois com os valores lildos calculada a média e o valor máximo.
import Le;
int soma=0;
int max=0;
int i=1;
//Ler 1º valor
System.out.print(“Insira o 1º inteiro:> ”);
int n = scanTeclado.nextInt();
while (n!=0)
{
//criar tabela temporária
int tabTemp[]=new int[tab.length+1];
//copiar valores
for(int j=0;j<tab.length;j++)
tabTemp[j]=tab[j];
//Mostrar tabela
for(i=0; i<tab.length; i++)
System.out.println(“tab[“ + i + "]="+ tab[i]);
//Mostrar resultados
System.out.println("Total:"+soma);
System.out.println("Media:"+(float)soma/n1.length);
System.out.println("Maximo:"+max);
Lista Telefónica
No exemplo seguinte pretende-se criar uma lista telefónica. Para tal, cria-se a classe Contacto, cujos atributos são o
nome e o telefone da pessoa:
//Atributos
String nome;
int telefone;
//Construtores
public Contacto(String nomeContacto, int telefoneContacto)
{
nome=nomeContacto;
telefone=telefoneContacto;
}
//Métodos
public String mostrarContacto()
{
return nome + " - " + telefone;
}
Em seguida veremos a classe Lista telefónica na qual é manipulado uma tabela de objectos do tipo Contacto:
//Atributos
private static final int MAXCONTACTOS= 20;
private Contacto contactos [];
private int nContactos=0;
//Construtores
public ListaTelefonica(){
contactos = new Contacto[MAXCONTACTOS];
}
//Métodos
public void adicionar()
{
String nome;
int telefone;
System.out.println("Introduza o nome:");
nome= scanTeclado.next();
System.out.println("Introduza o telefone:");
telefone= scanTeclado.nextInt();
Contacto c = new Contacto(nome,telefone);
contactos[nContactos]=c;
nContactos++;
}
if (i <= j)
trocaContactos(i++, j--);
}
while (i <= j) ;
quickSort(pri, j);
quickSort(i, ult);
}
int seleccao;
do
{
System.out.println("Organizer");
System.out.println("");
System.out.println("1 - Adicionar");
System.out.println("2 - Mostrar");
System.out.println("3 - Procurar");
System.out.println("4 - Ordenar");
System.out.println("5 - Sair");
System.out.println("_______________________");
System.out.println("Qual a sua escolha?");
seleccao = scanTeclado.nextInt();
switch(seleccao){
case 1:
listaTel.adicionar();
break;
case 2:
listaTel.mostrar();
break;
case 3:
listaTel.procurar();
break;
case 4:
listaTel.ordenar();
break;
case 5:
System.out.println("Adeus.");
break;
default:
System.out.println("Opção errada. Tente de Novo.");
break;
}
}
while(seleccao != 5);
}
}
Também é possível passar como parâmetro um elemento de uma tabela, desde que se sigam as regras referentes ao
seu tipo.
É possível passar como parâmetro um elemento de uma tabela, desde que se sigam as regras referentes ao seu tipo.
Exemplo:
array[0]=12;
array[1]=10;
for(int i=0;i<array.length;i++)
System.out.println(array[i]);
metodo_1(array);
for(int i=0;i<array.length;i++)
System.out.println(array[i]);
metodo_2(array[1]);
for(int i=0;i<array.length;i++)
System.out.println(array[i]);
}
Declaração:
<tipo> [ ] [ ] <variável>
Exemplo
int [ ] [ ] tab;
O código acima declara tab como uma tabela bidimensional de inteiros ou como uma tabela de tabelas, em que
apenas se cria uma referência.
tab[0] 1 2 3
tab[1] 4 5 6
tab[1][2]
Exemplo
O Java é uma linguagem fortemente tipada, sendo portanto o número de dimensões da matriz e o tipo dos elementos
são definidos. O número de elementos em cada dimensão pode variar e o conteúdo dos elementos de uma tabela
pode ser de quaisquer objectos ou tipos primitivos.
Exemplo
public class Ex {
Outra diferença consiste no facto de um objecto do tipo vector não ser declarado para guardar um tipo de valor. Um
objecto do tipo Vector gere uma lista de referências para a classe Object, permitindo assim guardar qualquer
tipo de valor.
Nota:
Para mais informações sobre os métodos disponibilizados por esta classe, consultar a documentação do Java.
Exemplo
Neste exemplo será criado um objecto da classe Vector que armazenará nomes. Serão depois aplicadas algumas
operações ao vector, como inserir, remover, verificar que elemento está num determinado índice.
nomes.addElement(“Ricardo”);
nomes.addElement(“João”);
nomes.addElement(“Nuno”);
nomes.addElement(“Daniel”);
System.out.println(nomes);
nomes.removeElement(“João”);
System.out.println(nomes);
System.out.println(“No índice 1: “ + nomes.elementAt(1));
nomes.insertAt(“Carlos”,2);
Exerc.2 - Implemente um método que retorna o produto dos elementos de uma tabela.
A assinatura do método é a que se segue:
Exerc.4 - Escrever um método que, dada uma sequência de n valores inteiros, retorne a diferença entre os valores
máximo e mínimo da sequência.
Exerc.5 - Escrever um método que retire todas ocorrências de um caracter carC de uma string. A assinatura do
método é a que se segue:
Exerc.6 - Escrever um programa que converta uma string numa tabela de caracteres.
Exerc.7 - Escrever um método que receba um número e uma tabela e devolva a posição na tabela onde este número
ocorra ou o valor -1 no caso do número não existir na tabela. A assinatura do método é a que se segue:
Exerc.8 - Escrever um método que inverta a ordem dos elementos de uma tabela de inteiros, passada como
parâmetro.
Exerc.9 - Escrever um método que devolva a posição da última ocorrência do valor máximo de uma tabela de valores
do tipo double.
Exerc.10 - Escrever um método que receba duas tabelas e retorne true se as tabelas são iguais.
Exerc.11 - Escrever um programa que, para um dada tabela de inteiros A, construa uma tabela P formada pelos
índices dos elementos pares de A. Exemplo: Para A = ( 1 3 6 7 8 ), o programa deve construir P = ( 3 5 ).
Exerc.12- Escrever um método (e um programa que exercite tal método) que determine se os elementos de uma
tabela encontram-se em ordem decrescente ou não.
Exerc.13 - Escrever um programa que, dada uma sequência de n valores inteiros (gerados aleatoriamente) que
representam o resultado de n lançamentos de um dado, determine o número de ocorrências de cada face na
sequência analisada.
Exerc.14 - Dada uma sequência de n números reais, fazer um programa que determine os números que compõem a
sequência e a frequência de tais números na sequência dada.
Exemplo:
n = 10
Sequência: 3.2 8.1 2.5 3.2 7.4 8.1 3.2 6.3 7.4 3.2
Saída:
Num Freq
3.2 4
8.1 2
2.5 1
7.4 2
6.3 1
Exerc.15 - Escrever um programa que determine o maior valor em uma matriz de valores inteiros com n>0 linhas e
m>0 colunas. Em seguida altere o programa anterior para indicar todas as posições da matriz em que se encontra tal
valor máximo.
Exerc.16 - Implemente um método que retorne o cálculo do produto externo entre duas matrizes. A matriz resultante
calcula-se da seguinte forma p[i][j]=x[i]*y[j], onde x e y são as tabelas de entrada.
A assinatura do método é:
Exerc.17 - Escrever um programa que inicialmente lê um número inteiro N. Se o valor introduzido for menor que 2
(dois) finalizar o programa. Caso contrário, cria dinamicamente uma matriz de inteiros NxN. Em seguida lê um valor
para cada posição da matriz e, no final, mostrar se a matriz possui algum valor repetido.
Exerc. 18 - Defina uma classe WebSite caracterizada pelo nome, endereço e lista de temas que se podem encontrar
no mesmo. Implemente as seguintes funcionalidades a esta classe:
• Determinação de quantos temas possui o website
• Verificar se um determinado tema existe no website
• Adicionar um tema
• Remover um tema
Elabore uma classe CatalogoWeb que implementa um arquivo sobre webSites e cujas funcionalidades são:
• Determinação do número total de websites do catálogo
• Listar os Websites que tratam um determinado tema T
• Remover um website
Exerc. 20 – Construa uma classe estática que tem como objectivo conter métodos utilitários para a manipulação de
tabelas. Esses métodos são os já criados nos exercícios 2,3,8 e 10.
7- Algoritmos de Procura/Pesquisa
7.1 - Introdução
A procura de informação é uma actividade quotidiana: procuramos números em listas telefónicas, palavras em
dicionários, etc.
A procura de informação é frequentemente executada em tabelas. Ao longo deste capítulo vamos utilizar uma tabela
preenchida. Vamos para tal fazer uso de uma classe (Tabela) que possui um método (preenche) que preenche uma
tabela, passada como parâmetro, com valores inteiros gerados aleatoriamente entre 0 e 100. O código da classe é
em seguida apresentado:
import java.util.Random;
class Tabela {
No exemplo seguinte pretende-se saber qual a posição que o valor 75 (se existir) ocupa na tabela de números.
0 1 2 3 4 5 Índice
10 20 75 9 4 3
Posição 0
Compara-se 75 com 10 → São diferentes
Incremento a posição
Posição 1
Compara-se 75 com 20 → São diferentes
Incremento a posição
Posição 2
Compara-se 75 com 75 → São iguais
Parar a procura
O próximo exemplo utiliza um programa que preenche uma tabela com números gerados aleatoriamente (entre 0 e
100) e permite, em seguida, procurar números que foram inseridos.
if ( i != tab.length)
return(i); // retorna indice;
else
return(-1);
}
Tabela.preenche(tabela);
System.out.println("Resultado:"+procura(tabela,67);
System.out.println("Resultado:"+procura(tabela,33));
System.out.println("Resultado:"+procura(tabela,1));
System.out.println("Resultado:"+procura(tabela,101));
}
}
O método procura retorna o índice do valor na tabela se encontrar o valor, caso contrário retorna -1. Note-se que no
ciclo correspondente à procura propriamente dita,
Embora a primeira verificação seja essencial a segunda seria desnecessária se pudesse garantir que o valor a
procurar se encontrava na tabela.
A utilização de um valor sentinela exige a existência de uma posição adicional na tabela para armazenar o valor
sentinela. A primeira operação a efectuar, ainda antes da procura, é a inserção do valor sentinela no final da tabela.
Se no fim da procura o valor encontrado for o sentinela, então o valor procurado não existe na tabela.
No exemplo seguinte procede-se a implementação da procura sequencial com sentinela, utilizando um programa que
preenche uma tabela com números gerados aleatoriamente (entre 0 e 100) e permite, em seguida, procurar números
que foram inseridos.
if ( i != tab.length-1)
return(i);
else
return(-1);
}
Tabela.preenche(tabela);
System.out.println("Resultado:"+procura(tab,67));
System.out.println("Resultado:"+procura(tab,33));
System.out.println("Resultado:"+procura(tab,1));
System.out.println("Resultado:"+procura(tab,102);
}
}
A procura binária é uma alternativa mais eficaz, mas exige que os elementos sobre os quais a procura está a ser
efectuada se encontrem ordenados.
O algoritmo é o seguinte:
O próximo exemplo utiliza um programa que lê 100 números e permite, em seguida, procurar os números que foram
inseridos.
System.out.println("Resultado:"+procuraBinaria (tabela,67));
System.out.println("Resultado:"+procuraBinaria (tabela,33));
System.out.println("Resultado:"+procuraBinaria (tabela,1));
System.out.println("Resultado:"+procuraBinaria (tabela,101));
}
}
baixo
x: 2
1ª iteração v: 1 3 3 5 7 8 9
(>x)
baixo meio alto
Valor a Procurar
2ª iteração 1 3 3 5 7 8 9
(>x)
baixo meio alto
3ª iteração 1 3 3 5 7 8 9
(<x)
baixo alto
4ª iteração 1 3 3 5 7 8 9
2ª iteração 1 3 3 5 7 8 9
(>x)
3ª iteração 1 3 3 5 7 8 9
(<x)
O valor 1 existe na tabela
Exerc.2 - Implemente o método mínimo que retorna o índice do elemento de menor valor da tabela tabF. A assinatura
do método é
Exerc.3 - Implemente o método procuraCar que retorna o índice da primeira ocorrência do caracter c1 na tabela
tabCar. Utilize o método de procura binária. A assinatura do método é:
Exerc.4 - Temos uma tabela com as temperaturas médias ao longo dos dias de mês. Queremos saber para um
determinado mês:
1. Qual foi a temperatura mais baixa
2. Em que dia ocorreu a temperatura mais elevada
3. Qual foi a diferença entre a temperatura mais baixa e a temperatura mais alta.
Exerc.5 - Implemente o método procuraFloat que procura na tabela v, que tem como critério de paragem o valor de s,
se esta contem o elemento x. Caso a procura seja realizada com sucesso retorna o índice do elemento, senão retorna
–1. assinatura do método é
Exerc.6 - Um sistema de controlo de presenças ligado ao sistema de marcação de ponto, mantém uma lista de todos
os trabalhadores que num determinado momento se encontram dentro da empresa. É necessário implementar um
método que dado o nome de uma pessoa, ele nos valide se esta pessoa se encontra dentro da empresa ou não.
Utilize a procura binária na implementação desta função.
8- Algoritmos de Ordenação
8.1 - Introdução
Um conjunto de elementos é normalmente ordenado para facilitar a procura. Ordenamos as coisas para facilitar a
procura e não por sermos naturalmente arrumados. Imagine que teria de procurar o número de telefone de um amigo
numa lista telefónica que não estivesse ordenada alfabeticamente. Seria caótico certamente.
Em programação, a utilização de tabelas ordenadas permite escrever algoritmos de procura mais eficientes.
Nos algoritmos de ordenação que serão em seguida introduzidos, vamos supor que pretendemos ordenar uma
sequência de inteiros com um máximo de 100 elementos.
Com o objectivo de separar algumas operações que se repetem ao longo dos exemplos de ordenação, vamos
apresentar de seguida uma classe (Tabela) que permite realizar as seguintes funções, sobre uma tabela:
• Preenchimento de uma tabela com valores inteiros, não ordenados, gerados aleatoriamente.
• Troca de elementos de uma tabela.
• Impressão dos elementos de uma tabela.
import java.util.Random;
class Tabela {
8.2 - BubbleSort
Este algoritmo consiste em:
• Percorrer os elementos a ordenar, comparando elementos adjacentes e trocando os pares de
elementos que se encontram fora de ordem.
• Até que se tenha efectuado uma passagem completa em que não haja nenhuma troca de posições.
Note que a tabela encontra-se ordenada quando se efectua uma passagem completa em que nenhum par de
elementos trocou de posição.
De um modo geral uma única passagem pelo conjunto de elementos não ordena a tabela.
0 1 2 3 4 5 Índice
4 8 17 3 11 2
Veremos a evolução da posição dos elementos ao longo das passagens e da variável trocou que permite saber se ao
longo da passagem houve trocas de posição.
Primeira passagem
trocou = false
4 8 17 3 11 2
4 8 17 3 11 2
4 8 3 17 11 2 (trocou=true)
4 8 3 11 17 2 (trocou=true)
4 8 3 11 2 17 (trocou=true)
Segunda passagem
trocou = false
4 8 3 11 2 17
4 3 8 11 2 17 (trocou=true)
4 3 8 11 2 17
4 3 8 2 11 17 (trocou=true)
4 3 8 2 11 17
Terceira passagem
trocou = false
3 4 8 2 11 17 (trocou=true)
3 4 8 2 11 17
3 4 2 8 11 17 (trocou=true)
3 4 2 8 11 17
3 4 2 8 11 17
Quarta passagem
trocou = false
3 4 2 8 11 17
3 2 4 8 11 17 (trocou=true)
3 2 4 8 11 17
3 2 4 8 11 17
3 2 4 8 11 17
Quinta passagem
trocou = false
2 3 4 8 11 17 (trocou=true)
2 3 4 8 11 17
2 3 4 8 11 17
2 3 4 8 11 17
2 3 4 8 11 17
Sexta passagem
trocou = false
2 3 4 8 11 17
2 3 4 8 11 17
2 3 4 8 11 17
2 3 4 8 11 17
2 3 4 8 11 17
trocou == false – Termina!
do
{
houveTroca = false;
for (int j = 0; j < tabela.length-1; j++)
if (tabela[j] > tabela[j+1]){
Tabela.troca(tabela, j, j+1);
houveTroca = true;
}
}while (houveTroca);
}
8.3 - ShellSort
A ordenação ShellSort é uma variante da ordenação BubbleSort e consiste em comparar e trocar, não elementos
adjacentes, mas sim elementos separados por um certo intervalo.
Este processo repete-se até que o intervalo seja 1 (correspondente a uma ordenação por BubbleSort).
Note que a ordenação ShellSort é mais eficiente (mais rápida) do que a ordenação BubbleSort porque as primeiras
passagens consideram apenas um subconjunto dos elementos a ordenar e as últimas passagens já os encontram
parcialmente ordenados.
4 8 17 3 11 2
Vejamos a evolução da posição dos elementos ao longo das passagens, da variável trocou que permite saber se ao
longo da passagem houve trocas de posição e da variável intervalo que define a distância entre elementos
comparados.
Passagem 1
Intervalo=3
trocou=False
3 8 17 4 11 2 (trocou=true)
3 8 17 4 11 2
3 8 2 4 11 17 (trocou=true)
Passagem 2
Intervalo=3
trocou=False
3 8 2 4 11 17
3 8 2 4 11 17
3 8 2 4 11 17
Passagem 3
Intervalo=1
trocou=False
3 8 2 4 11 17
3 2 8 4 11 17 (trocou=true)
3 2 4 8 11 17 (trocou=true)
3 2 4 8 11 17
3 2 4 8 11 17
Passagem 4
Intervalo=1
trocou=False
2 3 4 8 11 17 (trocou=true)
2 3 4 8 11 17
2 3 4 8 11 17
2 3 4 8 11 17
2 3 4 8 11 17
Passagem 5
Intervalo=1
trocou=False
2 3 4 8 11 17
2 3 4 8 11 17
2 3 4 8 11 17
2 3 4 8 11 17
2 3 4 8 11 17
do
{
do
{
nenhumaTroca = true;
}while (!nenhumaTroca);
intervalo = intervalo / 2;
4 8 17 3 11 2
Primeira passagem (4 8 17 3 11 2)
2 8 17 3 11 4
Segunda passagem (2 8 17 3 11 4)
2 3 17 8 11 4
Terceira passagem (2 3 17 8 11 4)
2 3 4 8 11 17
Tabela.troca(numbers, i, min);
}
}
o Passo Recursivo
Ordenar os dois grupos esquerdo e direito, usando o mesmo método recursivamente
n * log n
A ideia “de aceleração” por trás do algoritmo QuickSort é que é mais rápido ordenar duas sequências de n/2
elementos do que uma sequência de n elementos.
i x=4 j
v: 8 9 2 5 4 6 1 3 7
(3.1+3.2)
i j
8 9 2 5 4 6 1 3 7
(3.3) i j
3 9 2 5 4 6 1 8 7
(3.3) j
i
3 1 2 5 4 6 9 8 7
(3.1+3.2) i j
3 1 2 5 4 6 9 8 7
(3.3) j i
3 1 2 4 5 6< 9 8 7
≤x ≥x
Figura 51. Passo da divisão
Exerc.2 - Implemente um método que receba como parâmetro uma tabela de inteiros e retorne uma outra tabela
ordenada. Use o algoritmo Selection Sort.
Exerc.3 - Faça um método que receba como parâmetro uma tabela de objectos do tipo funcionário (nome e número
fiscal) e retorne uma tabela ordenada. Use o algoritmo Shell Sort
Exerc.4 - Implemente um programa que dado uma tabela de caracteres a ordene o algoritmo Selection Sort.
Exerc.6 - Uma loja pretende dispor os seus artigos de vestuário segundo cores. Para isso precisa de fazer uma
pequena alteração à aplicação de suporte à gestão da loja, de modo a acrescentar a funcionalidade de listagem de
artigos por ordem de cor, sendo a ordem de cores desejada (amarelo, laranja, castanho, beje, verde, azul, vermelho,
cinzento e preto).
Implemente o método que permite reordenar a tabela de objectos ArtigoVestuario segundo a ordem de cores desejada
9- Recursividade
9.1 - Definições
A repetição de execução de um grupo de instruções para diferentes valores de dados pode ser implementada de duas
formas:
• Iterativa (ou explícita).
• Recursiva (ou implícita). Quando um método está definido em termos dele próprio.
Por exemplo, vejamos o cálculo do factorial de um número natural. Este pode ser feito de duas formas:
• Forma iterativa
n! = n*(n-1)*(n-2)*…*1
Um método recursivo é um método que directa ou indirectamente faz uma chamada a si mesmo (recorre a si próprio
para resolver o problema).
Como um método F resolve um problema chamando a si mesmo? O F chama a si em diferentes instâncias, com os
valores diferentes.
Exemplos:
• Examinar todos os arquivos de um directório D, incluindo todos os sub-directórios;
• Procurar o significado de uma palavra num dicionário;
• Definição de uma expressão aritmética numa linguagem de programação.
O uso da recursão é útil na resolução de problemas que podem ser decompostos em subprogramas mais simples e
onde a solução final obtém-se pela composição das soluções desses subproblemas.
• Um caso elementar (ou critério de paragem). No mínimo, sempre tem que haver um caso que pode ser
resolvido sem recursividade.
No exemplo do cálculo do factorial este critério é dado por 1!=1.
• O caso geral, ou parte recursiva. Qualquer chamada recursiva tem que progredir em direcção ao caso
elementar.
No exemplo do cálculo do factorial este caso é n!=n*(n-1)!
A recursividade é muito usada, por exemplo, no cálculo de problemas matemáticos, contudo a recursividade pode
causar problemas de memória em virtude da duplicação de variáveis locais, incluindo os parâmetros.
Também devemos ter cuidado para não criar uma lógica circular que resulte em laços infinitos.
Recursividade é uma poderosa ferramenta de programação que pode levar a algoritmos pequenos e eficientes.
Recursividade, sempre leva a um código mais compacto.
Exemplos:
No exemplo seguinte será apresentado o cálculo do factorial de um número natural através da forma iteractiva e
recursiva.
Iterativa
/*
Output:
f(0) = 1
f(1) = 1
f(2) = 2
f(3) = 6
f(4) = 24
f(5) = 120
f(6) = 720
f(7) = 5040
f(8) = 40320
*/
Recursiva
/*
Output:
f(0) = 1
f(1) = 1
f(2) = 2
f(3) = 6
f(4) = 24
f(5) = 120
f(6) = 720
f(7) = 5040
f(8) = 40320
*/
f(3) = 3 * f(2) = 6
|
2 * f(1) = 2
|
1*1=1
Exerc.2 – Escreva um algoritmo recursivo e respectivo método, que permita obter o resultado de A+B sendo A e B
inteiros não negativos.
Nota:
a+b = a if b=0
a+b =(a+1) + (b-1)
Exerc.3 – A sequência de Fibonacci é definida como: 1, 1, 2, 3, 5, 8, 13, 21, 34, ... Em que cada elemento desta
sequência é a soma dos dois elementos precedentes:
Nota:
Se n <= 2 então Fib(n) := 1;
Se n > 2 então Fib(n) : = Fib(n-2)+ Fib(n-1);
Em que Fib(n) retorna o n-ésimo termo da sequência.
Exerc.4 - Faça um método recursivo que calcule o valor de S, onde S = 1 + ½ + 1/3 + ¼ + 1/5 + 1/N.
Exerc.5 - Escreva um método recursivo que calcula e retorna o valor de S(n), onde
Exerc.6 - Escreva um método recursivo que calcula e retorna o valor de S(n), onde
S = 2/4 + 5/5 + 10/6 + 17/7 + 26/8 + ... +(n*2+1)/(n+3)
Exerc.7 - Escreva um método recursivo que calcula e retorna a função potência Xⁿ.
Exerc.8 - Faça um método recursivo que recebe um inteiro e retorna o seu número de dígitos.
Exerc.9 – Escreva um método para calcular o valor da função de Ackmann, dada pela seguinte relação:
Nota:
1 se m = 0
A(m,n) = A(m-1,1) se m>0 e n=0
A(m-1,A(m,n-1)) se m>0 e n>0
Exerc.10 – Escreva um programa contendo o seguinte método recursivo que retorna o n-ésimo número Catalão:
static long c( int n)
n −1
c (n) = ∑ c(i ) . c(n − 1 − i ) = c(0) . c(n − 1) + c(1).c(n − 2) + .... + c(n − 2).c(1) + c(n − 1) . c(0)
i =0
Para efeitos comprovativos, em seguida, utilize a seguinte expressão e compare os resultados da sequência
obtida:
(2n)!
c ( n) =
n!. (n + 1)!
Exerc.11 - Faça um método recursivo que ordene por ordem crescente os n elementos de um vector de inteiros de n
posições.
Exerc.12 - Faça uma função recursiva que calcule o mdc entre dois números:
mdc(n1,n2) = n1 , se n2 = 0
mdc(n1,n2) = mdc( n2, n1 mod n2), se n2 != 0
inicio
se (n=m) ou (m=0)
entao
X = 1
senao
X = X(n-1,m) + X(n-1,m-1)
fim
Qual o valor de X(5,3) ?
Quantas chamadas serão feitas na avaliação acima ?
Neste anexo apresenta-se a título de exemplo introdutório ao Java a classe Aluno que foi teoricamente explicada no
capitulo 2.
//CONSTRUTORES
public Aluno(String nom, int num, String curs, int an, char turm)
{
nome = nom;
numero =num;
ano=an;
curso=curs;
turma=turm;
valorPropina=0;
valorPago=0;
}
// metodos selectores
public int getNumero()
{
return numero;
}
public String getNome()
{
return nome;
}
public String getCurso()
{
return curso;
}
public int getAno()
{
return ano;
}
public char getTurma()
{
return turma;
}
// metodos modificadores
public void setCurso(String curs)
{
curso=curs;
}
public int setAno()
{
return ano;
}
public void setTurma(char turm)
{
turma=turm;
}
public void setPropina(double valor)
{
valorPropina=valor;
}
// outros métodos
public String toString()
{
String str= "Nome:" + nome + "Numero :" + numero + " curso : " +
curso + " ano" + ano;
return str;
};
11 - Anexo II – Glossário
Nota:
Os significados apresentados para as palavras colocadas neste glossário são apenas os que se consideraram
necessários no âmbito da cadeira de Introdução à Programação.
As palavras encontram-se descritas por ordem alfabética.
Abordagem: conjunto de acções tomadas para definir a forma de atingir a solução de um determinado problema.
Ambiguidade: Expressão cujo significado não pode ser determinado a partir do contexto em que está inserida (ou
seja, que a partir de determinado ponto oferece mais do que um caminho a seguir, podendo levar a resultados
diferentes).
Conceber: Criar.
Compilador: Programa que sabe interpretar o código escrito numa determinada linguagem de programação. Recebe
código fonte como elemento de entrada e produz código executável.
Compilação: Processo de gerar código executável a partir de código fonte recorrendo a um compilador.
Estado: Situação em que se encontra uma variável em termos de valor actual. O estado de um objecto é o conjunto
de valores dos seus atributos. No que diz respeito a um problema, tem um estado inicial que corresponde ao valor do
resultado que ainda não existe, e um estado final que é o valor de resultado após passar pela fase de transformação.
Implementação: Código produzido por um programador e que representa uma solução possível para um determinado
problema.
Linguagem de Programação: Linguagem artificial utilizada para levar a cabo a implementação de programas de
computador.
Máquina Virtual: Na prática é um computador simulado dentro do próprio computador através de software próprio
criado para esse efeito. Comporta-se como se fosse um computador independente. A máquina virtual Java actua
como um ambiente de execução de programas Java auto-contido.
Notação: Mecanismo que serve para definir o formato de determinado tipo de informação. Por exemplo: a BNF serve
para definir gramáticas para variados tipos de linguagens, tal como o XML serve para definir formato de dados, os
símbolos musicais servem para definir músicas nas partituras, etc, etc.
Sistema Operativo: Programa de computador de grandes dimensões e complexidade que é responsável não só por
esconder os detalhes do hardware aos utilizadores e programadores, como também tem de gerir a memória
disponível para cada aplicação(programa em execução, também chamado de processo) e gerir a execução dessas
mesmas aplicações, gerir o sistema de comunicação com outras máquinas, etc.
Software: Programa de computador. Representa a parte lógica, ao contrário do hardware que representa a parte
física de um computador.
Subprograma: Também chamados de rotinas (ou métodos no caso de linguagens orientadas a objectos como é o
caso do Java), são pequenos programas que podem ser chamados e chamar outros programas para que em conjunto
colaborem na obtenção de uma solução para determinado problema.
Top-Down: Cosntrução de uma solução em que a partir do problema maior se tenta ir descendo de nível e
especificando a cada passo os níveis intermédios, ou seja, os problemas mais pequenos.