Esercitazioni Java: esercizi e soluzioni per studio o lavoro

Foto dell'autore

Andrea Barbieri

 

Home > News feed > Lavorare nel settore > Esercitazioni Java: esercizi e soluzioni per studio o lavoro

Esercitazione su Fondamenti di Java

  • Variabili e Tipi di Dati
  • Operatori e Espressioni
  • Controllo del Flusso

Esercizio

Scrivi un programma Java che legge un intero da input (usando Scanner), verifica se è pari o dispari e stampa il risultato. Inoltre, il programma deve verificare se il numero è positivo, negativo o zero, e stampare anche questo risultato.

Soluzione

import java.util.Scanner;

public class ParityAndSignChecker {
    
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        System.out.println("Inserisci un intero:");
        int numero = scanner.nextInt();
        
        if (numero % 2 == 0) {
            System.out.println("Il numero " + numero + " è pari.");
        } else {
            System.out.println("Il numero " + numero + " è dispari.");
        }
        
        if (numero > 0) {
            System.out.println("Il numero " + numero + " è positivo.");
        } else if (numero < 0) {
            System.out.println("Il numero " + numero + " è negativo.");
        } else {
            System.out.println("Il numero " + numero + " è zero.");
        }
        
        scanner.close();
    }
}

La soluzione contiene un esempio di utilizzo del costrutto di controllo del flusso if-else, dell’operatore modulo % per verificare la parità, e dell’uso della classe Scanner per leggere dati da input.

Esercitazione su Gestione delle Eccezioni e Errori

  • Try, Catch, Finally
  • Creazione di Eccezioni Personalizzate

Esercizio

Creare una classe BankAccount che rappresenta un conto bancario. La classe dovrebbe avere:

  • Un campo balance di tipo double che rappresenta il saldo attuale.
  • Un metodo deposit(double amount) per depositare una certa somma di denaro nel conto.
  • Un metodo withdraw(double amount) per ritirare una certa somma di denaro dal conto.

Implementare una eccezione personalizzata chiamata InsufficientFundsException che viene lanciata quando un cliente cerca di ritirare più denaro di quanto ne abbia nel conto.

Testare la classe BankAccount creando un oggetto di questa classe, depositando e ritirando fondi, e gestendo eventuali eccezioni InsufficientFundsException in un blocco try-catch.

Soluzione


// Definizione della classe InsufficientFundsException
class InsufficientFundsException extends Exception {
    public InsufficientFundsException(String message) {
        super(message);
    }
}

// Definizione della classe BankAccount
public class BankAccount {

    private double balance;

    public BankAccount() {
        this.balance = 0;
    }

    public void deposit(double amount) {
        this.balance += amount;
    }

    public void withdraw(double amount) throws InsufficientFundsException {
        if (amount > balance) {
            throw new InsufficientFundsException("Fondi insufficienti. Saldo disponibile: " + balance);
        }
        this.balance -= amount;
    }

    public double getBalance() {
        return balance;
    }
}

// Classe di test
public class TestBankAccount {
    public static void main(String[] args) {
        BankAccount account = new BankAccount();
        account.deposit(100.0);

        try {
            account.withdraw(120.0);
        } catch (InsufficientFundsException e) {
            System.err.println(e.getMessage());
        }

        System.out.println("Saldo attuale: " + account.getBalance());
    }
}

In questa esercitazione, la classe BankAccount rappresenta un conto bancario semplice con metodi per depositare e ritirare fondi. L’eccezione personalizzata InsufficientFundsException viene utilizzata per gestire i casi in cui un utente cerca di ritirare più denaro di quanto ne abbia disponibile nel conto. La classe di test TestBankAccount crea un oggetto BankAccount, effettua un deposito, tenta un prelievo e gestisce l’eccezione InsufficientFundsException in un blocco try-catch, se necessario.

Esercitazione su Programmazione Orientata agli Oggetti in Java

  • Classi e Oggetti
  • Ereditarietà
  • Polimorfismo
  • Incapsulamento

Esercizio

Creare una classe base Veicolo con i seguenti campi: marca, modello e velocità massima. Creare un metodo displayInfo() che stampa le informazioni del veicolo.

Estendere la classe Veicolo con due sottoclassi: Auto e Moto. Aggiungere alla classe Auto il campo numero di porte e alla classe Moto il campo tipo di manubrio. Sovrascrivere il metodo displayInfo() in entrambe le sottoclassi per includere i nuovi campi.

Creare una classe Main in cui si creano istanze di Auto e Moto, e si dimostra il polimorfismo invocando il metodo displayInfo() su entrambe le istanze.

Soluzione


// Classe base Veicolo
class Veicolo {
    private String marca;
    private String modello;
    private double velocitaMassima;

    public Veicolo(String marca, String modello, double velocitaMassima) {
        this.marca = marca;
        this.modello = modello;
        this.velocitaMassima = velocitaMassima;
    }

    public void displayInfo() {
        System.out.println("Marca: " + marca + ", Modello: " + modello + ", Velocità Massima: " + velocitaMassima + " km/h");
    }
}

// Sottoclasse Auto
class Auto extends Veicolo {
    private int numeroDiPorte;

    public Auto(String marca, String modello, double velocitaMassima, int numeroDiPorte) {
        super(marca, modello, velocitaMassima);
        this.numeroDiPorte = numeroDiPorte;
    }

    @Override
    public void displayInfo() {
        super.displayInfo();
        System.out.println("Numero di Porte: " + numeroDiPorte);
    }
}

// Sottoclasse Moto
class Moto extends Veicolo {
    private String tipoDiManubrio;

    public Moto(String marca, String modello, double velocitaMassima, String tipoDiManubrio) {
        super(marca, modello, velocitaMassima);
        this.tipoDiManubrio = tipoDiManubrio;
    }

    @Override
    public void displayInfo() {
        super.displayInfo();
        System.out.println("Tipo di Manubrio: " + tipoDiManubrio);
    }
}

// Classe Main per testare il polimorfismo
public class Main {
    public static void main(String[] args) {
        Veicolo auto = new Auto("Ferrari", "488", 330, 2);
        Veicolo moto = new Moto("Ducati", "Panigale", 290, "Sportivo");

        auto.displayInfo();
        moto.displayInfo();
    }
}

In questa esercitazione, la classe base Veicolo rappresenta un veicolo generico con campi per la marca, il modello e la velocità massima, e un metodo displayInfo() che stampa queste informazioni. Le classi Auto e Moto sono sottoclassi di Veicolo, e ognuna aggiunge campi specifici: Auto ha un campo per il numero di porte, e Moto ha un campo per il tipo di manubrio. Entrambe le sottoclassi sovrascrivono il metodo displayInfo() per stampare le loro informazioni specifiche.

La classe Main dimostra il polimorfismo creando istanze di Auto e Moto, ma riferendosi a entrambe tramite una variabile di tipo Veicolo, e invocando il metodo displayInfo() su entrambe le istanze.

Esercitazione su Collezioni e Strutture Dati in Java

  • ArrayList e LinkedList
  • Set e Map
  • Stack e Queue

Esercizio

Creare una classe Libreria che gestisce una collezione di Libri. Un Libro è caratterizzato da titolo, autore e numero di pagine.

  • Utilizzare un ArrayList per memorizzare i Libri.
  • Creare un metodo per aggiungere un Libro alla Libreria.
  • Creare un metodo che, dato un autore, restituisca un Set con i titoli dei libri di quell’autore.
  • Creare un metodo che restituisca una Map dove la chiave è l’autore e il valore è il numero totale di pagine scritte da quell’autore.

Soluzione

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

// Classe per rappresentare un Libro
class Libro {
    private String titolo;
    private String autore;
    private int numeroDiPagine;

    public Libro(String titolo, String autore, int numeroDiPagine) {
        this.titolo = titolo;
        this.autore = autore;
        this.numeroDiPagine = numeroDiPagine;
    }

    // Getters
    public String getTitolo() {
        return titolo;
    }

    public String getAutore() {
        return autore;
    }

    public int getNumeroDiPagine() {
        return numeroDiPagine;
    }
}

// Classe per rappresentare una Libreria
class Libreria {
    private ArrayList<Libro> libri;

    public Libreria() {
        this.libri = new ArrayList<>();
    }

    // Metodo per aggiungere un Libro alla Libreria
    public void aggiungiLibro(Libro libro) {
        libri.add(libro);
    }

    // Metodo per restituire i titoli dei libri di un determinato autore
    public Set<String> titoliDellAutore(String autore) {
        Set<String> titoli = new HashSet<>();
        for (Libro libro : libri) {
            if (libro.getAutore().equals(autore)) {
                titoli.add(libro.getTitolo());
            }
        }
        return titoli;
    }

    // Metodo per restituire il numero totale di pagine scritte da un autore
    public Map<String, Integer> paginePerAutore() {
        Map<String, Integer> paginePerAutore = new HashMap<>();
        for (Libro libro : libri) {
            paginePerAutore.put(libro.getAutore(), paginePerAutore.getOrDefault(libro.getAutore(), 0) + libro.getNumeroDiPagine());
        }
        return paginePerAutore;
    }
}

// Classe Main per testare la soluzione
public class Main {
    public static void main(String[] args) {
        Libreria libreria = new Libreria();

        libreria.aggiungiLibro(new Libro("Harry Potter", "J.K. Rowling", 400));
        libreria.aggiungiLibro(new Libro("Il Signore degli Anelli", "J.R.R. Tolkien", 500));
        libreria.aggiungiLibro(new Libro("Le due torri", "J.R.R. Tolkien", 350));

        System.out.println("Titoli di J.R.R. Tolkien: " + libreria.titoliDellAutore("J.R.R. Tolkien"));
        System.out.println("Pagine per Autore: " + libreria.paginePerAutore());
    }
}

In questa esercitazione, la classe Libro rappresenta un libro con i campi titolo, autore e numero di pagine. La classe Libreria mantiene una collezione di Libri in un ArrayList. Il metodo aggiungiLibro permette di aggiungere un Libro alla Libreria. Il metodo titoliDellAutore accetta il nome di un autore come parametro e restituisce un Set dei titoli dei libri di quell’autore. Il metodo paginePerAutore restituisce una Map dove la chiave è l’autore e il valore è il numero totale di pagine scritte da quell’autore.

La classe Main è un esempio di come usare la Libreria. In questo esempio, vengono creati alcuni libri, aggiunti alla libreria, e poi stampati i titoli di un determinato autore e il numero totale di pagine scritte da ogni autore.

Esercitazione su File I/O e Serializzazione

  • Lettura e Scrittura di File
  • Serializzazione degli Oggetti

Esercizio

Creare una classe Studente con campi per nome, cognome e matricola. Scrivere un programma che:

  • Salva una lista di oggetti Studente in un file
  • Legge la lista di oggetti Studente da un file
  • Serializza la lista di oggetti Studente
  • Deserializza la lista di oggetti Studente da un file serializzato

Soluzione


import java.io.*;
import java.util.ArrayList;
import java.util.List;

class Studente implements Serializable {
    private String nome;
    private String cognome;
    private int matricola;

    public Studente(String nome, String cognome, int matricola) {
        this.nome = nome;
        this.cognome = cognome;
        this.matricola = matricola;
    }

    @Override
    public String toString() {
        return "Studente [nome=" + nome + ", cognome=" + cognome + ", matricola=" + matricola + "]";
    }
}

public class Main {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        List studenti = new ArrayList<>();
        studenti.add(new Studente("Mario", "Rossi", 12345));
        studenti.add(new Studente("Luca", "Verdi", 67890));

        // Salvataggio della lista di Studenti in un file
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("studenti.dat"))) {
            oos.writeObject(studenti);
        }

        // Lettura della lista di Studenti da un file
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("studenti.dat"))) {
            List studentiDeserializzati = (List) ois.readObject();
            System.out.println("Studenti Deserializzati: " + studentiDeserializzati);
        }
    }
}

In questa esercitazione, la classe Studente rappresenta un studente con i campi nome, cognome e matricola, ed implementa l’interfaccia Serializable, che è necessaria per la serializzazione.

Il programma nella classe Main:

  • Crea una lista di oggetti Studente
  • Salva (serializza) questa lista in un file chiamato “studenti.dat” utilizzando ObjectOutputStream
  • Legge (deserializza) la lista di oggetti Studente dal file “studenti.dat” utilizzando ObjectInputStream

L’output del programma sarà la lista di studenti deserializzata, che verrà stampata sulla console.

Esercitazione su Multi-threading e Concorrenza

  • Creazione di Thread
  • Sincronizzazione

Esercizio

Creare una classe Contatore che mantiene un contatore intero. Implementare i metodi incrementa() e decrementa() che modificano il contatore. Creare due Thread, uno che incrementa il contatore e uno che lo decrementa.

Obiettivo: Assicurarsi che, anche in presenza di concorrenza, il contatore sia sempre accessibile in modo thread-safe utilizzando la sincronizzazione.

Soluzione


class Contatore {
    private int contatore = 0;

    public synchronized void incrementa() {
        contatore++;
    }

    public synchronized void decrementa() {
        contatore--;
    }

    public synchronized int getContatore() {
        return contatore;
    }
}

class IncrementaThread extends Thread {
    private final Contatore contatore;

    public IncrementaThread(Contatore contatore) {
        this.contatore = contatore;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            contatore.incrementa();
        }
    }
}

class DecrementaThread extends Thread {
    private final Contatore contatore;

    public DecrementaThread(Contatore contatore) {
        this.contatore = contatore;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            contatore.decrementa();
        }
    }
}

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Contatore contatore = new Contatore();
        
        Thread t1 = new IncrementaThread(contatore);
        Thread t2 = new DecrementaThread(contatore);

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        System.out.println("Valore Finale del Contatore: " + contatore.getContatore());
    }
}

In questa esercitazione:

  • La classe Contatore rappresenta un contatore con metodi per incrementare e decrementare il valore del contatore. Questi metodi sono synchronized, il che garantisce che solo un thread per volta possa accedere a questi metodi.
  • IncrementaThread e DecrementaThread sono due classi che estendono la classe Thread. Nel loro metodo run, chiamano rispettivamente i metodi incrementa e decrementa del contatore.
  • Nella classe Main, vengono creati un oggetto Contatore e due thread, t1 e t2. t1 è responsabile dell’incremento del contatore, mentre t2 è responsabile del decremento del contatore. Alla fine, attendiamo che entrambi i thread abbiano terminato con join() e stampiamo il valore finale del contatore.

Questo programma dimostra come gestire l’accesso concorrente a una risorsa condivisa (in questo caso, il Contatore) in modo thread-safe utilizzando la parola chiave synchronized.

Esercitazione su Networking in Java

Esercizio

Creare un semplice echo server che riceve una stringa dal client attraverso una connessione socket, e poi la reinvia indietro al client. Implementare sia il lato server che il lato client in Java.

Soluzione

Server:


import java.io.*;
import java.net.*;

public class EchoServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(8888);
        System.out.println("Server is running on port 8888");
        
        while (true) {
            Socket clientSocket = serverSocket.accept();
            System.out.println("New client connected");
            
            BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
            PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
            
            String inputLine;
            while ((inputLine = in.readLine()) != null) {
                System.out.println("Received: " + inputLine);
                out.println(inputLine);
            }
            
            clientSocket.close();
        }
    }
}

Client:


import java.io.*;
import java.net.*;

public class EchoClient {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("localhost", 8888);
        System.out.println("Connected to the server");
        
        BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
        
        out.println("Hello, Server!");
        String response = in.readLine();
        System.out.println("Server response: " + response);
        
        socket.close();
    }
}

In questa esercitazione:

  • La classe EchoServer rappresenta il server. Crea un ServerSocket in ascolto sulla porta 8888. Quando si connette un client, il server legge la stringa inviata dal client e la reinvia indietro al client.
  • La classe EchoClient rappresenta il client. Si connette al server sulla porta 8888, invia una stringa al server e stampa la risposta ricevuta dal server.

Per eseguire il codice, avviare prima il EchoServer, e successivamente il EchoClient. Il client invierà la stringa “Hello, Server!” al server, che poi la reinvierà indietro al client.

Lascia un commento