Les sis lliçons anteriors t'han donat peces soltes: l'estructura d'un fitxer Java, les variables i els seus tipus, els operadors i l'entrada/sortida per consola. Aquesta lliçó les munta totes juntes en el primer programa real del curs: BiblioTechApp, l'aplicació que Nexus Software farà servir per registrar la devolució d'un llibre de la seva biblioteca tècnica i emetre el rebut amb la multa corresponent. No l'escriurem de cop: el construirem en quatre versions successives, cadascuna millorant l'anterior, que és exactament com es desenvolupa programari a la vida real. Al final analitzarem amb honestedat què no sap fer encara i quin mòdul del curs resoldrà cada mancança.

Contingut

  1. L'especificació del programa
  2. Versió 1: l'esquelet amb dades fixes
  3. Versió 2: demanar les dades a l'usuari
  4. Versió 3: càlculs correctes amb l'operador ternari
  5. Versió 4: la sortida formatada i el sostre de multa
  6. El codi complet final, comentat
  7. Compilar i executar BiblioTechApp
  8. Jocs de dades de prova i sortida esperada
  9. Les limitacions del programa i com es resoldran
  10. Errors Habituals i Consells
  11. Exercicis

  1. L'especificació del programa

Abans d'escriure una línia, convé fixar què ha de fer el programa. Això és el primer que es fa en qualsevol projecte professional, i estalvia moltíssima feina.

Nom: BiblioTechApp Paquet: com.nexussoftware.bibliotech

Comportament esperat:

  1. Mostrar una capçalera de benvinguda amb el nom de l'empresa i de l'aplicació.
  2. Demanar per consola les dades de la devolució d'un préstec:
    • Nom de l'empleat
    • Títol del llibre
    • ISBN
    • Dies transcorreguts des que es va prestar
  3. Calcular:
    • Els dies de retard, que mai no poden ser negatius.
    • El retard expressat en setmanes completes i dies solts.
    • La multa: dies de retard multiplicats per la tarifa diària.
    • El sostre: la multa no pot superar mai el màxim establert.
    • L'estat del préstec: en termini o amb retard.
  4. Mostrar un rebut formatat i llegible amb totes aquestes dades.

Regles de negoci de la biblioteca de Nexus Software:

Regla Valor Constant
Durada estàndard del préstec 15 dies DIES_PRESTEC
Tarifa per dia de retard 0,25 € TARIFA_DIARIA
Multa màxima per préstec 20,00 € MULTA_MAXIMA
Llindar de retard lleu 7 dies LLINDAR_LLEU

Restriccions tècniques d'aquesta versió: un únic mètode main, sense condicionals, sense bucles, sense classes pròpies. Tot el que necessites ja ho has après.

flowchart TD
    A["Mostrar capçalera"] --> B["Demanar empleat, titol,<br/>ISBN i dies transcorreguts"]
    B --> C["Calcular dies de retard<br/>(saturats a zero)"]
    C --> D["Descompondre en<br/>setmanes i dies"]
    D --> E["Calcular multa base<br/>= retard x tarifa"]
    E --> F["Aplicar sostre<br/>MULTA_MAXIMA"]
    F --> G["Determinar estat<br/>i gravetat"]
    G --> H["Imprimir rebut<br/>formatat amb printf"]

  1. Versió 1: l'esquelet amb dades fixes

La primera versió no demana res: les dades estan escrites al codi. Serveix per validar l'estructura i els càlculs sense la complicació de l'entrada.

package com.nexussoftware.bibliotech;

public class BiblioTechApp {

    public static void main(String[] args) {

        final String NOM_EMPRESA = "Nexus Software";
        final int DIES_PRESTEC = 15;
        final double TARIFA_DIARIA = 0.25;

        String empleat = "Marta Ruiz";
        String titol = "Java Eficac";
        String isbn = "978-0000000001";
        int diesTranscorreguts = 27;

        int diesRetard = diesTranscorreguts - DIES_PRESTEC;
        double multa = diesRetard * TARIFA_DIARIA;

        System.out.println("=== BiblioTech - " + NOM_EMPRESA + " ===");
        System.out.println("Empleat: " + empleat);
        System.out.println("Llibre: " + titol);
        System.out.println("ISBN: " + isbn);
        System.out.println("Dies de retard: " + diesRetard);
        System.out.println("Multa: " + multa + " EUR");
    }
}

Sortida:

=== BiblioTech - Nexus Software ===
Empleat: Marta Ruiz
Llibre: Java Eficac
ISBN: 978-0000000001
Dies de retard: 12
Multa: 3.0 EUR

Funciona, però té tres defectes evidents que anirem corregint:

  1. Les dades són fixes. Per processar una altra devolució cal editar el codi i recompilar.
  2. Els càlculs són ingenus. Si el llibre es retorna als 9 dies, diesRetard valdria -6 i la multa -1.5 EUR: la biblioteca pagant a l'empleat.
  3. La sortida és pobra. 3.0 EUR no és un import presentable, i les etiquetes no estan alineades.

  1. Versió 2: demanar les dades a l'usuari

Substituïm els valors fixos per lectura amb Scanner, aplicant la regla de la lliçó anterior: tot es llegeix amb nextLine() i es converteix amb Integer.parseInt quan cal.

package com.nexussoftware.bibliotech;

import java.util.Scanner;

public class BiblioTechApp {

    public static void main(String[] args) {

        Scanner entrada = new Scanner(System.in);

        final String NOM_EMPRESA = "Nexus Software";
        final int DIES_PRESTEC = 15;
        final double TARIFA_DIARIA = 0.25;

        System.out.println("=== BiblioTech - " + NOM_EMPRESA + " ===");
        System.out.println("Registre de devolucio de prestec");
        System.out.println();

        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());

        int diesRetard = diesTranscorreguts - DIES_PRESTEC;
        double multa = diesRetard * TARIFA_DIARIA;

        System.out.println();
        System.out.println("Empleat: " + empleat);
        System.out.println("Llibre: " + titol);
        System.out.println("ISBN: " + isbn);
        System.out.println("Dies de retard: " + diesRetard);
        System.out.println("Multa: " + multa + " EUR");

        entrada.close();
    }
}

Ja és un programa útil: el mateix binari serveix per a qualsevol devolució. Però el defecte dels números negatius continua sent-hi. Prova-ho introduint 9 als dies transcorreguts i veuràs Multa: -1.5 EUR.

  1. Versió 3: càlculs correctes amb l'operador ternari

Aquí entra en joc l'operador ternari per saturar el retard a zero, i de passada hi afegim la descomposició en setmanes i l'estat del préstec. Aquest és el fragment que canvia:

// El retard no pot ser mai negatiu: si es retorna abans d'hora, es 0.
int diesRetard = diesTranscorreguts > DIES_PRESTEC
        ? diesTranscorreguts - DIES_PRESTEC
        : 0;

// Descomposicio del retard: / dona les setmanes completes, % els dies sobrants.
int setmanesRetard = diesRetard / 7;
int diesSolts = diesRetard % 7;

// Dies que queden de termini (0 si ja ha vencut).
int diesRestants = diesTranscorreguts < DIES_PRESTEC
        ? DIES_PRESTEC - diesTranscorreguts
        : 0;

double multaBase = diesRetard * TARIFA_DIARIA;

// Etiquetes de text triades amb ternaris.
String estat = diesRetard > 0 ? "AMB RETARD" : "EN TERMINI";
String gravetat = diesRetard == 0 ? "-"
                : diesRetard <= LLINDAR_LLEU ? "LLEU"
                : "GREU";

Aquest bloc concentra bona part del que s'ha après a la lliçó d'operadors: comparació, resta, divisió entera, mòdul, multiplicació amb promoció automàtica d'int a double, ternari simple i ternari imbricat. Res d'això no requereix if.

  1. Versió 4: la sortida formatada i el sostre de multa

Falten dues coses: aplicar el sostre de multa i presentar el rebut com un document. Totes dues es resolen amb el que hem vist a la lliçó 01-06.

// Sostre: si la multa base supera el maxim, es cobra el maxim.
double multaFinal = multaBase > MULTA_MAXIMA ? MULTA_MAXIMA : multaBase;
String sostreAplicat = multaBase > MULTA_MAXIMA ? "SI" : "NO";

// Rebut formatat: etiquetes alineades a l'esquerra en amplada 22,
// imports alineats a la dreta amb exactament dos decimals.
System.out.println("=".repeat(AMPLADA));
System.out.printf("%-22s %s%n", "Empleat:", empleat);
System.out.printf("%-22s %8.2f EUR%n", "MULTA A PAGAR:", multaFinal);

Amb això, 3.0 es converteix en 3,00 i les columnes queden alineades. Ja tenim el programa complet.

  1. El codi complet final, comentat

package com.nexussoftware.bibliotech;

import java.util.Scanner;

/**
 * BiblioTechApp - Sistema de gestio de la biblioteca tecnica interna
 * de Nexus Software.
 *
 * Versio 0.1 del modul 1: registre lineal de la devolucio d'un
 * prestec, amb calcul de retard i multa, i emissio d'un rebut.
 *
 * @author Equip BiblioTech - Nexus Software
 * @version 0.1
 */
public class BiblioTechApp {

    public static void main(String[] args) {

        // ---------------------------------------------------------------
        // 1. RECURSOS
        // ---------------------------------------------------------------
        // Un unic Scanner per a tota l'aplicacio, connectat al teclat.
        // Tancar un Scanner sobre System.in tanca System.in, aixi que
        // mai no se'n crea mes d'un.
        Scanner entrada = new Scanner(System.in);

        // ---------------------------------------------------------------
        // 2. REGLES DE NEGOCI
        // ---------------------------------------------------------------
        // Totes les constants van juntes i a dalt: canviar una tarifa
        // es editar UNA linia, no buscar el numero per tot el programa.
        // 'final' garanteix que ningu no les modifiqui per accident.
        final String NOM_EMPRESA = "Nexus Software";
        final String VERSIO = "0.1";
        final int DIES_PRESTEC = 15;         // durada estandard del prestec
        final double TARIFA_DIARIA = 0.25;   // euros per dia de retard
        final double MULTA_MAXIMA = 20.0;    // sostre absolut de la multa
        final int LLINDAR_LLEU = 7;          // fins a 7 dies, el retard es lleu
        final int DIES_SETMANA = 7;
        final int AMPLADA = 52;              // amplada de les linies del rebut

        // ---------------------------------------------------------------
        // 3. CAPCALERA DE BENVINGUDA
        // ---------------------------------------------------------------
        // "=".repeat(AMPLADA) construeix la linia separadora sense teclejar
        // 52 signes igual a ma. repeat() existeix des de Java 11.
        System.out.println("=".repeat(AMPLADA));
        System.out.printf("  BiblioTech v%s - %s%n", VERSIO, NOM_EMPRESA);
        System.out.println("  Registre de devolucio de prestec");
        System.out.println("=".repeat(AMPLADA));
        System.out.println();

        // ---------------------------------------------------------------
        // 4. RECOLLIDA DE DADES
        // ---------------------------------------------------------------
        // Es fa servir print (sense ln) perque el cursor es quedi a la
        // mateixa linia que la pregunta.
        // TOTA la lectura es fa amb nextLine(): aixi mai no queda un
        // salt de linia pendent al flux d'entrada, que es el que
        // provoca el classic problema de nextInt() seguit de 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: ");
        // El text llegit es converteix a numero amb parseInt.
        // Si l'usuari escriu alguna cosa que no es un enter, el programa
        // acaba amb NumberFormatException (es tractara al modul 6).
        int diesTranscorreguts = Integer.parseInt(entrada.nextLine());

        // ---------------------------------------------------------------
        // 5. CALCULS
        // ---------------------------------------------------------------

        // Dies de retard saturats a zero. Sense el ternari, una devolucio
        // anticipada donaria un retard negatiu i, per tant, una multa
        // negativa: la biblioteca pagant a l'empleat.
        int diesRetard = diesTranscorreguts > DIES_PRESTEC
                ? diesTranscorreguts - DIES_PRESTEC
                : 0;

        // Dies que queden de termini. Nomes te sentit si encara no ha vencut.
        int diesRestants = diesTranscorreguts < DIES_PRESTEC
                ? DIES_PRESTEC - diesTranscorreguts
                : 0;

        // Descomposicio del retard: la divisio entera dona les setmanes
        // completes i el modul dona els dies que sobren.
        int setmanesRetard = diesRetard / DIES_SETMANA;
        int diesSolts = diesRetard % DIES_SETMANA;

        // Multa base. diesRetard es int i TARIFA_DIARIA es double:
        // Java promociona l'int a double abans de multiplicar.
        double multaBase = diesRetard * TARIFA_DIARIA;

        // Sostre de la multa. L'import cobrat no supera mai MULTA_MAXIMA.
        double multaFinal = multaBase > MULTA_MAXIMA ? MULTA_MAXIMA : multaBase;
        String sostreAplicat = multaBase > MULTA_MAXIMA ? "SI" : "NO";

        // Etiquetes de text derivades dels calculs.
        String estat = diesRetard > 0 ? "AMB RETARD" : "EN TERMINI";

        // Ternari imbricat, llegit com una escala de dalt a baix.
        String gravetat = diesRetard == 0 ? "-"
                        : diesRetard <= LLINDAR_LLEU ? "LLEU"
                        : "GREU";

        // Codi de referencia del rebut: inicial de l'empleat en
        // majuscules + els quatre ultims digits de l'ISBN.
        // charAt(0) pren el primer caracter; substring retalla des de la
        // posicio indicada fins al final.
        String referencia = String.format("%c%s-%s",
                empleat.charAt(0),
                titol.substring(0, 3).toUpperCase(),
                isbn.substring(isbn.length() - 4));

        // ---------------------------------------------------------------
        // 6. REBUT
        // ---------------------------------------------------------------
        System.out.println();
        System.out.println("=".repeat(AMPLADA));
        System.out.println("  REBUT DE DEVOLUCIO");
        System.out.println("=".repeat(AMPLADA));

        // %-22s alinea l'etiqueta a l'ESQUERRA en amplada 22, creant
        // una columna uniforme. %s enganxa el valor a continuacio.
        System.out.printf("%-22s %s%n", "Referencia:", referencia);
        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));

        // %5d alinea els enters a la DRETA en amplada 5, de manera que
        // les unitats queden a la mateixa columna i es comparen d'un cop d'ull.
        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%n", "Restants de termini:", diesRestants);
        System.out.printf("%-22s %5d dies (%d set. i %d dies)%n",
                "Retard:", diesRetard, setmanesRetard, diesSolts);

        System.out.println("-".repeat(AMPLADA));

        // %8.2f: amplada total 8, exactament 2 decimals. Es el que
        // converteix un 3.0 cru en un 3,00 presentable.
        System.out.printf("%-22s %8.2f EUR%n", "Tarifa diaria:", TARIFA_DIARIA);
        System.out.printf("%-22s %8.2f EUR%n", "Multa calculada:", multaBase);
        System.out.printf("%-22s %8.2f EUR%n", "Multa maxima:", MULTA_MAXIMA);
        System.out.printf("%-22s %8s%n", "Sostre aplicat:", sostreAplicat);

        System.out.println("-".repeat(AMPLADA));

        System.out.printf("%-22s %8.2f EUR%n", "TOTAL A PAGAR:", multaFinal);
        System.out.printf("%-22s %s (%s)%n", "Estat:", estat, gravetat);

        System.out.println("=".repeat(AMPLADA));

        // Avis pel canal d'error: no forma part del rebut i es pot
        // redirigir per separat en executar el programa.
        System.err.println("[avis] Versio 0.1: el programa no valida les entrades.");

        // ---------------------------------------------------------------
        // 7. ALLIBERAMENT DE RECURSOS
        // ---------------------------------------------------------------
        entrada.close();
    }
}

  1. Compilar i executar BiblioTechApp

El fitxer ha d'estar al camí que correspon al seu paquet:

bibliotech/
└── src/
    └── main/
        └── java/
            └── com/
                └── nexussoftware/
                    └── bibliotech/
                        └── BiblioTechApp.java

Situat a src/main/java:

# Compilar. S'indica el CAMI del fitxer, amb barres.
# -d envia els .class a una carpeta 'out' separada del codi font.
javac -d ../../../out com/nexussoftware/bibliotech/BiblioTechApp.java

# Executar. S'indica el NOM DE LA CLASSE qualificat, amb punts.
# -cp diu a l'ordre java on ha de buscar les classes compilades.
java -cp ../../../out com.nexussoftware.bibliotech.BiblioTechApp

Si prefereixes treballar sense paquet per provar de pressa, elimina la primera línia (package ...;) i fes servir el mode de fitxer únic de Java 11+:

java BiblioTechApp.java

I si redirigir la sortida et resulta útil, recorda que el rebut i l'avís van per canals diferents:

# El rebut es desa en un fitxer; l'avis continua apareixent a pantalla
java -cp ../../../out com.nexussoftware.bibliotech.BiblioTechApp > rebut.txt

A l'IDE n'hi ha prou de prémer el botó d'executar sobre la classe; IntelliJ i Eclipse fan per sota exactament aquestes dues ordres.

  1. Jocs de dades de prova i sortida esperada

Provar un programa vol dir executar-lo amb dades triades a propòsit per cobrir situacions diferents. Aquests són els dos casos que has de verificar:

Dada Joc A: préstec amb retard Joc B: préstec en termini
Empleat Marta Ruiz Diego Alonso
Títol Java Eficac Refactoritzacio
ISBN 978-0000000001 978-0000000003
Dies transcorreguts 27 9
Dies de retard esperats 12 0
Dies restants esperats 0 6
Descomposició 1 setmana i 5 dies 0 setmanes i 0 dies
Multa calculada 3,00 EUR 0,00 EUR
Sostre aplicat NO NO
Total a pagar 3,00 EUR 0,00 EUR
Estat AMB RETARD (GREU) EN TERMINI (-)
Referència MJAV-0001 DREF-0003

Sortida completa del joc A

====================================================
  BiblioTech v0.1 - Nexus Software
  Registre de devolucio de prestec
====================================================

Empleat            : Marta Ruiz
Titol del llibre   : Java Eficac
ISBN               : 978-0000000001
Dies transcorreguts: 27

====================================================
  REBUT DE DEVOLUCIO
====================================================
Referencia:            MJAV-0001
Empleat:               Marta Ruiz
Llibre:                Java Eficac
ISBN:                  978-0000000001
----------------------------------------------------
Prestec estandard:        15 dies
Transcorreguts:           27 dies
Restants de termini:       0 dies
Retard:                   12 dies (1 set. i 5 dies)
----------------------------------------------------
Tarifa diaria:             0,25 EUR
Multa calculada:           3,00 EUR
Multa maxima:             20,00 EUR
Sostre aplicat:                 NO
----------------------------------------------------
TOTAL A PAGAR:             3,00 EUR
Estat:                 AMB RETARD (GREU)
====================================================

Sortida completa del joc B

====================================================
  BiblioTech v0.1 - Nexus Software
  Registre de devolucio de prestec
====================================================

Empleat            : Diego Alonso
Titol del llibre   : Refactoritzacio
ISBN               : 978-0000000003
Dies transcorreguts: 9

====================================================
  REBUT DE DEVOLUCIO
====================================================
Referencia:            DREF-0003
Empleat:               Diego Alonso
Llibre:                Refactoritzacio
ISBN:                  978-0000000003
----------------------------------------------------
Prestec estandard:        15 dies
Transcorreguts:            9 dies
Restants de termini:       6 dies
Retard:                    0 dies (0 set. i 0 dies)
----------------------------------------------------
Tarifa diaria:             0,25 EUR
Multa calculada:           0,00 EUR
Multa maxima:             20,00 EUR
Sostre aplicat:                 NO
----------------------------------------------------
TOTAL A PAGAR:             0,00 EUR
Estat:                 EN TERMINI (-)
====================================================

Observa el valor del joc B: el cas "en termini" és el que demostra que el ternari funciona. Sense ell, el retard seria -6 i la multa -1,50 EUR. Triar dades que exercitin els casos límit, i no només el cas normal, és el nucli de les proves de programari (mòdul 11).

Un tercer cas que val la pena provar pel teu compte: Nuria Vidal, Patrons de Disseny, ISBN 978-0000000002, 120 dies. El retard seria de 105 dies i la multa calculada de 26,25 EUR, per damunt del màxim: el programa ha de mostrar Sostre aplicat: SI i un total de 20,00 EUR.

  1. Les limitacions del programa i com es resoldran

BiblioTechApp funciona, però és un programa de joguina i convé ser explícit sobre el perquè. Reconèixer les mancances del teu propi codi és una de les habilitats més valuoses d'un desenvolupador.

Limitació Què passa avui Quan es resol
No valida les entrades Si escrius vint-i-set als dies, el programa peta amb NumberFormatException Mòdul 2 (comprovar amb hasNextInt) i mòdul 6 (capturar l'excepció i reaccionar)
No repeteix Processa una devolució i acaba. Per a la següent cal tornar a executar-lo Mòdul 2: bucles i menú interactiu (lliçó 02-06)
No pren decisions complexes Tot es resol amb ternaris, que només trien valors, no executen accions diferents Mòdul 2: if, switch
Les dades estan escampades titol, autor, isbn i disponible són variables soltes sense relació entre elles Mòdul 3: la classe Llibre les agruparà en un tipus propi
Només manega un llibre i un empleat No hi ha manera de gestionar un catàleg de centenars de títols Mòdul 5: ArrayList i HashMap
No desa res En tancar el programa, el rebut desapareix per sempre Mòdul 7: escriptura de fitxers, CSV
No avisa ningú Ningú no rep un recordatori abans que venci el termini Mòdul 8: tasques en segon pla
Només funciona a la teva màquina Un company no pot consultar el catàleg des del seu equip Mòduls 9 i 12: xarxa i aplicació web
Fa servir double per a diners Els imports poden acumular errors d'arrodoniment Mòdul 10: BigDecimal
No hi ha proves Verifiques la sortida a ull, executant a mà Mòdul 11: JUnit

Fixa't en la lògica del curs: cada mòdul posterior elimina una limitació concreta i visible del programa que acabes d'escriure. No aprendràs bucles perquè toqui, sinó perquè avui el teu programa només processa una devolució. No aprendràs classes perquè sí, sinó perquè avui titol i autor són dues variables sense res que les uneixi.

Errors Habituals i Consells

  • Oblidar l'entrada.nextLine() extra després d'un nextInt(). En aquest programa no passa perquè tota la lectura és amb nextLine(), però si barreges mètodes tornarà a aparèixer.
  • Escriure 0,25 en comptes de 0.25 en introduir decimals. Double.parseDouble espera el punt, encara que printf mostri coma.
  • Que substring(isbn.length() - 4) falli. Si l'ISBN té menys de 4 caràcters, llança StringIndexOutOfBoundsException. En aquesta versió assumim entrades correctes.
  • Que empleat.charAt(0) falli si l'usuari prem Enter sense escriure res: la cadena buida no té caràcter a la posició 0.
  • Fer servir %d amb un double al printf del total. Provoca IllegalFormatConversionException.
  • Oblidar %n en algun printf, cosa que enganxa dues línies del rebut.
  • Posar les constants disperses pel codi. Agrupa-les al principi: és la diferència entre canviar la tarifa en deu segons o buscar el 0.25 per tot el fitxer.
  • Consell: quan un càlcul tingui diversos passos (multa base → sostre → total), crea una variable per pas amb nom descriptiu. Una sola expressió gegant és impossible de depurar.
  • Consell: executa el programa amb els tres jocs de dades (retard normal, en termini, sostre superat) cada vegada que el modifiquis. És la teva primera bateria de proves manual, i al mòdul 11 la convertiràs en automàtica.
  • Consell: desa aquesta versió de BiblioTechApp.java. Al mòdul 2 en partiràs per afegir-hi el menú interactiu, i comparar totes dues versions et mostrarà el teu propi progrés.

Exercicis

Exercici 1: Ampliar el rebut amb la fiança i l'avís de venciment

Modifica BiblioTechApp per afegir al rebut:

  1. Una fiança del 10 % del preu del llibre (demana'l per consola com un double), que es retorna íntegra si no hi ha retard i es redueix en l'import de la multa si n'hi ha. La fiança a retornar no pot ser mai negativa.
  2. Un avís de venciment proper: un text que valgui "VENCIMENT PROPER" si queden 3 dies o menys de termini (i encara no ha vençut), "EN CURS" si en queden més, i "VENCUT" si ja hi ha retard.

Fes servir només ternaris, sense if. Formata tots els imports amb dos decimals.

Exercici 2: Percentatges i estadístiques del préstec

Amplia el rebut amb tres indicadors calculats, tots amb dos decimals:

  1. El percentatge de termini consumit: diesTranscorreguts / DIES_PRESTEC * 100. Compte amb la divisió entera.
  2. El cost mitjà per dia transcorregut: la multa final dividida entre els dies transcorreguts.
  3. El percentatge de la multa màxima que representa la multa final.

Mostra els tres alineats en una secció "INDICADORS" del rebut.

Exercici 3: Rebut per a dos préstecs simultanis

Sense fer servir bucles ni arrays, amplia el programa perquè registri la devolució de dos llibres del mateix empleat i mostri un rebut conjunt amb:

  • Les dades de tots dos llibres en una taula alineada amb printf.
  • La multa individual de cadascun.
  • El total acumulat, subjecte a un sostre conjunt de MULTA_MAXIMA.
  • Quin dels dos llibres acumula més retard (amb l'operador ternari).

Reflexiona al final: què passaria si Nexus Software volgués processar deu llibres? Anota la teva resposta; la reprendrem al mòdul 5.

Solucions

Solució 1

Afegeix la lectura del preu i aquest bloc de càlculs i sortida (la resta del programa no canvia):

        // --- Lectura addicional ---
        System.out.print("Preu del llibre    : ");
        double preuLlibre = Double.parseDouble(entrada.nextLine());

        // --- Calculs de fianca i avis ---
        final double PERCENTATGE_FIANCA = 0.10;   // 10 % del preu
        final int LLINDAR_AVIS = 3;               // dies per avisar del venciment

        // La fianca es el 10 % del preu: double * double -> double.
        double fianca = preuLlibre * PERCENTATGE_FIANCA;

        // Se'n descompta la multa, pero mai no pot quedar en negatiu:
        // el ternari satura el resultat a zero.
        double fiancaRetornada = fianca - multaFinal > 0
                ? fianca - multaFinal
                : 0.0;

        // La retencio es la part de la fianca que es queda la biblioteca.
        double fiancaRetinguda = fianca - fiancaRetornada;

        // Avis de venciment: escala de ternaris.
        // S'avalua de dalt a baix i s'atura a la primera condicio certa.
        String avis = diesRetard > 0 ? "VENCUT"
                    : diesRestants <= LLINDAR_AVIS ? "VENCIMENT PROPER"
                    : "EN CURS";

        // --- Sortida addicional ---
        System.out.println("-".repeat(AMPLADA));
        System.out.printf("%-22s %8.2f EUR%n", "Preu del llibre:", preuLlibre);
        System.out.printf("%-22s %8.2f EUR%n", "Fianca dipositada:", fianca);
        System.out.printf("%-22s %8.2f EUR%n", "Fianca retinguda:", fiancaRetinguda);
        System.out.printf("%-22s %8.2f EUR%n", "FIANCA A RETORNAR:", fiancaRetornada);
        System.out.printf("%-22s %s%n", "Avis:", avis);

Comprovació amb els tres casos:

Cas Preu Fiança (10 %) Multa Retinguda A retornar Avís
Marta, 27 dies 45,90 4,59 3,00 3,00 1,59 VENCUT
Diego, 9 dies 38,75 3,88 0,00 0,00 3,88 EN CURS
Diego, 13 dies 38,75 3,88 0,00 0,00 3,88 VENCIMENT PROPER

El punt clau de l'exercici és l'ordre de l'escala de ternaris: la comprovació de diesRetard > 0 ha d'anar la primera. Si es posés després, un préstec vençut amb diesRestants igual a 0 entraria per la branca de "VENCIMENT PROPER", que és falsa.

Solució 2

        // --- Indicadors ---
        // Compte: diesTranscorreguts i DIES_PRESTEC son int, aixi que
        // la divisio seria ENTERA. Cal forcar un double abans.
        double percentatgeTermini = (double) diesTranscorreguts / DIES_PRESTEC * 100;

        // Cost mitja per dia transcorregut. Aqui multaFinal ja es double,
        // aixi que la promocio es automatica i no cal cap cast.
        double costMitjaDiari = multaFinal / diesTranscorreguts;

        // Quin percentatge del sostre representa el que es cobrara.
        double percentatgeMaxim = multaFinal / MULTA_MAXIMA * 100;

        System.out.println("-".repeat(AMPLADA));
        System.out.println("  INDICADORS");
        System.out.println("-".repeat(AMPLADA));
        System.out.printf("%-22s %8.2f %%%n", "Termini consumit:", percentatgeTermini);
        System.out.printf("%-22s %8.4f EUR/dia%n", "Cost mitja diari:", costMitjaDiari);
        System.out.printf("%-22s %8.2f %%%n", "Sobre multa maxima:", percentatgeMaxim);

Sortida per al joc A (Marta Ruiz, 27 dies, multa 3,00):

----------------------------------------------------
  INDICADORS
----------------------------------------------------
Termini consumit:        180,00 %
Cost mitja diari:        0,1111 EUR/dia
Sobre multa maxima:       15,00 %

Tres detalls importants:

  • El cast (double) és obligatori al primer càlcul. Sense ell, 27 / 15 donaria 1 (divisió entera) i el percentatge seria 100,00 % en comptes de 180,00 %. És el parany de la lliçó 01-05 en un cas real.
  • %% imprimeix un símbol de percentatge literal. Un % solt faria que printf intentés interpretar-lo com l'inici d'un especificador i llancés UnknownFormatConversionException.
  • %.4f al cost diari perquè amb dos decimals el valor 0,1111 s'arrodoniria a 0,11 i perdria significat. La precisió s'ha de triar segons la magnitud de la dada.

Solució 3

        // ================= SEGON LLIBRE =================
        System.out.println();
        System.out.println("--- Segon llibre ---");

        System.out.print("Titol del llibre 2 : ");
        String titol2 = entrada.nextLine();

        System.out.print("ISBN 2             : ");
        String isbn2 = entrada.nextLine();

        System.out.print("Dies transcorreguts 2: ");
        int diesTranscorreguts2 = Integer.parseInt(entrada.nextLine());

        // Els mateixos calculs que per al primer, duplicats.
        // Aquesta duplicacio es precisament el que resoldran els METODES
        // (modul 3) i els BUCLES sobre colleccions (modul 5).
        int diesRetard2 = diesTranscorreguts2 > DIES_PRESTEC
                ? diesTranscorreguts2 - DIES_PRESTEC
                : 0;

        double multaBase2 = diesRetard2 * TARIFA_DIARIA;

        // Total conjunt, subjecte a un unic sostre per a tota la devolucio.
        double totalBrut = multaBase + multaBase2;
        double totalFinal = totalBrut > MULTA_MAXIMA ? MULTA_MAXIMA : totalBrut;

        // Quin acumula mes retard.
        String llibreMesRetardat = diesRetard >= diesRetard2 ? titol : titol2;
        int retardMesGran = diesRetard >= diesRetard2 ? diesRetard : diesRetard2;

        // ================= REBUT CONJUNT =================
        System.out.println();
        System.out.println("=".repeat(AMPLADA));
        System.out.println("  REBUT CONJUNT DE DEVOLUCIO");
        System.out.println("=".repeat(AMPLADA));
        System.out.printf("%-22s %s%n", "Empleat:", empleat);
        System.out.println("-".repeat(AMPLADA));

        // Capcalera de la taula i dues files amb el mateix format.
        System.out.printf("%-24s %-16s %5s %9s%n", "LLIBRE", "ISBN", "RETR.", "MULTA");
        System.out.printf("%-24s %-16s %5d %9.2f%n", titol, isbn, diesRetard, multaBase);
        System.out.printf("%-24s %-16s %5d %9.2f%n", titol2, isbn2, diesRetard2, multaBase2);

        System.out.println("-".repeat(AMPLADA));
        System.out.printf("%-22s %8.2f EUR%n", "Suma de multes:", totalBrut);
        System.out.printf("%-22s %8.2f EUR%n", "TOTAL A PAGAR:", totalFinal);
        System.out.printf("%-22s %s (%d dies)%n", "Retard mes gran:", llibreMesRetardat, retardMesGran);
        System.out.println("=".repeat(AMPLADA));

Exemple de sortida amb Marta Ruiz, "Java Eficac" (27 dies) i "Patrons de Disseny" (40 dies):

====================================================
  REBUT CONJUNT DE DEVOLUCIO
====================================================
Empleat:               Marta Ruiz
----------------------------------------------------
LLIBRE                   ISBN             RETR.     MULTA
Java Eficac              978-0000000001      12      3,00
Patrons de Disseny       978-0000000002      25      6,25
----------------------------------------------------
Suma de multes:             9,25 EUR
TOTAL A PAGAR:              9,25 EUR
Retard mes gran:       Patrons de Disseny (25 dies)
====================================================

La reflexió final de l'exercici és la més important de tot el mòdul. Per a deu llibres necessitaries titol1titol10, isbn1isbn10, diesRetard1diesRetard10: trenta variables, trenta blocs de lectura idèntics i trenta càlculs copiats. El codi seria tres vegades més llarg, impossible de mantenir, i n'hi hauria prou de teclejar diesRetard3 on volies diesRetard4 per produir un rebut erroni que ningú no detectaria.

Aquest dolor té tres solucions i les veuràs en aquest ordre: els bucles (mòdul 2) eliminen la repetició del procés; les classes (mòdul 3) agrupen titol, isbn i diesRetard en un únic objecte Llibre; i les col·leccions (mòdul 5) desen tants llibres com calgui en una sola variable. No és casualitat que el curs segueixi precisament aquest camí.

Conclusió

Has escrit el teu primer programa Java complet i funcional. BiblioTechApp dona la benvinguda a l'usuari, recull per consola les dades d'una devolució, calcula els dies de retard saturats a zero amb l'operador ternari, els descompon en setmanes i dies amb la divisió entera i el mòdul, obté la multa promocionant automàticament int a double, hi aplica un sostre, deriva etiquetes d'estat i gravetat amb ternaris imbricats, compon una referència amb mètodes de String i emet un rebut perfectament alineat amb printf. Tot això amb un únic main, constants final ben agrupades i un únic Scanner correctament tancat.

Amb aquesta lliçó tanques el mòdul 1. Saps què és Java i com s'executa, tens un entorn de desenvolupament funcionant, entens l'anatomia d'un fitxer font, manegues variables, tipus, literals i conversions, domines els operadors i els seus paranys, i saps comunicar-te amb l'usuari per consola en totes dues direccions. I, sobretot, saps exactament què li falta al teu programa: no repeteix, no decideix, no valida i no recorda res.

Al mòdul 2, Flux de control, començaràs a resoldre-ho: amb if i switch el teu programa prendrà decisions de debò, amb while i for processarà tantes devolucions com calgui, amb break i continue afinarà aquest control, aprendràs a depurar pas a pas per veure què passa dins del teu codi, i tot culminarà en el menú interactiu de BiblioTech, la versió que ja no acaba després d'un sol préstec.

Curs de Programació en Java

Mòdul 1: Introducció a Java

Mòdul 2: Flux de control

Mòdul 3: Programació orientada a objectes

Mòdul 4: Programació orientada a objectes avançada

Mòdul 5: Estructures de dades i col·leccions

Mòdul 6: Gestió d'excepcions

Mòdul 7: Entrada/sortida de fitxers

Mòdul 8: Multifil i concurrència

Mòdul 9: Xarxes

Mòdul 10: Temes avançats

Mòdul 11: Frameworks i llibreries de Java

Mòdul 12: Construcció d'aplicacions del món real

© Copyright 2026. Tots els drets reservats