Apèndix B — Classes útils: dates, números i cadenes

Objectius

  • Treballar amb dates i hores (LocalDate, LocalTime, LocalDateTime, Period, Duration, DateTimeFormatter) i saber per què les classes antigues Date i Calendar no es fan servir.
  • Fer càlculs numèrics precisos amb Math i BigDecimal.
  • Formatar i convertir números i monedes amb NumberFormat, Currency i DecimalFormat.
  • Construir i manipular cadenes de text de manera eficient amb String, StringBuilder i StringJoiner.
  • Generar valors aleatoris i identificadors únics amb Random, ThreadLocalRandom i UUID.

Aquest annex aplega classes de la JDK que no formen part del nucli del curs però que necessitaràs constantment en les pràctiques: dates a les factures i registres, monedes als pagaments, cadenes de text a les interfícies… No és una llista exhaustiva: és la caixa d’eines que faràs servir quan toqui.

B.1 Dates i hores: la nova API java.time

B.1.1 Explicació

Durant anys Java va gestionar les dates amb java.util.Date i java.util.Calendar, classes plenes de problemes de disseny. Des de Java 8 hi ha l’API java.time, que és la que s’ha de fer servir avui dia.

Les quatre classes fonamentals són:

Classe Representa Exemple
LocalDate Una data (dia, mes, any) 2026-08-16
LocalTime Una hora (hora, minut, segon) 21:30:15
LocalDateTime Data i hora juntes 2026-08-16T21:30:15
Duration / Period Un lapse de temps 3 dies / 2 hores
ConsellAnalogia: el calendari de la cuina

El calendari de la cuina (la data) i el rellotge del microones (l’hora) són dues coses diferents. LocalDate és el calendari; LocalTime, el rellotge; i LocalDateTime és la foto dels dos junts. Separar-los evita confusions: què vol dir “sumar 1 dia a les 15:30”?

B.1.2 Crear dates: now() i of()

import java.time.LocalDate;
import java.time.LocalTime;
import java.time.LocalDateTime;

LocalDate avui = LocalDate.now();            // data actual del sistema
LocalTime ara = LocalTime.now();             // hora actual
LocalDateTime araMateix = LocalDateTime.now();

LocalDate aniversari = LocalDate.of(2005, 3, 14);   // any, mes, dia
LocalTime classe = LocalTime.of(9, 0);               // 9:00
LocalDateTime festa = LocalDateTime.of(2026, 12, 31, 23, 59);

B.1.3 Llegir i modificar parts d’una data

Les classes tenen mètodes per consultar parts i per obtenir còpies modificades (les dates són immutables: cap mètode les canvia, sempre retorna una de nova).

LocalDate avui = LocalDate.now();
System.out.println(avui.getDayOfMonth());   // 16
System.out.println(avui.getMonthValue());   // 8
System.out.println(avui.getYear());         // 2026
System.out.println(avui.getDayOfWeek());    // SUNDAY

LocalDate dema = avui.plusDays(1);          // NO modifica avui: en retorna una de nova
LocalDate faUnMes = avui.minusMonths(1);
LocalDate primerDelMes = avui.withDayOfMonth(1);
AlertaError típic: pensar que les dates es modifiquen

avui.plusDays(1) no canvia avui. Les classes de java.time són immutables: el resultat s’ha de guardar en una variable nova. Si no, la data “sembla que no canvia mai”.

B.1.4 Comparar dates i calcular diferències

LocalDate avui = LocalDate.now();
LocalDate aniversari = LocalDate.of(2005, 3, 14);

System.out.println(aniversari.isBefore(avui));   // true
System.out.println(avui.isAfter(aniversari));    // true
System.out.println(aniversari.equals(avui));     // false

// Diferència en dies (amb Period, que serveix per a dates)
import java.time.Period;
Period p = Period.between(aniversari, avui);
System.out.println(p.getYears() + " anys");          // 21 anys
System.out.println(p.getMonths() + " mesos i " + p.getDays() + " dies");

// Diferència en hores/minuts (amb Duration, que serveix per a temps)
import java.time.Duration;
LocalTime inici = LocalTime.of(9, 0);
LocalTime fi = LocalTime.of(11, 30);
Duration d = Duration.between(inici, fi);
System.out.println(d.toMinutes() + " minuts");       // 150 minuts

Mini-repte. Quants dies falten per al teu proper aniversari? Calcula-ho amb LocalDate.now(), LocalDate.of(...) i Period.between(...).

B.1.5 Formatat: DateTimeFormatter

Per mostrar dates de manera llegible (i per llegir dates d’un text) es fa servir DateTimeFormatter.

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

LocalDate avui = LocalDate.now();

DateTimeFormatter f1 = DateTimeFormatter.ofPattern("dd/MM/yyyy");
System.out.println(avui.format(f1));                // 16/08/2026

DateTimeFormatter f2 = DateTimeFormatter.ofPattern("EEEE, d 'de' MMMM 'de' yyyy", new Locale("ca"));
System.out.println(avui.format(f2));                // diumenge, 16 de agost de 2026

// Llegir una data des d'un text
LocalDate data = LocalDate.parse("14/03/2005", f1);
System.out.println(data.getYear());                 // 2005

Els patrons més habituals són:

Patró Significat Exemple
d / dd Dia (1-2 dígits) 6 / 06
M / MM Mes (1-2 dígits) 3 / 03
MMM Mes abreujat gen.
MMMM Mes complet gener
yy / yyyy Any (2 / 4 dígits) 26 / 2026
H / HH Hora (0-23) 9 / 09
mm Minuts 05
ss Segons 30
EEEE Dia de la setmana diumenge
'text' Text literal de

B.1.6 Per què no fer servir Date i Calendar

AlertaLes classes antigues: només per interop

java.util.Date (mutable, amb el comptatge de mesos començant a 0, sense dates només-hora…) i Calendar (complexa i poc intuïtiva) estan desaconsellades. L’API java.time les substitueix. Fes servir les noves sempre que puguis.

B.2 Números: Math i BigDecimal

B.2.1 Explicació

La classe Math aporta operacions matemàtiques sobre double. Tots els seus mètodes són estàtics, per això es criden amb el nom de la classe.

double x = 7.4;

System.out.println(Math.round(x));     // 7     (arrodoneix a l'enter més proper)
System.out.println(Math.floor(x));     // 7.0   (cap avall)
System.out.println(Math.ceil(x));      // 8.0   (cap amunt)
System.out.println(Math.abs(-5));      // 5
System.out.println(Math.max(3, 9));    // 9
System.out.println(Math.pow(2, 10));   // 1024.0
System.out.println(Math.sqrt(144));    // 12.0
System.out.println(Math.PI);           // 3.14159...
System.out.println(Math.random());     // un double entre 0 (inclòs) i 1 (exclòs)

B.2.2 BigDecimal: el diner no s’arrodoneix

Els double no representen bé els decimals exactes: 0.1 + 0.2 no és 0.3. Per al diners (i qualsevol càlcul que hagi de ser exacte) s’usa BigDecimal.

import java.math.BigDecimal;
import java.math.RoundingMode;

double a = 0.1;
double b = 0.2;
System.out.println(a + b);               // 0.30000000000000004  (ERROR!)

BigDecimal c = new BigDecimal("0.1");
BigDecimal d = new BigDecimal("0.2");
System.out.println(c.add(d));            // 0.3  (exacte)

// Preu amb IVA i 2 decimals
BigDecimal preu = new BigDecimal("19.99");
BigDecimal iva = preu.multiply(new BigDecimal("0.21"));
BigDecimal total = preu.add(iva).setScale(2, RoundingMode.HALF_UP);
System.out.println(total);               // 24.19
AlertaError típic: crear BigDecimal a partir d’un double

new BigDecimal(0.1) guarda l’error del double dins. Sempre crea’l a partir d’un text: new BigDecimal("0.1").

Mini-repte. Un article costa 35.50 € i s’aplica un descompte del 15 %. Calcula el preu final amb BigDecimal i 2 decimals.

B.3 Monedes i percentatges: NumberFormat i Currency

B.3.1 Explicació

Formatar un número depèn de la llengua (català: coma decimal i punt dels milers; estatunidenc: a l’inrevés). NumberFormat ho fa automàticament a partir d’un Locale.

import java.text.NumberFormat;
import java.util.Currency;
import java.util.Locale;

double preu = 1234.5;

NumberFormat en = NumberFormat.getNumberInstance(Locale.US);      // 1,234.5
NumberFormat ca = NumberFormat.getNumberInstance(new Locale("ca")); // 1.234,5
System.out.println(en.format(preu));
System.out.println(ca.format(preu));

// Moneda: símbol i 2 decimals segons la llengua
NumberFormat euros = NumberFormat.getCurrencyInstance(new Locale("ca"));
euros.setCurrency(Currency.getInstance("EUR"));
System.out.println(euros.format(preu));    // 1.234,50 €

// Percentatge
NumberFormat pct = NumberFormat.getPercentInstance(new Locale("ca"));
System.out.println(pct.format(0.21));      // 21 %
NotaI DecimalFormat?

DecimalFormat permet patrons manualment ("#,##0.00", "00000") sense dependre de la llengua. Va bé quan vols un format fix, però NumberFormat amb un Locale és la manera “correcta” quan l’aplicació pot canviar d’idioma.

B.4 Cadenes de text: String, StringBuilder i StringJoiner

B.4.1 Explicació

String és immutable: cada operació que “modifica” una cadena crea una de nova. Per a concatenacions en bucle això és car. StringBuilder és la classe mutable per construir text de manera eficient. StringJoiner agrupa elements amb un separador.

import java.util.StringJoiner;

// String: immutable (cada + crea una cadena nova)
String nom = "Oriol";
String salutacio = "Hola, " + nom + "!";

// StringBuilder: eficient per a concatenacions en bucle
StringBuilder sb = new StringBuilder();
for (int i = 1; i <= 5; i++) {
    sb.append(i).append(", ");
}
sb.delete(sb.length() - 2, sb.length());   // treu l'últim ", "
System.out.println(sb);                    // 1, 2, 3, 4, 5

// StringJoiner: separador automàtic
StringJoiner sj = new StringJoiner(", ");
sj.add("Anna").add("Biel").add("Carla");
System.out.println(sj);                    // Anna, Biel, Carla

// String.format: plantilles (com printf però retorna una cadena)
String missatge = String.format("Benvingut/da, %s! Tens %d missatges.", nom, 3);
System.out.println(missatge);
ConsellQuan fer servir cadascuna?
  • String — per a text que no canvia, o que canvia poques vegades.
  • StringBuilder — per a concatenacions en bucles o text que es construeix a poc a poc.
  • StringJoiner — per ajuntar una llista d’elements amb un separador.
  • String.format — per a missatges amb plantilla (com printf).

Mini-repte. Converteix la llista ["Anna", "Biel", "Carla"] en el text "Anna · Biel · Carla" fent servir StringJoiner.

B.5 Aleatorietat: Random, ThreadLocalRandom i UUID

B.5.1 Explicació

Per generar valors aleatoris es fa servir java.util.Random (o ThreadLocalRandom en programes concurrents). Per a identificadors únics (un codi de comanda, un id de client) s’usa UUID, que genera un valor pràcticament irrepetible.

import java.util.Random;
import java.util.UUID;

Random r = new Random();
System.out.println(r.nextInt(100));         // un int entre 0 i 99
System.out.println(r.nextInt(10) + 1);      // un int entre 1 i 10
System.out.println(r.nextDouble());         // un double entre 0 i 1
System.out.println(r.nextBoolean());        // true o false

// Llavor: mateixa llavor → mateixa seqüència
Random r1 = new Random(42);
Random r2 = new Random(42);
System.out.println(r1.nextInt(100) == r2.nextInt(100));   // true

// Identificador únic
String id = UUID.randomUUID().toString();
System.out.println(id);    // per exemple 9f3c...-...-...-...-...

B.6 Resum

  • Les dates i hores es treballen amb l’API java.time: LocalDate, LocalTime, LocalDateTime, Period (dates), Duration (temps) i DateTimeFormatter (formatar/analitzar). Són classes immutables.
  • Math dóna operacions matemàtiques; per a càlculs exactes (diners) s’usa BigDecimal creat a partir d’un text.
  • NumberFormat + Locale formata números, monedes i percentatges segons la llengua; DecimalFormat serveix per a patrons fixos.
  • String és immutable; StringBuilder construeix text eficientment en bucles; StringJoiner ajunta elements amb un separador; String.format fa plantilles.
  • Random (amb llavor o no) i UUID generen valors aleatoris i identificadors únics.

B.7 Per practicar

  1. Quants dies has viscut? Calcula els dies entre la teva data de naixement i avui amb Period.between(...).getDays() (ajusta amb anys i mesos).

  2. Format de factura. Formata 1234567.891 com a moneda en català i en estatunidenc. Què canvia?

  3. Llista amb separador. Amb StringJoiner, mostra els teus 5 llibres preferits separats per " · ".

  4. Percentatge exacte. Calcula amb BigDecimal el 15 % de 99.99 € i arrodoneix a 2 decimals amb HALF_UP.

  5. Data des d’un text. Analitza "05/07/2003" amb un DateTimeFormatter i mostra el dia de la setmana en català.