package, interfacce, ereditarietà

60
Package, Interfacce, Ereditarietà Dott. Ing. Leonardo Rigutini Dipartimento Ingegneria dell’Informazione Università di Siena Via Roma 56 – 53100 – SIENA Uff. 0577233606 [email protected] www.dii.unisi.it/ ~rigutini /

Upload: jaron

Post on 06-Jan-2016

58 views

Category:

Documents


1 download

DESCRIPTION

Dott. Ing. Leonardo Rigutini Dipartimento Ingegneria dell’Informazione Università di Siena Via Roma 56 – 53100 – SIENA Uff. 0577233606 [email protected] www.dii.unisi.it/ ~rigutini /. Package, Interfacce, Ereditarietà. Package. I package. - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: Package, Interfacce, Ereditarietà

Package,Interfacce,

Ereditarietà

Dott. Ing. Leonardo RigutiniDipartimento Ingegneria dell’InformazioneUniversità di SienaVia Roma 56 – 53100 – SIENAUff. [email protected]/~rigutini/

Page 2: Package, Interfacce, Ereditarietà

Package

Page 3: Package, Interfacce, Ereditarietà

I package

Un programma JAVA è costituito da una raccolta di classi. Finora i nostri programmi erano composti da un piccolo numero di classi

Quando queste tuttavia aumentano di numero, limitarsi a distribuire le classi su più file non basta ed occorre un meccanismo per organizzare le collezioni di classi in modo chiaro e semplice

In JAVA questo meccanismo è fornito dai pacchetti: un pacchetto (package) è costituito da una serie di clssi correlate

La libreria JDK è costituita da centinaia di classi organizzate in dozzine di pacchetti: java.lang , java.awt, java.io , ecc…

Page 4: Package, Interfacce, Ereditarietà

Definire il package

Per inserire le classi in un pacchetto si usa la seguente sintassi all’inizio del file:

package NOME_PACCHETTO;

Il nome del pacchetto può essere costituito da una serie di identificatori, separati da punti: L’idea è quella di organizzare i package in maniera gerarchica e quindi

dare la possibilità di creare un albero di pacchetti Ogni punto individua un “ramo” dell’albero

Page 5: Package, Interfacce, Ereditarietà

Importare pacchetti

Se vogliamo utilizzare una classe di un pacchetto è necessario specificare al compilatore di quale pacchetto abbiamo bisogno

E’ possibile dereferenziare direttamente la classe utilizzando il percorso completo della classe in fase di dichiarazione di una variabile oggetto:

Questo sistema è evidentemente molto scomodo, quindi in alternativa è possibile importare all’inizio della definizione della classe i pacchetti che poi verranno utilizzati nel codice tramite la parola import:

java.awt.Rectangle R= new java.awt.Rectangle(5,10,20,30)

import java.awt.Rectangle;…Rectangle R= new Rectangle(5,10,20,30)

Page 6: Package, Interfacce, Ereditarietà

Importare package

Quando di un package sono utilizzate molte classi, è possibile collassare le molte righe relative a quel package in una riga

In questo modo viene specificato di importare tutte le classi presenti nel package

Nel caso si utilizzi package omonimi o classi omonime in package diversi, è necessario utilizzare comunque la notazione estesa per risolvere l’eventuale ambiguità

import java.awt.*;import java.awt.Rectangle;import java.awt.Color;

Page 7: Package, Interfacce, Ereditarietà

Come vengono organizzate le classi I package hanno una corrispondenza “fisica” sul progetto che si sta

sviluppando: Ovvero se una classe fa parte di un package, allora deve risiedere in

una cartella il cui percorso di directory coincide con la gerarchia del package

Es

package prova.bankaccount;

La classe deve essere salvata in[CLASSPATH]/prova/bankaccount/BankAccount.class

Page 8: Package, Interfacce, Ereditarietà

Come vengono organizzate le classi Notare come la root deve essere una directory nel CLASSPATH del

JCompiler e della JVM

Se vogliamo che la classe BankAccount o tutto il package bankaccount.prova.* siano visibili in una classe terza, è necessario inserire la directory da cui classes.bankaccount.* nasce:

C:\Programmi\JBuilder2005\jdk1.4\bin\javaw -classpath “D:\Documenti\Activities\Teaching\LinguaggiProgrammazione - storing2005\Examples\04.Package\01.BankAccount-Package\classes;”

Directory root per i package prova.* e prova.bankaccount.*

Page 9: Package, Interfacce, Ereditarietà

Pacchetti compressi

Usualmente il pacchetto viene fornito in formato compresso (jar): All’interno del file .jar vengono memorizzate le classi del pacchetto in

una struttura a directory che rispecchia i package di ogni classe

Nella creazione del jar è possibile specificare se includere anche le dipendenze, ovvero tutti quelle classi (con i relativi package) che sono utilizzate nel pacchetto che stiamo costruendo

Inoltre è possibile inserire nel file .jar anche la documentazione, in modo da avere un pacchetto completo memorizzato in un singolo file e compresso

Page 10: Package, Interfacce, Ereditarietà

…\04.Package\01.BankAccount-Package>java –cp BankAccount.jar prova.TestBankAccount1500.0

Pacchetti compressi

L’utilizzo dei file jar segue quello deli pacchetti normali: Nel CLASSPATH è possibile specificare come path di ricerca classi

anche i files .jar in cui sono memorizzati i package Il JCompiler e la JVM entrano all’interno dei file jar per utilizzare tutte le

classi necessarie

…\04.Package\01.BankAccount-Package>java TestBankAccountException in thread "main" java.lang.NoClassDefFoundError: TestBankAccount

File jar per la classe prova.TestBankAccount e per la classe prova.bankaccount.BankAccount

Chiamata della classe TestBankAccount specificando il package

Page 11: Package, Interfacce, Ereditarietà

Interfacce e polimorfismo

Page 12: Package, Interfacce, Ereditarietà

Riutilizzo

Supponiamo di avere una classe DataSet che calcola alcune statistiche di un insieme di valori in ingresso (media, massimo, varianza ecc…).

Vorremmo una classe che calcoli le stesse informazioni su oggetti BankAccount (diversi dai semplici numeri). La cosa che dobbiamo fare è modificare la classe Dataset aggiungendo

metodi che ricevono come parametro oggetti BankAccount

In questo modo però se vogliamo estendere le funzionalità della classe DataSet ad altre classi (Coin per esempio) rimane necessario ogni volta aggiungere metodi per la classe desiderata

Page 13: Package, Interfacce, Ereditarietà

Riutilizzo

public class DataSet {public void add(int n) {

sum+=n;cc++;

}public double avg() {

return sum/cc;}

}

public class DataSet {private int sum;private int cc=0;

public void add(BankAccount B) {sum+=B.getBalance();cc++;

}

public void add(Coin C) {

sum+=C.getValue();cc++;

}

public double avg() {return sum/cc;

}

Page 14: Package, Interfacce, Ereditarietà

Riutilizzo

Possiamo notare però che il meccanismo per l’analisi dei dati è il solito in tutti i casi: viene letto il valore dell’oggetto passato e viene calcolata l’informazione

richiesta

Se tutte le classi che noi vogliamo passare alle funzioni di DataSet potessero accordarsi su un unico metodo getValue() che ritorna il valore su cui la classe DataSet calcola le statistiche, potremmo definire una sola funzione add()

Page 15: Package, Interfacce, Ereditarietà

Riutilizzo

Ma che tipo di oggetto passiamo come parametro per add() ?Definiamo una classe fittizia Measurable che descrive solamente l’interfaccia comune e poi specifichiamo le diverse classi che “implementano” l’interfaccia

public interface Measurable {public double getValue();

}public class DataSet {

public void add(Measurable x) {sum+=x.getValue();cc++;

}public double avg() {

return sum/cc;}

}

Page 16: Package, Interfacce, Ereditarietà

Riutilizzo

Le classi che dovranno essere passate come parametri di add(Measurable x) dovranno essere specificate come implementazioni della classe Measurable e dovranno implementare il metodo specificato nell’interfaccia:

public class BankAccount implements Measurable {public double getValue() {

return balance;}

}

public class Coin implements Measurable {public double getValue() {

return value;}

}

Page 17: Package, Interfacce, Ereditarietà

Interfaccia

I metodi di una interfaccia non sono dichiarati public perché lo sono per impostazione predefinita. Tuttavia, i metodi di una classe non sono pubblici se ciò non viene specificato: Quindi nell’ implementazione di una interfaccia è necessario specificare i

metodi come public

La classe Measurable, è una descrizione di più alto livello delle classi BankAccount e Coin. E’ possibile quindi utilizzare una variabile oggetto di tipo Measurable per memorizzare un oggetto di tipo BankAccount o Coin

Measurable x=new Coin();

Page 18: Package, Interfacce, Ereditarietà

Operatore instanceof

L’operazione inversa, invece, non è immediata come la precedente: Se siamo sicuri che un oggetti di tipo Measurable sia di tipo Coin

possiamo “forzare” la conversione tramite un cast Nel caso però in cui la forzatura non sia valida, ossia a run-time l’oggetto

forzato non è di tipo Coin, viene ritornato errore

Per verificare a run-time la corretta appartenenza ad una classe di un oggetto è possibile utilizzare l’operatore instanceof:

Measurable x= …;Coin y=(Coin)x;

Measurable x= …;Coin y;If (x instanceof Coin) {

y=(Coin)x;}

Page 19: Package, Interfacce, Ereditarietà

Interfacce

Le interfacce non possono avere variabili, ma è possibile dichiarare constanti che verranno “ereditate” da tutte le classi che implementano l’interfaccia: Quando vengono dichiarate le costanti in una interfaccia dovrebbero

essere omesse le parole chiave public, static e final poiché tutte le variabili in una interfaccia sono definite automaticamente public static final

public interface Move{int NORTH=0;int EAST=3;int SOUTH=6;int WEST=9;

}

Page 20: Package, Interfacce, Ereditarietà

Polimorfismo

La riga di codice

è molto utilizzata in pratica e permette di avere la variabile x disponibile per memorizzare oggetti di classi diverse:

Occorre ricordare però che in realtà non esiste alcun oggetto di tipo Measurable. Il tipo dell’oggetto sarà sempre una classe che implementa l’interfaccia

Measurable x;

x=new BankAccount();x=new Coin();

Page 21: Package, Interfacce, Ereditarietà

Polimorfismo

Pensiamo adesso a cosa accade quando viene chiamata una funzione dell’interfaccia:la JVM risolve il tipo dell’oggetto su cui è chiamato il metodo d’interfaccia ed invoca quello relativo alla classe “corretta”

Ciò significa che l’invocazione di un metodo d’interfaccia può chiamare metodi diversi: Il principio secondo cui il tipo dell’oggetto determinato a run-time

determina il metodo da chiamare è detto polimorfismo

In realtà in JAVA tutti i metodi sono polimorfi, poiché la scelta del metodo “giusto” da eseguire può dipendere dalla valutazione delle classi passate come parametro: overloading

Page 22: Package, Interfacce, Ereditarietà

Inner Class

Page 23: Package, Interfacce, Ereditarietà

Inner Class

Le Inner class sono classi definite all’interno di altre classi.

class Homer {

class Son { public void Speak() { System.out.println(“Son: Eat my socks!"); } }

Son Bart = new Son(); public void Speak() { System.out.println("Homer: DOH!"); Bart.Speak(); } }

Page 24: Package, Interfacce, Ereditarietà

Inner Class

Le Inner class non possono essere istanziate direttamente con new da classi esterne:

Un modo per rendere visibile la classe è creare un metodo che ritorni una istanza della Inner Class:

Homer.son Bart = new Homer.Son();

ERRORE

class Homer { … public Son CreateSon() { return new Son(); } public static void main(String[] args) { Homer Homer1 = new Homer(); Homer.Son Bart = Homer1.CreateSon(); } }

OK

Page 25: Package, Interfacce, Ereditarietà

Inner Class

Le Inner class che implementano interfacce pubbliche possono essere visibili all’esterno pur avendo l’implementazione nascosta all’interno della classe contenitore

interface Simpson { public void Speak(); }

class Homer {

pricate class Son implements Simpson { public void Speak() { System.out.println(“Son: Eat my socks!"); } } : : }

Page 26: Package, Interfacce, Ereditarietà

Inner Class definite nei metodi

Le Inner class possono essere definite anche nei metodi, nascondendole del tutto al resto del programma

È possibile utilizzare queste classi all’esterno solo se posseggono una interfaccia pubblica o una classe base

class Homer {

public void Speak() { class Son { System.out.println(“Son: Eat my socks!"); } } : : }

Page 27: Package, Interfacce, Ereditarietà

Inner Class anonime

È possibile costruire classi innestate senza specificare il tipo, ma solo specificando l’interfaccia che implementano

interface Point { public void setXY(int x, int y) public int getX(); public int getY(); } public class Prova { static public Point getPoint(final int x, final int y) { return new Point() { private int X, Y; {X = x; Y = y; } public void setXY(int x, int y) {X = x; Y = y;} public int getX() { return X; } public int getY() { return Y; } } } public static void main(String[] args) { Point p = getPoint(0, 0); System.out.println(“P(”+p.getX()+“,” +p.getY()+“)”); }}

Inner Class anonima che implementa l’interfaccia Point

Page 28: Package, Interfacce, Ereditarietà

Ereditarietà

Page 29: Package, Interfacce, Ereditarietà

Organizzazione gerarchica

Abbiamo visto che i package forniscono una struttura gerarchica per l’organizzazione delle classi

Una organizzazione gerarchica può esistere però anche tra le classi: una classe può essere un tipo particolare di un’altra classe più generica:

Veicolo Auto Ferrari

È possibile definire una struttura tra classe in cui da una classe generale vengono derivate classi sempre più specifiche che ereditano le proprietà ed i metodi delle classi genitore

Page 30: Package, Interfacce, Ereditarietà

Eredità

Come detto un problema può essere descritto in maniera più chiara se decomposto concettualmente in classi

Molte volte può essere necessario individuare classi di classi: in molte situazioni infatti è possibile individuare oggetti generali da cui

discendono una serie di classi “del tipo di”

In tale immaginario, le classi derivate “ereditano” lo stato delle classi genitore, aggiungendo funzionalità proprie della classe specifica:

Page 31: Package, Interfacce, Ereditarietà

Ereditarietà

Se pensiamo ad un veicolo, esso avrà delle funzioni (muovi, frena, gira ecc…) e delle proprietà (peso, lunghezza, larghezza ecc…)

Da una classe di oggetti veicolo possono poi essere derivati altri tipi di oggetti: bicicletta, Autoveicolo, Motoveicolo

ognuno con delle funzionalità proprie: ad esempio la funzione accendi() non dovrebbe appartenere alla classe bicicletta

Inoltre la classe autoveicolo potrebbe a sua volta essere suddivisa in base al tipo di autoveicolo: Auto, camion, van, ecc…

ognuna con proprietà e funzioni proprie e diverse dall’altro

Page 32: Package, Interfacce, Ereditarietà

Ereditarietà

Veicolo

Autoveicolo MotoveicoloBicicletta

AutomobileAutocarro

Utilitaria Berlina Jeep

Page 33: Package, Interfacce, Ereditarietà

Ereditarietà

In Java è possibile dichiarare una classe come figlia di un’altra classe tramite la parola extends

La classe genitore viene riferita come superclasse, mentre la classe più specifica (figlia) viene chiamata sottoclasse

Nei diagrammi, l’ereditarietà siindica con una freccia a vuotodiretta dal figlio verso il padre

public class Auto extends Veicolo {

}

Veicolo

Auto

Page 34: Package, Interfacce, Ereditarietà

Eredità o interfaccia ?

A questo punto potremmo chiederci cosa ci sia di diverso tra l’ereditarietà e l’implementazione di una interfaccia?

Un’interfaccia non è una classe, non ha uno stato né un comportamento: indica solamente quali metodi sono da implementare.

Una superclasse ha uno stato ed un comportamaento e questi vengono ereditati dalla classe derivata.

L’ereditarietà realizza il concetto di riutilizzo del codice: grazie all’ereditarietà, infatti, non siamo costretti a rifare il lavoro di

progettazione della classe e di implementazione delle funzioni più generali

Page 35: Package, Interfacce, Ereditarietà

Ereditare variabili e metodi

Quando definiamo una classe come estensione di una superclasse, ereditiamo tutte le variabili e le funzioni della classe genitore ed in più possiamo aggiungere nuove variabili o funzioni

Cosa succede se ridefiniamo un metodo che già esiste nella superclasse?

Il metodo viene “sovrascritto” (override) ovvero il metodo della superclasse è sostituito dalla nuova implementazione del metodo nella sottoclasse. Ogni chiamata a quel metodo da oggetti della sottoclasse, viene risolta con la nuova implementazione

Page 36: Package, Interfacce, Ereditarietà

Ereditare variabili e metodi

Così come è possibile ridefinire le funzioni, è possibile ridefinire anche le variabili membro, mettendo in ombra così le variabili della superclasse omonime: in questo caso però, nella sottoclasse esiste comunque una istanza

della variabile della superclasse

In entrambi i casi è sempre possibile fare riferimento ad una variabile o un metodo della classe genitore tramite l’indicatore di oggetto super:

…super.drive();…int y = super.carburante;

Chiama esplicitamente la funzione drive() della classe genitore

Legge il valore della variabile membro carburante della classe genitore

Page 37: Package, Interfacce, Ereditarietà

Costruttore

Tramite la parola super, è possibile fare riferimento anche al costruttore della superclasse:

class Quadrato extends Rettangolo { public Quadrato(float lung){ super(lung,lung); }

public float diag(){ return Math.sqrt(2) * l; }}

....Quadrato q1=new Quadrato(2);System.out.println(q1.area());

class Rettangolo { float h, l; public Rettangolo(float alt, float lung){ h=alt; l=lung; }

public float diag(){ return Math.sqrt((h*h)+(l*l)); }}

override

costruttore super-class

Page 38: Package, Interfacce, Ereditarietà

Visibilità

L’ereditarietà implica alcuni problemi di visibilità dei metodi e delle variabili

Che diritti può avere una classe derivata su un metodo o una variabile membro della superclasse che è stata definita privata?

È possibile definire variabili private che siano private ma utilizzabili per le classi derivate?

Sono state definite le seguenti regole tra classi e sottoclassi: La visibilità di una variabile o di un metodo di una superclasse è

specificata da “specificatori di accesso”

Page 39: Package, Interfacce, Ereditarietà

Specificatori di accesso

Public – rende visibile la variabile o il metodo a tutte le classi

Private – nasconde la variabile o il metodo a tutte le classi, comprese le classi derivate

Protected – nasconde le variabili ed i metodi a tutte le classi tranne quelle nello stesso package

Nessun specificatore – utilizza Protected come visibilità di default

Page 40: Package, Interfacce, Ereditarietà

Visibilità

Stesso Package Package diversiEreditabile Accessibile EreditabileAccessibile

Public sモ sモ sモ sモProtected sモ sモ sモ noPrivate sモ sモ no no

Page 41: Package, Interfacce, Ereditarietà

Upcasting

È la proprietà più importante dell’ereditarietà

Il concetto è molto semplice: tutti i metodi che hanno una classe come parametro accettano anche tutti le classi derivate

Il termine upcasting deriva dalla direzione con cui ci si muove sugli alberi di ereditarietà:

Metodi

Stato

Metodi

Stato Veicolo

Auto

Page 42: Package, Interfacce, Ereditarietà

Upcasting

public class Concessionario { protected Veicolo[] parcheggio; // costruttore: specifica la dimensione del parcheggio public Concessionario(int dim) { parcheggio = new Veicolo[dim]; } // mette un oggetto nel parcheggio senza controllare lo spazio public void add (Veicolo v) {

parcheggio[parcheggio.length]=v;}

}

Inserimento di un oggetto Veicolo: può essere un oggetto Auto, Autocarro, ecc… upcast

Page 43: Package, Interfacce, Ereditarietà

Downcasting

Anche qui il concetto è semplice: se siamo sicuri che un handle, pur essendo di una classe, si riferisca ad un oggetto discendente, allora è possibile effettuare la conversione

La conversione è forzata: cast esplicito

// preleva un veicolo dal parcheggio: se è un oggetto Auto, // lo restituisce come oggetto Auto, altrimenti ritorna null public Auto getAuto(int x) {

if (parcheggio[x] instanceof Auto) return (Auto) parcheggio[x];return null;}

downcast

Page 44: Package, Interfacce, Ereditarietà

RTTI (Run-Time Type Identification) Per risolvere un riferimento ad una superclasse si può utilizzare la

risoluzione a run-time del tipo di oggetto

Avevamo già visto nel caso di interface come risolvere a run-time il tipo di oggetto: instanceof

L’RTTI (Run-Time Type Identification) è il sistema che permette di capire a che classe appartiene un oggetto.

Page 45: Package, Interfacce, Ereditarietà

Downcastingpublic class Concessionario { protected Veicolo[ ] parcheggio; // costruttore: specifica la dimensione del parcheggio public Concessionario(int dim) { parcheggio = new Veicolo[dim]; } // mette un oggetto nel parcheggio senza controllare lo spazio public void add (Veicolo v) {

parcheggio[parcheggio.length]=v;}

// preleva un veicolo dal parcheggio: se è un oggetto Auto, // lo restituisce come oggetto Auto, altrimenti ritorna null public Auto getAuto(int x) {

if (parcheggio[x] instanceof Auto) return (Auto) parcheggio[x];return null;}

}

Inserimento di un oggetto Veicolo: può essere un oggetto Auto, Autocarro, ecc… upcast

Prelievo di un Oggetto Auto: cast da Veicolo ad Auto downcast

Page 46: Package, Interfacce, Ereditarietà

Lo specificatore final

Avevamo visto che final era utilizzato all’interno di una classe per definire una costante: Una variabile inizializzata in fase di dichiarazione e non più modificabile

È possibile utilizzare final anche per i metodi: in tal caso il metodo non può più essere sovrascritto in una classe derivata

Page 47: Package, Interfacce, Ereditarietà

Lo specificatore abstract

Il JAVA permette di definire classi i cui metodi non hanno implementazione, queste classi si dicono astratte (abstract):

Una classe astratta non può essere istanziata ma solamente estesa da classi derivate

abstract class Shape {}

Page 48: Package, Interfacce, Ereditarietà

Lo specificatore abstract

Una classe astratta può avere dei metodi astratti che quindi non devono essere implementati:

Se una classe ha almeno un metodo astratto, deve essere dichiarata abstract

abstract class Poligono {…abstract double getArea();…

}

Page 49: Package, Interfacce, Ereditarietà

Lo specificatore abstract

I metodi astratti però devono essere implementati obbligatoriamente nelle classi derivate, a meno che anch’esse siano dichiarate abstract

abstract class Poligono {…abstract double getArea();double nLati() {

return n;}

}

class Quadrato extends Poligono {…double getArea() {

return l*l;}…

}

Page 50: Package, Interfacce, Ereditarietà

Classi astratte

I vantaggi nella definizione di classi abstract sono:

definire una classe abstract significa definire una interfacciacomune a tutte le classi derivate

una classe abstract non può essere istanziata: se ciò viene fattoil compilatore produce un errore. Questo permette al progettistadella classe di costringere l’utente a definire il comportamentodei metodi astratti

Page 51: Package, Interfacce, Ereditarietà

abstract ed interface

Ma cosa distingue una classe astratta da un interfaccia?

Nelle classi astratte è possibile definire proprietà utilizzando tutti gli specificatori di visibilità (public, private, ecc …) , mentre nelle interfacce questo non è possibile: in questo caso infatti le variabili divengono automaticamente STATIC and FINAL

Nella interface il concetto di implementazione è assolutamente inesistente. In pratica si stabilisce un protocollo di accesso, che poi verrà utilizzato da tutte le classi che utilizzano la medesima interfaccia

L’ interface permette l’ereditarietà multipla

Page 52: Package, Interfacce, Ereditarietà

La classe Object

L’ereditarietà in Java prevede una classe ROOT da cui discende qualsiasi altra classe: la classe Object

Ogni classe, che rappresenti qualsiasi entità, è sicuramente una derivazione della classe Object e quindi mette a disposizione i metodi di tale superclasse: protected  Object clone() – copia  Boolean equals(Object obj) – uguaglianza protected  void finalize() – dealloca l’oggetto (Garbage Collector) Class getClass() – ritorna il tipo dell’oggetto a run-time  int hashCode() – ritorna un hash per l’oggetto

Dovuti al fatto che JAVA lavora con i riferimenti

Risoluzione del tipo a run-time

Page 53: Package, Interfacce, Ereditarietà

La classe Object

void notify() – riprende un thread fermo su questo oggetto void notifyAll() – sveglia tutti i thread fermi su questo oggetto  String toString() – ritorna una rappresentazione in stringa dell’oggetto

void wait() – ferma l’oggetto fino a che non viene chiamato il notify() o notifyAll()

void wait(long timeout) – come sopra però con un timeout massimo void wait(long timeout, int nanos) – come sopra però permette di specificare

il timeout in nanosecondi

Ritorna una versione stampabile dell’oggetto

Page 54: Package, Interfacce, Ereditarietà

Garbage Collector

Page 55: Package, Interfacce, Ereditarietà

Quanto “vivono” gli oggetti?

Il programmatore si occupa di costruire gli oggetti, ma non di distruggerli

In JAVA la deallocazione degli oggetti è gestita automaticamente: quando un oggetto non è più utilizzato, la memoria utilizzata viene

liberata automaticamente

L’entità che si occupa di liberare memoria non più utile è il Garbage Collector

public void xxx(){

Float W = new Float(0.5);

} L’handle W scompare, ma non l’oggetto associato!

Page 56: Package, Interfacce, Ereditarietà

Il Garbage Collector

Il garbage collector è avviato automaticamente dalla JVM, ma è possibile forzare la chiamata al Garbage Collector tramite una funzione ereditata da tutte le classe dalla classe Object:

È possibile capire se un oggetto è ancora referenziato (vivo) utilizzando due possibili tecniche: Conteggio Ricostruzione

System.gc();

Page 57: Package, Interfacce, Ereditarietà

Conteggio

Questa tecnica prevede di contare i riferimenti agli oggetti.

Lo svantaggio è che molte volte gli oggetti si auto-referenziano:

B

C

A

1

1

1

da variabile

non utilizzatonon utilizzato

Page 58: Package, Interfacce, Ereditarietà

Ricostruzione Un’altra tecnica è quella di seguire a ritroso i riferimenti fino ad una

variabile static oppure presente nello stack ed ancora attiva:

A:

B:

C:

C

Anon utilizzato

B C:

MemoriaB:

D

Memoria

A C

B D

Page 59: Package, Interfacce, Ereditarietà

Strategie di GC

Ma cosa fare quando vengono trovati gli oggetti non più utilizzati?

Stop-and-Copy: una volta trovati gli oggetti da rimuovere, vengono copiati in un altro heap gli oggetti ancora referenziati, e modificati tutti i riferimenti a loro collegati. L’heap originario è sostituito dal nuovo heap.

Mark-and-Sweep: una volta trovati gli oggetti da rimuovere, questi vengono eliminati senza copiare quelli ancora referenziati. In seguito l’heap può essere riorganizzato spostando solo alcuni oggetti ancora vivi, più piccoli degli spazi rilasciati.

Page 60: Package, Interfacce, Ereditarietà

Adaptive Garbace Collection

La tendenza attuale è quella di utilizzare entrambe le tecniche (Adaptive Garbace Collection), ma in momenti diversi:

la tecnica Stop-and-Copy viene utilizzata solo quando l’heap è troppo frammentato, e quindi è necessaria una riorganizzazione

la tecnica Mark-and-Sweep è più veloce, ma l’heap viene frammentato