La memoria e gli oggetti

Cosa succede quando si fa p=q oppure p==q?

Come si copiano/confrontano gli oggetti?

Come vengono passati gli oggetti ai metodi?


Variabili e oggetti

  int x;
  Point p;

Per ogni variabile esiste una casellina (spazio di memoria)

Non c'è nessuna differenza! (finora)


Creazione di oggetti

La espressione new Point() crea un oggetto e restituisce l'indirizzo di memoria dell'oggetto.

Effetto di new Point():

new Point() è un metodo;
il valore restituito è la posizione dell'oggetto creato in memoria.


Oggetti e variabili

  p=new Point();

Significa: l'indirizzo di memoria in cui si trova l'oggetto creato va in p


La memoria, in realtà

Finora abbiamo visto la memoria come una lavagna.

In realtà la memoria è come una fila di cassetti numerati:

In ogni cassetto posso metterci un dato

Posso fare operazioni del tipo: metti un dato in un cassetto 32, vedi cosa c'è nel cassetto 9, ecc.

Per accedere a un cassetto (per metterci qualcosa o per vedere cosa c'è) devo specificare il numero

Il numero del cassetto si chiama indirizzo.


Indirizzo == valore?

indirizzo di un cassetto
il numero d'indice del cassetto (il numero scritto al lato, quello che identifica il cassetto)
valore in un cassetto
quello che c'è dentro

Il cassetto numerato 5 (indirizzo 5) contiene il valore 24.

I cassetti hanno tutti indirizzi diversi (non ci sono due cassetti numero 5)

Due cassetti possono contenere lo stesso dato (per esempio, 24 sta sia in 54 che in 1)


Dati in memoria

Nella realtà: quasi tutte le variabili e gli oggetti sono spezzati in più cassetti.

Per noi: facciamo finta che ogni variabile e ogni oggetto sta in un cassetto.


E a noi che ce ne importa?

Per le variabili scalari (interi, ecc) non ha importanza.

Le variabili oggetto contengono un numero, che è l'indirizzo della cella in cui si trova l'oggetto.

Questo ha importanza quando si copiano oggetti, si confrontano, si passano come parametri, ecc.


Creazione di un oggetto

Quando si fa p=new Point();:

  1. si esegue new Point();
  2. il valore che viene ritornato viene messo in p.

Vediamo la memoria un passo per volta.


Dichiarazione di variabili

  int x;
  Point p;

A ogni variabile viene assegnato un cassetto:

In questo esempio, la variabile x è il cassetto numero 1

È lui a scegliere quali cassetti associare alle variabili!

La seconda volta che si esegue il programma, la variabile x potrebbe essere associata al cassetto 29

Voi sapete solo che ogni volta che usate x oppure p, viene usato il cassetto giusto.


Uso delle variabili

  x=-131;

Assegnare un valore=mettere il valore nel cassetto associato all variabile.

indirizzo della variabile
numero di indice della cella, indirizzo della posizione di memoria associata (1 in questo caso)
valore della variabile
quello che c'è nel cassetto (-131 in questo caso)

Creazione di un oggetto

  new Point();

L'oggetto viene messo in un cassetto.

Il metodo new Point() restituisce un numero:

il costruttore restituisce l'indirizzo (numero di indice della cella) in cui si trova l'oggetto creato

In questo caso, viene restituito il valore 5


Assegnare oggetti

Quando faccio p=, l'indirizzo dell'oggetto va in p

In p viene messo 5 perchè l'oggetto creato si trova nel cassetto 5.


La lavagna

Sulla lavagna lo scrivo:

Ricordate che significa: nella variabile p c'è il numero della cella in cui si trova l'oggetto.


Comportamento delle variabili

Le variabili si comportano tutte nello stesso modo.

Non c'è nessuna differenza fra una variabile intera e una variabile oggetto, a parte il tipo


Conseguenze: esempio

  int y;
  Point q;

  y=x;
  q=p;

Cosa succede?

y=x
il valore di x viene copiato dentro y
q=p
il valore di p viene copiato in q

Il valore di una variabile è il numero scritto nel suo cassetto.

Quindi, il valore di x è -131

Il valore di p è 5!


Stato della memoria, prima e dopo

  y=x;
  q=p;

I valori (contenuto delle celle) di x e p vengono copiati (nelle celle) y e q


Memoria, nella metafora della lavagna

Le due variabili contengono una freccia verso lo stesso oggetto.

La freccia dice che in una variabile c'è l'indirizzo di una posizione di memoria.

Se due freccie puntano nello stesso punto, allora i due valori sono gli stessi.


E a noi che ce ne importa (2)?

  x=10;

  y=x;

  y=-5;

  System.out.println(x);

Quando faccio y=x significa solo che in y viene copiato il valore di x

Quando faccio y=-5 il valore di x resta inalterato.

Viene stampato 10


Se faccio lo stesso sugli oggetti?

  p.x=10;

  q=p;

  q.x=4;

  System.out.println(p.x);

È praticamente lo stesso programma di prima.


Cosa succede in memoria

Succedono le stesse cose di prima, però gli effetti sono completamente diversi.

Dopo q=p, l'indirizzo dell'oggetto viene scritto anche in q

Poi faccio q.x=4;
Ma l'oggetto il cui indirizzo sta in q è sempre quello!

Stampare p.x causa la stampa di 4


Differenza

Sembra che siano state fatte le stesse cose:

  var=valore;
  altravar=var;
  altravar=nuovo_valore;

Solo che per interi var non cambia, mentre per gli oggetti si

In realtà, anche sugli oggetti succede la stessa cosa.

Non è una eccezione sugli oggetti.

È una conseguenza del principio generale: nelle variabili c'è l'indirizzo di un oggetto (un numero)

Poi le variabili oggetto si comportano nello stesso modo.


Copia di oggetti

q=p copia l'indirizzo dell'oggetto, non copia l'oggetto

Gli oggetti si possono copiare, ma non facendo q=p

Modo giusto: creo un nuovo oggetto e ci metto dentro i valori dell'altro


Creazione nuovo oggetto

  Point q;
  q=new Point();

  q.x=p.x;
  q.y=p.y;

In memoria, viene creato un nuovo oggetto.

Quando faccio q.x=p.x viene copiato il valore nel nuovo oggetto.

Se ora faccio q.x=4, viene modificato solo il secondo oggetto!


Esercizio

Cosa viene stampato?

  Point p;
  p=new Point();

  p.move(10,20);

  Point q;
  q=new Point();

  q.move(-2,-2);

  q=p;

  p.x=4;

  System.out.println(q.x);

Cosa viene stampato: risposta sbagliata

Quando il meccanismo dei puntatori sarà chiaro,
non saranno necessarie le figure.

Per ora invece servono.

Regola generale: assegnare = copiare il valore

Nel caso delle variabili per oggetti, il valore è l'indirizzo.


Creazione primo oggetto

Viene creato un oggetto, il cui riferimento (indirizzo di memoria) va in p

p.move modifica i campi dell'oggetto il cui riferimento sta in p

I metodi lavorano sull'oggetto dato il suo riferimento


Creazione secondo oggetto

Stessa cosa: creo un oggetto, metto il riferimento in q, invoco il metodo.


Assegnamento fra oggetti

Faccio l'assegnamento q=p

Quello che è successo: il numero scritto in p è stato copiato in q

È la stessa cosa che viene fatta quando si fa x=y con due variabili intere.


Modifico un oggetto

  p.x=4;

La variabile p.x viene modificata.

Quanto vale q.x?

È la componente x dell'oggetto il cui indirizzo sta in q

Quindi, è sempre 4


Perdita di oggetti

Non c'è più nessuna variabile che contiene l'indirizzo del secondo oggetto.

Il secondo oggetto non si può più usare.

L'oggetto viene cancellato in modo automatico.


Se volevo ancora usare il secondo oggetto?

Point t;

t=q;
q=s;

In questo caso, ottengo questo stato della memoria:

Posso ancora usare il secondo oggetto, dato che il suo indirizzo sta in t


Come ottenere la soluzione giusta

Quando si lavora con oggetti, le variabili contengono numeri (indirizzi).

Le variabili si comportano come variabili intere.

Per evitare di scrivere dei numeri a caso, uso le freccie. Ma è solo un modo semplificato.


Assegnamento

Copio i valori.

Questo equivale a ``copiare le freccie''

q=p mette in q una freccia che va nello stesso punto della freccia di p

Da adesso in poi, facciamo tutte le figure con le freccie.

Ricordate che si tratta solo di un modo per rappresentare indirizzi.


Esercizio

class Segmento {
  Point inizio;
  Point fine;
}

Sia dato questo programma:

  Segmento s, r;
  s=new Segmento();

  r=s;

Si può fare?


Si può fare

L'unico vincolo è che non posso usare gli oggetti fino a che non li ho creati.

Ma posso usare s anche se s.inizio ed s.fine non sono stati creati.

Posso memorizzare un valore in r anche se non ho creato l'oggetto.

Non posso fare r.inizio=... prima di aver fatto r=s soltanto perchè non esiste l'oggetto puntato da r


Esercizio: continuazione

  Segmento s, r;
  s=new Segmento();

  s.inizio=new Point();
  s.inizio.x=12;

  r=new Segmento();
  r.inizio=s.inizio;
  
  s.inizio.x=-4;

  System.out.println(r.inizio.x);

Cosa viene stampato?

Fare la figura con lo stato della memoria


Copiatura di oggetti

Stato dopo la creazione di s ed r

Scrivo i nomi delle classi su ogni oggetto

Per ora, ci serve a non fare confusione.

Poi si vedrà che serve.


Assegnamento r.inizio=s.inizio

Vale la solita regola: il valore della variabile viene copiato.

Ma s.inizio indica un oggetto, quindi la variabile contiene un riferimento.

Il riferimento viene copiato.


Risultato

s.inizio.x ed r.inizio.x sono la stessa cosa.

Viene stampato -4

sbagliato:
s.inizio.x ed r.inizio.x sono due variabili che hanno lo stesso valore
giusto:
s.inizio.x ed r.inizio.x sono la stessa variabile

Confronto fra variabili

  int x, y;
  Point p, q;

  ...

  if(x==y) ...

  if(p==q) ...
x==y
è vera se le due variabili contengono lo stesso valore
p==q
è vera se le due variabili contengono lo stesso valore

È sempre la stessa cosa!


Ma in pratica...

p==q non dice se l'oggetto p è uguale all'oggetto q!

  Point p, q;

  p=new Point();
  p.move(10,10);

  q=new Point();
  q.move(10,10);

  if(p==q)
    System.out.println("Sono uguali");
  else
    System.out.println("Non sono uguali");

Cosa viene stampato?


Risposta sbagliata

Dato che i due punti sono uguali,
la condizione p==q è vera.

Cosa succede in memoria?


In memoria

Dopo aver creato i due oggetti:

Ogni volta che faccio new, ho un nuovo oggetto, in una nuova posizione.

Sono due oggetti diversi, per cui le loro posizioni in memoria sono diverse!

Il contenuto di p e di q è lo stesso?

p contiene 5 mentre q contiene 7.

Sono diversi.


Ma allora q==p è sempre falso?

  q=p;

  if(q==p)
    ...

Ora p e q contengono lo stesso valore.


Confronti e freccie

p==q è vera se e solo se le freccie di p e di q vanno nello stesso punto:


Interpretazione delle freccie

creare nuovo oggetto
viene creato un nuovo rettangolo
assegnare un puntatore
mettere una freccia verso lo stesso punto di arrivo dell'altra (vale anche per la creazione dell'oggetto, pensando che new Tipo() è una variabile che punta all'oggetto creato.
confronto fra puntatori
vedere se le freccie vanno nello stesso punto

Ma allora come li confronto?

Basta guardare i valori:

  Point p, q;

  p.move(10,10);
  q.move(10,10);

  if((p.x==q.x) && (p.y==q.y))
    ...

Attenzione agli oggetti con dentro altri oggetti!
(non va fatto il confronto fra due variabili oggetto anche se stanno dentro un altro oggetto)


Metodo equals

Su alcuni tipi, è definito il metodo equals che confronta gli oggetti, non gli indirizzi.

  Point p, q;

  p.move(10,10);
  q.move(10,10);

  if(p.equals(q))
    ...

Definire un metodo che confronta

Definire un metodo uguale che confronta due oggetti NumeroComplesso

class NumeroComplesso {
  double reale;
  double imm;
}

Mettere il metodo dentro la classe.


Metodo di confronto fra complessi: firma

Ha un oggetto di invocazione; l'altro oggetto va passato.

Restituisce un boolean.

  boolean uguale(NumeroComplesso n) {
    ...
  }

Metodo di confronto fra complessi: corpo

Ritorno true solo se i due numeri sono uguali.

  boolean uguale(NumeroComplesso n) {
    return ((this.reale==n.reale) && (this.imm==n.imm));
  }

Fra dati scalari (interi, ecc) il confronto con == confronta il valore!


Equals

Su tutti gli oggetti è definito un metodo equals

Fa cose diverse a seconda della classe:

  Point p, q;
  p=new Point();
  p.move(10,20);
  q=new Point();
  q.move(10,20);

Qui p.equals(q) vale true

  Studente s, t;
  s=new Studente();
  s.nome="Paolo";
  s.media=19;
  t=new Studente();
  t.nome="Paolo";
  t.media=19;

Qui s.equals(t) è false

Vedremo in dettaglio l'ereditarietà, e poi come è definito esattamente il metodo equals

Per ora: se voglio aggiungere un metodo di confronto fra oggetti in una classe, lo chiamo uguale invece di equals


Puntatori e stringhe

Le variabili oggetto contengono il riferimento all'oggetto.

q=p
copia un riferimento (locazione di memoria)
q==p
confronta due riferimenti (locazioni di memoria)

Vale anche per le stringhe.


Le stringhe costanti

Quando una stringa costante, tipo "abcd" appare due volte in un programma, è lo stesso oggetto

  String s, q;

  s="abcd";
  q="abcd";

  System.out.println(s==q)

Viene stampato true


Stringhe costanti in memoria

Quando appare due volta la stessa stringa costante, viene usato lo stesso oggetto.

In questo caso, s==t ritorna true


Allora == confronta le stringhe?

NO

    s="abcd";
    q="abcd";

    System.out.println(s==q);

    String a, b;
    a="ab";
    b="cd";

    String t=a.concat(b);

    System.out.println(s==t);
    System.out.println(q==t);

Stato della memoria:

Questo discorso dello stesso oggetto vale solo per le stringhe costanti.
(quelle scritte con "...")


Come fare il confronto fra oggetti?

Dipende da cosa fa equals:

usare equals:
tipo predefinito in cui equals confronta i valori (Point, Rectangle, String)
non usare equals:
tutti gli altri tipi, inclusi quelli che si ottengono per estensione di Point ecc.

Nel secondo caso, si può definire un metodo uguale che confronto i valori numerici.


Passaggio dei parametri

Quando si invoca un metodo, i parametri attuali vengono copiati nei parametri formali.

Questo è un assegnamento come quelli visti finora.

Segue le regole dell'asegnamento:
se il parametro formale è un intero, si copia l'intero;
se è un oggetto, si copia il riferimento.

Conseguenze: altra lezione.