Escolar Documentos
Profissional Documentos
Cultura Documentos
Java para Iniciantes
Java para Iniciantes
2a edição
Gravataí/RS
Edição do Autor
2017
Copyright © Luiz Fernando Duarte Júnior, 2017
Java para Iniciantes
Luiz Duarte
Duarte, Luiz,
Java para Iniciantes - 2a edição
ISBN 978-65-900538-1-7
Seu trabalho também pode ser visto em mais detalhes em seu perfil no
LinkedIn e na sua página no Facebook.com/luiztools.
Entre em contato, estou sempre disposto a ajudar meus leitores e ouvir suas
críticas e sugestões.
Segundo, este livro exige que você já tenha conhecimento técnico prévio
sobre computadores, que ao menos saiba mexer em um e que
preferencialmente possua um, bem como conexão com a Internet. Não
importa seu sistema operacional ou requisitos de hardware, se possui um
computador com menos de 7 anos de uso, ele vai dar conta do recado!
Ao término deste livro você estará apto a construir softwares simples para
computadores usando qualquer sistema operacional que suporte a máquina
virtual Java, bem como compreenderá o básico da principal linguagem de
programação para computadores da atualidade, sendo capaz de criar
programas interativos com o usuário e pequenos jogos que estimularão sua
lógica e raciocínio. Além disso, terá uma noção abrangente do mercado de
desenvolvimento Java em que está se inserindo.
1 Introdução
Java é uma linguagem de programação introduzida no mercado na década de
90 pela Sun Microsystems, que provocou grande entusiasmo em
programadores, analistas e projetistas de software na época, graças à
quantidade de inovações que trouxe consigo. No início dos anos 2000, a
empresa Sun foi comprada pela Oracle, fazendo com que o Java mudasse de
dono mas sem perder o seu brilho e importância no mercado.
Mas por que o Java é uma linguagem tão importante de ser aprendida?
Orientada à Objetos
Java é uma linguagem puramente orientada à objetos pois, com exceção de
seus tipos primitivos de dados, tudo em Java são classes ou instância de uma
classe. Java atende todos os requisitos necessários para uma linguagem ser
considerada orientada à objetos que resumidamente são oferecer mecanismos
de abstração, encapsulamento e hereditariedade. Esse é um conceito
intermediário que você deve ouvir falar no futuro, mas que será ignorado
nesta obra para simplificar seu aprendizado.
Independente de Plataforma
Java é uma linguagem independente de plataforma pois os programas Java
são compilados para uma forma intermediária de código denominada
bytecodes que utiliza instruções e tipos primitivos de tamanho fixo,
ordenação big-endian e um biblioteca de classes padronizada. Os bytecodes
são como uma linguagem de máquina destinada a uma única plataforma, a
máquina virtual Java (JVM – Java Virtual Machine), um interpretador de
bytecodes. Pode-se implementar uma JVM para qualquer plataforma assim
temos que um mesmo programa Java pode ser executado em qualquer
arquitetura que disponha de uma JVM.
Se você já mexeu com Delphi sabe como é frustrante não poder desenvolver
softwares para vários sistemas operacionais...Além disso, esse modelo de
máquina virtual foi tão bem sucedido que diversas outras plataformas de
desenvolvimento migraram para esta arquitetura, como a famosa plataforma
.NET da Microsoft, que veio muitos anos depois do Java.
Sem Ponteiros
Java não possui ponteiros, isto é, Java não permite a manipulação direta de
endereços de memória nem exige que o objetos criados sejam destruídos
livrando os programadores de uma tarefa complexa. Além disso a JVM
possui um mecanismo automático de gerenciamento de memória conhecido
como garbage collector (coletor de lixo), que recupera a memória alocada
para objetos não mais referenciados pelo programa.
Performance
Java foi projetada para ser compacta, independente de plataforma e para
utilização em rede o que levou a decisão de ser interpretada através dos
esquema de bytecodes. Como uma linguagem interpretada a performance é
razoável, não podendo ser comparada a velocidade de execução de código
nativo. Para superar esta limitação várias JVM dispõem de compiladores just
in time (JIT) que compilam os bytecodes para código nativo durante a
execução otimizando a mesma, que nestes casos melhora significativamente a
performance de programas Java.
Isto faz toda a diferença quando você programa grandes aplicações, e um dia
espero que você chegue lá!
Segurança
Considerando a possibilidade de aplicações obtidas através de uma rede, a
linguagem Java possui mecanismos de segurança que podem evitar qualquer
operação no sistema de arquivos da máquina-alvo, minimizando problemas
de segurança. Tal mecanismo é flexível o suficiente para determinar se uma
programa Java é considerado seguro especificando nesta situação diferentes
níveis de acesso ao sistema-alvo.
A máquina virtual Java tem sido alvo de muitos "ataques" e especulações nos
últimos anos no que diz respeito à segurança. No entanto, em todas as
ocasiões foi comprovado que as ditas "brechas" eram culpa de plugins para
navegadores mal construídos ou ainda outros tipos de brechas causadas por
falta de atualização e uso indevido da tecnologia. O Java continua
posicionado como uma das plataformas mais seguras do mundo, sendo
utilizado em sistemas que exigem altíssima segurança, de governos de países
às maiores empresas do mundo listadas na bolsa, incluindo grandes bancos
públicos e privados.
Permite Multithreading
Java oferece recursos para o desenvolvimento de aplicações capazes de
executar múltiplas rotinas concorrentemente bem como dispõe de elementos
para a sincronização destas várias rotinas. Cada um destes fluxos de execução
é o que se denomina thread, um importante recurso de programação de
aplicações mais sofisticadas.
Além disso, o Java é uma linguagem bastante robusta, oferece tipos inteiros e
ponto flutuante compatíveis com as especificações IEEE, suporte para
caracteres UNICODE (isto significa suporte à muitos alfabetos diferentes), é
extensível dinamicamente além de ser naturalmente voltada para o
desenvolvimento de aplicações em rede ou aplicações distribuídas.
Tudo isto (e muito mais que você verá durante seu aprendizado da
linguagem) torna o Java uma linguagem de programação única.
Mercado
O ranking TIOBE mede a popularidade de linguagens de programação
através da Internet, levando em conta videos no Youtube, resultados do
Google, quantidade de tutoriais e materiais sobre as linguagens, etc. O Java
reina em primeiro lugar há diversos anos!
Fonte: http://www.tiobe.com/tiobe-index/
Que tal conferir este outro gráfico abaixo, organizado segundo pesquisa do
famoso site StackOverflow, a maior fonte de perguntas e respostas para
desenvolvedores do mundo, onde o Java aparece em terceiro lugar, logo atrás
de Javascript e SQL.
Sendo assim, para deixar o seu computador pronto para desenvolvermos Java
ao longo deste livro será necessário que baixe a versão mais recente do JDK
no link abaixo, que aponta para o site oficial do fabricante:
http://www.oracle.com/technetwork/java/javase/downloads/index.html
A instalação não requer nenhuma instrução especial, apenas avance cada uma
das etapas e aceite o contrato de uso da plataforma. Na data em que escrevo
este livro a Oracle também tem disponibilizado uma versão do NetBeans
(outro software que iremos utilizar) já com o JDK embutido, para facilitar a
instalação. Minha sugestão é que baixe ambos softwares e instale-os em
separado, assim como vou mostrar aqui, para evitar problemas futuros com
outros softwares que possam precisar do JDK.
https://netbeans.org/downloads/
Você notará que existem diversas opções para download, isso porque o
NetBeans não é uma IDE exclusiva para o Java, permitindo desenvolvimento
em C/C++, PHP, Web e diversas plataformas Java diferentes, sendo que a
que usaremos aqui é a Java SE (Standard Edition, Edição Padrão). Caso
esteja com espaço em disco sobrando e esteja pensando em programar para
outras plataformas mais adiante, baixe a versão completa (All) mesmo. Da
mesma forma que o Java em si, o NetBeans possui instaladores para os
principais sistemas operacionais do mercado.
Mande executar a ferramenta NetBeans e você verá a tela de boas vindas, que
deve se parecer com essa abaixo, dependendo da versão mais atual da
ferramenta. Chamamos esta tela de Boas Vindas (Welcome Screen).
Esses passos citados acima são sempre os mesmos toda vez que criarmos um
novo projeto Java. Marque essa página para futuras consultas, pois não
descreverei mais estes passos com tantos detalhes mais tarde.
Uma vez que você deixou marcada a caixa de verificação Criar classe
principal no assistente para Novo projeto, a IDE criou um classe básica para
você. Você pode adicionar a mensagem "Olá mundo!" ao código de exemplo
substituindo a linha:
pela linha:
System.out.println("Olá Mundo!");
Classe?
O Java é uma linguagem da família das linguagens Orientadas à Objetos, o
que quer dizer que seu programa será estruturado em classes, sendo cada
arquivo Java uma classe (class). Cada classe pode ter vários códigos Java
dentro dela. Nossos programas serão básicos, então teremos apenas uma
classe por programa e não se preocupe muito com isso por enquanto.
Já a linha
Código 3: disponível em http://www.luiztools.com.br/livro-java-i-fontes
A palavra class introduz uma definição de classe em Java, ou seja, define que
aquele código entre as chaves será uma classe. Ela deve possuir o nome da
classe à sua direita, no caso OlaMundo.
A linha
Para uma classe que seja um programa Java, sempre devemos ter um método
main que deve ser definido tal qual como está na linha descrita anteriormente.
Se o método main não for escrito exatamente daquele jeito, o interpretador do
Java não executará o programa.
A palavra reservada void indica que este método realizará uma tarefa (neste
caso, imprimir a linha de texto na tela) mas NÃO retornará informação
alguma ao seu término.
O termo "String args[]" dentro dos parênteses são argumentos passados para
esse programa e são obrigatórios no main, embora não iremos utilizá-lo aqui.
A linha
System.out.println("Olá Mundo!");
As últimas palavras, grifadas em cinza, não são usadas pela linguagem, mas
ainda assim são reservadas, devido à comandos da especificação original da
linguagem Oak, precursora do Java.
TipoDaVariavel nomeDaVariavel;
byte b = 0;
short s = 20;
int i = 200;
long l = 2000;
float f = 1.5f;
double d = 2000.678;
Note como o valor literal da variável float está sendo atribuído com um ‘f’ no
final. Isso se deve ao fato de que, em Java, valores literais com ponto
flutuante são considerados double por padrão.
Caractere (Character)
Representação Significado
‘\n’ quebra de linha
‘\r’ retorno ao início da linha (carriage return)
‘\t’ tabulação (tab)
‘\’’ apóstrofe (também chamado de aspas simples)
‘\"’ aspas (ou aspas duplas)
‘\\’ barra invertida (backslash)
O valor literal de caracteres deve estar delimitado por aspas simples (‘ ’).
char c = 'a';
Booleano (Boolean)
/* comentário
de múltiplas linhas */
//declaração da classe
public class OlaMundo{
//bloco principal, que inicia a aplicação
public static void main(String[] args){
//diretiva que imprime no console
System.out.println("Olá mundo!");
}
}
Conforme você for avançando e pegando experiência com Java, não precisará
usar mais tantos comentários, provavelmente apenas em blocos de código
complexos, como forma de documentação para uso futuro. Por ora, comente
tudo o que achar conveniente para seus estudos.
Para fazer isso é bem simples. Primeiro, declare quantas variáveis quiser, de
preferência de tipos diferentes e atribua um valor inicial para cada uma, como
fiz no exemplo abaixo com a variável do tipo int.
int x = 0;
Segundo, peça ao usuário que digite um valor para essa variável, apenas
imprimindo uma mensagem para ele:
int x = 0;
System.out.println("Digite um valor inteiro para x:");
Vou falar com mais detalhes delas futuramente, mas por ora, apenas entenda
a primeira linha cria um objeto que simboliza o teclado do computador
(teclado), e a segunda linha lê o primeiro número inteiro que o usuário digitar
no teclado (teclado.nextInt).
int x = 0;
System.out.println("Digite um valor inteiro para x:");
Depois, para cada variável digitada pelo usuário, digite o comando abaixo,
que imprime o valor da variável logo após a frase "Variável x: ", sendo que
troque ‘x’ pelo nome da sua variável.
int x = 0;
System.out.println("Digite um valor inteiro para x:");
Se você digitar o valor 10, por exemplo, o programa vai encerrar com a
seguinte aparência:
Digite um valor inteiro para x:
10
Variável x: 10
Note que quando você coloca o nome de uma variável entre aspas
(consequentemente dentro de um texto), é impresso o nome da mesma no
console. Já quando coloca o nome da variável sem aspas, é impresso o valor
da mesma, neste meu exemplo, o valor numérico 10.
Note também que usamos um sinal de ‘+’ para concatenar (combinar) o texto
"Variável x: " com o valor numérico da variável. Quando mandamos executar
esse bloco de código, o texto combinado é exibido ao invés de suas partes
individuais, o que nos mostra que o operador ‘+’ pode ter efeitos diferentes
do que estamos acostumados, dependendo se estamos lidando com números
ou palavras.
Isso tudo por causa de um tipo especial de objeto, as Strings, que falaremos
mais adiante, no próximo capítulo, juntamente com os operadores!
4 Operadores e o tipo String
A linguagem Java oferece um conjunto bastante amplo de operadores
destinados a realização de operações aritméticas, lógicas, relacionais e de
atribuição. Essas operações servem para processar os dados recebidos em seu
sistema computacional, visando realizar alguma tarefa para o usuário, o
objetivo do seu sistema.
Usamos alguns destes operadores no capítulo anterior, mas sem entrar muito
nos detalhes do seu funcionamento, o que faremos agora.
O tipo String, que também será abordado neste capítulo, é um tipo mais
avançado que os primitivos, falados no capítulo anterior. Ele será abordado
ao término desta unidade.
Operadores Aritméticos
Como na maioria das linguagens de programação, o Java possui vários
operadores aritméticos. Considere nos exemplos que a e b são variáveis
numéricas (int, short, etc).
Crie um novo projeto no NetBeans (Arquivo > Novo Projeto > Aplicação
Java) e dê o nome de Aritmetica pra ele, sendo que esse será o nome da
classe principal com o main dentro. Em seguida, cole o código abaixo dentro
do bloco main da referida classe/arquivo.
a=6
b=1
int a = 15;
int b = 12;
System.out.println("a = " + a);
System.out.println("b = " + b);
System.out.println("a == b : " + (a == b));
System.out.println("a != b : " + (a != b));
System.out.println("a < b : " + (a < b));
System.out.println("a > b : " + (a > b));
System.out.println("a <= b : " + (a <= b));
System.out.println("a >= b : " + (a >= b));
Operadores Lógicos
Como seria esperado o Java também possui operadores lógicos, isto é,
operadores que permitem conectar logicamente o resultado de diferentes
expressões aritméticas ou relacionais construindo assim uma expressão
resultante composta de várias partes e portanto mais complexa.
int i = 0;
int y = i + 2;
System.out.println("Valor do i: " + i);
Realize testes ao menos uma vez com cada operador, para entender na prática
como eles funcionam. Se não conseguir realizar testes com os operadores
lógicos, não tem problema, revisitaremos ele no próximo capítulo: Estruturas
de Controle.
O tipo String
Todos os tipos utilizados na linguagem Java, com exceção dos tipos
primitivos (int, double, char e boolean), são "objetos". O tipo String, com S
maiúsculo, é um dos objetos mais utilizados, isso porque ele é utilizado para
criar variáveis que guardam texto dentro de si, algo muito recorrente em
desenvolvimento de software.
Enquanto que o tipo char permite armazenar apenas um caracter por vez, o
tipo String permite armazenar um número virtualmente infinito de caracteres,
formando palavras, frases, textos, etc. Apesar de todo esse "poder", sua
declaração é tão simples quanto a de qualquer outra variável:
Note que a palavra ‘Luiz’ está entre aspas (duplas), logo após a declaração da
variável. Isso porque valores de texto literais devem estar entre aspas (duplas)
para serem considerados Strings.
Assim como vimos anteriormente, quando usamos o operador ‘+’ com textos
(que agora chamaremos de Strings), ele não soma os seus valores, mas sim
concatena (junta) eles. Isso vale tanto para Strings literais (textos entre aspas)
quanto para variáveis Strings.
Isso vai imprimir o texto "Prof. Luiz tem 28 anos!", copiando o valor da
variável idade pra dentro da nova String completa. Note que isso não altera o
tipo da variável idade ou seu valor, apenas faz uma cópia dela em forma de
texto, automaticamente.
Agora vamos criar um programa bem simples, que pede o nome e o peso de
duas pessoas, para exercitar alguns tipos de variáveis que aprendemos até
agora!
O tipo Scanner, que estamos usando para criar nosso objeto teclado, não
possui um método nextString, em analogia ao que ocorre com inteiros
(nextInt) por exemplo. Porém possui dois métodos para ler Strings: next e
nextLine, sendo que o primeiro lê a próxima palavra, e o segundo lê a
próxima linha digitada pelo usuário!
Estrutura FOR
Em Java dispomos da diretiva for cuja sintaxe é dada a seguir:
O primeiro campo é usado para dar valor inicial a uma variável de controle
(um contador). Nele declaramos nossa variável de controle, geralmente um
inteiro, e inicializamos ele, geralmente com 0, como no exemplo abaixo.
Mas se i = 0, quando que ele será maior ou igual à 10 para o for encerrar a
repetição? Aí que entra o terceiro campo do for: o incremento/decremento.
Neste último campo nós mudamos o valor da variável de comando, para mais
ou para menos. Esse incremento/decremento acontece uma vez a cada
repetição do laço, logo após a execução da diretiva e antes da condição
(segundo campo do for) ser analisada novamente. Isto faz com que, em
determinado momento, o laço pare de se repetir, como no exemplo abaixo.
Neste caso, a diretiva será executada 10 vezes e sequência, uma vez que a
cada execução o valor de i aumentará em uma unidade até alcançar o valor
10, o que fará com que a condição ‘i < 10’ se torne falsa e o for acabe sua
execução.
A tradução literal do for é PARA, e lemos o código anterior como "para uma
dada variável i, inicializada com 0, até ela se tornar 10, imprima o valor de i e
incremente-o logo após".
Estrutura WHILE
O while (ENQUANTO) é o que chamamos de laço condicional, isto é, um
conjunto de instruções que é repetido enquanto o resultado de uma expressão
lógica (uma condição) é avaliado como verdadeiro. Abaixo segue a sintaxe
desta diretiva:
while (expressão_lógica)
diretiva;
Neste exemplo inicializamos uma variável chave do tipo boolean como true
(verdadeiro) e depois usamos a própria variável como condição de parada do
while. Enquanto chave for true, o while continuará sendo executado (i.e. a
diretiva será executada). No entanto, note que um problema típico
relacionado a avaliação da condição da diretiva while é o seguinte: se a
condição nunca se tornar falsa o laço será repetido indefinidamente.
Para que isso não aconteça, devemos nos certificar de que exista algo na
diretiva que, em dado momento, modifique o valor da condição, como no
exemplo abaixo:
Claro, esse é um exemplo pouco útil, uma vez que a única instrução existente
é para encerrar o while. O que acontece geralmente é que o while possua
mais de uma diretiva e, assim como for, elas devem ser circundadas por
chaves:
int i = 0;
while(i < 10){
//outra instrução qualquer
System.out.println("Imprime!");
i++;
}
Nesse caso, o texto "Imprime!" será impresso 10 vezes, assim como seria
possível fazer com um for.
Estrutura DO/WHILE
O do/while também é um laço condicional, isto é, tal como o while é um
conjunto de instruções que são repetidas enquanto o resultado da condição é
avaliada como verdadeira mas, diferentemente do while, a diretiva associada
é executada antes da avaliação da expressão lógica e assim temos que esta
diretiva é executada pelo menos uma vez.
Por exemplo:
Código 35: disponível em http://www.luiztools.com.br/livro-java-i-fontes
Um exercício que você pode fazer para testar o for, o while e o do/while é o
seguinte: antes de conhecer os laços de repetição, como você faria para
declarar uma variável e imprimi-la de 0 a 9?
int x = 0;
System.out.println(x++);
System.out.println(x++);
System.out.println(x++);
System.out.println(x++);
System.out.println(x++);
System.out.println(x++);
System.out.println(x++);
System.out.println(x++);
System.out.println(x++);
int x=0;
while(x < 10)
System.out.println(x++);
int x=0;
do{
System.out.println(x);
} while(x++ < 10);
int x=0;
System.out.println(x++);
Já neste exemplo…
int x=0;
System.out.println(++x);
A resposta é 1. Capicce?
Então vamos passar à próxima estrutura, temos muito pra ver ainda dentro do
básico de Java!
Estruturas de desvio de fluxo
Existem várias estruturas de desvio de fluxo que podem provocar a
modificação da maneira com que as diretivas de um programa são executadas
conforme a avaliação de uma condição. O Java dispõe de duas destas
estruturas: if/else e switch/case.
Estrutura IF/ELSE
O if/else é uma estrutura simples de desvio de fluxo de execução, isto é, é
uma diretiva que permite a seleção entre dois caminhos distintos para
execução dependendo do resultado falso ou verdadeiro resultante de uma
expressão lógica.
if (expressão_lógica)
diretiva1;
int x = 0;
if(x == 0)
System.out.println("X é zero!");
int x = 0;
if(x == 0){
System.out.println("X é zero!");
x++;
}
Note também que a expressão lógica que vai dentro dos parênteses do if
aceitam qualquer expressão que retorne true ou false, usando os operadores
lógicos (<, >, <=, >=, ==, != e !). Sendo assim, podemos fazer coisas muito
mais complexas com um if do que apenas uma comparação de igualdade,
como no exemplo abaixo, onde vemos se um número é PAR (isto é, divisível
por 2):
int x = 0;
if(x % 2 == 0){
System.out.println("X é PAR!");
}
Este for que criei no exemplo anterior está ligeiramente diferente. Primeiro,
eu inicializei a variável de controle com 1 ao invés do 0 tradicional. Além
disso, coloquei na expressão de controle o operador ‘menor ou igual que’,
comparando com o número 20, para que o 20 em si seja impresso também. Já
o if eu aproveitei do exemplo anterior, então nenhuma novidade aqui.
Copie e cole este código e veja como ele imprime corretamente somente os
números pares de 1 a 20.
Essa talvez tenha sido uma solução pensada por você, mas está errada.
Execute ela dentro de um método main no NetBeans e notará que ela sempre
imprime que todos os números são ÍMPARES, além de imprimir quando
algum é PAR também.
Porquê?
Uma vez que a impressão de que o número é ÍMPAR está no fluxo normal,
ela sempre será executada após o if.
if (expressão_lógica)
diretiva1;
else
diretiva2;
Lemos o trecho mais interno do algoritmo acima como: "se i for divisível por
2, então imprima que ele é PAR, senão, imprime que ele é ímpar".
Para finalizar o estudo do if/else, cabe ressaltar que muitas vezes também
precisamos que nossos else’s também possuam condições, que o algoritmo
não entre dentro do else simplesmente porque não atendeu à condição do if.
Neste caso, podemos adicionar um if ao else, o que chamamos de else if.
if(x == 1){
diretiva1
} else if(x == 2){
diretiva2
}
if(x == 1){
diretiva1
} else if(x == 2){
diretiva2
} else {
diretiva3
}
Estrutura SWITCH/CASE
O switch/case é uma diretiva de desvio múltiplo de fluxo, isto é, baseado na
avaliação de uma variável é escolhido um caminho de execução dentre vários
possíveis. O switch/case equivale logicamente a um conjunto de diretivas if
encadeadas, embora seja usualmente mais eficiente durante a execução.
switch (variavel) {
case valor1: diretiva1;
break;
case valor2: diretiva2;
break;
default: diretiva_default;
}
if(variavel == valor1)
diretiva1;
else if(variavel == valor2)
diretiva2;
else
diretiva_default;
Para guardar valores dentro do array, usamos o nome que demos a ele,
seguido de colchetes e o número da posição onde queremos guardar o valor,
como abaixo:
Agora outro exemplo, onde crio um array e populo com nomes de pessoas:
Mas voltando ao desafio inicial deste tópico, como faríamos para armazenar
um número arbitrário de valores?
Note que usamos a própria variável de controle ‘i’ par acessar a posição do
array de números onde queremos guardar os valores. Notou algo estranho?
Sim, eu guardei o número 1 em todas posições do array.
Não entendeu? Use o seguinte código para imprimir todas posições do array:
Agora sim, se você executar este código dentro de um método main verá que
ele pedirá 10 números inteiros e depois imprimirá os 10 digitados em
sequência!
Para finalizar os testes e reforçar os conceitos, como você faria para que o
usuário escolhesse a quantidade de números que ele quer digitar? Essa
quantidade seria usada pelo sistema depois para coletar os números e
imprimi-los.
O primeiro passo é pedir pro usuário digitar essa quantidade, usando ela para
criar o array no tamanho necessário (lembrando que não podemos alterar o
tamanho do array depois, então já temos de criá-lo do tamanho certo!).
Esses são os requisitos mínimos de estrutura para que suas aplicações console
funcionem corretamente. Ao longo dos últimos capítulos criamos alguns
programas simples para console, testando-os no console do NetBeans. Neste
capítulo vamos ver em detalhes como funciona a entrada, processamento e
saída de dados de programas Java console, bem como criar programas
interativos completos para diversão e aprendizado.
Saída de Dados
As aplicações de console utilizam como padrão a stream de dados out,
disponível estaticamente através da classe System. Uma stream pode ser
entendida como um duto capaz de transportar dados de um lugar (um arquivo
ou dispositivo) para um outro lugar diferente. O conceito de stream é
extremamente importante pois é utilizado tanto para manipulação de dados
existentes em arquivos como também para comunicação em rede e outros
dispositivos.
A stream de saída out é um objeto de escrita de dados que pode ser utilizado
através dos seguintes comandos:
Comando Descrição
print(valor) Imprime a variável ou valor literal passado dentro dos
parênteses, podendo ser de qualquer tipo primitivo ou
String
println(valor) Faz o mesmo que o método print, porém inclui uma
quebra de linha no final do valor escrito (lê-se print line)
Assim sendo, podemos escrever elementos no console usando uma das duas
opções abaixo, conforme nossa estratégia:
System.out.print(valor);
System.out.println(valor);
Devemos observar que embora pronta para receber dados dos usuários, os
comandos disponíveis para a entrada destes dados são muito ruins, uma vez
que são orientados a ler bytes "crus" (raw bytes). Desta maneira, geralmente
utilizamos a entrada in em conjunto com algum outro objeto Java que nos
permita uma leitura de dados mais prática, como a classe Scanner.
Uma vez declarada esta variável ‘teclado’, podemos usá-la quando quisermos
para ler as entradas digitadas pelo usuário e, para isso, temos vários
comandos à disposição, de acordo com a entrada que esperamos ler.
Comando Descrição
nextInt() Lê o próximo valor inteiro (4 bytes)
nextFloat(), Idem ao anterior, mas o valor é do tipo
nextDouble(), especificado no nome do método (float, double,
nextLong(), etc etc)
next() Lê a próxima palavra (palavras são separadas por
espaço em branco (‘ ’)
nextLine() Lê todo o texto que o usuário digitou, até
encontrar a primeira quebra de linha (‘\n’)
Como assim?
Se o usuário digitar uma letra (a-z) e você chamar o comando nextInt(), por
exemplo, a aplicação irá travar! Claro, existem mecanismos em Java para
evitar isso, mas por ora, vamos confiar que o usuário seguirá corretamente as
instruções que você fornecerá à ele via System.out.
Para que você entenda melhor o que estou falando, execute o seguinte código
no seu NetBeans:
Curioso, não?
Agora se você executar, verá que ele vai esperar normalmente até que você
digite uma palavra e tecle Enter, porque "limpamos" o ‘\n’ do buffer do
teclado chamando um nextLine "sozinho" uma linha após o nextInt. É isso,
basta chamar esse nextLine sozinho após seus nextInt, nextDouble, etc e
tudo funcionará como esperado!
E isso é tudo que você precisa saber sobre a teoria de desenvolver aplicações
console. Que tal agora criarmos um sistema console de verdade?
Então vamos!
Criando Aplicações
Apesar das aplicações de console não terem o apelo atrativo das interfaces
gráficas, ainda assim é possível construir-se aplicações interessantes. Vamos
fazer algumas para exercitar?
Par ou Ímpar
Que tal criarmos um jogo de par ou ímpar? Esse é um jogo que todo mundo
conhece e que podemos facilmente fazê-lo para exercitar tudo o que vimos
até aqui.
Como começamos?
if(escolha == 0)
System.out.println("O jogador escolheu PAR.");
else
System.out.println("O jogador escolheu IMPAR.");
Terceiro, agora precisamos pedir que o jogador faça a sua jogada, que deve
ser somente 1 ou 2, para simplificar.
Mas agora, vamos encerrar o nosso jogo. O que falta? Ver quem ganhou, é
claro!
Para isso, devemos somar as duas jogadas e ver se o número resultante é par
ou ímpar, depois, comparar com a escolha do jogador, se ele quis par (0) ou
ímpar (1) lá no início do sistema.
System.out.println("Calculando o vencedor");
int soma = jogada1 + jogada2;
boolean resultadoPar = soma % 2 == 0;
if(resultadoPar == true && escolha == 0)
System.out.println("O jogador ganhou");
else if(resultadoPar == false && escolha == 1)
System.out.println("O jogador ganhou!");
else
System.out.println("A CPU ganhou!");
if(escolha == 0)
System.out.println("O jogador escolheu PAR.");
else
System.out.println("O jogador escolheu IMPAR.");
System.out.println("Calculando o vencedor");
Thread.sleep(1000);//atrasa 1 segundo
int soma = jogada1 + jogada2;
boolean resultadoPar = soma % 2 == 0;
if(resultadoPar == true && escolha == 0)
System.out.println("O jogador ganhou");
else if(resultadoPar == false && escolha == 1)
System.out.println("O jogador ganhou!");
else
System.out.println("A CPU ganhou!");
Claro que sim! Que tal um esquema de rounds ou melhor de 3? Você pode
criar um array de 3 posições para armazenar quem venceu cada um dos
rounds e, se alguém levar 2 de 3, é o vencedor definitivo!
Mas ‘peraí, como que vamos rolar três partidas em sequência sem copiar e
colar esse monte de código três vezes?
Se você executar agora este jogo, vai conseguir jogar apenas um round e
depois o jogo vai encerrar, e não é isso que queremos. O que podemos fazer
para repetir o jogo por três rounds consecutivos?
Que tal um laço de repetição em volta do algoritmo, que se repete três vezes?
Excelente escolha! ;)
Não é muito difícil. Que tal se após a segunda partida/iteração do laço while
(roundAtual == 2) a gente verifique se não há dois ganhadores repetidos no
array? (JJ ou CC). Pode funcionar, certo?! Nesse caso bastaria encerrar o laço
usando o comando break, como no código abaixo, que vai no final do laço
while, pouco antes dele fechar chaves.
if(roundAtual == 2){
if(rounds[0] == 'J' && rounds[1] == 'J'){
System.out.println("O jogador ganhou duas em sequência!");
break;
} else if(rounds[0] == 'C' && rounds[1] == 'C'){
System.out.println("A CPU ganhou duas em sequência!");
break;
}
}
}
E para verificar quem ganhou ao término do terceiro round, basta comparar o
último vencedor dos rounds (round 3, o de desempate, posição 2 do array),
logo depois da verificação do round 2, com um else if:
if(roundAtual == 2){
if(rounds[0] == 'J' && rounds[1] == 'J'){
System.out.println("O jogador ganhou duas em sequência!");
break;
} else if(rounds[0] == 'C' && rounds[1] == 'C'){
System.out.println("A CPU ganhou duas em sequência!");
break;
}
} else if(roundAtual == 3){ //else if adicionado para ver o vencedor do
round 3
if(rounds[2] == 'J'){
System.out.println("O jogador ganhou a melhor de 3!");
break;
} else if(rounds[2] == 'C'){
System.out.println("A CPU ganhou a melhor de 3!");
break;
}
}
}
Segue abaixo o jogo completo, basta copiar e colar dentro do método main do
arquivo principal do projeto:
if(escolha == 0)
System.out.println("O jogador escolheu PAR.");
else
System.out.println("O jogador escolheu IMPAR.");
System.out.println("Calculando o vencedor");
Thread.sleep(1000);//atrasa 1 segundo
int soma = jogada1 + jogada2;
boolean resultadoPar = soma % 2 == 0;
if(resultadoPar == true && escolha == 0){
System.out.println("O jogador ganhou este round");
rounds[roundAtual++] = 'J';
} else if(resultadoPar == false && escolha == 1){
System.out.println("O jogador ganhou este round!");
rounds[roundAtual++] = 'J';
} else {
System.out.println("A CPU ganhou este round!");
rounds[roundAtual++] = 'C';
}
if(roundAtual == 2){
if(rounds[0] == 'J' && rounds[1] == 'J'){
System.out.println("O jogador ganhou duas em sequência!");
break;
} else if(rounds[0] == 'C' && rounds[1] == 'C'){
System.out.println("A CPU ganhou duas em sequência!");
break;
}
} else if(roundAtual == 3){ //else if adicionado para ver o vencedor do
round 3
if(rounds[2] == 'J'){
System.out.println("O jogador ganhou a melhor de 3!");
break;
} else if(rounds[2] == 'C'){
System.out.println("A CPU ganhou a melhor de 3!");
break;
}
}
}
Note que sempre que ambos escolhem a mesma jogada, há empate, nesse
caso tendo de jogar novamente, até que um vença. Para os demais casos, a
lógica é que papel enrola pedra, pedra quebra tesoura e tesoura corta papel.
Vamos começar? Vou ser mais direto dessa vez, me atendo mais às partes
novas do algoritmo, tomando como base que você conseguiu executar com
sucesso o anterior.
Primeiro, crie um novo projeto no NetBeans (Arquivo > Novo projeto >
Aplicação Java) com o nome de PapelPedraTesoura e, no seu arquivo
principal, coloque dentro do main o seguinte código, que vai pedir para o
usuário escolher uma jogada, salvando a jogada em uma variável inteira.
Código 80: disponível em http://www.luiztools.com.br/livro-java-i-fontes
if (jogada1 == 1)
System .out.println( "O jogador escolheu PAPEL." );
else if(jogada1 == 2)
System.out.println("O jogador escolheu PEDRA.");
else
System.out.println("O jogador escolheu TESOURA.");
Para a CPU jogar, faremos a mesma lógica com números aleatórios, que
fizemos pra sortear se ela ia jogar 1 ou 2 no jogo anterior, lembra? Aqui,
vamos sortear se ela vai jogar PAPEL (1), PEDRA (2) ou TESOURA (3),
mas de uma maneira um pouco diferente, porque não queremos apenas 1 ou
2, mas sim 1, 2 ou 3.
if(jogada2 == 1)
System.out.println("A CPU escolheu PAPEL.");
else if(jogada2 == 2)
System.out.println("A CPU escolheu PEDRA.");
else
System.out.println("A CPU escolheu TESOURA.");
Note que incluí aquele truque de fazer a CPU esperar 1 segundo para dar sua
jogada (Thread.sleep) para dar aquele suspense.
O próximo passo é ver quem ganhou, o que devemos fazer logo abaixo do
código anterior, comparando as jogadas do jogador e da CPU segundo a
lógica deste jogo em questão.
Neste trecho de código temos a regra do jogo em si, represetada por lógica de
programação em Java. Testamos primeiro se houve empate (o que necessitará
que o jogo aconteça novamente!), depois as três possíveis situações do
jogador ganhar a partida, para, caso contrário (senão), cair no else que dá a
vitória para a máquina.
Simples, não?!
while( true ){
if(jogada1 == 1)
System.out.println("O jogador escolheu PAPEL.");
else if(jogada1 == 2)
System.out.println("O jogador escolheu PEDRA.");
else
System.out.println("O jogador escolheu TESOURA.");
if(jogada2 == 1)
System.out.println("A CPU escolheu PAPEL.");
else if(jogada2 == 2)
System.out.println("A CPU escolheu PEDRA.");
else
System.out.println("A CPU escolheu TESOURA.");
System.out.println("Processando o vencedor");
Thread.sleep(1000);//atrasa 1 segundo
if(jogada1 == jogada2){
System.out.println("Empatou!");
} else if((jogada1 == 1 && jogada2 == 2) ||
(jogada1 == 2 && jogada2 == 3) ||
(jogada1 == 3 && jogada2 == 1)){
System.out.println("Uhu, o jogador ganhou!");
break;
} else {
System.out.println("Se deu mal, a CPU ganhou!");
break;
}
}
Para explicar melhor aquele ifzão que fizemos ali, primeiro é necessário
entender que o uso dos parênteses serviu para "isolar" cada uma das
expressões AND (&&) que representam cada uma das possibilidades de
vitória pro jogador. As expressões são calculadas/resolvidas conforme os
parênteses mais internos para os mais externos, assim como em expressões
numéricas, que vemos na quarta série do ensino fundamental.
Em nosso terceiro e último jogo desta etapa do seu aprendizado de Java, vou
pegar pesado!
Vinte Um
Vamos fazer um jogo de cartas chamado Vinte Um?
Existem mais algumas regras, mas chego nelas quando for a hora.
Em cada uma dessas posições (0 a 51), vamos guardar uma carta, de 1 a 13,
sendo que teremos quatro cartas número 1, quatro cartas número 2, etc,
representando a variação de naipes, que aqui vamos ignorar pois não afeta o
jogo. O código abaixo ilustra a criação desse baralho virtual.
//criação do baralho
int [] baralho = new int [52];
for ( int naipe=0; naipe <= 3; naipe++){
for ( int numero=1; numero <= 13; numero++){
baralho[(naipe * 13) + numero - 1] = numero;
}
}
Note como encadeei dois laços for, um dentro do outro, para fazer o efeito de
gerar 4 cartas de cada um dos 13 números. Também usei uma expressão não
usual para determinar em qual posição do array cada uma das cartas deveria
estar, em ordem numérica, sem sobrepor nenhuma. A ideia é que as primeiras
treze cartas vão ocupar as posições de 0 à 12, na sequência, graças ao cálculo
que coloquei dentro do índice do array (os colchetes), o próximo jogo de 13
cartas vai ocupar da posição 13 à 25 e assim por diante.
Mas desse jeito elas vão ficar todos em ordem, facilitando o jogo, certo?
Sim, por isso nosso próximo passo, sempre que estivermos fazendo um jogo
de cartas, é embaralhar as mesmas.
Caso ache que esse laço não é o suficiente, você pode colocar um mais
externo, para repetir o embaralhamento mais de uma vez, assim como
fazemos em carteados tradicionais.
Uma vez que temos nosso baralho pronto, é hora de sacarmos uma carta por
vez, na ordem, enquanto o jogador quiser mais cartas, ao mesmo tempo em
que somamos cada carta retirada do baralho à pontuação dele.
do{
cartaAtual = baralho[proximaCarta++];
System.out.println("Você sacou um " + cartaAtual);
pontosJogador+= cartaAtual;
System.out.println("Sua pontuação atual é " + pontosJogador);
System.out.println("Digite 1 para sacar mais uma carta, ou qualquer outro
número para parar.");
maisCarta = teclado.nextInt() == 1;
}while(maisCarta);
Alguns pontos neste código são novidade e merecem a nossa atenção antes de
continuarmos.
Mas vamos avançar. O próximo passo é ver o que aconteceu após o do/while,
que termina quando o jogador "diz" que não quer sacar mais cartas. Nesse
ponto que aplicamos as regras do jogo, para saber se o jogador venceu ou
não.
Código 87: disponível em http://www.luiztools.com.br/livro-java-i-fontes
E por fim, o ponto mais curioso do código acima, se ele fez menos de 21
pontos, é a vez da ‘banca’ jogar. Banca é o nome que damos à ‘casa de jogos’
ou ao profissional que dá as cartas, contratado do cassino. O que quero dizer
aqui, é que o próximo passo é a CPU jogar, para ver se ela vencerá a partida
ou o jogador.
Basicamente a CPU vai sacar cartas com a seguinte lógica: ela quer fazer
mais pontos que o jogador, até um máximo de 21 pontos. Se a CPU fizer
mais de 21 pontos, ela perde automaticamente. Se ela fizer menos pontos que
o jogador, ela perde também. Se ela fizer mais pontos que o jogador, até um
máximo de 21, a vitória é da CPU.
Note que essa lógica é o que vai ditar o comportamento da CPU, a IA dela, e
não mais a aleatoriedade, como estávamos fazendo antes. Sendo assim,
vamos ao laço de inteligência artifical da CPU!
do {
System .out.println( "A CPU vai sacar uma carta!" );
Thread .sleep(1000);
cartaAtual = baralho[proximaCarta++];
System .out.println( "A CPU sacou um " + cartaAtual);
pontosCpu += cartaAtual;
System .out.println( "A pontuação da CPU é " + pontosCpu );
Aqui eu criei uma variável para armazenar a pontuação da CPU (afinal, não
podemos usar a variável antiga para não perder o comparativo), zerei
novamente a variável de cartaAtual e iniciei um novo do/while, que
representa a IA da CPU.
Após o aviso de que será sacado uma carta, e nossa espera intencional de 1s,
a CPU saca uma carta usando a mesma lógica do jogador, mas ao invés da
tradicional pergunta se ela deseja sacar mais uma carta, a intrépida CPU se
foca apenas em calcular se ainda pode ganhar ou não. Se ela já passou de 21
pontos, ela perdeu, encerramos com um break. Se ela já passou dos pontos do
jogador, ela ganhou a partida. Caso contrário, ela vai sacar novamente uma
carta, até ganhar ou perder.
Isso por si só conclui o jogo. Algumas variações de Vinte Um que você pode
encontrar pela Internet ou em livros colocam a possibilidade de empate, caso
o jogador e a CPU alcancem 21 pontos. Nesse caso quem tivesse tirado mais
cartas ganharia, mas preferi dar a vitória para o jogador para simplificar, pois
isso iria requerer mais duas variáveis para mantermos controle, uma para o
número de cartas sacada pelo jogador e outra para a CPU.
Outro ajuste que poderia ser feito, esse bem mais interessante, seria um
esquema de apostas. Aqui, teríamos de manter uma variável numérica com o
saldo do jogador e outra com o total de apostas da rodada, sendo que a
banca/CPU sempre cobriria a aposta do jogador. Ele poderia jogar indefinidas
partidas, enquanto tivesse saldo.
Ufa, é bastante trabalho, não é mesmo? Tenho certeza que agora que vimos
tudo isso, você tem plena capacidade para implementar essas modificações.
Caso queira uma ajuda, apenas mostrar como resolveu ou ainda uma solução
possível, entre em contato comigo através do meu blog luiztools.com.br ou
minha página no Facebook.com/luiztools.
//criação do baralho
int [] baralho = new int [52];
for ( int naipe=0; naipe <= 3; naipe++){
for ( int numero=1; numero <= 13; numero++){
baralho[(naipe * 13) + numero - 1] = numero;
}
}
//embaralhamento
for(int i=0; i < 52; i++){
int carta = baralho[i];
int novaPosicao = (int)(Math.random() * 52);
baralho[i] = baralho[novaPosicao];
baralho[novaPosicao] = carta;
}
do{
cartaAtual = baralho[proximaCarta++];
System.out.println("Você sacou um " + cartaAtual);
pontosJogador+= cartaAtual;
System.out.println("Sua pontuação atual é " + pontosJogador);
System.out.println("Digite 1 para sacar mais uma carta, ou qualquer outro
número para parar.");
maisCarta = teclado.nextInt() == 1;
}while(maisCarta);
if(pontosJogador == 21){
System.out.println("Na mosca, o jogador venceu!");
return;
}
else if(pontosJogador > 21){
System.out.println("Sinto muito, não foi dessa vez!");
return;
}
else
System.out.println("Você fez " + pontosJogador+ " pontos. Agora é a vez
da banca!");
int pontosCpu = 0;
cartaAtual = 0;
do{
System.out.println("A CPU vai sacar uma carta!");
Thread.sleep(1000);
cartaAtual = baralho[proximaCarta++];
System.out.println("A CPU sacou um " + cartaAtual);
pontosCpu += cartaAtual;
System.out.println("A pontuação da CPU é " + pontosCpu );
Pois é, certamente você está agora com uma vontade louca de aprender mais
e criar aplicações incríveis que resolvam problemas das empresas e de quebra
que o deixem cheio de dinheiro na conta bancária, não é mesmo?
Este livro é propositalmente pequeno, com pouco mais de 100 páginas. Como
professor, costumo dividir o aprendizado de alguma tecnologia (como Java)
em duas grandes etapas: aprender o básico e executar o que foi aprendido no
mercado, para alcançar os níveis intermediários e avançados. Acho que este
livro atende bem ao primeiro requisito, mas o segundo só depende de você.
De nada adianta saber muita teoria se você não aplicar ela. Então agora que
terminou de ler este livro, inicie hoje mesmo (não importa se for tarde) um
projeto de aplicação novo. Ou então adicione os elementos que foram
mencionados, mas não implementados, no jogo Vinte Um do último capítulo.
Caso tenha gostado do material, indique esse livro a um amigo que também
deseja aprender a desenvolver em Java. Não tenha medo da concorrência e
abrace a ideia de ter um sócio que possa lhe ajudar nos projetos.