OOP in Java: Classi e Oggetti — Guida Completa con Esempi
La programmazione orientata agli oggetti in Java (OOP Java classi oggetti) permette di modellare problemi reali usando classi e oggetti. In questa guida, vedrai passo passo cosa sono classi e oggetti, come funzionano costruttori, metodi, modificatori di accesso e la parola chiave this. Inoltre, arriverai a un esempio completo che integra tutti i concetti.

1. Introduzione a OOP e perché Java è orientato agli oggetti
Java è un linguaggio progettato fin dall’inizio per supportare la programmazione orientata agli oggetti. Infatti, quasi tutto in Java ruota intorno a classi e oggetti. Questo approccio aiuta a organizzare il codice in unità coerenti, riutilizzabili e più facili da manutenere.
In primo luogo, in OOP un programma viene visto come un insieme di oggetti che collaborano tra loro. Ogni oggetto ha uno stato, rappresentato da campi, e un comportamento, rappresentato da metodi. Di conseguenza, una classe è il modello da cui gli oggetti vengono creati.
// Esempio di programma orientato agli oggetti in Java
// Qui usiamo una classe semplice per modellare un saluto personalizzato
public class Saluto {
String destinatario;
void saluta() {
System.out.println("Ciao " + destinatario + "!");
}
}
public class MainIntroduzione {
public static void main(String[] args) {
Saluto saluto = new Saluto();
saluto.destinatario = "Java";
saluto.saluta();
}
}
2. Cos’è una Classe in Java (OOP)
Una classe è un modello che descrive le caratteristiche e il comportamento di un insieme di oggetti. In particolare, definisce i campi che rappresentano lo stato e i metodi che rappresentano le operazioni che si possono compiere su quello stato. Se vuoi approfondire i concetti di classi interne e anonime, consulta questa guida completa.
Nell’esempio seguente, definiamo una classe Persona con due campi di istanza e un metodo che stampa le informazioni principali.
// Definizione di una classe Persona
// rappresenta una persona con nome ed eta
public class Persona {
// Campi di istanza
String nome;
int eta;
// Metodo che stampa una descrizione della persona
void descrivi() {
System.out.println("Mi chiamo " + nome + " e ho " + eta + " anni.");
}
}
// Classe di test con il metodo main
public class MainClasse {
public static void main(String[] args) {
// Per ora dichiariamo solo la classe. La useremo meglio nelle sezioni successive.
Persona p = new Persona();
p.nome = "Luca";
p.eta = 28;
p.descrivi();
}
}
3. Oggetti Java: come istanziare una Classe
Un oggetto è un’istanza concreta di una classe. Quando crei un oggetto, la JVM riserva memoria per i campi della classe e ti restituisce un riferimento a quella porzione di memoria. In altre parole, in Java gli oggetti si creano con l’operatore new seguito da una chiamata a un costruttore.
Nel codice seguente, creiamo più istanze della classe Persona e mostriamo che ogni oggetto mantiene il proprio stato indipendente.
// Creazione di oggetti a partire dalla classe Persona
public class PersonaOggetto {
String nome;
int eta;
void descrivi() {
System.out.println("Mi chiamo " + nome + " e ho " + eta + " anni.");
}
}
public class MainOggetti {
public static void main(String[] args) {
// Creazione del primo oggetto PersonaOggetto
PersonaOggetto persona1 = new PersonaOggetto();
persona1.nome = "Anna";
persona1.eta = 30;
// Creazione del secondo oggetto PersonaOggetto
PersonaOggetto persona2 = new PersonaOggetto();
persona2.nome = "Marco";
persona2.eta = 25;
// Ogni oggetto mantiene il proprio stato
persona1.descrivi();
persona2.descrivi();
}
}
4. Costruttori
I costruttori sono metodi speciali usati per inizializzare gli oggetti. Hanno lo stesso nome della classe e non hanno tipo di ritorno. Se non definisci nessun costruttore, il compilatore genera un costruttore di default senza parametri.
Nel prossimo esempio, vedrai un costruttore di default esplicito e un costruttore con parametri che permette di creare oggetti già inizializzati con valori specifici.
// Esempio di classe con costruttori
public class PersonaCostruttore {
String nome;
int eta;
// Costruttore di default senza parametri
public PersonaCostruttore() {
nome = "Sconosciuto";
eta = 0;
}
// Costruttore con parametri
public PersonaCostruttore(String nomeIniziale, int etaIniziale) {
nome = nomeIniziale;
eta = etaIniziale;
}
void descrivi() {
System.out.println("Mi chiamo " + nome + " e ho " + eta + " anni.");
}
}
public class MainCostruttori {
public static void main(String[] args) {
// Uso del costruttore di default
PersonaCostruttore p1 = new PersonaCostruttore();
p1.descrivi();
// Uso del costruttore con parametri
PersonaCostruttore p2 = new PersonaCostruttore("Giulia", 22);
p2.descrivi();
}
}
5. Metodi, getter, setter e metodi personalizzati
I metodi definiscono cosa un oggetto può fare. Inoltre, i getter e i setter sono metodi specializzati per leggere e modificare i campi in modo controllato. Infine, altri metodi personalizzati rappresentano operazioni di alto livello sullo stato interno della classe.
Nel seguente esempio, la classe ContoBancario espone metodi per depositare e prelevare denaro e usa un getter per leggere il saldo in modo sicuro. Ad esempio, potrai osservare come i campi privati proteggono l’integrità dei dati.
// Classe con getter, setter e metodi personalizzati
public class ContoBancario {
// Campi privati per proteggere lo stato interno
private String intestatario;
private double saldo;
public ContoBancario(String intestatario, double saldoIniziale) {
this.intestatario = intestatario;
saldo = saldoIniziale;
}
// Getter per leggere il saldo corrente
public double getSaldo() {
return saldo;
}
// Setter per cambiare l'intestatario
public void setIntestatario(String nuovoIntestatario) {
intestatario = nuovoIntestatario;
}
// Metodo personalizzato per depositare denaro
public void deposita(double importo) {
if (importo <= 0) {
System.out.println("Importo non valido");
return;
}
saldo = saldo + importo;
}
// Metodo personalizzato per prelevare denaro
public void preleva(double importo) {
if (importo <= 0) {
System.out.println("Importo non valido");
return;
}
if (importo > saldo) {
System.out.println("Saldo insufficiente");
return;
}
saldo = saldo - importo;
}
}
public class MainMetodi {
public static void main(String[] args) {
ContoBancario conto = new ContoBancario("Mario Rossi", 1000.0);
conto.deposita(250.0);
conto.preleva(100.0);
double saldoAttuale = conto.getSaldo();
System.out.println("Saldo finale: " + saldoAttuale);
}
}
6. Modificatori di accesso
I modificatori di accesso controllano la visibilità di classi, campi e metodi. In Java, i principali sono public, private e protected. Pertanto, usarli in modo corretto aiuta a rispettare il principio di incapsulamento e a limitare le dipendenze tra le parti del codice.
Nell’esempio che segue, la classe Utente mostra come combinare diversi modificatori per esporre solo ciò che serve all’esterno.
// Esempio di uso dei modificatori di accesso
public class Utente {
// Campo privato: accessibile solo dentro la classe Utente
private String username;
// Campo protetto: accessibile nelle sottoclassi e nello stesso package
protected String ruolo;
// Campo pubblico: accessibile da qualsiasi parte del programma
public boolean attivo;
public Utente(String username, String ruolo) {
this.username = username;
this.ruolo = ruolo;
attivo = true;
}
// Metodo pubblico: puo essere chiamato da chiunque
public String getUsername() {
return username;
}
// Metodo protetto: pensato per essere usato o ridefinito da sottoclassi
protected void cambiaRuolo(String nuovoRuolo) {
ruolo = nuovoRuolo;
}
// Metodo privato: solo di supporto interno alla classe
private void logInterno(String messaggio) {
System.out.println("Log interno per " + username + ": " + messaggio);
}
public void disattiva() {
attivo = false;
logInterno("Utente disattivato");
}
}
class UtenteAdmin extends Utente {
public UtenteAdmin(String username) {
super(username, "ADMIN");
}
public void promuoviUtente(Utente utente) {
// Accesso consentito al campo protetto ruolo attraverso un metodo dedicato
utente.attivo = true;
}
}
public class MainAccesso {
public static void main(String[] args) {
Utente utente = new Utente("luca", "USER");
UtenteAdmin admin = new UtenteAdmin("admin");
utente.disattiva();
admin.promuoviUtente(utente);
System.out.println("Utente attivo: " + utente.attivo);
}
}
7. La parola chiave this
La parola chiave this si riferisce all’oggetto corrente. Inoltre, si usa spesso per distinguere i campi dai parametri del costruttore o dei metodi, per passare il riferimento all’istanza corrente ad altri metodi o per chiamare un altro costruttore della stessa classe.
Nell’esempio seguente, la classe Rettangolo usa this sia per accedere ai campi, sia per richiamare un costruttore da un altro costruttore.
// Uso della parola chiave this
public class Rettangolo {
private int larghezza;
private int altezza;
// Costruttore principale
public Rettangolo(int larghezza, int altezza) {
this.larghezza = larghezza;
this.altezza = altezza;
}
// Costruttore che usa this per riutilizzare la logica di inizializzazione
public Rettangolo(int lato) {
this(lato, lato);
}
public int calcolaArea() {
return larghezza * altezza;
}
public void ridimensiona(int larghezza, int altezza) {
this.larghezza = larghezza;
this.altezza = altezza;
}
}
public class MainThis {
public static void main(String[] args) {
Rettangolo r1 = new Rettangolo(4, 6);
System.out.println("Area r1: " + r1.calcolaArea());
Rettangolo r2 = new Rettangolo(5);
System.out.println("Area r2: " + r2.calcolaArea());
r2.ridimensiona(2, 3);
System.out.println("Area r2 dopo ridimensionamento: " + r2.calcolaArea());
}
}
8. Esempio completo con classe Automobile
Ora mettiamo insieme tutti i concetti visti finora in un esempio realistico. La classe Automobile usa campi privati, costruttori, metodi pubblici, getter e setter, modificatori di accesso e la parola chiave this per gestire lo stato di un veicolo semplice.
L’esempio comprende una classe di test con un metodo main che crea alcune auto, le modifica e stampa informazioni riassuntive. Quindi, potrai vedere come tutti questi elementi lavorano insieme.
// Esempio completo con classe Automobile
public class Automobile {
// Campi privati per marca, modello e velocita corrente
private String marca;
private String modello;
private int velocitaCorrente;
private boolean accesa;
// Costruttore con parametri
public Automobile(String marca, String modello) {
this.marca = marca;
this.modello = modello;
velocitaCorrente = 0;
accesa = false;
}
// Costruttore di default
public Automobile() {
this("Sconosciuta", "Sconosciuto");
}
// Getter per marca
public String getMarca() {
return marca;
}
// Setter per marca
public void setMarca(String nuovaMarca) {
marca = nuovaMarca;
}
// Getter per modello
public String getModello() {
return modello;
}
// Setter per modello
public void setModello(String nuovoModello) {
modello = nuovoModello;
}
// Metodo per accendere l'auto
public void accendi() {
if (accesa) {
System.out.println("L'auto e gia accesa.");
} else {
accesa = true;
System.out.println("Auto accesa.");
}
}
// Metodo per spegnere l'auto
public void spegni() {
if (!accesa) {
System.out.println("L'auto e gia spenta.");
} else {
accesa = false;
velocitaCorrente = 0;
System.out.println("Auto spenta.");
}
}
// Metodo per accelerare
public void accelera(int incremento) {
if (!accesa) {
System.out.println("Non puoi accelerare. L'auto e spenta.");
return;
}
if (incremento <= 0) {
System.out.println("Incremento non valido.");
return;
}
velocitaCorrente = velocitaCorrente + incremento;
System.out.println("Nuova velocita: " + velocitaCorrente + " km/h");
}
// Metodo per frenare
public void frena(int decremento) {
if (!accesa) {
System.out.println("Non puoi frenare. L'auto e spenta.");
return;
}
if (decremento <= 0) {
System.out.println("Decremento non valido.");
return;
}
velocitaCorrente = velocitaCorrente - decremento;
if (velocitaCorrente < 0) {
velocitaCorrente = 0;
}
System.out.println("Nuova velocita: " + velocitaCorrente + " km/h");
}
// Metodo che restituisce una descrizione dell'auto
public String descrizione() {
String stato = accesa ? "accesa" : "spenta";
return "Automobile " + marca + " " + modello + " in stato " + stato + " alla velocita di " + velocitaCorrente + " km/h";
}
}
public class MainAutomobile {
public static void main(String[] args) {
Automobile auto1 = new Automobile("Fiat", "Panda");
Automobile auto2 = new Automobile();
auto1.accendi();
auto1.accelera(30);
auto1.accelera(20);
auto1.frena(10);
System.out.println(auto1.descrizione());
auto2.setMarca("Toyota");
auto2.setModello("Yaris");
auto2.accendi();
auto2.accelera(50);
System.out.println(auto2.descrizione());
}
}
9. Conclusione: OOP Java, Classi e Oggetti
In conclusione, in questo articolo hai visto cosa sono classi e oggetti in Java e come si usano nella pratica tramite costruttori, metodi, modificatori di accesso e la parola chiave this. L’esempio completo con la classe Automobile mostra come questi concetti lavorano insieme in un contesto realistico. Padroneggiare OOP Java classi oggetti è fondamentale per comprendere ereditarietà, polimorfismo e gli altri pilastri dell’OOP in Java. Per continuare il tuo percorso di apprendimento, puoi approfondire i concetti di programmazione concorrente con i thread e scoprire come gestire il file I/O in Java.
Rispondi