%%{init: {"theme": "neutral", "themeVariables": {"noteBkgColor": "#eef7fd", "noteBorderColor": "#31BAE9", "noteTextColor": "#495057"}}}%%
classDiagram
class Llibre {
+String titol
+String autor
+boolean prestat
+prestar()
+retornar()
}
note "La CLASSE és el plànol.<br />Cada objecte és una còpia concreta feta amb aquest plànol."
11 Utilització d’objectes
Objectius
- Explicar què és un objecte i quines característiques té (estat, comportament i identitat).
- Distingir entre classe i objecte.
- Instanciar objectes amb l’operador
newi guardar-los en variables (referències). - Cridar mètodes sobre un objecte i passar-los paràmetres.
- Accedir a les propietats d’un objecte.
- Distingir els membres d’instància dels membres estàtics (
static). - Entendre el paper dels constructors en la creació d’objectes.
- Comprendre com Java allibera la memòria amb el recol·lector de brossa (garbage collector).
Fins ara has escrit programes com una recepta: una llista d’instruccions que manipulen variables soltes (int, double, String, arrays…). Aquesta manera de treballar es diu programació estructurada.
A partir d’ara faràs un salt de mentalitat. Enlloc de pensar en dades i passos separats, pensaràs en objectes: peces que agrupen dins seu unes dades i les operacions que hi actuen. Aquest capítol és la porta d’entrada a la programació orientada a objectes (POO).
11.1 Què és un objecte?
Un objecte és una entitat del programa que representa “una cosa” del món (o del problema) i que conté:
- Estat: les dades que descriuen aquesta cosa en un moment donat. També se’n diu atributs o propietats.
- Comportament: les accions que aquesta cosa pot fer o que li podem demanar. Es materialitza en mètodes.
- Identitat: cada objecte és únic i es distingeix de qualsevol altre, encara que tingui el mateix estat.
Pensa en el teu cotxe. El seu estat és el color, la matrícula, els litres de combustible que li queden i la velocitat actual. El seu comportament és accelerar, frenar o tocar el clàxon. La seva identitat és que és aquest cotxe i no un altre: encara que el veí en tingui un d’idèntic (mateixa marca, mateix color), són dos cotxes diferents.
Classe i objecte no són el mateix
Molt sovint es confonen. La diferència és fonamental:
- Una classe és el motlle o el plànol: descriu quines dades i quins comportaments tindran els objectes d’aquell tipus. La classe no ocupa “coses reals”, és una definició.
- Un objecte és un exemplar concret creat a partir d’aquell motlle. També se’n diu instància.
D’una sola classe Llibre en pots crear molts objectes: El Quixot, Mecanoscrit del segon origen, Solenoide… Tots comparteixen la mateixa estructura (tenen títol, autor i estat de préstec), però cadascun té els seus propis valors.
| Aspecte | Classe | Objecte |
|---|---|---|
| Què és | Definició / motlle | Exemplar concret (instància) |
| Quantitat | N’hi ha una | En pots crear molts |
| Existeix quan | S’escriu el codi | S’executa el programa (new) |
| Ocupa memòria | No (és una plantilla) | Sí (cada objecte té el seu espai) |
| Exemple | Llibre |
El Quixot, Solenoide |
Quan escrivies Scanner teclat = new Scanner(System.in); (Capítol 4) ja creaves un objecte de la classe Scanner! I String, que has fet servir contínuament, també és una classe amb els seus mètodes (length(), toUpperCase()…). La POO ja hi era; ara la faràs explícita.
Mini-repte. Descriu amb paraules una classe Soci d’una biblioteca: quin estat (atributs) creus que hauria de tenir i quins tres comportaments (mètodes) li demanaries.
11.2 Crear objectes: l’operador new
Per crear un objecte s’usa la paraula clau new seguida del nom de la classe i uns parèntesis. Aquest procés es diu instanciació.
// new demana memòria per a un objecte nou i el retorna
Scanner teclat = new Scanner(System.in);
Random daus = new Random();Analitzem-ho pas a pas amb un exemple d’una classe imaginària Comptador:
Comptador c = new Comptador();Aquí passen tres coses alhora:
new Comptador()demana a la JVM un tros de memòria per a un objecteComptadornou i l’inicialitza (crida el constructor, ho veurem més avall).newretorna una referència a aquest objecte: una mena d’adreça que ens diu on és.- La referència es guarda a la variable
c, de tipusComptador.
11.2.1 Variables de referència: l’objecte no viu dins la variable
Aquest és un dels punts que més costa al principi. Amb els tipus primitius (int, double…), la variable conté el valor. Amb els objectes, la variable no conté l’objecte: conté una referència cap a ell. L’objecte viu en un altre lloc de la memòria.
La variable és com el comandament a distància; l’objecte és el televisor. El comandament no és el televisor: només hi apunta. Pots tenir dos comandaments que controlen el mateix televisor, i pots tenir un comandament que encara no apunta a cap televisor.
Llibre a = new Llibre("El Quixot", "Cervantes");
Llibre b = a; // b NO és un llibre nou: apunta AL MATEIX objecte que aAquí només hi ha un objecte Llibre a la memòria, i dues variables (a i b) que hi apunten. Si modifiques l’objecte a través de b, el veuràs canviat també a través de a, perquè és el mateix objecte.
flowchart LR
a["a"] --> obj["Objecte Llibre<br /> titol = El Quixot"]
b["b"] --> obj
11.2.2 null: una referència que no apunta enlloc
Una variable de referència pot no apuntar a cap objecte. Aquest valor especial es diu null.
Llibre preferit = null; // Encara no tinc cap llibre preferitNullPointerException
Si intentes fer servir un objecte a través d’una referència que val null, el programa peta amb una NullPointerException (sovint abreujada NPE). És l’error més habitual en Java.
Llibre preferit = null;
System.out.println(preferit.getTitol()); // ERROR en execució: NullPointerExceptionNo hi ha cap objecte on preguntar el títol! Abans d’usar una referència, assegura’t que apunta a un objecte de veritat (que no és null).
Mini-repte. Escriu dues línies que creïn dos objectes Random diferents. Després escriu-ne una tercera que faci que una nova variable apunti al primer d’ells (sense crear-ne cap més).
11.3 Cridar mètodes i passar paràmetres
Un objecte no és una caixa de dades morta: li podem demanar coses. Es fa amb l’operador punt (.): s’escriu la referència, un punt, el nom del mètode i, entre parèntesis, els arguments (els valors que li passem).
referencia.nomDelMetode(arguments)Vegem-ho amb la classe String, que ja coneixes:
String nom = "biblioteca";
int longitud = nom.length(); // mètode sense paràmetres → 10
String majuscules = nom.toUpperCase(); // → "BIBLIOTECA"
boolean comenca = nom.startsWith("bib"); // mètode amb un paràmetre → true11.3.1 Paràmetres i valor de retorn
- Els paràmetres són les dades d’entrada que el mètode necessita per fer la seva feina. Van entre parèntesis. Un mètode pot no tenir-ne cap, o tenir-ne diversos separats per comes.
- El valor de retorn és el resultat que el mètode et torna. El pots guardar en una variable o usar directament. Si un mètode no retorna res, el seu tipus de retorn és
void.
Random daus = new Random();
int tirada = daus.nextInt(6) + 1; // paràmetre 6, retorna un int entre 1 i 6
System.out.println("Ha sortit un " + tirada);Cridar un mètode és com prémer un botó de la màquina de cafè. Els paràmetres són les opcions que tries (quantitat de sucre, mida del got). El valor de retorn és el cafè que en surt. Alguns botons no et tornen res visible (per exemple, “netejar”): serien mètodes void.
11.3.2 Encadenar crides
Com que molts mètodes retornen un objecte, pots encadenar crides una darrere l’altra:
String resultat = " Hola Món ".trim().toLowerCase(); // → "hola món"trim() retorna un String sense espais, i sobre aquest resultat cridem toLowerCase().
Un mètode SEMPRE porta parèntesis, encara que no tingui paràmetres. nom.length (sense parèntesis) no crida el mètode; provoca un error de compilació. Ha de ser nom.length().
Mini-repte. Donada String frase = "Programació Orientada a Objectes";, escriu una expressió que retorni quants caràcters té la frase en majúscules.
11.4 Accedir a les propietats
A més de cridar mètodes, de vegades es pot llegir o modificar directament l’estat d’un objecte a través dels seus atributs, també amb l’operador punt:
Punt p = new Punt();
p.x = 3; // escriure una propietat
p.y = 5;
System.out.println(p.x + ", " + p.y); // llegir una propietat → 3, 5Poder tocar directament p.x és còmode, però trencar la protecció de les dades d’un objecte no és una bona pràctica. Al Capítol 12 veuràs l’encapsulació: normalment els atributs es fan privats i s’hi accedeix amb mètodes controlats (getters i setters). De moment, quede’t amb la idea que un objecte té estat i que s’hi pot arribar.
11.5 Membres d’instància i membres estàtics
Fins ara tot el que hem cridat pertanyia a un objecte concret: teclat.nextLine() actua sobre aquell Scanner. Es diuen membres d’instància perquè necessiten una instància (un objecte) per funcionar.
Però hi ha membres que pertanyen a la classe sencera, no a cap objecte en particular. Es marquen amb la paraula clau static i es criden usant el nom de la classe en comptes d’una referència.
// Mètode estàtic: es crida amb el nom de la classe
double arrel = Math.sqrt(144); // → 12.0
int gran = Math.max(7, 3); // → 7
// Constant estàtica
double pi = Math.PI; // → 3.141592...No has creat mai cap objecte Math amb new Math(), i no cal: sqrt, max o PI són estàtics, viuen a la classe.
Imagina una botiga. Cada treballador (objecte) atén els seus propis clients: és un membre d’instància. Però hi ha informació de tota la botiga —l’horari, el nom de l’empresa, el total de vendes del dia— que no pertany a cap treballador concret, sinó a la botiga sencera. Això seria un membre static.
| Membre d’instància | Membre estàtic (static) |
|
|---|---|---|
| Pertany a | Cada objecte | La classe sencera |
| Com es crida | objecte.membre |
Classe.membre |
Necessita new? |
Sí | No |
| Còpies en memòria | Una per objecte | Una de sola, compartida |
| Exemple | teclat.nextLine() |
Math.sqrt(9) |
int n = Integer.parseInt("42"); // BÉ: parseInt és static
String s = String.valueOf(42); // BÉ: valueOf és static
Scanner.nextLine(); // ERROR: nextLine NO és static;
// necessita un objecte Scanner concretSi l’IDE et diu “non-static method cannot be referenced from a static context”, és que estàs cridant un mètode d’instància sense un objecte al davant.
Mini-repte. La classe Integer té el mètode estàtic Integer.parseInt(String). Escriu una línia que converteixi el text "128" en un int i el guardi en una variable.
11.6 Els constructors
Quan escrius new Scanner(System.in), els parèntesis del final no són decoratius: estàs cridant un constructor. Un constructor és un bloc de codi especial que s’executa just en crear l’objecte i que serveix per inicialitzar-lo (donar valors inicials al seu estat, preparar recursos, etc.).
// El "System.in" és un argument que rep el constructor de Scanner
Scanner teclat = new Scanner(System.in);
// Molts constructors admeten diferents arguments (o cap)
Random r1 = new Random(); // Constructor sense arguments
Random r2 = new Random(42); // Constructor amb una llavorCaracterístiques clau d’un constructor:
- Es diu exactament igual que la classe.
- No té tipus de retorn (ni tan sols
void). - Es crida amb
newi només aquell moment.
Aprendràs a escriure els teus propis constructors al Capítol 12. De moment, l’important és que entenguis que els arguments que poses dins el new(...) van a parar al constructor per deixar l’objecte a punt.
Crear un objecte Soci és com fer la fitxa d’alta a la biblioteca. El constructor és el formulari inicial: en el moment de crear el soci li poses el nom i el DNI. Sense aquest pas, tindries una fitxa buida i inservible.
Mini-repte. Busca a la documentació (o a l’IDE) quants constructors diferents té la classe StringBuilder. Escriu una línia que en creï un a partir del text inicial "Hola".
11.7 Destrucció d’objectes i recol·lector de brossa
En alguns llenguatges (com C o C++), quan un objecte ja no es necessita, el programador ha d’alliberar-ne la memòria manualment. Si se n’oblida, la memòria es va omplint (una fuita de memòria).
En Java això és automàtic. La JVM té el recol·lector de brossa (garbage collector, GC): un procés que, de tant en tant, busca objectes que ja no són accessibles (cap variable hi apunta) i n’allibera la memòria sense que tu hi facis res.
Llibre ll = new Llibre("Efímer", "Anònim");
ll = null; // Ara ningú apunta a l'objecte anterior:
// El GC el podrà eliminar quan vulguiTu no has de portar les escombraries a l’abocador municipal: deixes la bossa i el servei de neteja passa quan toca. El GC és aquest servei: tu deixes d’usar objectes i ell els recull quan li va bé. No controles el moment exacte en què passa.
finalize()? Millor oblidar-se’n
Antigament hi havia un mètode finalize() per executar codi just abans que el GC eliminés un objecte. Està desaconsellat i obsolet: el seu moment d’execució és imprevisible. Per alliberar recursos (fitxers, connexions) s’usen altres mecanismes, com try-with-resources (Capítol 8).
null “esborra” l’objecte a l’instant
Posar una referència a null no destrueix l’objecte immediatament ni allibera memòria a l’acte. Només trenca l’enllaç. L’objecte desapareixerà quan el GC decideixi passar, cosa que no controles.
Mini-repte. Explica amb les teves paraules per què aquest fragment deixa un objecte “orfe” (sense cap referència) i què li passarà:
Random r = new Random();
r = new Random();11.8 Resum
- Un objecte agrupa estat (atributs), comportament (mètodes) i té identitat pròpia.
- Una classe és el motlle; un objecte és una instància concreta feta amb aquest motlle. De cada classe se’n poden crear molts objectes.
- Els objectes es creen amb
new, que crida el constructor i retorna una referència. - Les variables d’objecte no contenen l’objecte, sinó una referència cap a ell. Dues variables poden apuntar al mateix objecte. Una referència pot valer
null. - Els mètodes es criden amb l’operador punt (
.), amb els paràmetres entre parèntesis, i poden retornar un valor. - Els membres estàtics (
static) pertanyen a la classe (Math.sqrt), no a cap objecte; els membres d’instància necessiten un objecte concret. - El recol·lector de brossa allibera automàticament la memòria dels objectes que ja no són accessibles.
11.9 Per practicar
Objectes quotidians. Tria tres coses del teu voltant (una motxilla, un mòbil, un rellotge). Per a cadascuna, escriu en paper 3 atributs (estat) i 3 mètodes (comportament).
Traça de referències. Sense executar-ho, dibuixa què apunta a què després d’aquest codi i digues què imprimeix (assumeix que
Puntté atributs públicsxiy):
Punt p = new Punt();
p.x = 1;
Punt q = p;
q.x = 9;
System.out.println(p.x);Estàtic o d’instància? Classifica:
Math.random(),teclat.nextInt(),Integer.parseInt("5"),"hola".length(),System.out.println("hi").Caça la NPE. El codi següent llança
NullPointerException. Explica per què i corregeix-lo:
String s;
System.out.println(s.length());- Experiment amb
Random. Crea dos objectesRandomamb la mateixa llavor (new Random(1)) i imprimeix el primernextInt(100)de cadascun. Què observes? Per què creus que passa?