1. Classi e Oggetti
Definizione delle Classi
Una classe è un modello o una struttura che definisce le caratteristiche (variabili di istanza o membri) e i comportamenti (metodi) di un oggetto.
Gli elementi principali di una classe includono:
Variabili di Membro
: Rappresentano gli attributi di una classe. Ad esempio:
public class Persona { String nome; int età; }
Metodi di Membro
: Definiscono il comportamento di una classe. Ad esempio:
public void saluta() { System.out.println("Ciao!"); }
Costruttori
: Inizializzano gli oggetti di una classe. Ad esempio:
public Persona(String nome, int età) { this.nome = nome; this.età = età; }
Creazione degli Oggetti
Gli oggetti vengono creati utilizzando la parola chiave
new
:Persona persona = new Persona("Mario", 30);
Memorizzazione degli Oggetti in Memoria
- Heap: Dove sono memorizzati gli oggetti creati con
new
.
- Stack: Dove sono memorizzate le variabili di riferimento agli oggetti.
- Metaspace: Memorizza informazioni sulle classi.
3. Incapsulamento
Modificatori di Accesso
I modificatori di accesso controllano la visibilità dei membri di una classe:
public
: Accessibile da qualsiasi classe.
protected
: Accessibile dalle classi nello stesso package o dalle sottoclassi.
default
(nessun modificatore): Accessibile solo dalle classi nello stesso package.
private
: Accessibile solo all'interno della stessa classe.
Getter e Setter
Questi metodi permettono di accedere e modificare i membri privati di una classe:
private String nome; public String getNome() { return nome; } public void setNome(String nome) { this.nome = nome; }
Benefici dell'Incapsulamento
- Migliora la sicurezza dei dati.
- Consente di modificare l'implementazione senza cambiare il codice esterno.
- Facilita il debug e la manutenzione.
4. Ereditarietà
Parola Chiave extends
Permette a una classe di ereditare attributi e metodi da un'altra classe:
public class Studente extends Persona { private String corso; }
Parola Chiave super
- Costruttori della Classe Padre:
public Studente(String nome, int età, String corso) { super(nome, età); this.corso = corso; }
- Accesso ai Membri della Classe Padre:
System.out.println(super.nome);
Metodo Override
Consente a una sottoclasse di fornire una nuova implementazione di un metodo della classe padre:
@Override public void saluta() { System.out.println("Ciao, sono uno studente."); }
Regole del Override
- La firma del metodo deve essere identica.
- La visibilità non può essere ridotta.
- Il metodo deve essere dichiarato in una classe padre o interfaccia.
5. Polimorfismo
Concetti di Upcasting e Downcasting
Upcasting
: Conversione di un oggetto di una sottoclasse in un tipo della classe padre:
Persona persona = new Studente();
Downcasting
: Conversione di un oggetto della classe padre in un tipo della sottoclasse (richiede il controllo del tipo):
if (persona instanceof Studente) { Studente studente = (Studente) persona; }
Overload vs Override
Overload
(Sovraccarico): Stesso nome di metodo, ma firme diverse:
public int somma(int a, int b); public double somma(double a, double b);
Override
: Stesso nome e firma del metodo della classe padre, con una nuova implementazione.
Binding Dinamico
Consente di determinare quale metodo chiamare a runtime:
Persona persona = new Studente(); persona.saluta(); // Chiama il metodo della sottoclasse
6. Classi Astratte e Interfacce
Parola Chiave abstract
- Una classe astratta non può essere istanziata.
- Può contenere metodi astratti (senza implementazione) e metodi concreti (con implementazione):
public abstract class Animale { public abstract void verso(); public void dorme() { System.out.println("Zzz..."); } }
Interfacce
Definiscono un contratto che le classi devono rispettare:
- Dichiarate con
interface
.
- Implementate con
implements
.
public interface Volante { void vola(); } public class Aereo implements Volante { public void vola() { System.out.println("L'aereo vola."); } }
Differenze tra Interfacce e Classi Astratte
- Le interfacce non possono avere costruttori.
- Una classe può implementare più interfacce, ma estendere una sola classe astratta.
- Le interfacce supportano metodi di default a partire da Java 8.
8. Funzioni Lambda e Interfacce Funzionali
Le interfacce funzionali (interfacce con un solo metodo astratto) possono essere utilizzate con espressioni Lambda:
@FunctionalInterface public interface Operazione { int calcola(int a, int b); } Operazione somma = (a, b) -> a + b; System.out.println(somma.calcola(5, 3)); // Output: 8