Java while e do-while
Até agora, cada linha do seu programa rodava uma vez. Mas muita coisa no dia a dia é repetição: contar de 1 a 10, guardar dinheiro todo mês até bater uma meta, pedir uma senha até a pessoa acertar. Para isso existem os laços (ou loops). Nesta aula você conhece os dois mais simples: o while e o do-while.
- O que é um laço e como o
whiledecide se repete ou para. - Como montar um contador (o padrão mais comum de todos).
- Laços que dependem de uma condição, e não de uma contagem.
- O
do-while, que roda pelo menos uma vez, e quando ele é a escolha certa. - O que é um laço infinito, por que ele acontece e como pará-lo com Ctrl+C.
- Como usar o
do-whilepara pedir um valor até ele ser válido.
Todos os exemplos foram rodados no terminal da Ana, a usuária da máquina fictícia notebook, com o JDK 25, dentro da pasta ~/java. Para rodar, usamos o modo "arquivo-fonte" que você viu em javac e java: java Main.java compila e executa de uma vez.
A ideia antes do código
Imagine que a Ana está lavando a louça. Ninguém diz a ela "lave o prato 1, lave o prato 2, lave o prato 3…". A instrução é uma só:
Enquanto ainda houver louça suja na pia, lave mais uma peça.
Repare nas duas partes dessa frase:
- Uma pergunta que tem resposta sim ou não: "ainda há louça suja?". Em Java, é uma expressão booleana, que vale
trueoufalse. - Uma tarefa que se repete enquanto a resposta for "sim": "lave mais uma peça".
E tem um detalhe escondido, mas essencial: a tarefa muda a resposta da pergunta. Cada peça lavada deixa a pia mais vazia, até que um dia a resposta vira "não" e a Ana para. Se ela só olhasse para a pia sem lavar nada, ficaria ali para sempre. Guarde isso: é a causa número 1 dos laços infinitos, que você vai ver mais adiante.
while, em inglês, quer dizer exatamente enquanto.
O primeiro while: um contador
Vamos fazer o computador escrever "Volta 1" até "Volta 5". Sem laço, seriam cinco println iguais. Com laço:
public class Main {
public static void main(String[] args) {
int contador = 1;
while (contador <= 5) {
System.out.println("Volta " + contador);
contador++;
}
System.out.println("Fim! O contador parou em " + contador);
}
}
$ java Main.java Volta 1 Volta 2 Volta 3 Volta 4 Volta 5 Fim! O contador parou em 6
Vamos ver linha por linha:
| Linha | O que faz |
|---|---|
int contador = 1; | Cria a variável que vai contar as voltas e começa em 1. É o início. |
while (contador <= 5) | A condição: "enquanto o contador for menor ou igual a 5". Vem sempre entre parênteses. |
{ … } | O corpo do laço: as linhas que se repetem. |
System.out.println("Volta " + contador); | A tarefa: mostra o número da volta. |
contador++; | Soma 1 ao contador (é o mesmo que contador = contador + 1, veja Operadores de Atribuição). É o passo, a linha que "lava mais um prato". |
System.out.println("Fim! …"); | Está fora das chaves: roda uma vez só, depois que o laço termina. |
Anatomia do while
- while
- A palavra-chave: "enquanto".
- (contador <= 5)
- A condição, entre parênteses. Precisa dar
trueoufalse. É testada antes de cada volta. - {
- Abre o corpo do laço.
- …
- As instruções que se repetem. Uma delas precisa mudar algo da condição.
- }
- Fecha o corpo. Ao chegar aqui, o Java volta para a condição e testa de novo.
O que acontece, volta por volta
Programadores chamam cada repetição de iteração (do latim iterare, "repetir"). Acompanhe o valor do contador em cada teste da condição:
| Teste | contador vale | contador <= 5? | O que o Java faz |
|---|---|---|---|
| 1º | 1 | true | Mostra "Volta 1", contador vira 2 |
| 2º | 2 | true | Mostra "Volta 2", contador vira 3 |
| 3º | 3 | true | Mostra "Volta 3", contador vira 4 |
| 4º | 4 | true | Mostra "Volta 4", contador vira 5 |
| 5º | 5 | true | Mostra "Volta 5", contador vira 6 |
| 6º | 6 | false | Pula o corpo e continua depois da } |
Repare em dois fatos que confundem muita gente:
- A condição foi testada 6 vezes, mas o corpo rodou 5. O último teste é o que dá
falsee encerra o laço. - Depois do laço, o contador vale 6, e não 5. Foi justamente o 6 que fez a condição falhar. A saída "O contador parou em 6" confirma.
Início (int contador = 1;), condição (contador <= 5) e passo (contador++). Esqueceu uma, o laço quebra: sem início, nem compila; sem passo, nunca termina. O for, da próxima aula, junta essas três peças numa linha só.
Contando para baixo
O passo não precisa ser ++. Para uma contagem regressiva, comece alto, teste com >= e diminua com --:
public class Main {
public static void main(String[] args) {
int contador = 10;
while (contador >= 1) {
System.out.println(contador);
contador--;
}
System.out.println("Feliz Ano Novo!");
}
}
$ java Main.java 10 9 8 7 6 5 4 3 2 1 Feliz Ano Novo!
Missão: a Ana vai largar uma corrida. Mude o programa para contar de 5 até 1 e depois mostrar Já!. Use o while. Dica: são três mudanças (início, condição e passo) e a última mensagem.
public class Main {
public static void main(String[] args) {
int contador = 1;
while (contador <= 5) {
System.out.println(contador);
contador++;
}
System.out.println("Fim");
}
}
public class Main {
public static void main(String[] args) {
int contador = 5;
while (contador >= 1) {
System.out.println(contador);
contador--;
}
System.out.println("Já!");
}
}
Laços que não são contagem
O while brilha quando você não sabe de antemão quantas voltas serão. A Ana quer juntar R$ 1000 guardando R$ 150 por mês. Quantos meses? Em vez de fazer a conta de cabeça, deixe o laço descobrir:
public class Main {
public static void main(String[] args) {
double saldo = 0;
int meses = 0;
while (saldo < 1000) {
saldo = saldo + 150;
meses++;
System.out.println("Mês " + meses + ": R$ " + saldo);
}
System.out.println("A meta de R$ 1000 foi atingida em " + meses + " meses.");
}
}
$ java Main.java Mês 1: R$ 150.0 Mês 2: R$ 300.0 Mês 3: R$ 450.0 Mês 4: R$ 600.0 Mês 5: R$ 750.0 Mês 6: R$ 900.0 Mês 7: R$ 1050.0 « 1050 não é menor que 1000: o laço para aqui A meta de R$ 1000 foi atingida em 7 meses.
A condição agora é sobre o saldo, e não sobre um contador. A variável meses só conta quantas voltas foram dadas, para mostrar no final. (O .0 aparece porque saldo é um double, veja double e float.)
Outro exemplo: qual é a primeira potência de 2 que passa de 100? Comece em 1 e dobre enquanto for pequeno:
public class Main {
public static void main(String[] args) {
int numero = 1;
while (numero < 100) {
numero = numero * 2;
}
System.out.println("Primeira potência de 2 maior ou igual a 100: " + numero);
}
}
$ java Main.java Primeira potência de 2 maior ou igual a 100: 128
O número passou por 1, 2, 4, 8, 16, 32, 64 e 128. Quando chegou a 128, a condição 128 < 100 deu false e o laço parou.
Um while pode rodar zero vezes
Como a condição é testada antes da primeira volta, se ela já começar falsa, o corpo nunca roda. A loja da Ana tem um laço que vende uma unidade de cada vez enquanto houver estoque. E se o estoque já for zero?
public class Main {
public static void main(String[] args) {
int estoque = 0;
while (estoque > 0) {
System.out.println("Vendendo uma unidade...");
estoque--;
}
System.out.println("Estoque final: " + estoque);
}
}
$ java Main.java Estoque final: 0 « o corpo do while nem chegou a rodar
Esse é o comportamento certo: não dá para vender o que não existe. Guarde este exemplo, porque o do-while vai se comportar de outro jeito.
O do-while: primeiro faz, depois pergunta
O do-while (do = "faça") é o while de cabeça para baixo: o corpo vem primeiro e a condição vem no fim. Por isso ele roda pelo menos uma vez, mesmo que a condição seja falsa desde o começo.
Uma comparação do dia a dia: provar a comida. Você prova primeiro e só depois decide se precisa de mais sal. Não dá para decidir antes de provar.
- do
- "Faça": marca o início do corpo.
- { … }
- O corpo, que roda uma vez sem nenhum teste.
- while
- Depois da primeira volta, a pergunta "repito?".
- (condição)
- Se der
true, volta para odo; se derfalse, segue em frente. - ;
- O ponto e vírgula obrigatório no fim. O
whilecomum não tem; odo-whiletem.
Veja a mesma loja, com estoque zero, agora com do-while:
public class Main {
public static void main(String[] args) {
int estoque = 0;
do {
System.out.println("Vendendo uma unidade...");
estoque--;
} while (estoque > 0);
System.out.println("Estoque final: " + estoque);
}
}
$ java Main.java Vendendo uma unidade... « rodou uma vez antes de testar a condição Estoque final: -1 « vendeu o que não tinha!
A condição estoque > 0 era falsa desde o início, mas o corpo rodou uma vez antes de ela ser testada, e o estoque ficou negativo. Aqui o do-while foi a escolha errada. Ele é a escolha certa quando a primeira volta precisa acontecer, como ao pedir um dado ao usuário: você precisa perguntar uma vez antes de saber se a resposta é válida. É o que vem a seguir.
while | do-while | |
|---|---|---|
| Quando testa a condição | Antes de cada volta | Depois de cada volta |
| Mínimo de voltas | 0 | 1 |
| Ponto e vírgula no fim | Não | Sim: } while (…); |
| Com a condição falsa desde o início | Não roda nada | Roda uma vez |
| Uso típico | Repetir enquanto algo for verdade (que pode já ser falso) | Fazer algo e repetir se precisar (menus, pedir dados) |
Validar a entrada com do-while
A Ana está fazendo um programa para lançar as notas da turma. A nota precisa estar entre 0 e 10. Se o professor digitar 15 por engano, o programa deve reclamar e pedir de novo, quantas vezes for preciso.
Para ler o que a pessoa digita, usamos o Scanner, que tem uma aula só para ele: Ler do Teclado (Scanner). Por enquanto, basta saber que teclado.nextInt() espera a pessoa digitar um número inteiro e apertar Enter.
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner teclado = new Scanner(System.in);
int nota;
do {
System.out.print("Digite uma nota de 0 a 10: ");
nota = teclado.nextInt();
if (nota < 0 || nota > 10) {
System.out.println("Nota inválida! Tente de novo.");
}
} while (nota < 0 || nota > 10);
System.out.println("Nota registrada: " + nota);
}
}
| Linha | O que faz |
|---|---|
import java.util.Scanner; | Avisa que vamos usar a classe Scanner (veja Scanner). |
Scanner teclado = new Scanner(System.in); | Cria o "leitor do teclado". |
int nota; | Declara a variável fora do laço, porque ela será usada na condição e depois do laço. |
do { … } | Pergunta a nota. Sempre roda pelo menos uma vez: é o que queremos. |
if (nota < 0 || nota > 10) | Se a nota for inválida, avisa (veja if e o "ou" || em Operadores Lógicos). |
while (nota < 0 || nota > 10); | Repete enquanto a nota for inválida. Uma nota válida faz a condição dar false e o laço termina. |
Rodando no terminal, o professor digitou 15, depois -2 e por fim 8:
$ java Main.java Digite uma nota de 0 a 10: 15 Nota inválida! Tente de novo. Digite uma nota de 0 a 10: -2 Nota inválida! Tente de novo. Digite uma nota de 0 a 10: 8 Nota registrada: 8
while comum?
Daria, mas você teria que pedir a nota duas vezes no código: uma antes do laço (para a condição ter o que testar) e outra dentro dele. Com o do-while, o pedido aparece uma vez só. "Pergunte primeiro, verifique depois" é o caso clássico do do-while.
A mesma entrada, sem digitar
Você pode mandar as respostas prontas para o programa com o printf do terminal e um | (barra vertical, o "cano" que liga a saída de um comando à entrada do outro). Cada \n é um Enter:
$ printf '15\n-2\n8\n' | java Main.java Digite uma nota de 0 a 10: Nota inválida! Tente de novo. Digite uma nota de 0 a 10: Nota inválida! Tente de novo. Digite uma nota de 0 a 10: Nota registrada: 8
O programa é o mesmo; a diferença está na tela. Quando você digita, o terminal mostra as teclas e o Enter pula a linha. Quando a entrada vem pelo |, ninguém digita nada, então os números não aparecem e as mensagens ficam emendadas. É assim também no editor do guia quando a caixa já traz a entrada pronta.
Laço infinito: quando a condição nunca fica falsa
Um laço infinito é um laço cuja condição nunca vira false. O programa fica preso dando voltas para sempre. Quase sempre é um engano, e o mais comum é esquecer o passo.
Ele compila e roda, mas nunca termina. Se testar no seu computador, esteja pronto para apertar Ctrl+C.
public class Main {
public static void main(String[] args) {
int contador = 1;
while (contador <= 5) {
System.out.println("Volta " + contador);
}
}
}
Falta o contador++. O contador fica em 1 para sempre, 1 <= 5 é sempre true, e o programa escreve "Volta 1" sem parar. No terminal da Ana, em dois segundos ele escreveu mais de meio milhão de linhas, até ela apertar Ctrl+C. Este é o fim da sessão:
$ java Main.java Volta 1 Volta 1 Volta 1 « … mais de 500 mil linhas iguais … ^CVolta 1 « a Ana apertou Ctrl+C; o terminal mostra ^C Volta 1 « algumas linhas que já estavam a caminho Volta 1 Volta 1 Volta 1 $ echo $? 130 « 130 = programa interrompido pelo Ctrl+C
Como parar um programa preso
- Clique na janela do terminal onde o programa está rodando.
- Aperte Ctrl+C (segure Ctrl e toque em C). O terminal mostra
^Ce o programa é encerrado. - O prompt
ana@notebook:~/java$volta. O código de saída, que você vê comecho $?, é 130: o jeito do Linux de dizer "interrompido pelo usuário". - Conserte o laço (quase sempre falta o passo, ou ele vai na direção errada) e rode de novo.
Nas IDEs (veja Editores e IDEs), o mesmo papel é do botão de parar, um quadrado vermelho perto do botão de rodar.
O laço infinito silencioso
Nem todo laço infinito enche a tela. Este aqui quer somar 1 + 2 + 3 + 4 + 5, mas também esqueceu o passo e não escreve nada dentro do laço:
public class Main {
public static void main(String[] args) {
int contador = 1;
int soma = 0;
while (contador <= 5) {
soma = soma + contador;
}
System.out.println("Soma: " + soma);
}
}
$ java Main.java ^C « nada aconteceu por 4 segundos; a Ana apertou Ctrl+C $ echo $? 130
O programa parece "travado": nenhuma mensagem, nenhum erro, o cursor parado. Na verdade ele está trabalhando sem parar, somando 1 na variável soma milhões de vezes por segundo. Quando um programa "não faz nada e não termina", desconfie de um laço infinito.
while (true)
Às vezes um laço infinito é desejado: um servidor que atende pedidos o dia todo, um menu que só termina quando a pessoa escolhe "Sair". Nesses casos se escreve while (true) e, dentro dele, um break que encerra o laço na hora certa. O break tem a sua própria aula: break e continue.
Missão: este programa deveria mostrar Soma: 15 (1 + 2 + 3 + 4 + 5), mas está preso num laço infinito. Encontre a linha que falta e conserte. Leia o código com calma e conserte antes de clicar em Rodar.
public class Main {
public static void main(String[] args) {
int contador = 1;
int soma = 0;
while (contador <= 5) {
soma = soma + contador;
}
System.out.println("Soma: " + soma);
}
}
public class Main {
public static void main(String[] args) {
int contador = 1;
int soma = 0;
while (contador <= 5) {
soma = soma + contador;
contador++;
}
System.out.println("Soma: " + soma);
}
}
Formas diferentes de repetir
Para a mesma tarefa (mostrar 1, 2 e 3) há várias formas. Elas dão a mesma saída; o que muda é a clareza.
| Forma | Código | Quando usar |
|---|---|---|
while com contador | int i = 1; while (i <= 3) { System.out.println(i); i++; } | Funciona, mas para contar o for é mais claro. |
do-while | int i = 1; do { System.out.println(i); i++; } while (i <= 3); | Quando a primeira volta tem que acontecer sempre. |
for | for (int i = 1; i <= 3; i++) { System.out.println(i); } | Contagens: você sabe quantas voltas serão. Veja for. |
while com condição | while (saldo < 1000) { … } | Quando o número de voltas depende do que acontece dentro do laço. |
Regra prática: sabe quantas vezes? Use for. Repete até algo acontecer? Use while. Precisa fazer uma vez antes de decidir? Use do-while.
Quando dá errado
public class Main {
public static void main(String[] args) {
int contador;
while (contador <= 5) {
System.out.println(contador);
contador++;
}
}
}
$ java Main.java
Main.java:4: error: variable contador might not have been initialized
while (contador <= 5) {
^
1 error
error: compilation failed
O que quer dizer: "a variável contador pode não ter sido inicializada". Você criou a variável, mas não deu um valor inicial a ela. Na primeira volta, o Java não saberia comparar "nada" com 5, então ele se recusa a compilar. O ^ aponta para a variável problemática.
Como consertar: dê o valor inicial na declaração: int contador = 1;.
do-while
public class Main {
public static void main(String[] args) {
int contador = 1;
do {
System.out.println(contador);
contador++;
} while (contador <= 3)
}
}
$ java Main.java
Main.java:7: error: ';' expected
} while (contador <= 3)
^
1 error
error: compilation failed
O que quer dizer: "esperava um ponto e vírgula". O do-while termina com ; depois do parêntese, e ele faltou. O ^ aponta exatamente onde o ; deveria estar.
Como consertar: } while (contador <= 3);.
; depois do while
public class Main {
public static void main(String[] args) {
int contador = 1;
while (contador <= 5); {
System.out.println("Volta " + contador);
contador++;
}
System.out.println("Fim!");
}
}
$ java Main.java ^C « nenhuma "Volta"; a Ana teve que apertar Ctrl+C $ echo $? 130
O que aconteceu: o ; logo depois de while (contador <= 5) é um "corpo vazio". Para o Java, o laço é while (contador <= 5) ;, que repete nada para sempre. As chaves que vêm depois viram um bloco comum, que nunca é alcançado. O javac não reclama, porque é Java válido.
Como consertar: apague o ; entre o ) e a {. Só o do-while leva ;, e no fim.
while (false)
public class Main {
public static void main(String[] args) {
int contador = 1;
while (false) {
System.out.println(contador);
}
}
}
$ java Main.java
Main.java:4: error: unreachable statement
while (false) {
^
1 error
error: compilation failed
O que quer dizer: "instrução inalcançável". Se a condição é literalmente false, o corpo nunca poderia rodar, e o javac considera isso um engano. Com uma variável (while (estoque > 0)) não há erro, porque o valor só é conhecido quando o programa roda.
Você queria 5 voltas e saíram 4 (ou 6). É o erro mais comum com laços e tem até nome: erro de "um a mais" (off-by-one). Confira três coisas: o valor inicial (0 ou 1?), o operador (< ou <=?) e o limite. int i = 1; while (i < 5) dá 4 voltas (1 a 4); int i = 1; while (i <= 5) dá 5. Na dúvida, monte a tabela "volta por volta" como fizemos lá em cima.
Referência
| Forma | O que faz | Mínimo de voltas |
|---|---|---|
while (condição) { … } | Testa e, se true, roda o corpo; repete | 0 |
do { … } while (condição); | Roda o corpo e, se a condição der true, repete | 1 |
while (true) { … break; … } | Laço infinito de propósito, encerrado por break | até o break |
contador++ / contador-- | O passo: soma ou subtrai 1 | |
| Ctrl+C | Interrompe um programa no terminal (código de saída 130) |
Exercícios
1. Complete a condição
Complete para o laço mostrar os números de 1 a 10:
2. O passo que falta
Complete o passo da contagem regressiva de 3 a 1:
3. Fechando o do-while
Complete o fim do do-while que repete enquanto senha for diferente de 1234:
4. Quantas vezes "Oi" aparece? int x = 10; while (x < 5) { System.out.println("Oi"); x++; }
5. E agora? int x = 10; do { System.out.println("Oi"); x++; } while (x < 5);
6. Depois de int i = 0; while (i < 3) { i++; }, quanto vale i?
7. Seu programa está rodando há um minuto sem mostrar nada nem terminar. O que fazer?
Missão final: faça o programa pedir a idade com Sua idade: e repetir a pergunta enquanto ela for menor que 1 ou maior que 120. No fim, mostre Idade registrada: seguido da idade. Use um do-while. A entrada de teste é 0, depois 200, depois 29.
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner teclado = new Scanner(System.in);
int idade;
// escreva o do-while aqui
}
}
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner teclado = new Scanner(System.in);
int idade;
do {
System.out.print("Sua idade: ");
idade = teclado.nextInt();
} while (idade < 1 || idade > 120);
System.out.println("Idade registrada: " + idade);
}
}
Resumo
- Um laço repete um bloco de código. Cada repetição é uma iteração.
while (condição) { … }testa a condição antes de cada volta; pode rodar 0 vezes.do { … } while (condição);testa depois; roda pelo menos 1 vez e termina com;.- Um contador tem três peças: início, condição e passo. Depois do laço, o contador vale o primeiro valor que fez a condição falhar.
- Se nada dentro do laço muda a condição, ele é infinito. Pare com Ctrl+C (código 130) e procure o passo que falta.
- Cuidado com o
;logo depois dewhile (…): vira um laço vazio e infinito. - Para pedir um dado até ele ser válido, use
do-while: pergunte primeiro, verifique depois.