OOP in Java: Classi e Oggetti — Guida Completa con Esempi

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.

OOP Java classi oggetti — diagramma UML classi e istanze

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.

Commenti

6 risposte a “OOP in Java: Classi e Oggetti — Guida Completa con Esempi”

  1. […] OOP in Java: Classi e Oggetti […]

  2. […] OOP in Java: Classi e Oggetti […]

  3. […] Programmazione orientata agli oggetti in Java: classi, oggetti e interfacce […]

  4. […] OOP in Java: Classi e Oggetti […]

  5. […] OOP in Java Classi e Oggetti […]

Rispondi

Scopri di più da App Tutorial

Abbonati ora per continuare a leggere e avere accesso all'archivio completo.

Continua a leggere