Els bucles de la lliçó 02-02 són obedients: comencen, fan totes les voltes previstes i acaben. Però la feina real gairebé mai no és tan disciplinada. Quan busques el primer préstec amb multa màxima, no té sentit continuar revisant els mil següents un cop l'has trobat. Quan recorres devolucions pendents i topes amb una de ja pagada, la vols saltar sense executar la resta del cos. Per a aquestes dues necessitats —sortir abans d'hora i saltar una iteració— Java ofereix break i continue. Són eines esmolades: ben fetes servir fan el codi més ràpid i més clar; mal fetes servir converteixen un bucle en un laberint. Aquesta lliçó cobreix totes dues, la seva interacció amb el switch (que amaga el parany més clàssic del llenguatge), les etiquetes per trencar bucles imbricats i les alternatives més llegibles a les etiquetes.
Contingut
break: abandonar el buclecontinue: saltar a la iteració següent- Tots dos sobre el mateix bucle: diagrama comparat
- El parany clàssic:
breakdins d'unswitchdins d'un bucle - Etiquetes:
break etiquetaicontinue etiqueta - Alternatives més llegibles a les etiquetes
returndins d'un bucle almain- Taula comparativa:
break,continue,returni bandera - Cas complet: recórrer les devolucions pendents de BiblioTech
- Errors Habituals i Consells
- Exercicis
break: abandonar el bucle
break: abandonar el buclebreak acaba immediatament el bucle més intern que el conté. L'execució continua a la primera sentència posterior al bucle; ni s'executa la resta del cos, ni s'avalua la condició, ni es fa l'actualització.
final double TARIFA_DIARIA = 0.25;
final double MULTA_MAXIMA = 20.0;
for (int dies = 1; dies <= 200; dies++) {
double multa = dies * TARIFA_DIARIA;
if (multa >= MULTA_MAXIMA) {
System.out.printf("El sostre s'assoleix als %d dies de retard.%n", dies);
break; // no te sentit continuar fins a 200
}
}
System.out.println("Cerca acabada.");Sortida:
Sense el break, el bucle faria 200 voltes i trobaria el sostre 121 vegades. Amb ell, s'atura a la 80. Aquest és el patró de cerca amb sortida primerenca, i és la raó de ser principal de break: tan bon punt tens la resposta, deixar de treballar.
Compara-ho amb l'alternativa de la lliçó 02-02, la bandera booleana:
// Amb bandera: continua iterant encara que ja hagi trobat la resposta
boolean trobat = false;
int diesSostre = 0;
for (int dies = 1; dies <= 200; dies++) {
if (!trobat && dies * TARIFA_DIARIA >= MULTA_MAXIMA) {
diesSostre = dies;
trobat = true;
}
}Funciona, però fa 200 iteracions i afegeix una condició extra a cadascuna. Quan la sortida primerenca és correcta, break és més clar i més eficient.
continue: saltar a la iteració següent
continue: saltar a la iteració següentcontinue no surt del bucle: abandona la iteració actual i salta al cicle següent. I aquí hi ha un matís que cal tenir molt present:
- En un
for,continuesalta a l'actualització (i++) i després a la condició. - En un
whileodo-while, salta directament a la condició. Si l'actualització era al cos, després delcontinue, no s'executa: bucle infinit garantit.
// Recorrer els primers 10 dies i informar nomes dels que generen multa "rodona"
final double TARIFA_DIARIA = 0.25;
for (int dies = 1; dies <= 10; dies++) {
double multa = dies * TARIFA_DIARIA;
if (multa != Math.floor(multa)) {
continue; // no es un import enter: es descarta
}
System.out.printf("%2d dies -> %.2f EUR (import enter)%n", dies, multa);
}Sortida:
I el perill amb while:
// BUCLE INFINIT: el continue salta l'i++
int i = 0;
while (i < 10) {
if (i == 3) {
continue; // torna a la condicio amb i valent 3 per sempre
}
System.out.println(i);
i++;
}La correcció és actualitzar abans del continue, o —millor— fer servir un for, on l'actualització és a la capçalera i no se salta mai.
continue és el patró de filtratge: descartar els elements que no interessen i quedar-se amb el camí principal sense sagnat extra. De fet és la versió "dins del bucle" de la clàusula de guarda que vas veure a la lliçó 02-01:
// Amb if imbricat: la feina real queda sagnada
for (int n = 1; n <= 10; n++) {
if (esValid(n)) {
// ... vint linies de feina aqui dins
}
}
// Amb continue: la feina real queda plana
for (int n = 1; n <= 10; n++) {
if (!esValid(n)) {
continue;
}
// ... vint linies de feina, sense sagnat extra
}
- Tots dos sobre el mateix bucle: diagrama comparat
La diferència essencial en un sol dibuix. El bucle és el mateix; només canvia on salta la fletxa:
flowchart TD
INI["Inicialitzacio"] --> COND{"Condicio del bucle"}
COND -- "false" --> FIN["Sentencia posterior al bucle"]
COND -- "true" --> A["Primera part del cos"]
A --> Q{"Comprovacio interna"}
Q -- "continue" --> UPD["Actualitzacio"]
Q -- "break" --> FIN
Q -- "continua" --> B["Resta del cos"]
B --> UPD
UPD --> COND
Llegeix-ho així:
continuetalla cap a l'actualització: el bucle viu, la iteració mor.breaktalla cap a fora del bucle: el bucle mor, i amb ell totes les iteracions que faltaven.
- El parany clàssic:
break dins d'un switch dins d'un bucle
break dins d'un switch dins d'un bucleAquest és, sense exagerar, un dels tres o quatre paranys més famosos de Java, i produeix errors que costen tardes senceres. La regla és simple i despietada: break afecta el bloc més intern que el conté, i un switch compta com a bloc intern.
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.print("Opcio (1=consultar, 0=sortir): ");
String opcio = scanner.nextLine().trim();
switch (opcio) {
case "1":
System.out.println("Consultant cataleg...");
break; // surt del switch. Correcte.
case "0":
System.out.println("Sortint...");
break; // surt del SWITCH, no del WHILE!
default:
System.out.println("Opcio no valida.");
}
}El programador va escriure break al case "0" creient que acabava el programa. El que fa és sortir del switch i tornar al principi del while (true): el menú és impossible d'abandonar. L'usuari tria 0, veu "Sortint…" i el menú reapareix. Per sempre.
Hi ha tres maneres correctes de resoldre-ho:
Solució 1: bandera booleana (la més llegible, i la que fa servir BiblioTech).
boolean sortir = false;
while (!sortir) {
String opcio = scanner.nextLine().trim();
switch (opcio) {
case "1":
System.out.println("Consultant cataleg...");
break;
case "0":
System.out.println("Sortint...");
sortir = true; // la condicio del while se n'encarrega
break;
default:
System.out.println("Opcio no valida.");
}
}Solució 2: switch amb fletxes, on res no convida a escriure break perquè les branques ja no el necessiten per separar-se. Convé ser precís, perquè és un matís que gairebé ningú no explica bé: si t'hi obstines i escrius case "0" -> { System.out.println("Sortint..."); break; }, això compila i cau exactament en el mateix parany (surt del switch, no del bucle). El que no compila és case "0" -> break; a seques, ni un break dins d'un switch d'expressió. La sintaxi de fletxes no et protegeix del parany: simplement fa que no se t'acudeixi caure-hi.
boolean sortir = false;
while (!sortir) {
String opcio = scanner.nextLine().trim();
switch (opcio) {
case "1" -> System.out.println("Consultant cataleg...");
case "0" -> {
System.out.println("Sortint...");
sortir = true;
}
default -> System.out.println("Opcio no valida.");
}
}Solució 3: etiqueta al bucle, que és el tema de l'apartat següent.
bucleMenu:
while (true) {
String opcio = scanner.nextLine().trim();
switch (opcio) {
case "1":
System.out.println("Consultant cataleg...");
break;
case "0":
System.out.println("Sortint...");
break bucleMenu; // trenca el BUCLE, no el switch
default:
System.out.println("Opcio no valida.");
}
}Un apunt sobre continue: continue no té cap significat dins d'un switch. Si l'hi escrius, salta la iteració del bucle que envolta el switch, que sol ser el que vols; no hi ha ambigüitat perquè un switch no és un bucle. El parany és exclusiu de break.
- Etiquetes:
break etiqueta i continue etiqueta
break etiqueta i continue etiquetaUna etiqueta és un identificador seguit de dos punts, col·locat just abans d'un bucle. Permet que break i continue actuïn sobre un bucle extern, no només sobre el més intern.
nomEtiqueta:
for (...) {
for (...) {
break nomEtiqueta; // surt del bucle EXTERN
continue nomEtiqueta; // passa a la iteracio seguent de l'EXTERN
}
}Sense etiqueta, break en un bucle imbricat només abandona l'intern i l'extern continua com si res. Exemple realista de BiblioTech: cercar, entre diversos empleats i els seus llibres, la primera devolució que ha assolit la multa màxima, i aturar tota la cerca allà.
package com.nexussoftware.bibliotech;
public class BiblioTechApp {
public static void main(String[] args) {
final double TARIFA_DIARIA = 0.25;
final double MULTA_MAXIMA = 20.0;
String empleatTrobat = "-";
int llibreTrobat = 0;
int diesTrobats = 0;
// Registre simulat: tres empleats, quatre llibres cadascun.
// Els dies de retard es generen amb una formula per poder repetir
// la prova sempre igual (encara no podem desar una llista real).
cerca:
for (int empleat = 1; empleat <= 3; empleat++) {
for (int llibre = 1; llibre <= 4; llibre++) {
int diesRetard = (empleat * 29 + llibre * 17) % 130;
double multa = diesRetard * TARIFA_DIARIA;
System.out.printf("Empleat %d, llibre %d: %3d dies -> %6.2f EUR%n",
empleat, llibre, diesRetard, multa);
if (multa >= MULTA_MAXIMA) {
empleatTrobat = "Empleat " + empleat;
llibreTrobat = llibre;
diesTrobats = diesRetard;
break cerca; // abandona ELS DOS bucles
}
}
}
System.out.println();
if (diesTrobats > 0) {
System.out.printf("Primera multa amb sostre: %s, llibre %d (%d dies).%n",
empleatTrobat, llibreTrobat, diesTrobats);
} else {
System.out.println("Cap llibre no ha assolit la multa maxima.");
}
}
}Sortida:
Empleat 1, llibre 1: 46 dies -> 11,50 EUR Empleat 1, llibre 2: 63 dies -> 15,75 EUR Empleat 1, llibre 3: 80 dies -> 20,00 EUR Primera multa amb sostre: Empleat 1, llibre 3 (80 dies).
Si en lloc de break cerca; haguessis escrit break;, el bucle intern s'hauria abandonat, però l'extern hauria passat a l'empleat 2 i la cerca hauria continuat, trobant més resultats dels que volies.
continue etiqueta és menys freqüent, però té el seu forat: "aquesta fila ja no m'interessa, passa a la següent".
// Si un empleat te algun llibre amb retard greu, saltem al seguent
// empleat sense revisar els seus llibres restants.
perEmpleat:
for (int empleat = 1; empleat <= 3; empleat++) {
System.out.println("Revisant empleat " + empleat);
for (int llibre = 1; llibre <= 4; llibre++) {
int diesRetard = (empleat * 29 + llibre * 17) % 130;
if (diesRetard > 60) {
System.out.println(" Retard greu detectat. Es marca l'empleat.");
continue perEmpleat; // salta al seguent empleat
}
System.out.println(" Llibre " + llibre + " correcte (" + diesRetard + " dies)");
}
System.out.println(" Empleat sense incidencies.");
}L'advertència obligatòria. Les etiquetes són legals i de vegades són la solució més directa, però abusar-ne és una olor de disseny (code smell). Un mètode amb tres etiquetes i salts creuats és tan difícil de seguir com el goto que els llenguatges estructurats van voler eliminar. Regla pràctica de l'ofici: si necessites una etiqueta, primer pregunta't si el bucle intern no hauria de ser un mètode a part. Gairebé sempre la resposta és que sí.
- Alternatives més llegibles a les etiquetes
Alternativa 1: bandera booleana a la condició del bucle extern.
boolean trobat = false;
for (int empleat = 1; empleat <= 3 && !trobat; empleat++) {
for (int llibre = 1; llibre <= 4 && !trobat; llibre++) {
if (/* condicio d'aturada */) {
trobat = true;
}
}
}Avantatge: no hi ha salts; la condició de sortida és a la vista a la capçalera. Inconvenient: la bandera apareix en tres llocs i cal recordar d'actualitzar-la. Per a dos nivells és perfectament llegible; per a tres, ja no.
Alternativa 2: extreure el bucle intern a un mètode. Quan el bucle intern viu al seu propi mètode, un simple return l'abandona del tot i el bucle extern decideix què fer amb el resultat. És la solució preferida en codi professional, perquè a més posa nom al que fa aquest bucle. Encara no la pots aplicar —els mètodes s'estudien a la lliçó 03-03—, però tingues-la present: quan hi arribis, gran part de les teves etiquetes desapareixeran.
Alternativa 3: replantejar el bucle. Moltes cerques imbricades es poden expressar com un sol bucle sobre un índex combinat, o se simplifiquen radicalment quan disposes de col·leccions (mòdul 5) i, més endavant, de l'API de streams (lliçó 10-04), on "troba el primer que compleixi X" és una única línia.
return dins d'un bucle al main
return dins d'un bucle al mainreturn acaba el mètode en què apareix. Com que en aquest mòdul tot el teu codi viu al main, un return dins d'un bucle acaba el programa, saltant-se la resta de tot.
for (int dies = 1; dies <= 200; dies++) {
if (dies * TARIFA_DIARIA >= MULTA_MAXIMA) {
System.out.println("Sostre assolit als " + dies + " dies.");
return; // acaba main: el programa s'acaba aqui
}
}
System.out.println("Aquesta linia NO s'executa si s'ha fet return.");Compte amb una conseqüència poc òbvia: si tens recursos oberts (com el Scanner), un return prematur se salta el scanner.close(). En un programa de consola petit no és greu —la JVM allibera tot en acabar—, però és un mal hàbit que al mòdul 6 corregiràs definitivament amb try-with-resources.
Dins del main, return i break d'un bucle exterior semblen equivalents, però no ho són: break deixa que el programa continuï amb el que vingui després del bucle (el resum, el tancament del Scanner, el comiat); return ho mata tot. Fes servir break per sortir d'un bucle i reserva return per acabar el programa de debò.
- Taula comparativa:
break, continue, return i bandera
break, continue, return i bandera| Mecanisme | Què abandona | S'executa després | Ús típic | Risc |
|---|---|---|---|---|
break |
El bucle (o switch) més intern |
La sentència següent al bucle | Cerca amb sortida primerenca | Confondre el switch amb el bucle |
break etiqueta |
El bucle etiquetat, amb tot el que hi ha imbricat | La sentència següent a aquest bucle | Cerca en bucles imbricats | Il·legible si se n'abusa |
continue |
La iteració actual | L'actualització i la condició | Filtrar i descartar elements | Bucle infinit en while |
continue etiqueta |
La iteració actual del bucle etiquetat | L'actualització del bucle etiquetat | Descartar una "fila" sencera | Molt poc freqüent; sorprèn el lector |
return |
El mètode complet (aquí, main) |
Res més del mètode | Acabar el programa | Se salta el codi de tancament |
| Bandera booleana | Res: el bucle acaba per la seva condició | La resta del cos (llevat que es protegeixi) | Menús, sortides controlades | Verbosa; cal actualitzar-la sempre |
- Cas complet: recórrer les devolucions pendents de BiblioTech
Reunim tot en l'exemple que dona sentit a la lliçó. Nexus Software vol revisar la cua de devolucions pendents amb dues regles:
- Les devolucions ja pagades s'ignoren (no sumen als totals).
- Tan bon punt aparegui una devolució amb multa màxima, s'atura la revisió i s'escala el cas a Recursos Humans.
Com que encara no pots emmagatzemar una llista, el registre es simula amb dues fórmules deterministes, de manera que la sortida sigui sempre la mateixa i la puguis comprovar. Aquest és el contingut equivalent del registre simulat:
| Núm. | diesRetard = (n * 37) % 130 |
pagada = n % 2 == 0 |
Multa (amb sostre 20,00 €) |
|---|---|---|---|
| 1 | 37 | no | 9,25 € |
| 2 | 74 | sí | — (pagada) |
| 3 | 111 | no | 20,00 € (amb sostre) |
| 4 | 18 | sí | — (pagada) |
| 5 | 55 | no | 13,75 € |
| 6 | 92 | sí | — (pagada) |
| 7 | 129 | no | 20,00 € (amb sostre) |
| 8 | 36 | sí | — (pagada) |
package com.nexussoftware.bibliotech;
public class BiblioTechApp {
public static void main(String[] args) {
final String NOM_EMPRESA = "Nexus Software";
final double TARIFA_DIARIA = 0.25;
final double MULTA_MAXIMA = 20.0;
final int LLINDAR_LLEU = 7;
final int TOTAL_PENDENTS = 8;
// Acumuladors de la revisio
int revisades = 0;
int omesesPerPagament = 0;
double totalPendent = 0.0;
boolean escalat = false;
int devolucioEscalada = 0;
System.out.println("=== REVISIO DE DEVOLUCIONS PENDENTS ===");
System.out.println("Empresa: " + NOM_EMPRESA);
System.out.println();
for (int n = 1; n <= TOTAL_PENDENTS; n++) {
// --- Dades simulades del registre ---
int diesRetard = (n * 37) % 130;
boolean pagada = (n % 2 == 0);
// --- CONTINUE: filtrar el que no interessa ---
if (pagada) {
omesesPerPagament++;
System.out.printf(" #%d ... ja pagada, s'omet%n", n);
continue; // salta al seguent n, sense tocar els totals
}
// A partir d'aqui sabem que la devolucio esta pendent.
double multa = diesRetard * TARIFA_DIARIA;
boolean ambSostre = multa >= MULTA_MAXIMA;
if (ambSostre) {
multa = MULTA_MAXIMA;
}
String gravetat;
if (diesRetard == 0) {
gravetat = "SENSE RETARD";
} else if (diesRetard <= LLINDAR_LLEU) {
gravetat = "LLEU";
} else {
gravetat = "GREU";
}
revisades++;
totalPendent += multa;
System.out.printf(" #%d %3d dies %-13s %6.2f EUR%s%n",
n, diesRetard, gravetat, multa,
ambSostre ? " <-- SOSTRE" : "");
// --- BREAK: aturar tan bon punt es compleix la condicio d'escalat ---
if (ambSostre) {
escalat = true;
devolucioEscalada = n;
System.out.println(" Multa maxima assolida: s'atura la revisio.");
break; // abandona el bucle del tot
}
}
System.out.println();
System.out.println("=== RESUM ===");
System.out.printf("%-28s %d de %d%n", "Devolucions revisades:", revisades,
TOTAL_PENDENTS);
System.out.printf("%-28s %d%n", "Omeses per estar pagades:", omesesPerPagament);
System.out.printf("%-28s %.2f EUR%n", "Import pendent revisat:", totalPendent);
if (escalat) {
System.out.printf("%-28s si (devolucio #%d)%n", "Escalat a RRHH:",
devolucioEscalada);
System.out.println("AVIS: queden devolucions sense revisar despres de l'aturada.");
} else {
System.out.printf("%-28s no%n", "Escalat a RRHH:");
}
}
}Sortida:
=== REVISIO DE DEVOLUCIONS PENDENTS ===
Empresa: Nexus Software
#1 37 dies GREU 9,25 EUR
#2 ... ja pagada, s'omet
#3 111 dies GREU 20,00 EUR <-- SOSTRE
Multa maxima assolida: s'atura la revisio.
=== RESUM ===
Devolucions revisades: 2 de 8
Omeses per estar pagades: 1
Import pendent revisat: 29,25 EUR
Escalat a RRHH: si (devolucio #3)
AVIS: queden devolucions sense revisar despres de l'aturada.Analitza el que ha passat, perquè condensa tota la lliçó:
- La devolució #2 va activar el
continue: es va comptar com a omesa i no va entrar als acumuladors. El bucle va continuar. - La devolució #3 va activar el
break: es va comptabilitzar primer (per aixòtotalPendentla inclou) i després es va abandonar el bucle. Les devolucions #4 a #8 no es van arribar a mirar. - L'ordre dins del cos importa: si el
breakfos abans detotalPendent += multa, la multa amb sostre no s'hauria sumat. Col·loca sempre les sortides on tingui sentit segons el que hagis de comptabilitzar. - I una vegada més, la limitació de fons: en acabar només queden xifres agregades. No hi ha manera de llistar quines van ser les devolucions revisades, ni de reprendre la revisió on es va quedar. Això exigeix desar les dades, i per a això calen les col·leccions del mòdul 5.
Errors Habituals i Consells
1. Creure que break en un switch surt del bucle. És el parany de l'apartat 4. Si el teu menú "no es tanca mai", és això.
2. continue en un while sense haver actualitzat abans. Bucle infinit. Si fas servir continue, prefereix for.
3. Codi inabastable després de break o continue.
if (multa >= MULTA_MAXIMA) {
break;
System.out.println("No s'imprimeix mai"); // ERROR: unreachable statement
}Java ho detecta en compilació, cosa que és una ajuda.
4. break al lloc equivocat dins del cos. Sortir abans d'acumular o després d'imprimir canvia el resultat. Decideix conscientment: la iteració que dispara la sortida compta o no compta?
5. Etiquetes mal col·locades. L'etiqueta ha d'anar immediatament abans del bucle a què es refereix. Si hi ha una sentència entremig, no compila.
6. Abusar de les etiquetes. Més d'una en el mateix mètode sol indicar que falta descompondre en mètodes. És legal, no és bo.
7. Confondre return amb break al main. return mata el programa sencer i se salta el resum final i el tancament del Scanner.
8. Consell: un sol punt de sortida per bucle, si pots. Un bucle amb quatre break repartits és difícil de raonar. Si et passa, la condició d'aturada probablement mereix ser una variable booleana amb nom.
9. Consell: continue al principi del cos, break on correspongui. El patró idiomàtic és agrupar tots els continue de filtratge a les primeres línies del cos, com a clàusules de guarda, i deixar la feina real plana a sota.
10. Consell: comenta la raó, no la mecànica. break; // surt del bucle no aporta res. break; // ja hem trobat el primer, no cal continuar sí.
Exercicis
Exercici 1: primer dia que supera un pressupost
Nexus Software assigna un pressupost de 50,00 € per a multes per empleat i trimestre. Escriu un programa que recorri els dies de retard d'1 a 500 acumulant la multa sense sostre (dies * TARIFA_DIARIA, sumada dia a dia) i que s'aturi amb break tan bon punt l'acumulat superi el pressupost. Ha d'imprimir en quin dia va passar i quin era l'acumulat exacte en aquell moment. Afegeix-hi també un comptador d'iteracions efectivament executades per comprovar que el break ha estalviat feina.
Exercici 2: filtrar amb continue i detectar la fallada del while
Escriu dues versions del mateix programa, que recorre els números de referència de préstec de l'1 al 20 i imprimeix únicament els que no són múltiples de 3 (els múltiples de 3 corresponen a préstecs ja arxivats i es descarten amb continue):
- Amb un bucle
for. - Amb un bucle
while.
A la versió while, col·loca deliberadament l'increment després del continue per reproduir el bucle infinit, observa'l (interromp-lo amb Ctrl + C) i després corregeix-lo. Explica per escrit per què el for no pateix aquest problema.
Exercici 3: menú amb switch clàssic i sortida correcta
Escriu un menú en bucle amb aquestes opcions, fent servir switch clàssic (amb : i break), no de fletxes:
El repte és que el programa acabi de debò en triar 0. Implementa'l de dues maneres diferents i compara-les:
- Versió A: amb una bandera booleana a la condició del
while. - Versió B: amb una etiqueta i
break etiqueta.
Afegeix-hi un comptador d'operacions realitzades que es mostri en sortir, i comprova que en totes dues versions s'imprimeix (pista: si fas servir return en lloc de break, no s'imprimirà).
Solucions
Solució 1
package com.nexussoftware.bibliotech;
public class BiblioTechApp {
public static void main(String[] args) {
final double TARIFA_DIARIA = 0.25;
final double PRESSUPOST = 50.0;
final int DIES_MAXIMS = 500;
double acumulat = 0.0;
int diaDeTall = 0;
int iteracions = 0;
for (int dies = 1; dies <= DIES_MAXIMS; dies++) {
iteracions++;
acumulat += dies * TARIFA_DIARIA;
if (acumulat > PRESSUPOST) {
diaDeTall = dies;
break; // ja tenim la resposta: no continuem sumant
}
}
System.out.println("=== CONTROL DE PRESSUPOST DE MULTES ===");
System.out.printf("%-28s %.2f EUR%n", "Pressupost:", PRESSUPOST);
if (diaDeTall > 0) {
System.out.printf("%-28s dia %d%n", "Se supera el:", diaDeTall);
System.out.printf("%-28s %.2f EUR%n", "Acumulat en aquest punt:", acumulat);
} else {
System.out.printf("%-28s mai en %d dies%n", "Se supera el:", DIES_MAXIMS);
}
System.out.printf("%-28s %d de %d%n", "Iteracions executades:",
iteracions, DIES_MAXIMS);
System.out.printf("%-28s %d%n", "Iteracions estalviades:",
DIES_MAXIMS - iteracions);
}
}Sortida:
=== CONTROL DE PRESSUPOST DE MULTES === Pressupost: 50,00 EUR Se supera el: dia 20 Acumulat en aquest punt: 52,50 EUR Iteracions executades: 20 de 500 Iteracions estalviades: 480
Comprovació manual: la suma d'1 + 2 + … + 20 és 210; multiplicada per 0,25 dona 52,50 €, que efectivament supera els 50 €. Amb 19 dies serien 190 × 0,25 = 47,50 €, encara per sota. El break ha estalviat el 96 % de la feina, i aquest estalvi és l'argument principal per fer-lo servir en cerques.
Solució 2
Versió amb for:
package com.nexussoftware.bibliotech;
public class BiblioTechApp {
public static void main(String[] args) {
int mostrades = 0;
int arxivades = 0;
System.out.println("Referencies de prestec actives (1-20):");
for (int referencia = 1; referencia <= 20; referencia++) {
if (referencia % 3 == 0) {
arxivades++;
continue; // en un for, salta a referencia++ i despres a la condicio
}
System.out.printf(" REF-%03d%n", referencia);
mostrades++;
}
System.out.printf("Mostrades %d, arxivades %d.%n", mostrades, arxivades);
}
}Sortida (abreujada): REF-001, REF-002, REF-004, REF-005, REF-007… i al final Mostrades 14, arxivades 6.
Versió amb while, primer trencada:
int referencia = 1;
while (referencia <= 20) {
if (referencia % 3 == 0) {
continue; // ERROR: salta l'increment de sota
}
System.out.printf(" REF-%03d%n", referencia);
referencia++;
}En executar-la, la sortida s'atura després de REF-002 i el programa es queda penjat sense imprimir res: referencia val 3, la condició de l'if es compleix sempre i mai no s'arriba al referencia++. Interromp-lo amb Ctrl + C.
Versió amb while corregida: cal incrementar abans de saltar, cosa que obliga a treballar amb una còpia del valor:
int referencia = 0;
int mostrades = 0;
int arxivades = 0;
while (referencia < 20) {
referencia++; // l'increment va PRIMER, sempre s'executa
if (referencia % 3 == 0) {
arxivades++;
continue; // ara es segur
}
System.out.printf(" REF-%03d%n", referencia);
mostrades++;
}
System.out.printf("Mostrades %d, arxivades %d.%n", mostrades, arxivades);Per què el for no pateix el problema: en un for, l'actualització (referencia++) forma part de la capçalera, no del cos. continue salta al final del cos, però l'actualització s'executa després d'això, sempre, sigui quina sigui la manera en què la iteració hagi acabat. En un while, l'actualització és una línia més del cos i continue se la pot saltar. Aquesta és la raó pràctica de la regla "si has de fer servir continue, fes servir for".
Solució 3
Versió A: bandera booleana.
package com.nexussoftware.bibliotech;
import java.util.Scanner;
public class BiblioTechApp {
public static void main(String[] args) {
final double TARIFA_DIARIA = 0.25;
final double MULTA_MAXIMA = 20.0;
Scanner scanner = new Scanner(System.in);
boolean sortir = false;
int operacions = 0;
while (!sortir) {
System.out.println();
System.out.println("=== BIBLIOTECH (versio A) ===");
System.out.println(" 1. Revisar devolucions pendents");
System.out.println(" 2. Consultar sostre de multa");
System.out.println(" 0. Sortir");
System.out.print("Opcio: ");
String opcio = scanner.nextLine().trim();
switch (opcio) {
case "1":
System.out.println(" 3 devolucions pendents de revisio.");
operacions++;
break; // surt del switch: correcte i suficient
case "2":
int diesSostre = (int) Math.ceil(MULTA_MAXIMA / TARIFA_DIARIA);
System.out.printf(" El sostre de %.2f EUR s'assoleix als %d dies.%n",
MULTA_MAXIMA, diesSostre);
operacions++;
break;
case "0":
sortir = true; // la CONDICIO del while tanca el bucle
break;
default:
System.out.println(" Opcio no reconeguda.");
}
}
System.out.printf("Sessio finalitzada. Operacions realitzades: %d%n", operacions);
scanner.close();
}
}Versió B: etiqueta.
package com.nexussoftware.bibliotech;
import java.util.Scanner;
public class BiblioTechApp {
public static void main(String[] args) {
final double TARIFA_DIARIA = 0.25;
final double MULTA_MAXIMA = 20.0;
Scanner scanner = new Scanner(System.in);
int operacions = 0;
bucleMenu: // etiqueta, just abans del bucle
while (true) {
System.out.println();
System.out.println("=== BIBLIOTECH (versio B) ===");
System.out.println(" 1. Revisar devolucions pendents");
System.out.println(" 2. Consultar sostre de multa");
System.out.println(" 0. Sortir");
System.out.print("Opcio: ");
String opcio = scanner.nextLine().trim();
switch (opcio) {
case "1":
System.out.println(" 3 devolucions pendents de revisio.");
operacions++;
break; // surt nomes del switch
case "2":
int diesSostre = (int) Math.ceil(MULTA_MAXIMA / TARIFA_DIARIA);
System.out.printf(" El sostre de %.2f EUR s'assoleix als %d dies.%n",
MULTA_MAXIMA, diesSostre);
operacions++;
break;
case "0":
break bucleMenu; // trenca el BUCLE etiquetat
default:
System.out.println(" Opcio no reconeguda.");
}
}
System.out.printf("Sessio finalitzada. Operacions realitzades: %d%n", operacions);
scanner.close();
}
}Sessió idèntica en totes dues versions:
Opcio: 2 El sostre de 20,00 EUR s'assoleix als 80 dies. Opcio: 5 Opcio no reconeguda. Opcio: 1 3 devolucions pendents de revisio. Opcio: 0 Sessio finalitzada. Operacions realitzades: 2
Comparació. La versió A no necessita etiquetes i la seva condició de sortida és visible a la capçalera (while (!sortir)), cosa que la fa més fàcil de llegir per a algú que arriba nou al codi; a canvi, la variable sortir és un element extra que cal mantenir. La versió B és més directa —un sol salt, sense variable auxiliar— però exigeix que el lector conegui les etiquetes i, si el mètode creixés amb més bucles, començaria a resultar confusa. Per a un menú, el curs recomana la versió A, i és la que es farà servir al projecte de la lliçó 02-06.
I comprova el detall de la pista: si al case "0" de qualsevol de les dues versions escrivissis return; en lloc de break bucleMenu; o sortir = true;, el programa acabaria a l'instant i la línia Sessio finalitzada. Operacions realitzades: 2 no s'imprimiria mai, a més de deixar el Scanner sense tancar. És exactament la diferència entre "sortir del bucle" i "acabar el programa".
Conclusió
Ja controles el flux dins d'un bucle. break abandona el bucle tan bon punt tens la resposta i converteix una cerca exhaustiva en una cerca eficient; continue descarta les iteracions que no interessen i manté el cos pla, com una clàusula de guarda. Coneixes el parany més famós del llenguatge —el break que surt del switch i no del bucle— i les seves tres solucions: bandera, switch amb fletxes i etiqueta. Saps fer servir break etiqueta i continue etiqueta per governar bucles imbricats, i també per què convé desconfiar-ne i preferir banderes o, tan bon punt els sàpigues escriure, mètodes amb return. I tens clara la diferència entre sortir d'un bucle i acabar el programa.
BiblioTechApp pot ara recórrer la cua de devolucions pendents, saltar-se les ja pagades, aturar-se a la primera amb multa màxima i escalar el cas, tot amb acumuladors escalars i un únic bucle. El programa fa força coses, i aquí apareix el problema següent: quan el resultat no és l'esperat, ja no n'hi ha prou amb rellegir el codi. Hi ha massa variables canviant en massa iteracions.
La lliçó següent, Depuració i traces d'execució, et dona les eines per veure què passa realment dins del teu programa: traces amb System.out.println ben col·locades, el depurador de l'IDE amb punts d'interrupció (inclosos els condicionals, per aturar-se només quan diesRetard > 30), la finestra de variables, l'avaluació d'expressions, com llegir una traça de pila i, sobretot, un mètode sistemàtic per localitzar un error en lloc de buscar-lo a cegues.
Curs de Programació en Java
Mòdul 1: Introducció a Java
- Introducció a Java
- Configuració de l'entorn de desenvolupament
- Sintaxi i estructura bàsica
- Variables i tipus de dades
- Operadors
- Entrada i sortida per consola
- El teu primer programa complet: BiblioTech
Mòdul 2: Flux de control
- Sentències condicionals
- Bucles
- Sentències switch
- Break i continue
- Depuració i traces d'execució
- Projecte: menú interactiu de BiblioTech
Mòdul 3: Programació orientada a objectes
- Introducció a la POO
- Classes i objectes
- Mètodes
- Constructors
- Herència
- Polimorfisme
- Encapsulament
- Abstracció
- La classe Object: equals, hashCode i toString
Mòdul 4: Programació orientada a objectes avançada
- Interfícies
- Classes abstractes
- Classes internes
- Classes anònimes
- Expressions lambda
- Interfícies funcionals i referències a mètodes
- Enumeracions i registres
Mòdul 5: Estructures de dades i col·leccions
- Arrays
- El framework de col·leccions
- ArrayList
- LinkedList
- HashMap
- HashSet
- Cua i Deque
- Pila
- Ordenació i cerca en col·leccions
Mòdul 6: Gestió d'excepcions
- Introducció a les excepcions
- Bloc try-catch
- Throw i throws
- Excepcions personalitzades
- Bloc finally
- Try-with-resources i AutoCloseable
- Estratègies de gestió d'errors i logging
Mòdul 7: Entrada/sortida de fitxers
- Lectura de fitxers
- Escriptura de fitxers
- Fluxos de fitxers
- BufferedReader i BufferedWriter
- Serialització
- L'API NIO.2: Path i Files
- Formats d'intercanvi: CSV i Properties
Mòdul 8: Multifil i concurrència
- Introducció al multifil
- Creació de fils
- Cicle de vida d'un fil
- Sincronització
- Utilitats de concurrència
- Col·leccions concurrents i variables atòmiques
- Tasques asíncrones amb CompletableFuture
Mòdul 9: Xarxes
- Introducció a les xarxes
- Sockets
- ServerSocket
- DatagramSocket i DatagramPacket
- URL i HttpURLConnection
- El client HTTP modern
Mòdul 10: Temes avançats
- Genèrics
- Anotacions
- Reflexió
- Característiques de Java 8: Streams i Optional
- Dates i hores amb java.time
- Java 9 i més enllà
- Memòria, recol·lecció de brossa i rendiment
Mòdul 11: Frameworks i llibreries de Java
- Introducció als frameworks de Java
- Spring Framework
- Hibernate
- JUnit
- Maven
- Proves avançades amb Mockito
- Llibreries essencials de l'ecosistema
