01 - Introduzione
Struttura del sistema e prospettive
Un sistema orientato agli oggetti deve essere studiato da due prospettive e gerarchie distinte:
- Struttura degli oggetti (gerarchia PART-OF): Illustra come i diversi oggetti collaborano tra loro attraverso schemi di interazione che chiamiamo meccanismi (metodi). Gli oggetti sono intesi come fornitori di servizi che interagiscono tra loro per ottenere un servizio composito.
- Struttura delle classi (gerarchia IS-A): Evidenzia la struttura e il comportamento comuni all'interno di un sistema, classificando gli oggetti in gruppi di astrazioni correlate per distinguerne le proprietà comuni e distinte.
Definizione di object-oriented programming (OOP)
La programmazione orientata agli oggetti è un metodo di implementazione in cui i programmi sono organizzati come collezioni cooperative di oggetti, ognuno dei quali rappresenta un'istanza di una classe e le cui classi sono tutte membri di una gerarchia di classi unite da relazioni di ereditarietà.
Questa definizione include tre parti fondamentali:
- La programmazione orientata agli oggetti utilizza come elementi logici fondamentali gli oggetti, non gli algoritmi (gerarchia PART-OF).
- Ogni oggetto è un'istanza di una classe.
- Le classi possono essere collegate tra loro tramite relazioni di ereditarietà (gerarchia IS-A).
Il significato dell'astrazione
Un'astrazione denota le caratteristiche essenziali di un oggetto che lo distinguono da tutti gli altri tipi di oggetti, fornendo così confini concettuali ben definiti, rispetto alla prospettiva dell'osservatore.
L'astrazione si concentra sulla visione esterna di un oggetto e serve a separare il comportamento essenziale di un oggetto dalla sua implementazione. Rappresenta le caratteristiche essenziali e distintive di un oggetto dal punto di vista di chi lo guarda.
Il significato di incapsulamento (encapsulation, information hiding)
L'astrazione e l'incapsulamento sono concetti complementari:
- L'astrazione si concentra sul comportamento osservabile di un oggetto.
- L'incapsulamento si concentra sull'implementazione che dà origine a questo comportamento.
L'astrazione di un oggetto deve precedere le decisioni sulla sua implementazione. Una volta scelta l'implementazione, questa deve essere trattata come un segreto dell'astrazione e nascosta alla maggior parte dei client. Nessuna parte di un sistema complesso dovrebbe dipendere dai dettagli interni di un'altra parte.
L'incapsulamento si ottiene attraverso l'information hiding, ovvero il processo di nascondere tutti i segreti di un oggetto che non contribuiscono alle sue caratteristiche essenziali. In genere, la struttura di un oggetto viene nascosta, così come l'implementazione dei suoi metodi.
Ogni classe deve di conseguenza essere divisa in due parti:
- L'interfaccia di una classe: Cattura solo la sua visione esterna, comprendendo l'astrazione del comportamento comune a tutte le istanze della classe. È l'unico luogo in cui si affermano tutte le ipotesi che un client può fare su qualsiasi istanza della classe.
- L'implementazione di una classe: Comprende la rappresentazione dell'astrazione e i meccanismi che consentono di ottenere il comportamento desiderato. Incapsula i dettagli su cui nessun client può fare ipotesi.
Classi e oggetti (istanze)
- Oggetto (o istanza): È un'entità che ha uno stato, un comportamento e un'identità. La struttura e il comportamento di oggetti simili sono definiti nella loro classe comune.
- Classe: È un insieme di oggetti che condividono una struttura comune, un comportamento comune e una semantica comune. Una classe rappresenta l'insieme degli oggetti che condividono le proprietà che essa specifica. Gli oggetti ereditano le proprietà definite dalla classe.
Il potere dell'ereditarietà risiede nella sintesi: si definiscono le proprietà nelle classi, limitate in numero, e le loro numerose istanze (oggetti) le ereditano senza richiedere una descrizione individuale.
Lo stato di un oggetto
Lo stato di un oggetto include i valori delle sue proprietà. Tutte le proprietà hanno un valore, che può essere una semplice quantità o può denotare un altro oggetto.
Le responsabilità (responsibilities) di un oggetto
Le "Responsibilities" di un oggetto sono tutti i servizi che offre, in termini di metodi offerti ai client, per tutti i contratti che supporta, cioè per tutti i ruoli che può giocare nel sistema complessivo.
02 - Classi, attributi e metodi
Definizione formale di classe e oggetto
- Classe: Costrutto di programmazione utilizzato per creare oggetti. Definisce i campi e i metodi. Un'istanza di una classe non può possedere attributi o metodi che non siano stati esplicitamente definiti nella classe stessa.
- Oggetto (o istanza): È l’indirizzo di un gruppo di dati più semplici, che costituiscono la rappresentazione in memoria degli attributi. Possiede uno stato, i valori contenuti negli attributi/campi, modificabile tramite operazioni dette metodi.
L'oggetto null: Ogni classe contiene un oggetto degenere, definito come "oggetto che non esiste", avente indirizzo null. Rappresenta la mancanza di un'istanza, è identico per tutte le classi e non possiede né campi né metodi. Cercare di accedere a un membro di un oggetto null genera un errore a tempo di esecuzione, Runtime Error / NullPointerException.
Array come oggetto: In Java, un array, ad esempio di interi, è un oggetto a tutti gli effetti: viene identificato dal suo indirizzo di memoria, risiede in un blocco di elementi consecutivi e possiede l'attributo nativo length che ne definisce la lunghezza.
Creazione di istanze e mappa della memoria
Gli oggetti si costruiscono tramite l'operatore new, il quale alloca un'area di memoria per gli attributi dell'oggetto, inizializzati ai valori di default, e ne restituisce l’indirizzo. Eseguendo l'istruzione C x = new C();, la variabile x non contiene l'oggetto, ma l'indirizzo di memoria dell'oggetto appena creato.
La memoria della Java Virtual Machine (JVM) è tassativamente divisa in tre aree:
- Heap: Zona di memoria in cui vengono salvati tutti gli oggetti istanziati tramite il comando new.
- Stack: Zona di memoria che contiene i frame, record di attivazione, dei metodi in esecuzione e le variabili locali, tra cui i riferimenti/indirizzi che puntano agli oggetti nell'Heap.
- Area delle classi (Class Area): Zona che memorizza le informazioni strutturali sulle classi e le variabili statiche.
Eseguibilità e visibilità (package e file)
- Eseguibilità: Una classe è eseguibile autonomamente se e solo se contiene il metodo punto di ingresso main(). In assenza di esso, la classe può essere utilizzata unicamente come libreria invocata da altre classi.
- Package: In Java, una cartella di sistema viene definita package.
- Regole di visibilità del file sorgente .java:
- class C: La classe è visibile e utilizzabile solo dalle classi appartenenti allo stesso package.
- public class C: La classe è visibile a qualsiasi classe di qualsiasi package, previa istruzione import.
- Vincolo strutturale: Un file .java può contenere al massimo una sola classe pubblica, e il nome del file deve coincidere esattamente con il nome di tale classe pubblica.
Metodi dinamici vs metodi statici
- Metodo statico (static): Operazione associata alla classe. Riceve parametri in ingresso e restituisce un output, o modifica la memoria. È concettualmente equivalente a una funzione del linguaggio C.
- Metodo dinamico (di istanza): Operazione associata al singolo oggetto, qualsiasi metodo non marcato esplicitamente come static. Viene invocato "mandando un messaggio" a un oggetto specifico tramite la notazione dot-pitch, x.metodo(). L'oggetto x funge da prefisso e da argomento implicito del metodo.
Nota di design OOP: In Java si privilegiano i metodi dinamici; le proprietà e i comportamenti legati all'individuo, stato, devono essere dinamici, riservando lo static solo alle caratteristiche invarianti o universali della classe, ad esempio costanti.
Variabili statiche vs variabili dinamiche
- Variabili statiche (static): Contengono informazioni comuni a tutti gli oggetti della classe. Rappresentano un valore unico e condiviso da tutte le istanze durante l'intera computazione.
- Variabili dinamiche (di istanza / campi): Vengono dichiarate dentro la classe senza il modificatore static. Assumono un valore specifico, autonomo e indipendente per ogni singolo oggetto. L'insieme dei loro valori determina lo stato dell’istanza.
La variabile riservata this
La parola chiave this è una variabile implicita e riservata, presente in ogni metodo dinamico.
- Contiene l'indirizzo dell'oggetto su cui il metodo è stato invocato.
- All'interno del metodo, this agisce come un alias della variabile esterna che ha rimpallato la chiamata.
- Viene utilizzato per referenziare i campi e i metodi dell'oggetto stesso, ad esempio this.name = name;
- Può essere omesso, syntactic sugar, se nel blocco di codice non sussistono omonimie o ambiguità con variabili locali o parametri formali.
Il metodo toString()
Il metodo dinamico public String toString() appartiene allo standard Java ed è definito nativamente all'interno della classe Object, la superclasse da cui derivano, direttamente o indirettamente, tutte le classi Java.
- Comportamento di default: Restituisce una stringa composta dal nome della classe e dall'indirizzo di memoria dell'oggetto espresso in esadecimale: "Classe@indirizzo_esadecimale".
- Regola fondamentale: Il metodo toString() NON stampa a video, bensì si limita a restituire una stringa che serializza lo stato dell'oggetto.
- Invocazione implicita: Quando si passa un oggetto a un metodo di stampa come System.out.println(x), il sistema invoca automaticamente x.toString() e ne stampa il valore testuale restituito. Se il programmatore effettua l'override del metodo nella propria classe, la println() mostrerà la stringa personalizzata anziché l'indirizzo esadecimale.
Passaggio dei parametri in Java
In Java esiste esclusivamente il passaggio di parametri per valore.
Tuttavia, poiché le variabili d'oggetto non contengono i dati ma l'indirizzo di memoria dell'oggetto allocato nell'Heap, passare un oggetto come parametro significa copiare e passare il suo indirizzo. Questo realizza, di fatto, un passaggio per riferimento implicito.
Differenze architetturali: struct (C) vs class (Java)
Sebbene entrambe le strutture permettano di aggregare tipi di dati eterogenei, differiscono per tre pilastri:
- Comportamento: Le classi contengono i metodi, operazioni che agiscono sullo stato, le struct del C contengono esclusivamente dati.
- Identità: Gli oggetti Java possiedono un'identità data dal loro indirizzo di memoria, due oggetti allocati separatamente con gli stessi identici dati restano entità distinte. Due struct C con i medesimi dati, a livello logico, non sono distinguibili.
- Incapsulamento: I dati di una struct C sono sempre esposti e modificabili da qualsiasi codice esterno. I membri di una classe Java possono essere incapsulati, nascosti tramite il modificatore private, permettendo di alterarne l'implementazione interna senza costringere a riscrivere il codice client.
Il flusso di compilazione e la JVM
- Linguaggio C: Il codice sorgente viene compilato in un file binario eseguibile nativo, il cui formato è strettamente dipendente dall'architettura della macchina e dal Sistema Operativo ospite.
- Java: Il codice sorgente .java viene compilato tramite il comando javac in un formato intermedio e universale chiamato bytecode, file .class, del tutto indipendente dall'hardware.
- Esecuzione: Il bytecode viene fatto girare invocando la JVM, Java Virtual Machine, tramite il comando java.
- Compromesso: Questo approccio garantisce la totale portabilità del codice su qualsiasi piattaforma, pagando il prezzo di un'esecuzione leggermente più lenta rispetto a un binario nativo.
03 - Metodi di get e set, il modificatore final
Terminologia: attributo, campo e stato
- Attributo: Si riferisce alle caratteristiche astratte di un'entità in fase di progettazione.
- Campo: È la controparte dell'attributo all'interno di un'implementazione software.
- Stato: È l'insieme dei valori assunti da tutti i campi ad ogni singolo passo di computazione.
Attributi privati e metodi di get e set
- Attributo privato (private): Può essere letto o modificato direttamente solo dall'interno della classe che lo definisce. Si specifica sostituendo la parola chiave public con private.
- Metodi di get (getter): Metodi pubblici che consentono l'accesso in lettura a un attributo privato.
- Metodi di set (setter): Metodi pubblici che consentono l'accesso in scrittura, modifica, a un attributo privato.
Vantaggi dell'incapsulamento tramite Get/Set:
- Controllo fine dell'accesso: Omettendo il metodo di set, si impone che un attributo sia disponibile esclusivamente in sola lettura per il codice client.
- Integrità dei dati: Impedisce l'inserimento di dati contraddittori o non validi attraverso verifiche puntuali all'interno del metodo di set, ad esempio controllare che il valore della popolazione sia positivo prima di assegnarlo.
Identità vs uguaglianza
- Oggetti identici: Occupano la stessa area di memoria, hanno lo stesso indirizzo.
- Oggetti uguali: Hanno gli stessi identici valori attributo per attributo, ma possiedono un indirizzo di memoria, identità, diverso.
Caso di studio: la classe Calculator
La classe Calculator funge da esempio di progettazione con attributi e metodi privati. Rappresenta una calcolatrice semplificata per operazioni tra interi positivi a una cifra, 0 ... 9, tramite gli operatori binari + e *.
- Struttura dati interna: Utilizza un array operante come uno stack (Last-In-First-Out; LIFO) per memorizzare i risultati dei calcoli precedenti e un indice size per tracciare il numero di elementi inseriti.
- Notazione RPN (Reverse Polish Notation): Adotta la notazione postfissa, in cui gli operatori seguono i loro argomenti anziché comparire tra di essi, ad esempio la forma infissa (2+3)*9 viene scritta in RPN come 23+9*.
- Progettazione della visibilità (Public vs Private):
- Metodo pubblico (public int): L'unico metodo esposto al codice client è execute(String instructions), che riceve la stringa dei tasti premuti e restituisce il risultato finale.
- Membri privati (private): Gli attributi stack, size e i metodi di servizio dinamici int pop() per prelevare e cancellare l'ultimo valore, void push(int x) per inserire un valore, sono nascosti per proteggere il funzionamento interno dell'oggetto.
Il modificatore final (attributi costanti)
Il modificatore final si utilizza per specificare che il valore di un campo non può cambiare durante l'esecuzione del programma dopo essere stato inizializzato. L'assegnazione deve avvenire tassativamente all'atto della creazione dell'oggetto, nel costruttore.
import java.util.*;
class MyClass {
private final int num;
private final Date date; // Classe Java per le date del calendario
public MyClass(int n) {
num = n;
date = new Date();
// num = 3; // ERRORE: la variabile num potrebbe essere già stata assegnata
}
public double getNum() { return num; }
public Date getDate() { return date; }
}
Comportamento di final sui tipi primitivi vs oggetti:
- Sui tipi primitivi, ad esempio int num: Il valore numerico memorizzato nel campo è bloccato e non può essere modificato o riassegnato dopo l'inizializzazione.
- Sui riferimenti a oggetti, ad esempio Date date: Il campo dovrà sempre puntare all'esatto indirizzo di memoria dell'oggetto creato nel costruttore. Il riferimento, il puntatore, è immutabile e non può essere riassegnato a un nuovo oggetto, ma lo stato interno dell'oggetto puntato rimane modificabile.
Costanti di classe: Le costanti dichiarate in una classe possono essere marcate anche come static final se si desidera che il valore appartenga alla classe stessa e non entri a far parte dello stato dei singoli oggetti creati.
04 - Costruttori, assegnazioni di oggetti, metodo equals()
I costruttori in Java
Data una classe C, l'istruzione new C() esegue due azioni distinte:
- L'operatore new alloca lo spazio di memoria necessario all'oggetto all'interno dell'heap.
- L'espressione C() invoca un metodo speciale, detto costruttore, che inizializza gli attributi dell'oggetto ai loro valori di default.
- Valori di default: Dipendono strettamente dal tipo dell'attributo, ad esempio 0 per i tipi primitivi int, null per i riferimenti a oggetti, incluse le stringhe String.
Costr
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
-
Riassunto esame Programmazione orientata agli oggetti, Prof. Nigro Libero, libro consigliato Programmazione orienta…
-
Appunti di Programmazione orientata agli oggetti e Java
-
Appunti per Esame Programmazione orientata agli oggetti
-
Programmazione orientata agli oggetti - Appunti