Fins ara els teus programes han estat monòlegs: les dades estaven escrites a foc dins del codi i la sortida es limitava a concatenar cadenes amb +, produint imports com ara 13.875 EUR que cap rebut no acceptaria. Aquesta lliçó tanca les dues mancances. Aprendràs a donar format professional a la sortida amb printf (alineacions, amplades, decimals controlats) i a llegir dades de l'usuari amb Scanner, inclòs el famós problema de barrejar nextInt() amb nextLine() que desconcerta tothom la primera vegada. En acabar, BiblioTech podrà conversar amb qui el fa servir i emetre una fitxa de llibre amb aspecte de document real.
Tot el codi d'aquesta lliçó és lineal: sense condicionals ni bucles, que arriben al mòdul 2.
Contingut
- La sortida estàndard:
System.out printdavant deprintlnprintfi els especificadors de format- Amplades, alineació i altres modificadors
String.format: el mateix format, dins d'una variableSystem.erri quan fer-lo servir- Llegir dades amb
Scanner - Els mètodes de lectura i què retorna cadascun
- El problema clàssic: barrejar
nextInt()ambnextLine() - Tancar el
Scanner - Conversió de text a número:
parseIntiparseDouble - La fitxa de llibre de BiblioTech
- Errors Habituals i Consells
- Exercicis
- La sortida estàndard:
System.out
System.outSystem.out és un objecte que representa la sortida estàndard: normalment, la consola. Està sempre disponible sense necessitat de cap import, perquè la classe System pertany a java.lang.
Desglossem l'expressió que has anat escrivint des de la primera lliçó:
Systemés una classe dejava.langque dona accés a recursos del sistema.outés un camp d'aquesta classe: un objectePrintStreamconnectat a la consola.printlnés un mètode d'aquell objecte.
Els tres mètodes que faràs servir:
| Mètode | Què fa |
|---|---|
print(x) |
Escriu x sense salt de línia |
println(x) |
Escriu x i salta de línia |
printf(format, args...) |
Escriu amb format controlat, sense salt automàtic |
print davant de println
print davant de printlnLa diferència és únicament el salt de línia final, però canvia molt la composició de la sortida:
System.out.print("Titol: ");
System.out.print("Java Eficac");
System.out.println(); // println() sense arguments nomes salta de linia
System.out.println("ISBN: 978-0000000001");Sortida:
Tots dos mètodes accepten qualsevol tipus: String, números, boolean, char. Java converteix automàticament el valor a text.
System.out.println(2018); // 2018
System.out.println(3.75); // 3.75
System.out.println(true); // true
System.out.println('J'); // JI admeten concatenació amb +, que és el que has estat fent servir:
String titol = "Java Eficac";
int anyPublicacio = 2018;
System.out.println("Llibre: " + titol + " (" + anyPublicacio + ")");Aquest estil funciona, però es torna illegible tan bon punt hi ha quatre o cinc variables, i no permet controlar el format: no pots dir "mostra'm aquest double amb exactament dos decimals". Per a això hi ha printf.
printf i els especificadors de format
printf i els especificadors de formatprintf (de print formatted) rep una cadena de format amb marcadors que comencen per %, seguida dels valors que ompliran aquells marcadors, en ordre.
String titol = "Java Eficac";
double multa = 13.875;
System.out.printf("El llibre %s acumula una multa de %.2f EUR%n", titol, multa);Sortida:
Fixa't en el que acaba de passar: 13.875 s'ha mostrat com a 13.88, arrodonit a dos decimals. Aquest control és exactament el que faltava a la lliçó anterior.
Els especificadors essencials
| Especificador | Tipus que espera | Exemple d'ús | Resultat |
|---|---|---|---|
%s |
Qualsevol cosa (text) | printf("%s", "Java Eficac") |
Java Eficac |
%d |
Enter (int, long) |
printf("%d", 2018) |
2018 |
%f |
Decimal (double, float) |
printf("%f", 3.5) |
3,500000 |
%.2f |
Decimal amb 2 decimals | printf("%.2f", 13.875) |
13,88 |
%b |
Booleà | printf("%b", true) |
true |
%c |
Caràcter | printf("%c", 'J') |
J |
%n |
Salt de línia portable | printf("Fi%n") |
salta de línia |
%% |
Un símbol % literal |
printf("100%%") |
100% |
Tres punts crítics:
%fper defecte mostra SIS decimals.printf("%f", 3.5)imprimeix3,500000. Gairebé sempre voldràs%.2f.- Fes servir
%ni no\n.%ngenera el salt de línia propi del sistema operatiu (\na Unix,\r\na Windows);\ngenera sempre el d'Unix. A la consola amb prou feines es nota, però és la pràctica correcta. printfno salta de línia per si sol. Si oblides el%nfinal, la sortida següent s'enganxarà a l'anterior.
El detall de la coma decimal
Hauràs notat que la taula mostra 13,88 amb coma. printf respecta la configuració regional del sistema: aquí el separador decimal és la coma. Si necessites un punt de manera determinista (per exemple, per generar un fitxer CSV), pots indicar la configuració regional explícitament:
És un matís que convé conèixer ara perquè no et sorprengui la sortida a la teva màquina.
Diversos valors alhora
Els valors es consumeixen en ordre, d'esquerra a dreta:
String empleat = "Marta Ruiz";
String titol = "Java Eficac";
int diesRetard = 12;
double multa = 3.0;
System.out.printf("%s te %s amb %d dies de retard: %.2f EUR%n",
empleat, titol, diesRetard, multa);Sortida:
Si el nombre de marcadors no coincideix amb el de valors, o els tipus no encaixen, el programa falla en execució amb MissingFormatArgumentException o IllegalFormatConversionException. El cas més freqüent és fer servir %d per a un double: %d és només per a enters.
- Amplades, alineació i altres modificadors
Aquí és on printf deixa de ser una comoditat i es converteix en una eina: pot alinear columnes.
L'estructura completa d'un especificador és:
| Modificador | Significat | Exemple | Resultat (amb | marcant les vores) |
|---|---|---|---|
%10s |
Amplada 10, alineat a la dreta | printf("|%10s|", "Java") |
| Java| |
%-10s |
Amplada 10, alineat a l'esquerra | printf("|%-10s|", "Java") |
|Java | |
%5d |
Enter en amplada 5, a la dreta | printf("|%5d|", 12) |
| 12| |
%-5d |
Enter en amplada 5, a l'esquerra | printf("|%-5d|", 12) |
|12 | |
%05d |
Omple amb zeros per l'esquerra | printf("%05d", 42) |
00042 |
%8.2f |
Amplada 8 total, 2 decimals | printf("|%8.2f|", 13.875) |
| 13,88| |
%,d |
Separador de milers | printf("%,d", 1250000) |
1.250.000 |
%+d |
Mostra sempre el signe | printf("%+d", 12) |
+12 |
Punts a recordar:
- L'amplada és un mínim, no un màxim. Si el text és més llarg, es mostra sencer i desborda la columna.
- El guionet activa l'alineació a l'esquerra. Sense ell, tot s'alinea a la dreta.
- Convenció habitual: text a l'esquerra, números a la dreta. Els números alineats a la dreta permeten comparar magnituds d'un cop d'ull, perquè les unitats queden a la mateixa columna.
Aplicat a una taula del catàleg de BiblioTech:
System.out.printf("%-22s %-16s %6s %10s%n", "TITOL", "AUTOR", "ANY", "MULTA");
System.out.println("-".repeat(58));
System.out.printf("%-22s %-16s %6d %10.2f%n", "Java Eficac", "Joshua Bloch", 2018, 3.0);
System.out.printf("%-22s %-16s %6d %10.2f%n", "Patrons de Disseny", "Erich Gamma", 1994, 13.88);
System.out.printf("%-22s %-16s %6d %10.2f%n", "Refactoritzacio", "Martin Fowler", 1999, 0.0);Sortida:
TITOL AUTOR ANY MULTA ---------------------------------------------------------- Java Eficac Joshua Bloch 2018 3,00 Patrons de Disseny Erich Gamma 1994 13,88 Refactoritzacio Martin Fowler 1999 0,00
Amb println i concatenació, aquesta alineació seria pràcticament impossible. I observa el truc de "-".repeat(58): el mètode repeat de String (Java 11+) repeteix una cadena, perfecte per a línies separadores.
String.format: el mateix format, dins d'una variable
String.format: el mateix format, dins d'una variableDe vegades no vols imprimir el text, sinó desar-lo. String.format fa servir exactament la mateixa sintaxi que printf però retorna un String en comptes d'escriure'l:
double multa = 13.875;
String liniaRebut = String.format("Multa acumulada: %.2f EUR", multa);
System.out.println(liniaRebut);Les dues formes següents són equivalents:
System.out.printf("Multa: %.2f EUR%n", multa);
System.out.println(String.format("Multa: %.2f EUR", multa));String.format és l'opció quan el text formatat s'ha de reutilitzar: desar-se en un fitxer (mòdul 7), enviar-se per la xarxa (mòdul 9) o compondre un missatge que es mostrarà més tard.
System.err i quan fer-lo servir
System.err i quan fer-lo servirAl costat de la sortida estàndard existeix la sortida d'error estàndard, System.err, amb els mateixos mètodes:
System.out.println("Prestec registrat correctament");
System.err.println("AVIS: l'ISBN introduit no te el format esperat");A la consola de l'IDE, System.err sol aparèixer en vermell. Però la seva raó de ser veritable és que són dos canals independents que el sistema operatiu pot redirigir per separat:
# Nomes la sortida normal va al fitxer; els errors es continuen veient a pantalla
java BiblioTechApp > rebuts.txt
# Cada canal a un fitxer diferent
java BiblioTechApp > rebuts.txt 2> errors.logAixò és el que permet que un procés automatitzat desi les dades netes en un lloc i els problemes en un altre.
| Canal | Es fa servir per a | Exemple a BiblioTech |
|---|---|---|
System.out |
Resultats, informació normal | El rebut del préstec |
System.err |
Errors, avisos, diagnòstics | "No s'ha pogut llegir el catàleg" |
Un matís tècnic: System.err no fa servir memòria intermèdia i System.out sí, per la qual cosa en barrejar-los els missatges poden aparèixer desordenats a la pantalla. No és una errada teva.
En aplicacions professionals, System.err acaba substituït per un sistema de logging amb nivells (INFO, WARN, ERROR), que s'estudia a la lliçó 06-07. Per al mòdul 1, System.err n'hi ha prou.
- Llegir dades amb
Scanner
ScannerScanner és la classe estàndard per llegir entrada de l'usuari. Viu a java.util, així que necessita import:
import java.util.Scanner;
public class LecturaBiblioTech {
public static void main(String[] args) {
// Creem un Scanner connectat a l'entrada estandard (el teclat)
Scanner entrada = new Scanner(System.in);
System.out.print("Nom de l'empleat: ");
String empleat = entrada.nextLine();
System.out.println("Benvingut a BiblioTech, " + empleat);
entrada.close();
}
}Analitzem la línia clau:
new Scanner(...)crea un objecte nou.newés l'operador de creació d'objectes; l'estudiaràs a fons al mòdul 3, però aquí ja el necessites.System.inés l'entrada estàndard: el teclat, anàleg aSystem.outper a la sortida.entradaés el nom que li donem. Podria sersc,teclatolector; convé que sigui descriptiu.
Un detall d'usabilitat: per demanar dades es fa servir System.out.print (sense ln), de manera que el cursor es quedi a la mateixa línia que la pregunta. És un detall petit amb molt d'impacte en la sensació de qualitat del programa.
- Els mètodes de lectura i què retorna cadascun
| Mètode | Retorna | Què llegeix |
|---|---|---|
nextLine() |
String |
Tota la línia, espais inclosos, fins a l'Enter |
next() |
String |
Una sola paraula (fins al primer espai) |
nextInt() |
int |
L'enter següent |
nextDouble() |
double |
El decimal següent |
nextBoolean() |
boolean |
true o false |
hasNextInt() |
boolean |
El que ve és un enter? No consumeix res |
hasNextLine() |
boolean |
Queda alguna línia? No consumeix res |
La diferència entre nextLine() i next() importa molt a BiblioTech, on els títols i els noms porten espais:
System.out.print("Titol del llibre: ");
String titol = entrada.nextLine(); // "Java Eficac" sencer
System.out.print("Titol del llibre: ");
String paraula = entrada.next(); // nomes "Java"; "Eficac" queda pendentRegla pràctica: per a noms i títols, fes servir sempre nextLine().
Els mètodes hasNextXxx() són especials: pregunten sense consumir. Serveixen per comprovar si el que ve és del tipus esperat abans de llegir-ho:
System.out.print("Dies transcorreguts: ");
boolean esNumeroValid = entrada.hasNextInt();
System.out.println("L'entrada es un enter? " + esNumeroValid);La seva utilitat plena requereix condicionals (mòdul 2) per reaccionar al resultat, i bucles per insistir fins a obtenir una dada vàlida (lliçó 02-02). De moment queda't amb que existeixen i amb què fan.
- El problema clàssic: barrejar
nextInt() amb nextLine()
nextInt() amb nextLine()Aquest és el bug que pateix absolutament tothom en aprendre Java. Observa:
Scanner entrada = new Scanner(System.in);
System.out.print("Dies transcorreguts: ");
int dies = entrada.nextInt();
System.out.print("Nom de l'empleat: ");
String empleat = entrada.nextLine(); // <- NO ESPERA: es salta la pregunta
System.out.println("[" + empleat + "]"); // imprimeix []El programa demana els dies, i després mostra la segona pregunta i continua de llarg sense deixar-te escriure res. empleat queda buit.
Per què passa
Scanner no llegeix "per preguntes", sinó d'un flux continu de caràcters. Quan escrius 27 i prems Enter, en aquell flux hi ha:
nextInt() consumeix els caràcters del número (27) i s'atura just abans del \n, deixant-lo al flux. Després, nextLine() fa el que li correspon: llegeix fins al salt de línia següent. Com que el salt és allà mateix, retorna una cadena buida immediatament, sense esperar ningú.
flowchart TD
A["L'usuari escriu: 27 + Enter"] --> B["Flux d'entrada: '27\\n'"]
B --> C["nextInt() consumeix '27'<br/>i DEIXA el '\\n' pendent"]
C --> D["nextLine() troba el '\\n'<br/>i retorna cadena buida sense esperar"]
D --> E["Sembla que el programa<br/>s'hagi saltat la pregunta"]
Les dues solucions
Solució 1: consumir el salt pendent amb un nextLine() extra.
System.out.print("Dies transcorreguts: ");
int dies = entrada.nextInt();
entrada.nextLine(); // consumeix el \n que ha deixat nextInt()
System.out.print("Nom de l'empleat: ");
String empleat = entrada.nextLine(); // ara SI que esperaSolució 2 (la recomanada): llegir-ho tot amb nextLine() i convertir.
System.out.print("Dies transcorreguts: ");
int dies = Integer.parseInt(entrada.nextLine()); // llegeix la linia SENCERA i la converteix
System.out.print("Nom de l'empleat: ");
String empleat = entrada.nextLine(); // funciona sense sorpresesComparació dels dos enfocaments:
| Enfocament | Avantatges | Inconvenients |
|---|---|---|
nextInt() + nextLine() extra |
Menys escriptura | Fàcil d'oblidar; cal recordar on posar-lo |
Només nextLine() + parseInt |
Coherent i predictible; un mètode per a tot | Una crida de conversió per dada |
Aquest curs adopta la segona opció a BiblioTechApp. Quan tota la lectura passa per nextLine(), el problema desapareix per construcció i el codi és uniforme.
- Tancar el
Scanner
ScannerScanner és un recurs del sistema i s'ha de tancar quan ja no es necessita:
Posa-ho al final de main. Si no ho fas, el compilador i l'IDE mostraran un avís de "recurs no tancat" (resource leak).
Un advertiment important: tancar un Scanner creat sobre System.in tanca també System.in. A partir d'aquell moment no pots crear un altre Scanner per llegir del teclat en el mateix programa. Per això la regla és: un sol Scanner per a tota l'aplicació, creat al principi i tancat al final.
Al mòdul 6 veuràs try-with-resources, que tanca els recursos automàticament i és la manera professional de fer-ho.
- Conversió de text a número:
parseInt i parseDouble
parseInt i parseDoubleTot el que arriba de la consola és text, encara que l'usuari escrigui dígits. Per operar-hi cal convertir-ho:
String textDies = "27";
int dies = Integer.parseInt(textDies); // 27 com a numero
String textTarifa = "0.25";
double tarifa = Double.parseDouble(textTarifa); // 0.25 com a numeroÉs la raó per la qual aquestes dues línies es comporten de manera tan diferent:
System.out.println("27" + 15); // "2715" -> concatenacio de text
System.out.println(Integer.parseInt("27") + 15); // 42 -> suma de numerosQuè passa si el text no és un número
El programa s'atura amb un error d'execució:
Exception in thread "main" java.lang.NumberFormatException: For input string: "vint-i-set" at java.base/java.lang.Integer.parseInt(Integer.java:668) at BiblioTechApp.main(BiblioTechApp.java:12)
Això és una excepció, el mecanisme de Java per assenyalar errors en execució. Capturar-les i respondre-hi de manera elegant (tornar a demanar la dada, mostrar un missatge amable) és l'objecte del mòdul 6. De moment queda't amb dues idees:
- Saps què significa aquell error si apareix.
- Els programes d'aquest mòdul assumeixen que l'usuari introdueix dades correctes. És una limitació real que reconeixerem explícitament a la lliçó següent.
Un detall útil: Double.parseDouble espera el punt com a separador decimal, no la coma. Double.parseDouble("0,25") falla; Double.parseDouble("0.25") funciona. És l'asimetria inversa a la de printf, i convé tenir-la present quan provis els teus programes.
- La fitxa de llibre de BiblioTech
Ajuntem-ho tot. Aquest programa demana per consola les dades d'un llibre del catàleg i n'emet una fitxa formatada:
import java.util.Scanner;
public class FitxaLlibreBiblioTech {
public static void main(String[] args) {
// Un unic Scanner per a tota l'aplicacio.
Scanner entrada = new Scanner(System.in);
// --- Constants de negoci ---
final String NOM_EMPRESA = "Nexus Software";
final int ANY_ACTUAL = 2026;
final int AMPLADA_FITXA = 46;
// --- Capcalera de benvinguda ---
System.out.println("=".repeat(AMPLADA_FITXA));
System.out.printf(" BiblioTech - %s%n", NOM_EMPRESA);
System.out.println(" Alta de llibre al cataleg");
System.out.println("=".repeat(AMPLADA_FITXA));
System.out.println();
// --- Recollida de dades ---
// TOTA la lectura es fa amb nextLine() per evitar el problema
// del salt de linia pendent que deixa nextInt().
System.out.print("Titol del llibre : ");
String titol = entrada.nextLine();
System.out.print("Autor : ");
String autor = entrada.nextLine();
System.out.print("ISBN : ");
String isbn = entrada.nextLine();
System.out.print("Any de publicacio : ");
int anyPublicacio = Integer.parseInt(entrada.nextLine());
System.out.print("Nombre de pagines : ");
int pagines = Integer.parseInt(entrada.nextLine());
System.out.print("Preu de compra : ");
double preu = Double.parseDouble(entrada.nextLine());
// --- Calculs derivats ---
int antiguitat = ANY_ACTUAL - anyPublicacio;
double preuPerPagina = preu / pagines; // double / int -> double
boolean disponible = true; // tot llibre nou entra disponible
// --- Fitxa formatada ---
System.out.println();
System.out.println("=".repeat(AMPLADA_FITXA));
System.out.println(" FITXA DE CATALEG");
System.out.println("=".repeat(AMPLADA_FITXA));
System.out.printf("%-20s %s%n", "Titol:", titol);
System.out.printf("%-20s %s%n", "Autor:", autor);
System.out.printf("%-20s %s%n", "ISBN:", isbn);
System.out.printf("%-20s %d%n", "Publicacio:", anyPublicacio);
System.out.printf("%-20s %d anys%n", "Antiguitat:", antiguitat);
System.out.printf("%-20s %,d%n", "Pagines:", pagines);
System.out.printf("%-20s %8.2f EUR%n", "Preu:", preu);
System.out.printf("%-20s %8.4f EUR%n", "Preu/pagina:", preuPerPagina);
System.out.printf("%-20s %b%n", "Disponible:", disponible);
System.out.println("=".repeat(AMPLADA_FITXA));
System.err.println("[info] Fitxa generada sense validacio de dades.");
entrada.close();
}
}Exemple d'execució (el que escriu l'usuari apareix després dels dos punts):
============================================== BiblioTech - Nexus Software Alta de llibre al cataleg ============================================== Titol del llibre : Java Eficac Autor : Joshua Bloch ISBN : 978-0000000001 Any de publicacio : 2018 Nombre de pagines : 416 Preu de compra : 45.9 ============================================== FITXA DE CATALEG ============================================== Titol: Java Eficac Autor: Joshua Bloch ISBN: 978-0000000001 Publicacio: 2018 Antiguitat: 8 anys Pagines: 416 Preu: 45,90 EUR Preu/pagina: 0,1103 EUR Disponible: true ==============================================
Cinc decisions de disseny que val la pena assenyalar:
- Un únic
Scanner, creat al principi i tancat al final. - Tota la lectura amb
nextLine(), amb conversió explícita. Mai no es barreja ambnextInt(). System.out.printper a les preguntes, perquè el cursor es quedi a la línia.%-20sa les etiquetes, que crea una columna esquerra perfectament alineada.%8.2fper als imports: dos decimals i amplada fixa, de manera que les xifres quedin alineades per la dreta.
Errors Habituals i Consells
- Oblidar
import java.util.Scanner;. Produeixcannot find symbol: class Scanner. És l'import que més oblidaràs de tot el curs. - Barrejar
nextInt()ambnextLine(). El programa es salta preguntes. Fes servirnextLine()per a tot i converteix ambparseInt. - Fer servir
next()per llegir títols o noms. Es talla al primer espai: "Marta Ruiz" es converteix en "Marta". - Fer servir
%dper a undouble. LlançaIllegalFormatConversionException.%dés només per a enters; per a decimals,%f. - Que
%fmostri sis decimals. És el seu comportament per defecte; especifica sempre la precisió:%.2f. - Oblidar
%nal final d'unprintf. Totes les línies s'enganxen. - Crear un
Scannernou després de tancar-ne un altre sobreSystem.in. No funciona:System.inqueda tancat. Un solScannerper programa. - Introduir
0,25en comptes de0.25.Double.parseDoubleespera el punt i llançaNumberFormatExceptionamb la coma. - Consell: defineix constants per a les amplades de format (
AMPLADA_FITXA) en comptes de repetir números. Canviar el disseny es converteix en canviar una línia. - Consell:
"=".repeat(46)és molt més net que escriure 46 signes igual a mà, i no t'equivocaràs en comptar-los. - Consell: quan una sortida no quedi alineada, imprimeix una línia de referència amb
|a les vores (printf("|%-20s|%n", text)) per veure exactament on cauen les columnes.
Exercicis
Exercici 1: Predir el comportament
Analitza aquest programa sense executar-lo. Indica què s'imprimeix, quines dades aconsegueix llegir realment i per què. Després escriu-ne dues versions corregides: una amb la tècnica del nextLine() extra i una altra llegint-ho tot amb nextLine().
import java.util.Scanner;
public class ProvaScanner {
public static void main(String[] args) {
Scanner entrada = new Scanner(System.in);
System.out.print("Dies transcorreguts: ");
int dies = entrada.nextInt();
System.out.print("Nom de l'empleat: ");
String empleat = entrada.nextLine();
System.out.print("Titol del llibre: ");
String titol = entrada.nextLine();
System.out.println("Empleat: [" + empleat + "]");
System.out.println("Titol: [" + titol + "]");
System.out.println("Dies: " + dies);
entrada.close();
}
}Exercici 2: Taula del catàleg alineada
Escriu la classe TaulaCataleg que imprimeixi, sense demanar res a l'usuari, la taula exacta següent amb els tres llibres de BiblioTech. Fes servir només printf i "-".repeat(...).
+------------------------+------------------+-------+------------+ | TITOL | AUTOR | ANY | PREU | +------------------------+------------------+-------+------------+ | Java Eficac | Joshua Bloch | 2018 | 45,90 | | Patrons de Disseny | Erich Gamma | 1994 | 52,00 | | Refactoritzacio | Martin Fowler | 1999 | 38,75 | +------------------------+------------------+-------+------------+ | TOTAL | | 136,65 | +------------------------+------------------+-------+------------+
Pista: defineix constants amb les amplades de cada columna i construeix la línia separadora amb repeat.
Exercici 3: Registre de devolució
Escriu la classe RegistreDevolucio que demani per consola:
- Nom de l'empleat
- Títol del llibre
- ISBN
- Dies transcorreguts des del préstec
- Tarifa diària en euros
i mostri un rebut formatat amb: les dades introduïdes, els dies de retard (saturats a zero amb el ternari), el retard expressat en setmanes i dies, la multa amb dos decimals i una etiqueta d'estat (EN TERMINI / AMB RETARD) obtinguda amb l'operador ternari. Fes servir System.err per emetre un avís que el programa no valida les entrades. Sense if ni bucles.
Solucions
Solució 1
Comportament observat. El programa mostra les tres preguntes, però només et deixa escriure dues vegades: els dies i, després, el que acabarà sent el títol. La sortida és una cosa així:
Dies transcorreguts: 27 Nom de l'empleat: Titol del llibre: Marta Ruiz Empleat: [] Titol: [Marta Ruiz] Dies: 27
Per què. nextInt() consumeix els caràcters 27 però deixa el \n de l'Enter al flux d'entrada. El primer nextLine() troba aquell salt immediatament i retorna una cadena buida sense esperar, per la qual cosa empleat queda a "" i la pregunta del nom passa de llarg. El segon nextLine() sí que espera, i el que hi escrius (encara que sigui el nom) acaba a titol. Les dades queden desplaçades una posició.
Correcció 1: nextLine() extra.
import java.util.Scanner;
public class ProvaScannerCorregida1 {
public static void main(String[] args) {
Scanner entrada = new Scanner(System.in);
System.out.print("Dies transcorreguts: ");
int dies = entrada.nextInt();
entrada.nextLine(); // <- CLAU: consumeix el \n pendent que ha deixat nextInt()
System.out.print("Nom de l'empleat: ");
String empleat = entrada.nextLine();
System.out.print("Titol del llibre: ");
String titol = entrada.nextLine();
System.out.println("Empleat: [" + empleat + "]");
System.out.println("Titol: [" + titol + "]");
System.out.println("Dies: " + dies);
entrada.close();
}
}Correcció 2: tot amb nextLine() (recomanada).
import java.util.Scanner;
public class ProvaScannerCorregida2 {
public static void main(String[] args) {
Scanner entrada = new Scanner(System.in);
System.out.print("Dies transcorreguts: ");
// nextLine() consumeix la linia COMPLETA, salt inclos.
// parseInt converteix despres aquell text en numero.
int dies = Integer.parseInt(entrada.nextLine());
System.out.print("Nom de l'empleat: ");
String empleat = entrada.nextLine();
System.out.print("Titol del llibre: ");
String titol = entrada.nextLine();
System.out.println("Empleat: [" + empleat + "]");
System.out.println("Titol: [" + titol + "]");
System.out.println("Dies: " + dies);
entrada.close();
}
}La segona correcció és preferible perquè elimina la causa en comptes de pedaçar el símptoma: si totes les lectures fan servir el mateix mètode, mai no queda res pendent al flux.
Solució 2
public class TaulaCataleg {
public static void main(String[] args) {
// Amplades de columna centralitzades en constants:
// canviar el disseny es canviar aquests numeros.
final int AMPLADA_TITOL = 22;
final int AMPLADA_AUTOR = 16;
final int AMPLADA_ANY = 5;
final int AMPLADA_PREU = 10;
// La linia separadora es construeix una vegada i es reutilitza.
// Els +2 corresponen als espais que envolten cada cella.
String separador = "+" + "-".repeat(AMPLADA_TITOL + 2)
+ "+" + "-".repeat(AMPLADA_AUTOR + 2)
+ "+" + "-".repeat(AMPLADA_ANY + 2)
+ "+" + "-".repeat(AMPLADA_PREU + 2)
+ "+";
// Dades del cataleg
double preuJava = 45.90;
double preuPatrons = 52.00;
double preuRefactor = 38.75;
double total = preuJava + preuPatrons + preuRefactor;
System.out.println(separador);
// Capcalera: text a l'esquerra (-), numeros a la dreta (sense -)
System.out.printf("| %-22s | %-16s | %5s | %10s |%n",
"TITOL", "AUTOR", "ANY", "PREU");
System.out.println(separador);
// Files de dades. %5d alinea els anys, %10.2f alinea els imports
// per la dreta amb exactament dos decimals.
System.out.printf("| %-22s | %-16s | %5d | %10.2f |%n",
"Java Eficac", "Joshua Bloch", 2018, preuJava);
System.out.printf("| %-22s | %-16s | %5d | %10.2f |%n",
"Patrons de Disseny", "Erich Gamma", 1994, preuPatrons);
System.out.printf("| %-22s | %-16s | %5d | %10.2f |%n",
"Refactoritzacio", "Martin Fowler", 1999, preuRefactor);
System.out.println(separador);
// Fila de total: fusionem visualment les dues primeres columnes
// fent servir una amplada igual a la suma de totes dues mes el separador intermedi.
System.out.printf("| %-41s | %5s | %10.2f |%n", "TOTAL", "", total);
System.out.println(separador);
}
}Sortida:
+------------------------+------------------+-------+------------+ | TITOL | AUTOR | ANY | PREU | +------------------------+------------------+-------+------------+ | Java Eficac | Joshua Bloch | 2018 | 45,90 | | Patrons de Disseny | Erich Gamma | 1994 | 52,00 | | Refactoritzacio | Martin Fowler | 1999 | 38,75 | +------------------------+------------------+-------+------------+ | TOTAL | | 136,65 | +------------------------+------------------+-------+------------+
El detall que cal interioritzar és el 41 de la fila del total: surt de 22 + 3 + 16 (les dues amplades més el " | " que les separava). En fusionar cel·les cal sumar les amplades i els separadors intermedis.
Solució 3
import java.util.Scanner;
public class RegistreDevolucio {
public static void main(String[] args) {
Scanner entrada = new Scanner(System.in);
// === Regles de negoci de BiblioTech ===
final String NOM_EMPRESA = "Nexus Software";
final int DIES_PRESTEC = 15;
final int AMPLADA = 48;
// === Capcalera ===
System.out.println("=".repeat(AMPLADA));
System.out.printf(" BiblioTech - %s%n", NOM_EMPRESA);
System.out.println(" Registre de devolucio");
System.out.println("=".repeat(AMPLADA));
System.out.println();
// === Recollida de dades (tot amb nextLine) ===
System.out.print("Empleat : ");
String empleat = entrada.nextLine();
System.out.print("Titol del llibre : ");
String titol = entrada.nextLine();
System.out.print("ISBN : ");
String isbn = entrada.nextLine();
System.out.print("Dies transcorreguts: ");
int diesTranscorreguts = Integer.parseInt(entrada.nextLine());
System.out.print("Tarifa diaria (EUR): ");
double tarifaDiaria = Double.parseDouble(entrada.nextLine());
// === Calculs ===
// El ternari satura a zero: una devolucio anticipada
// no pot produir mai un retard negatiu.
int diesRetard = diesTranscorreguts > DIES_PRESTEC
? diesTranscorreguts - DIES_PRESTEC
: 0;
int setmanes = diesRetard / 7; // quocient enter
int diesSolts = diesRetard % 7; // residu
double multa = diesRetard * tarifaDiaria; // int * double -> double
String estat = diesRetard > 0 ? "AMB RETARD" : "EN TERMINI";
// === Rebut ===
System.out.println();
System.out.println("=".repeat(AMPLADA));
System.out.println(" REBUT DE DEVOLUCIO");
System.out.println("=".repeat(AMPLADA));
System.out.printf("%-22s %s%n", "Empleat:", empleat);
System.out.printf("%-22s %s%n", "Llibre:", titol);
System.out.printf("%-22s %s%n", "ISBN:", isbn);
System.out.println("-".repeat(AMPLADA));
System.out.printf("%-22s %5d dies%n", "Prestec estandard:", DIES_PRESTEC);
System.out.printf("%-22s %5d dies%n", "Transcorreguts:", diesTranscorreguts);
System.out.printf("%-22s %5d dies (%d set. i %d dies)%n",
"Retard:", diesRetard, setmanes, diesSolts);
System.out.println("-".repeat(AMPLADA));
System.out.printf("%-22s %8.2f EUR%n", "Tarifa diaria:", tarifaDiaria);
System.out.printf("%-22s %8.2f EUR%n", "MULTA A PAGAR:", multa);
System.out.printf("%-22s %s%n", "Estat:", estat);
System.out.println("=".repeat(AMPLADA));
// Avis pel canal d'error: no forma part del rebut.
System.err.println("[avis] Aquest programa no valida les entrades introduides.");
entrada.close();
}
}Exemple d'execució:
================================================ BiblioTech - Nexus Software Registre de devolucio ================================================ Empleat : Nuria Vidal Titol del llibre : Patrons de Disseny ISBN : 978-0000000002 Dies transcorreguts: 34 Tarifa diaria (EUR): 0.25 ================================================ REBUT DE DEVOLUCIO ================================================ Empleat: Nuria Vidal Llibre: Patrons de Disseny ISBN: 978-0000000002 ------------------------------------------------ Prestec estandard: 15 dies Transcorreguts: 34 dies Retard: 19 dies (2 set. i 5 dies) ------------------------------------------------ Tarifa diaria: 0,25 EUR MULTA A PAGAR: 4,75 EUR Estat: AMB RETARD ================================================
Conclusió
Els teus programes ja es comuniquen en totes dues direccions. Pel costat de la sortida, domines print, println i sobretot printf amb els seus especificadors (%s, %d, %.2f, %n), les seves amplades i la seva alineació, cosa que et permet construir fitxes i taules amb aspecte professional; coneixes String.format per desar aquell text en comptes d'imprimir-lo, i saps que System.err és un canal a part per a avisos i errors. Pel costat de l'entrada, saps crear un Scanner sobre System.in, quan fer servir nextLine davant de next, per què barrejar nextInt() amb nextLine() trenca el flux i com evitar-ho llegint-ho tot com a línia i convertint amb Integer.parseInt i Double.parseDouble, i per què el Scanner s'ha de crear una vegada i tancar-se al final.
Amb això tens totes les peces del mòdul: sintaxi, variables, tipus, operadors i entrada/sortida. A la lliçó següent, El teu primer programa complet: BiblioTech, les muntaràs totes juntes a BiblioTechApp: un programa que dona la benvinguda, recull les dades d'un préstec, calcula el retard i la multa, i emet un rebut formatat. El construirem per versions successives, el compilaràs i executaràs, en comprovaràs la sortida amb dos jocs de dades de prova i analitzaràs amb honestedat què no sap fer encara i quin mòdul ho resoldrà.
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
