Guida Java in italiano | Aulab

GUIDE PER ASPIRANTI PROGRAMMATORI

Guida Java in italiano

Imparerai i concetti fondamentali del linguaggio Java, uno dei linguaggi di programmazione più richiesti dal mercato del lavoro: dal polimorfismo alla sintassi di base, dalle Java collections alle ArrayList, esplorerai tutti i concetti fondamentali di uno dei linguaggi di backend per eccellenza. Questi elementi ti permetteranno di scrivere codice Java efficace e di comprendere meglio le dinamiche alla base dello sviluppo di applicazioni complesse. Pronto a cominciare?

Immagine di copertina

Vuoi avviare una nuova carriera o fare un upgrade?

Trova il corso Digital & Tech più adatto a te nel nostro catalogo!

1

Introduzione a Java

1.1

Java: cos'è?

Il linguaggio Java è un linguaggio di programmazione che non solo ha segnato una pietra miliare nell'ambito dello sviluppo software, ma continua a essere una scelta prediletta da molti sviluppatori, sia novizi che esperti. Ma quali sono le caratteristiche fondamentali del linguaggio Java? E qual è la sua storia? Scopriamolo insieme!   Quali sono le caratteristiche chiave di Java Portabilità: la Java Virtual Machine (JVM) è il cuore della portabilità, poiché consente al codice Java di girare su qualsiasi dispositivo e sistema operativo. Robustezza: grazie al suo sistema di gestione delle eccezioni e al garbage collector, Java aiuta a prevenire molti errori comuni di programmazione e perdite di memoria. Sicurezza: Java offre molte funzionalità di sicurezza come classloaders, security managers e il già citato robusto sistema di gestione delle eccezioni, rendendolo una scelta solida per applicazioni critiche. Performance: sebbene non sia veloce quanto i linguaggi di programmazione compilati come C++, le performance di Java sono più che adeguate per la maggior parte delle applicazioni.   Storia e evoluzione di Java Il linguaggio Java ha le sue radici nelle ricerche condotte presso la Stanford University all'inizio degli anni '90. Il suo primo nome, Oak, fu coniato nel 1992 da un gruppo di esperti guidati da James Gosling, operanti sotto l'egida di Sun Microsystems. Tuttavia, a causa di problemi di copyright con un omonimo linguaggio di programmazione, Oak fu presto rinominato in Java, un riferimento ad una varietà di caffè indonesiano, simbolizzato dal logo di una tazzina di caffè. Inizialmente, Sun Microsystems mirava ad utilizzare Java per sviluppare applicazioni complesse per piccoli dispositivi elettronici. Fu solo con l'esplosione di Internet nel 1993 che Java iniziò a emergere come strumento potente per la programmazione web. Un momento cruciale fu l'adozione di Java da parte di Netscape Corporation, che integrò la Java Virtual Machine (JVM) nel suo browser. Questa mossa trasformò il web, rendendo le pagine interattive a livello client attraverso l'uso di applet Java, permettendo, così, l'esecuzione di applicazioni direttamente sul dispositivo dell'utente.   Java fu ufficialmente annunciato il 23 maggio 1995 durante l'evento SunWorld. Un altro passo significativo fu compiuto il 13 novembre 2006, quando Sun Microsystems rese disponibile il suo compilatore Java e la JVM sotto licenza GPL. Tuttavia, non tutte le implementazioni di Java sono libere. La versione libera dell'ambiente Java è nota come IcedTea. Il 8 maggio 2007, Sun ha rilasciato anche le librerie (esclusi alcuni componenti non di sua proprietà) sotto licenza GPL, confermando Java come linguaggio di programmazione con un'implementazione di riferimento libera. Il linguaggio è definito da un documento chiamato "The Java Language Specification" (JLS), la cui prima edizione è stata pubblicata nel 1996. Da allora, Java ha subito numerose modifiche e integrazioni, registrate nelle successive edizioni del JLS.
1.2

Perché imparare Java

Imparare Java può essere altamente vantaggioso per una serie di motivi, sia per gli sviluppatori principianti che per quelli esperti. Ecco alcune ragioni per cui potresti considerare l'apprendimento di Java:   Ampia Applicabilità: Java è utilizzato in una vasta gamma di applicazioni, dai sistemi commerciali e-government alle applicazioni mobili. È anche alla base di molte applicazioni Android, che rappresenta la piattaforma mobile più diffusa al mondo. Piattaforma Indipendente: Uno dei principi cardine di Java è "Write Once, Run Anywhere" (WORA), che permette agli sviluppatori di scrivere il codice una volta e di eseguirlo su qualsiasi dispositivo che supporti la Java Virtual Machine (JVM), rendendo Java una scelta eccellente per gli sviluppatori multi-piattaforma. Comunità di Sviluppatori: Java ha una comunità di sviluppatori vasta e attiva che può fornire supporto, librerie e framework. Questa comunità può essere una risorsa inestimabile per l'apprendimento e la risoluzione dei problemi. Elevata Richiesta di Lavoro: La domanda per gli sviluppatori Java rimane alta, con molte opportunità di lavoro in diverse industrie. Imparare Java può aumentare la tua employability e offrirti una carriera stabile. Performance: sebbene, come già detto, Java non possa considerarsi veloce come i linguaggi compilati come C o C++, la sua performance è più che adeguata per la maggior parte delle applicazioni, ed è notevolmente migliore rispetto ad altri linguaggi interpretati. Sicurezza: Java offre robuste funzionalità di sicurezza, inclusa la garbage collection, la gestione delle eccezioni e la verifica dei bytecode, che aiutano a prevenire potenziali bug e vulnerabilità di sicurezza Java vs altri linguaggi Se volessimo operare un confronto tra il linguaggio Java e altri linguaggi, i punti da analizzare sono quelli racchiusi in questa tabella:   Caratteristica Java Python C++ JavaScript Performance Alta (Grazie al compilatore JIT) Media Molto alta Media Sintassi Verbosa Chiara e concisa Complessa Semplice, ma diversa da Java Librerie e Framework Ampie e variate Ampie, con vantaggio in Data Science e AI Ampie, ma meno coerenti Molte librerie per sviluppo web Gestione della Memoria Automatica (Garbage Collector) Automatica (Garbage Collector) Manuale Automatica (Garbage Collector) Indipendenza dalla Piattaforma Si (WORA) Sì No (Necessita di compilazione specifica per piattaforma) Sì (Eseguito nei browser) Utilizzo Principale Applicazioni enterprise, Android, Sistemi Embedded Data Science, AI, Web Development Sistemi di basso livello, Performance-critical applications Sviluppo web lato client Supporto della Comunità Vasta e attiva Vasta e attiva Vasta, ma meno attiva rispetto a Java e Python Vasta e attiva Curva di Apprendimento Media Bassa Alta Bassa a Media  
1.3

Java Virtual Machine (JVM)

La Java Virtual Machine, o JVM, è una sorta di traduttore speciale che permette ai programmi scritti in linguaggio Java di funzionare su qualsiasi tipo di computer, indipendentemente dal sistema operativo. Quando scrivi un programma in Java, prima viene convertito in un "linguaggio intermedio" chiamato bytecode, che è come una versione semplificata del tuo programma originale. La JVM prende questo bytecode e lo traduce in un linguaggio che il tuo computer può capire ed eseguire, permettendo così al tuo programma di funzionare.   Una delle cose belle della JVM è che gestisce la memoria del tuo computer per te. Mentre il tuo programma è in esecuzione, la JVM tiene traccia di quale parte della memoria del computer è in uso e quale no, liberando spazio quando non è più necessario, simile a come un custode pulisce e organizza le cose.   La JVM ha anche una sorta di "libreria" dove tiene traccia di tutte le parti del tuo programma, in modo da sapere dove trovare le informazioni di cui ha bisogno. E ha delle regole di sicurezza per assicurarsi che il tuo programma non faccia nulla di dannoso al tuo computer mentre viene eseguito.
1.4

Java (SE) vs Java Enterprise (EE)

Java SE (Standard Edition) e Java EE (Enterprise Edition) rappresentano due facce della stessa medaglia nel mondo della programmazione Java, ciascuna progettata per soddisfare esigenze diverse.   Java SE è la fondazione della piattaforma Java. È la versione standard che fornisce le API (Application Programming Interfaces) essenziali e la macchina virtuale Java (JVM), che sono il cuore dello sviluppo in Java. Java SE è ideale per lo sviluppo di applicazioni general-purpose, come applicazioni desktop, strumenti di gestione e piccoli server. È ampiamente utilizzato per creare applicazioni robuste e portabili che possono essere eseguite su una varietà di dispositivi e sistemi operativi. Questa edizione mette a disposizione una vasta gamma di librerie per grafica, accesso ai database, interfaccia utente grafica (GUI), e molto altro.   Java EE, d'altra parte, è una versione espansa di Java SE, specializzata per lo sviluppo di applicazioni enterprise. È progettata per rispondere alle esigenze delle applicazioni di grandi dimensioni e ad alte prestazioni, tipiche delle grandi organizzazioni. Java EE include tutte le caratteristiche di Java SE ma aggiunge ulteriori API e framework per semplificare lo sviluppo di applicazioni distribuite, scalabili e transazionali su larga scala. Queste applicazioni sono spesso basate su architetture multilivello e richiedono funzionalità avanzate come gestione delle transazioni, sicurezza, scalabilità, concorrenza e gestione delle risorse.   Differenze Java SE e Java EE Vediamo ora in modo più dettagliato e schematico le maggiori differenze tra i due:   Aspetto Java Standard Edition (Java SE) Java Enterprise Edition (Java EE / Jakarta EE) Uso Primario Applicazioni desktop, applet e applicazioni console. Applicazioni server di livello enterprise, servizi web e sistemi distribuiti su larga scala. API e Librerie Librerie di base per compiti fondamentali (I/O, networking, accesso a database, GUI). API estese incluse Servlets, JSP, JSF, EJB, JPA, JMS, JTA, ecc. Ottimizzazione delle Performance Ottimizzato per il computing ad alte prestazioni su computer individuali o server. Progettato per scalabilità, affidabilità e disponibilità in sistemi distribuiti su larga scala. Portabilità Alta compatibilità cross-platform tramite JVM. Le applicazioni sono portabili su server Java EE conformi. Complessità Più semplice, con un focus su compiti di programmazione fondamentali. Più complesso a causa dei servizi aggiuntivi a livello enterprise. Componenti Include solo i componenti di base necessari per lo sviluppo di applicazioni Java. Include tutti i componenti di Java SE più API aggiuntive per funzionalità enterprise. Ambiente di Esecuzione Funziona su qualsiasi macchina con una JVM. Richiede un server di applicazioni Java EE (es. WildFly, Payara, GlassFish). Gestione delle Transazioni Gestione manuale delle transazioni o uso di framework di terze parti. Gestione robusta delle transazioni distribuite con JTA. Supporto per Servizi Distribuiti Supporto di base tramite API di networking e concorrenza. Supporto nativo per il computing distribuito e la concorrenza gestita.

2

Come installare Java

2.1

Java e gli ambienti di sviluppo

Un ambiente di sviluppo, noto anche come Integrated Development Environment (IDE), è uno strumento software che fornisce servizi e strumenti comprensivi ai programmatori per lo sviluppo di software. Un IDE rende il processo di sviluppo più semplice, più rapido e più organizzato rispetto all'utilizzo di editor di testo semplici e comandi manuali per compilare ed eseguire il codice.  Un ambiente di sviluppo, ad esempio, facilita il lavoro del programmatore utilizzando diversi colori per evidenziare differenti parti del codice e segnalando automaticamente gli errori di sintassi. Questo tipo di assistenza visiva e di correzione in tempo reale aiuta a rendere il processo di codifica più intuitivo e meno propenso agli errori. Il colore viene utilizzato per distinguere vari elementi del codice, come parole chiave, variabili e stringhe, permettendo al programmatore informatico di leggere e comprendere il codice più facilmente.   Per iniziare con la programmazione in Java, ci sono due percorsi principali che puoi seguire: utilizzare un Integrated Development Environment (IDE) online o installare un IDE sul tuo computer. Ognuno di questi approcci ha i suoi pro. Vediamoli!   Utilizzo di un IDE Online I vantaggi di utilizzare un IDE online sono:   Facilità di Accesso: gli IDE online sono accessibili da qualsiasi browser web, il che significa che puoi iniziare a programmare in Java senza dover installare nulla sul tuo computer. Portabilità: poiché gli IDE online sono accessibili via web, puoi accedere al tuo ambiente di sviluppo da qualsiasi computer con accesso a Internet. Meno Configurazione: gli IDE online spesso richiedono meno configurazione rispetto a quelli installati localmente.   Come utilizzare un IDE online Scegli un IDE online come Repl.it, JDoodle o Ideone. Visita il sito web dell'IDE scelto, crea un account (se necessario), e inizia a scrivere, eseguire e salvare il tuo codice Java direttamente nel browser.   Installazione di un IDE in Locale: (opzionale) Anche installare l’IDE in locale ha i suoi vantaggi:   Prestazioni Superiori: Gli IDE installati localmente tendono ad essere più veloci e reattivi. Funzionalità Complete: Gli IDE locali come Eclipse o IntelliJ IDEA offrono una gamma completa di funzionalità avanzate per aiutarti nello sviluppo. Lavoro Offline: Puoi lavorare sui tuoi progetti Java anche senza una connessione internet.
2.2

Come installare Java

Java Download  Vai sul sito web ufficiale di Oracle per scaricare l'ultima versione del JDK: Oracle JDK Downloads. Saranno, poi, disponibili al download le versioni per:   Windows: x64 Installer macOS: ARM64 DMG Installer (se il tuo processore è ARM64) o x64 DMG Installer (se il tuo processore è Intel) Linux: x64 RPM Package (se usi Fedora, RHEL o CentOS) o x64 Debian Package (se utilizzi Debian, Ubuntu o Linux Mint)   Dopo aver scaricato il file, fai doppio clic sull'installer per avviare il processo di installazione. Segui le istruzioni a schermo per completare l'installazione.   Windows Finita l’installazione dovrai configurare le variabili d'ambiente per assicurarti che il sistema riconosca dove Java è installato. Vai a "Pannello di controllo" -> "Sistema" -> "Impostazioni sistema avanzate" -> "Variabili d'ambiente". Aggiungi la directory bin della tua installazione Java alla variabile Path.   macOS Finita l’installazione aprire il Terminale e digita java -version per verificare che Java sia stato installato correttamente.   Linux Finita l’installazione Come per Windows, potrebbe essere necessario configurare le variabili d'ambiente. Modifica il file ~/.bashrc o ~/.bash_profile e aggiungi la directory bin della tua installazione Java alla variabile PATH.   Come installare un IDE in locale  Dopo l’installazione di Java dovremo installare il nostro ambiente di sviluppo, in questo caso Eclipse scaricabile da questo link. Procediamo, poi, con l’installazione con “Eclipse IDE for Java Developers”.
2.3

Creare il primo progetto in Java

Nel linguaggio Java, come in molti altri linguaggi di programmazione, il primo programma è costituito da un classico programma chiamato "Hello World". È un semplice esercizio che ti permette di vedere come si scrive, si compila e si esegue un programma Java.  Ma perché "Hello World"? La tradizione di usare "Hello World" come primo esercizio risale a decenni fa, ed è diventata un rito di passaggio per i nuovi programmatori. È un modo semplice per iniziare a capire come funziona un nuovo linguaggio di programmazione. Per prima cosa andiamo a creare un nuovo progetto (New Project); quello che ci apparirà a questo punto sarà una schermata vuota che dovremo riempire con il nostro codice. *immagine* Il codice che andremo ad inserire è il seguente public class HelloWorld { public static void main(String[] args) { System.out.println("Hello, World!"); } } In questa fase non presteremo attenzione ad ogni linea di codice, ma solo a quella che riporta “System.out.println("Hello, World!");”. Andiamo ad analizzare il nostro codice: System.out.println(): stiamo chiedendo di stampare nella console il contenuto tra parentesi “Hello, World!”: sarà la parte che verrà stampata dal codice, possiamo ovviamente sostituire questa parte di codice con qualsiasi testo, come ad esempio “Ciao Luca”, l’importante sia sempre tra virgolette.   Se abbiamo fatto tutto correttamente cliccando sul tasto “Execute” avremo come risultato il testo che avremo messo tra parentesi. *immagine* È sempre possibile salvare il nostro codice dal menu laterale in modo da non perdere i nostri lavori.   Errori e Debugging in Java Mentre procedi con la programmazione in Java, è inevitabile incontrare errori nel tuo codice.  In programmazione, gli errori di sintassi sono quei tipi di errori che commetti quando non segui correttamente le regole grammaticali del linguaggio, come dimenticare un punto e virgola alla fine di una dichiarazione o una parentesi graffa mancante.   Ecco un esempio del codice precedente ma con questa volta un errore di sintassi. public class HelloWorld { public static void main(String[] args) { System.out.println(Hello, World!); } } Eseguendolo, avremo come risultato un errore.  *immagine* L’ambiente di sviluppo ci sta avvisando che vi è un qualcosa che non funziona nel nostro codice: in particolare, la mancanza delle virgolette in “Hello, World!” hanno bloccato la corretta esecuzione del nostro codice. Questa è una perfetta occasione per parlare del debugging. Il debug, o debugging, è un processo cruciale nello sviluppo del software che implica l'identificazione e la correzione degli errori o dei bug nel codice di un programma. Quando un programma non funziona come previsto o produce errori, il debug aiuta a trovare e risolvere questi problemi per garantire che il software funzioni correttamente. Il debugging diventa essenziale nel momento nel quale un programmatore si ritrova a lavorare su tante linee di codice e identificare l’errore diventa un lavoro difficile.   Commenti in Java I commenti sono un modo per lasciare piccoli messaggi a te stesso o ad altri programmatori che spiegano cosa sta facendo il tuo codice. I commenti in Java iniziano con due barre (//) e il linguaggio Java ignorerà tutto ciò che viene dopo queste barre quando esegue il tuo programma. Ecco un esempio di un commento: // Questo codice stamperà "Ciao, mondo!" sullo schermo System.out.println("Ciao, mondo!"); I commenti, nella programmazione informatica in generale, sono fondamentali per diversi motivi che contribuiscono a rendere il codice più comprensibile e gestibile, sia per te stesso sia per altri sviluppatori che potrebbero lavorare sul tuo codice in futuro. Ecco alcuni dei motivi principali: Chiarimenti: I commenti possono fornire spiegazioni su cosa fa una particolare sezione di codice. Questo è particolarmente utile in codice complesso dove la funzione di un particolare frammento potrebbe non essere immediatamente ovvia. Facilità di Manutenzione: Con i commenti, è più facile per te o per altri sviluppatori tornare indietro e capire il codice, rendendo la manutenzione e l'aggiornamento del codice molto più semplici. Debugging: Durante la fase di debugging, i commenti possono aiutarti a capire meglio cosa dovrebbe fare il codice, rendendo più facile individuare dove potrebbe esserci un errore. Segnalazione di aree da migliorare: I commenti possono essere utilizzati per segnalare parti del codice che potrebbero necessitare di ulteriori miglioramenti o revisioni.

3

Variabili e Costanti in Java

3.1

Variabili, costanti e tipi di dati in Java

Dichiarazione e variabili in Java Nel linguaggio Java, le variabili e le costanti sono elementi fondamentali che permettono di memorizzare dati nel tuo programma. Le variabili sono come scatole dove puoi mettere valori che potrebbero cambiare nel tempo; le costanti, invece, sono  scatole con coperchi sigillati che contengono valori che non cambieranno mai. Quando dichiariamo una variabile è come se stessimo dicendo al programma che abbiamo bisogno di una scatola per contenere un certo tipo di dato.    Tipi di dati in Java Prima di immergerci più nel vivo nei concetti di dichiarazione e inizializzazione di variabili in Java, è essenziale capire i tipi di dati più comuni che possono essere utilizzati. I tipi di dati sono fondamentali perché indicano al programma la natura dei dati che la variabile o la costante terrà. Ecco alcuni dei tipi di dati più comuni in Java:   int: Utilizzato per memorizzare numeri interi, cioè numeri senza decimali. Esempio: -10, 0, 25, 1000. float: Utilizzato per memorizzare numeri con decimali. Esempio: -10.5, 0.0, 25.76, 1000.01. char: Utilizzato per memorizzare un singolo carattere. Esempio: 'a', 'B', '1', '$'. boolean: Utilizzato per memorizzare valori veri o falsi. String: Utilizzato per memorizzare una sequenza di caratteri o testo. Esempio: "Ciao", "Java123", "Benvenuto in Java!".   I termini "dichiarazione", "inizializzazione" e "assegnazione" di una variabile hanno significati specifici, anche se nel linguaggio comune possono essere usati in modo un po' intercambiabile o approssimativo. Ecco una spiegazione dettagliata di ciascun termine:   Dichiarazione di una variabile: La dichiarazione di una variabile avviene quando si specifica il tipo di una variabile e il suo nome, senza necessariamente assegnarle un valore iniziale. La dichiarazione stabilisce il tipo di dati che la variabile può contenere e riserva lo spazio in memoria per quella variabile. Inizializzazione di una variabile: L'inizializzazione di una variabile avviene quando si assegna un valore iniziale alla variabile al momento della sua dichiarazione. L'inizializzazione può essere vista come una "dichiarazione con assegnazione".  Assegnazione di una variabile: L'assegnazione si verifica quando si fornisce o si modifica il valore di una variabile già dichiarata. Questo può avvenire subito dopo la dichiarazione o in qualsiasi punto successivo nel codice.    Nel gergo comune, questi termini possono essere usati in modo sinonimico. Ad esempio, potresti sentire qualcuno dire "dichiaro la variabile x a 5" intendendo che la sta inizializzando con il valore 5, o "inizializzo y" quando in realtà sta effettuando un'assegnazione a una variabile già dichiarata. Tuttavia, è importante comprendere le differenze tecniche tra questi termini per evitare confusione, specialmente quando si legge o si scrive codice più formale o documentazione.   Ecco qualche esempio.   String nome; // Dichiarazione di una variabile String nome = "John Doe"; // Inizializzazione di una variabile nome = "John Doe"; // Assegnazione di una variabile   String: Questa parte indica il tipo di dato che la variabile può contenere, in questo caso può contenere una sequenza di caratteri, che può essere una parola, una frase o anche una sequenza più lunga di testo. nome: Questa parte è il nome che viene dato alla variabile. È il modo in cui ci riferiremo a questa particolare porzione di dati nel resto del nostro programma. Quando vogliamo riferirci al testo "John Doe" nel codice, utilizzeremo il nome ‘nome’. “John Doe”: Questa parte, racchiusa tra virgolette doppie, è il valore iniziale che viene assegnato alla variabile ‘nome’. In Java, è importante racchiudere il testo tra virgolette doppie quando si lavora con stringhe. Il testo tra virgolette doppie indica il contenuto esatto che la variabile ‘nome’ conterrà.   Applichiamo, ora, quello che abbiamo imparato sul codice precedente   public class HelloWorld { public static void main(String[] args) { //ricordiamoci sempre di mettere il nostro codice qui dentro) String messaggio = "Hello, World!"; System.out.println("Il mio messaggio è " + messaggio); } } In questo caso, stiamo dichiarando la variabile ‘messaggio’, e per visualizzare il suo contenuto sulla console, la inseriremo come argomento del metodo ‘System.out.println()’. Un elemento nuovo in questa istruzione è l'uso dell'operatore ‘+’, che in questo contesto è utilizzato per unire (o concatenare, per usare un termine tecnico) la stringa "Il mio messaggio è " con il contenuto della variabile ‘messaggio’.   Ecco quale sarà il risultato per il nostro codice.   Costanti in Java A differenza delle variabili, come già detto, le costanti sono valori che, una volta definiti, non possono essere modificati nel corso dell'esecuzione del programma.  Le costanti sono utili, dunque, quando si ha un valore che non dovrebbe cambiare, indipendentemente dalle circostanze. Ad esempio, il valore di Pi (π) in matematica è una costante, così come il numero di giorni in una settimana. Nel codice, le costanti aiutano a rendere il programma più leggibile e a prevenire errori che potrebbero derivare dalla modifica accidentale di un valore che dovrebbe rimanere fisso.   Nel linguaggio Java le costanti sono dichiarate utilizzando la parola chiave ‘final’ prima del tipo di dato. La convenzione di nomenclatura suggerisce di utilizzare lettere maiuscole per i nomi delle costanti e, se il nome è composto da più parole, queste dovrebbero essere separate da un underscore ‘_’.    final int GIORNI_SETTIMANA = 7;

4

Operatori in Java

4.1

Operatori aritmetici in Java

Nel mondo della programmazione informatica, la capacità di eseguire operazioni matematiche è cruciale. Molte funzionalità di base dei programmi dipendono dalla manipolazione di numeri attraverso operazioni aritmetiche: non è un caso che abbiamo già incontrato questi temi in altre guide avanzate, come la guida Javascript in italiano o la guida PHP in italiano. Nel linguaggio di programmazione Java, come in molti altri linguaggi di programmazione, abbiamo a disposizione degli operatori aritmetici che ci permettono di eseguire queste operazioni. In questa lezione, esploreremo gli operatori aritmetici di base: addizione (+), sottrazione (-), moltiplicazione (*), divisione (/) e modulo (%) e vedremo come possono essere utilizzati per manipolare dati numerici nel codice.   Operatore di addizione in Java (+)  L'operatore di addizione è simbolicamente rappresentato con un segno più (+). Esso permette di sommare due o più numeri insieme. La sintassi per l'addizione è semplice: basta posizionare l'operatore + tra i numeri che si desidera sommare.   int a = 10; int b = 20; int somma = a + b; // Risultato: 30 Operatore di Sottrazione In Java (-) L'operatore di sottrazione è rappresentato con un segno meno (-). Esso è utilizzato per sottrarre un numero da un altro. L'operatore - viene posizionato tra i numeri, dove il numero a sinistra è il minuendo e quello a destra è il sottraendo.   int a = 50; int b = 30; int differenza = a - b; // Risultato: 20 Operatore di Moltiplicazione in Java (*) L'operatore di moltiplicazione è rappresentato con un asterisco (*). Esso permette di moltiplicare due numeri insieme. Come gli altri operatori, l'asterisco viene posizionato tra i numeri che si desidera moltiplicare.   int a = 5; int b = 4; int prodotto = a * b; // Risultato: 20 Operatore di Divisione in Java (/) L'operatore di divisione è rappresentato con una barra (/). Esso è utilizzato per dividere un numero per un altro. L'operatore / viene posizionato tra i numeri, dove il numero a sinistra è il dividendo e quello a destra è il divisore. Esempio tra due numeri interi   int c = 10; int d = 5; float risultato = c / d; // Risultato: 2 Esempio tra due numeri non interi int c = 5; float d = 4; float risultato = c / d; // Risultato: 1.25 Operatore Modulo in Java (%) L'operatore modulo, rappresentato dal segno percentuale (%), calcola il resto di una divisione tra due numeri.   int e = 10; int f = 3; int resto = e % f; // Risultato: 1, perché 10 diviso 3 dà un resto di 1 Questi operatori aritmetici rappresentano uno strumento essenziale per chi si approccia al mondo della programmazione, fornendo le basi per iniziare a comprendere e a manipolare le dinamiche fondamentali dell'elaborazione di dati e calcoli all'interno del codice. La loro comprensione e applicazione pratica permette di muovere i primi passi con maggiore consapevolezza e competenza nell'ambito della programmazione, gettando le fondamenta per lo sviluppo di abilità più avanzate e per l'esplorazione di concetti più complessi.
4.2

Operatori di confronto in Java

Gli operatori di confronto, conosciuti anche come operatori relazionali, giocano un ruolo cruciale nella programmazione. Essi permettono di confrontare due valori e di determinare il tipo di relazione che esiste tra di loro. In Java, gli operatori di confronto principali sono: uguaglianza (==), disuguaglianza (!=), minore di (<), maggiore di (>), minore o uguale a (<=), e maggiore o uguale a (>=). In questa lezione, esploreremo questi operatori e vedremo come possono essere utilizzati per prendere decisioni nel codice.   Molte logiche di programmazione dipendono dalla capacità di confrontare valori e prendere decisioni basate su tali confronti. Gli operatori di confronto confrontano due valori e restituiscono un valore booleano (true o false) a seconda che la condizione sia vera o falsa.   Operatore di Uguaglianza in Java (==) L'operatore di uguaglianza è simbolicamente rappresentato con due simboli di uguale (==). Esso permette di verificare se due valori sono uguali. Se i valori sono uguali, l'operatore restituirà true; altrimenti, restituirà false.   int a = 5; int b = 10; boolean risultato = (a == b); // Risultato: false Operatore di Diversità in Java (!=) L'operatore di diversità è rappresentato con un simbolo di esclamazione seguito da un simbolo di uguale (!=). Esso permette di verificare se due valori sono diversi. Se i valori sono diversi, l'operatore restituirà true; altrimenti, restituirà false.   int a = 5; int b = 10; boolean risultato = (a != b); // Risultato: true Operatori Minore e maggiore in Java (< >) Gli operatori minore e maggiore sono rappresentati con i simboli (< o >). Permettono, come è facile intuire, di verificare se un valore è minore o maggiore dell'altro. Usando il minore, se il valore a sinistra dell'operatore è minore di quello a destra, l'operatore restituirà true; altrimenti, restituirà false. Viceversa avverrà usando il maggiore.   int a = 5; int b = 10; boolean risultato = (a < b); // Risultato: true Operatori Minore uguale e Maggiore uguale in Java (>= <=) Gli operatori maggiore e uguale e minore e uguale sono rappresentati con un simbolo di maggiore o minore seguito da un simbolo di uguale (>= o <=). Nell’esempio sottostante, il codice mi permette di verificare se un valore è maggiore o uguale all'altro. Se il valore a sinistra dell'operatore è maggiore o uguale a quello a destra, l'operatore restituirà true; altrimenti, restituirà false.   int a = 5; int b = 5; boolean risultato = (a >= b); // Risultato: true
4.3

Operatori logici in Java

Se è vero che nel mondo della programmazione informatica la capacità di eseguire operazioni aritmetiche è importante, quella di eseguire operazioni logiche non è da meno, poiché molte funzionalità di base dei programmi dipendono dalla valutazione di condizioni e dalla manipolazione di valori booleani. Nel linguaggio di programmazione Java, come anche in molti altri linguaggi di programmazione, abbiamo a disposizione degli operatori logici che ci permettono di eseguire queste operazioni. In questo capitolo esploreremo gli operatori logici di base: AND (&&), OR (||), e NOT (!), e vedremo come possono essere utilizzati per manipolare dati logici nel codice.   Operatore logico AND in Java (&&) L'operatore logico AND è rappresentato con due simboli di e commerciale (&&). Esso permette di valutare se entrambe le espressioni logiche a sinistra e a destra dell'operatore sono vere. La sintassi per l'AND logico è semplice: basta posizionare l'operatore && tra le espressioni che si desidera valutare.   boolean x = true; boolean y = false; boolean risultato = x && y; // Risultato: false Operatore logico OR in Java (||) L'operatore logico OR è rappresentato con due barre verticali (||). Esso è utilizzato per valutare se almeno una delle espressioni logiche a sinistra o a destra dell'operatore è vera. L'operatore || viene posizionato tra le espressioni, facilitando la valutazione di condizioni multiple.   boolean x = true; boolean y = false; boolean risultato = x || y; // Risultato: true Operatore logico NOT in Java (!) L'operatore logico NOT è rappresentato con un punto esclamativo (!). Esso permette di invertire il valore logico dell'espressione a cui è applicato, trasformando un valore true in false e viceversa.   boolean x = true; boolean risultato = !x; // Risultato: false Gli esempi forniti illustrano chiaramente come gli operatori possono essere utilizzati per manipolare vari tipi di dati e come influenzano il flusso di controllo all'interno di un programma. Attraverso la pratica e l'esplorazione di questi operatori, gli aspiranti programmatori possono acquisire una migliore comprensione delle operazioni fondamentali, preparandosi a esplorare concetti più avanzati e complessi in futuro. La padronanza di questi concetti basici è cruciale per progredire nel campo della programmazione, contribuendo a formare una base solida su cui costruire conoscenze più avanzate.
4.4

Operatori di assegnazione in Java

Nel vasto mondo della programmazione, l'efficienza e la chiarezza del codice sono di fondamentale importanza. Un ruolo chiave in questo contesto è giocato dagli operatori di assegnazione. In Java, così come in molti altri linguaggi di programmazione, questi operatori sono utilizzati non solo per assegnare valori alle variabili, ma anche per semplificare e rendere più leggibile il codice.   L'Operatore di Assegnazione Base in Java (=) L'operatore = è l'operatore di assegnazione più basilare. Serve per assegnare il valore alla destra dell'operatore alla variabile situata a sinistra.   int x = 10; // Assegna il valore 10 alla variabile x Operatori di Assegnazione composta += e -= Questi operatori combinano un'operazione matematica (come l'addizione o la sottrazione) con l'assegnazione. Sono utili per modificare il valore di una variabile e assegnarle il nuovo valore in un solo passaggio.   int a = 5; a += 3; // Equivale a a = a + 3; ora a è 8 int b = 10; b -= 4; // Equivale a b = b - 4; ora b è 6 Operatori di Assegnazione composta *= e /= Gli operatori *= e /= seguono lo stesso principio. int c = 7; c *= 2; // Equivale a c = c * 2; ora c è 14 int d = 20; d /= 4; // Equivale a d = d / 4; ora d è 5 Gli operatori di assegnazione composta rendono il codice più snello e leggibile, riducendo la necessità di ripetere il nome della variabile. La chiarezza e la sintesi del codice facilitano la manutenzione e la comprensione per altri sviluppatori. Non per ultimo, questi operatori permettono di scrivere codice più rapidamente, un vantaggio non trascurabile, soprattutto in progetti di grandi dimensioni.

5

Flusso di esecuzione in Java

5.1

Istruzioni condizionali in Java

Nel mondo della programmazione informatica, la capacità di controllare il flusso di esecuzione di un programma è fondamentale per costruire applicazioni dinamiche e interattive. Le istruzioni condizionali permettono ai programmatori di specificare blocchi di codice che dovrebbero essere eseguiti solo quando certe condizioni sono soddisfatte. In Java, come in molti altri linguaggi di programmazione, le istruzioni condizionali principali sono if, else e switch-case. In questa lezione, esploreremo queste istruzioni condizionali e vedremo come possono essere utilizzate per dirigere il flusso di controllo all'interno di un programma.   If in Java L'istruzione ‘if’ rappresenta il blocco condizionale più elementare ed essenziale. Esso verifica una condizione e, se questa risulta vera, il blocco di codice all'interno dell'istruzione ‘if’ viene eseguito. Se la condizione è falsa, il blocco di codice viene ignorato. Per gestire anche il caso in cui la condizione è falsa, si può estendere la struttura condizionale utilizzando l'istruzione ‘else’. In questo modo, si può specificare un blocco di codice da eseguire quando la condizione è falsa.    if (a > 5) { System.out.println("a è maggiore di 5"); } else { System.out.println("a non è maggiore di 5"); } In questo scenario, il programma esamina se la variabile ‘a’ è maggiore di 5. Se la condizione è soddisfatta, il programma restituirà la frase "a è maggiore di 5"; in caso contrario, verrà visualizzata la frase "a non è maggiore di 5".   Esercizio con operatori e if in Java Supponiamo di avere un negozio e di voler applicare sconti ai clienti in base all'importo totale della loro spesa. Gli sconti saranno applicati come segue:   Se la spesa è inferiore a €50, non viene applicato alcuno sconto. Se la spesa è uguale o superiore a €50, viene applicato uno sconto del 20%.   public class CalcolatoreSconto { public static void main(String[] args) { float spesaTotale = 120; // Imposta l'importo totale della spesa float sconto = 0; float spesaDopoSconto = 0; if (spesaTotale < 50) { sconto = 0; } else{ sconto = spesaTotale * 0.20f; // Sconto del 20% } spesaDopoSconto = spesaTotale - sconto; System.out.println("Importo sconto: €" + sconto); System.out.println("Importo dopo sconto: €" + spesaDopoSconto); } } Suggerimento: per il momento, stiamo mettendo da parte l'interpretazione di 'public class' e 'public static void main', focalizzandoci esclusivamente sul resto del codice. È importante notare, tuttavia, che l'esecuzione del programma prende il via dal metodo main, proseguendo, riga per riga, dall'alto verso il basso a meno che il flusso non venga deviato da, per esempio, errori nel codice. Questo implica che operazioni quali la dichiarazione delle variabili debbano essere effettuate obbligatoriamente prima del loro utilizzo nel codice.   Spiegazione: nella prima fase del codice abbiamo dichiarato tre variabili utilizzando il tipo float in modo da rendere ammissibili anche risultati con la virgola e abbiamo assegnato loro dei valori, tra cui anche l’importo della spesa effettuata.   float spesaTotale = 120; // Imposta l'importo totale della spesa float sconto = 0; float spesaDopoSconto = 0; Nella seconda fase abbiamo verificato lo sconto spettante per la nostra spesa; nella prima condizione ‘if’ verifichiamo che la spesa sia minore di 50 e, se questa affermazione si dimostra vera, lo sconto applicato sarà di 0; se questo, però, non dovesse essere vero - come nel nostro caso, - il programma passerà al ‘else’ il quale calcolerà lo sconto facendo facendo l’operazione ‘sconto = 120 * 0.20’.   if (spesaTotale < 50) { sconto = 0; } else{ sconto = spesaTotale * 0.20f; // Sconto del 20% (se facciamo calcoli con i float dobbiamo ricordarci di inserire la f alla fine del numero) } Nella parte conclusiva del codice, calcoliamo l'importo della variabile ‘spesaDopoSconto’ sottraendo l'importo dello sconto determinato in precedenza, dall'importo totale della spesa. Successivamente, vengono visualizzati sia l'importo dello sconto (che in questo caso ammonta a €24) sia l'importo finale da pagare dopo l'applicazione dello sconto (che in questo caso è di €96).   spesaDopoSconto = spesaTotale - sconto; System.out.println("Importo sconto: €" + sconto); System.out.println("Importo dopo sconto: €" + spesaDopoSconto); Attraverso questo esercizio, abbiamo esplorato come gli operatori aritmetici e le strutture di controllo come if e else possano essere utilizzati in un contesto pratico per risolvere problemi reali. In particolare, abbiamo visto come calcolare sconti in base all'importo speso, una situazione comune che potrebbe presentarsi in molti ambiti, non solo nel contesto di un negozio.   Switch-case in Java L'istruzione switch-case è un'alternativa all'uso di molteplici istruzioni if-else quando si hanno diverse condizioni da verificare che dipendono dal valore di una singola variabile. Mentre l'if-else è più flessibile e può valutare una vasta gamma di condizioni, lo switch-case è generalmente più leggibile e organizzato quando si confrontano le stesse variabili con valori specifici e definiti.   public class Main { public static void main(String[] args) { int day = 3; switch (day) { case 1: System.out.println("Monday"); break; case 2: System.out.println("Tuesday"); break; case 3: System.out.println("Wednesday"); break; case 4: System.out.println("Thursday"); break; case 5: System.out.println("Friday"); break; case 6: System.out.println("Saturday"); break; case 7: System.out.println("Sunday"); break; } } } Quando ‘giorno’ è impostato a 3, il codice passerà attraverso lo switch e raggiungerà il ‘case 3’, dove stamperà "Wednesday" e, poi, uscirà dallo switch a causa del break (che verrà trattato in seguito in modo più dettagliato). Non raggiungerà gli atri case o il default perché il break impedisce di continuare nell'esecuzione dei casi successivi .Nel linguaggio Java, l'operatore switch è stato migliorato nelle versioni più recenti per supportare i tipi String rendendo lo strumento più potente e flessibile.
5.2

Cicli in Java

I cicli sono strumenti potenti nella programmazione informatica che permettono di eseguire un blocco di codice ripetutamente finché una certa condizione è verificata. Nel linguaggio Java, i cicli sono implementati attraverso tre principali costrutti: for, while e do-while. Questi cicli sono fondamentali per lavorare con collezioni di dati, eseguire operazioni ripetitive e gestire flussi di controllo complessi. Vediamo ciascuno di essi più nel dettaglio.   Ciclo For in Java Il ciclo ‘for’ è ideale quando sappiamo in anticipo quante volte vogliamo eseguire un certo blocco di codice. La sintassi del ciclo ‘for’ include l'inizializzazione, la condizione e l'incremento.   for (int i = 0; i < 10; i++) { System.out.println("Il valore di i è: " + i); } Inizializzazione int i = 0: prima di entrare nel ciclo, viene creato un contatore i ed è inizializzato a 0. Questa inizializzazione avviene solo una volta, all'inizio del ciclo. Condizione i < 10: ogni volta che il ciclo viene eseguito, il linguaggio Java controlla se la condizione i < 10 è vera. Se lo è, il ciclo continua; se non lo è, il ciclo si ferma e il programma procede con l'istruzione successiva al ciclo. Incremento i++: dopo che il blocco di codice è stato eseguito, l'istruzione ‘i++’ incrementa il valore di ‘i’ di 1. Questo significa che ‘i’ aumenterà di 1 ad ogni iterazione del ciclo.   In questo esempio, quando si avvia il programma, il ciclo for inizierà e compirà dieci iterazioni, stampando il messaggio contenuto nella funzione ‘System.out.println’ ad ogni giro. Ogni volta, verrà visualizzato il testo "Il valore di i è: " seguito dal valore corrente di i, che cambierà ad ogni iterazione incrementandosi di 1, partendo da 0 fino a 9. Al termine delle dieci iterazioni, il ciclo for si concluderà, ed il programma proseguirà con l'esecuzione del resto del codice che segue il blocco del ciclo for.   Ciclo foreach in Java Il foreach in Java è una forma semplificata di iterazione utilizzata per attraversare gli elementi di una collezione, come array, liste o insiemi. Questo costrutto è particolarmente utile quando si desidera esaminare ogni elemento senza preoccuparsi degli indici o dell'accesso diretto agli elementi stessi. Ecco un esempio di come si utilizza il foreach in Java:   String[] colors = {"Red", "Green", "Blue", "Yellow"}; for (String color : colors) { System.out.println(color); } In questo esempio, colors è un array di stringhe. Il costrutto for each viene utilizzato nella forma for (tipo elemento : collezione) dove tipo è il tipo di dati degli elementi nell'array (String in questo caso), elemento è il nome della variabile che rappresenta ogni elemento durante l'iterazione e collezione è l'array o la collezione attraverso cui si desidera iterare. Ad ogni iterazione del ciclo, la variabile color assume il valore dell'elemento corrente nell'array. Quindi, nel nostro esempio, color assumerà i valori "Red", "Green", "Blue" e "Yellow" durante le quattro iterazioni del ciclo. All'interno del blocco del ciclo, possiamo utilizzare color per accedere all'elemento corrente e eseguire le operazioni necessarie su di esso.   L'uso del foreach in Java rende il codice più leggibile e meno soggetto a errori rispetto ai tradizionali cicli for o while quando si lavora con collezioni di dati.   Ciclo while in Java Il ciclo ‘while’ esegue un blocco di codice finché una certa condizione è vera. È utile quando non sappiamo quante volte il ciclo dovrà essere eseguito.   int j = 0; while (j < 10) { System.out.println("Il valore di j è: " + j); j++; } In questo esempio, il ciclo continuerà finché ‘j’ è minore di 10, incrementando ‘j’ di 1 ad ogni iterazione.   Ciclo do-while in Java Il ciclo ‘do-while’ in Java è simile al ciclo ‘while’, ma esegue il blocco di codice almeno una volta prima di controllare la condizione.   int k = 0; do { System.out.println("Il valore di k è: " + k); k++; } while (k < 10); Anche qui, il ciclo continuerà finché ‘k’ è minore di 10, ma a differenza del ciclo ‘while’, il blocco di codice verrà eseguito almeno una volta anche se la condizione iniziale non è verificata.   Esercizio con cicli Java Scrivere un programma in Java che stampi i numeri pari tra 0 e 20.   public class NumeriPari { public static void main(String[] args) { // Utilizziamo un ciclo for per iterare attraverso i numeri da 0 a 20 for (int i = 0; i <= 20; i++) { // Usiamo l'operatore modulo (%) per verificare se un numero è pari if (i % 2 == 0) { System.out.println(i); // Stampa il numero se è pari } } } } Nel primo pezzo di codice, incontriamo un ciclo ‘for’ che ci permette di esaminare tutti i numeri da 0 a 20, uno per volta. Questa parte del codice è responsabile di generare i numeri che verranno poi valutati per determinare se sono pari o dispari. Il segmento di codice subito dopo, che contiene l'istruzione ‘if’, si occupa di effettuare questo controllo. Utilizza l'operatore modulo (‘%’) per verificare se il numero corrente è pari o dispari, controllando se il resto della divisione del numero per 2 è zero o no. In caso affermativo, il numero è identificato come pari e viene stampato su console.
5.3

Break e Continue in Java

Il comando ‘break’ e il comando ‘continue’ sono due strumenti molto utili quando lavoriamo con i cicli in Java. Essi offrono un maggior controllo sul flusso del programma durante l'esecuzione di cicli, permettendo, rispettivamente, di interrompere un ciclo o di saltare un'iterazione.   Break in Java Il comando ‘break’ in Java è utilizzato per uscire anticipatamente da un blocco di codice. Una volta che il comando break viene eseguito, il ciclo si interrompe immediatamente e il controllo del programma passa all'istruzione successiva dopo il ciclo. Questo può essere utile quando abbiamo trovato ciò che stavamo cercando in un ciclo e non c'è più bisogno di continuare ulteriormente.   for(int i = 0; i < 10; i++){ if(i == 5){ break; // Termina il ciclo quando i è uguale a 5 } System.out.println(i); } // Output: 0 1 2 3 4 Nell'esempio sopra, il ciclo si interrompe non appena la variabile i raggiunge il valore 5, grazie al comando break. Quindi, solo i numeri da 0 a 4 vengono stampati.   Continue in Java Il comando ‘continue’ in Java è utilizzato per saltare l'iterazione corrente di un ciclo e procedere direttamente alla successiva. Questo può essere utile quando, per esempio, abbiamo una condizione particolare che, se verificata, rende inutile eseguire il resto del codice nel ciclo per l'iterazione corrente.   for(int i = 0; i < 10; i++){ if(i == 5){ continue; // Salta l'iterazione quando i è uguale a 5 } System.out.println(i); } // Output: 0 1 2 3 4 6 7 8 9 Nell'esempio sopra, quando la variabile ‘i’ raggiunge il valore 5, il comando ‘continue’ fa sì che il resto del codice nel ciclo venga saltato per quella particolare iterazione, e il ciclo procede con l'iterazione successiva. Pertanto, il numero 5 non viene stampato, ma il ciclo continua e stampa i numeri da 6 a 9.   Sia ‘break’ che ‘continue’ sono strumenti potenti che, se usati correttamente, possono rendere il codice più efficiente e leggibile. Tuttavia, è importante usarli con cautela per evitare di creare codice confuso o di introdurre bug involontari.

6

Array in Java

6.1

Array in java

Un array in Java è un contenitore di dati che ha una dimensione fissa. La dimensione dell'array determina quanti elementi può contenere. Una volta creato un array, la sua dimensione non può essere modificata. Gli array sono utili quando si desidera raggruppare e gestire insieme un insieme di elementi correlati.   Per creare un array in Java, è necessario specificare il tipo di dati che l'array conterrà, seguito da parentesi quadre, e infine il nome dell'array. Ad esempio, per creare un array di interi chiamato ‘numeri’, scrivere:   int[] numeri; Quindi, è possibile inizializzare l'array con una dimensione specifica usando l'operatore ‘new’, così:   numeri = new int[5]; Ora, ‘numeri’ è un array che può contenere cinque interi.   Gli elementi in un array sono accessibili tramite un indice, che inizia da 0. Per assegnare un valore a un elemento dell'array, usare l'operatore di assegnazione (=) e l'indice dell'elemento, così:   numeri[0] = 10; Per scorrere tutti gli elementi di un array, è comune usare un ciclo ‘for’. Ad esempio, per stampare tutti gli elementi di un array, fare:   for (int i = 0; i < numeri.length; i++) { System.out.println(numeri[i]); } L'attributo ‘length’ di un array fornisce la sua dimensione, che può essere utilizzata per controllare che l'indice sia valido.   Array multidimensionali in Java Un array multidimensionale è un array di array. Ad esempio, un array bidimensionale può essere pensato come una tabella, con righe e colonne. Per creare un array bidimensionale, usare due set di parentesi quadre, in questo modo:   int[][] matrice = new int[3][3]; Per accedere o assegnare un valore in un array multidimensionale, specificare gli indici per ogni dimensione, così:   matrice[0][0] = 1; int valore = matrice[0][0];
6.2

Esercizio con array in Java

Immaginiamo di essere studenti e di voler calcolare la media dei nostri voti. Creeremo un programma Java che utilizzi un array per immagazzinare i voti e poi calcoli e stampi la media, qui i voti 25, 30, 28, 24, 29, 30.   public class MediaVoti { public static void main(String[] args) { int[] voti = {25, 30, 28, 24, 29, 30}; int somma = 0; for(int i = 0; i < voti.length; i++) { somma += voti[i]; } float media = (float) somma / voti.length; System.out.println("La media dei voti è: " + media); } } In questa parte del codice stiamo dichiarando un array di interi chiamato ‘voti’ e inizializzandolo con sei valori.   int[] voti = {25, 30, 28, 24, 29, 30}; All'interno del ciclo, stiamo aggiungendo il valore dell'elemento corrente dell'array voti alla variabile somma in modo da poter alla fine del ciclo for avere una somma di tutti i valori dell’array.   int somma = 0; for(int i = 0; i < voti.length; i++) { somma += voti[i]; } Il programma termina con il semplice calcolo per la media costituito dalla somma totale dei voti/il numero dei voti (è dato dalla formula ‘voti.lenght’) e dalla stampa del valore finale.   float media = (float) somma / voti.length; System.out.println("La media dei voti è: " + media); Se hai voglia di sperimentare altri esercizi Java per skillarti, ne abbiamo preparati alcuni per te, corri a scoprirli!

7

Gestione delle eccezioni in Java

7.1

Cos'è un'eccezione in Java

Un'eccezione nel linguaggio Java rappresenta una situazione anomala o un errore che si verifica durante l'esecuzione di un programma. Queste eccezioni interrompono il flusso normale di esecuzione del codice, causando un trasferimento del controllo a una sezione specifica del programma designata per gestire tali situazioni anomale. Questa sezione del programma è comunemente conosciuta come gestore di eccezioni. Il meccanismo delle eccezioni fornisce un modo strutturato e robusto per gestire errori e anomalie, consentendo allo stesso tempo una separazione chiara tra il codice normale e il codice di gestione degli errori.   La gestione delle eccezioni in Java si basa su tre componenti fondamentali: i blocchi di codice 'try', 'catch' e 'finally'. Vediamo, nei capitoli a seguire, come funziona ciascuno di loro e quali sono le eccezioni più comuni in cui puoi incombere nella programmazione Java.
7.2

Try, catch, finally in Java

La gestione delle eccezioni in Java avviene principalmente attraverso tre blocchi di codice: ‘try’, ‘catch’ e ‘finally’. Questi blocchi forniscono il framework necessario per monitorare le porzioni di codice per gli errori, gestire gli errori se si verificano e pulire le risorse del programma dopo che il codice è stato eseguito.    Blocco Try Java Il blocco ‘try’ contiene il codice che potrebbe causare un'eccezione. È una sezione di codice che viene "provata" per vedere se si verificano errori durante l'esecuzione. Se si verifica un'eccezione, l'esecuzione del blocco ‘try’ viene interrotta e il controllo viene passato al blocco ‘catch’ corrispondente.   Blocco Catch Java Il blocco ‘catch’ segue il blocco ‘try’ e contiene il codice che gestirà l'eccezione se si verifica. Ogni blocco ‘catch’ è associato a un tipo di eccezione e gestirà solo quelle eccezioni che sono compatibili con quel tipo. Puoi avere più blocchi ‘catch’ per gestire diversi tipi di eccezioni.   try { int risultato = 10 / 0; // Questa linea causerà un'eccezione } catch (ArithmeticException e) { System.out.println("Errore: Divisione per zero!"); } Nel nostro software stiamo cercando di eseguire una divisione per zero all'interno di un blocco ‘try’. Questa operazione causerà un'eccezione di tipo ‘ArithmeticException’ in Java in quanto il tentativo di dividere 10 per 0 è un'operazione matematica non permessa.  A causa dell'eccezione generata nel blocco ‘try’, il controllo del programma passa immediatamente al blocco ‘catch’ corrispondente. Nel blocco catch, abbiamo specificato il tipo di eccezione che vogliamo catturare, che in questo caso è ArithmeticException. All'interno del blocco ‘catch’, stiamo gestendo l'eccezione stampando un messaggio di errore sulla console “Errore: Divisione per zero!”. In questo modo, il codice gestisce l'eccezione in modo controllato, evitando che il programma si interrompa bruscamente e fornendo un feedback utile sull'errore che si è verificato.   Blocco Finally Java Il blocco ‘finally’ è opzionale e viene eseguito indipendentemente dal fatto che si sia verificata un'eccezione o meno. Questo blocco è utile per eseguire codice di pulizia che deve essere eseguito in ogni caso, come chiusura di file o rilascio di risorse di sistema.   finally { System.out.println("Questo blocco viene eseguito indipendentemente dal fatto che si sia verificata un'eccezione o meno."); }
7.3

Eccezioni comuni in Java

Nel linguaggio Java si possono incontrare diverse eccezioni comuni durante lo sviluppo o l'esecuzione di un programma. È importante conoscerle per poterle gestire in modo efficace. Ecco alcune delle eccezioni più comuni in Java:   NullPointerException: questa eccezione viene lanciata quando si tenta di accedere a un membro di un riferimento null.   String testo = null; int lunghezza = testo.length(); // Genererà una NullPointerException ArrayIndexOutOfBoundsException: questa eccezione viene lanciata quando si tenta di accedere a un indice di un array che è fuori dal range valido.   int[] numeri = {1, 2, 3}; int numero = numeri[5]; // Genererà una ArrayIndexOutOfBoundsException ArithmeticException: questa eccezione viene lanciata in caso di condizioni eccezionali durante operazioni aritmetiche, come la divisione per zero.   int risultato = 10 / 0; // Genererà una ArithmeticException NumberFormatException: questa eccezione viene lanciata quando una conversione tra una stringa e un tipo numerico fallisce.   int numero = Integer.parseInt("nonUnNumero"); // Genererà una NumberFormatException ClassCastException: Questa eccezione viene lanciata quando si tenta di castare un oggetto a una classe della quale non è un'istanza.   Object x = new Integer(0); System.out.println((String)x); // Genererà una ClassCastException FileNotFoundException: Questa eccezione viene lanciata quando un file con il percorso specificato non viene trovato.   File file = new File("percorso/non/esistente"); FileInputStream fis = new FileInputStream(file); // Genererà una FileNotFoundException IOException: Questa è una classe di eccezione generale per rappresentare errori di I/O che potrebbero verificarsi.   FileInputStream fis = new FileInputStream("file.txt"); int x = fis.read(); // Potrebbe generare una IOException se ci sono problemi di lettura

8

Classi e metodi in Java

8.1

Classi in Java

Le classi in Java sono strutture che definiscono le caratteristiche e i comportamenti degli oggetti che saranno creati a partire da esse. Sono fondamentali per l'astrazione dei dati e l'incapsulamento, due pilastri della programmazione orientata agli oggetti.   Definizione di una classe in Java Per definire una classe, si utilizza la parola chiave class, seguita dal nome della classe. Ecco come si presenta una definizione di classe base:   ‘’’java public class Auto { // Attributi di classe String modello; // Variabile di istanza } ‘’’ Modificatori di Accesso in Java Nel linguaggio Java, i modificatori di accesso sono parole chiave utilizzate per controllare l'accesso ai membri di una classe, come attributi e metodi, da parte di altre classi. Ci sono quattro modificatori di accesso principali   public: La classe è accessibile da qualsiasi altra classe in qualsiasi pacchetto. private: Questo modificatore non si applica alle classi, ma solo alle variabili di istanza e metodi all'interno delle classi. protected: I membri contrassegnati come protetti sono accessibili dalle classi dello stesso pacchetto e dalle sottoclassi (classi figlie), anche se si trovano in pacchetti diversi. default (package-private): se non viene specificato alcun modificatore di accesso, il membro è considerato di accesso "default" o "package-private". Questo significa che il membro è accessibile solo dalle classi nello stesso pacchetto.
8.2

Metodi in Java

I metodi definiscono le azioni che gli oggetti di una classe possono eseguire.   Definizione di metodo in Java e tipi di ritorno Un metodo nel linguaggio Java può avere un tipo di ritorno specifico, come int, double, String, o può essere void se non restituisce nulla.   ‘’’Java public class Auto { // Attributi di classe String modello; // Metodo che non restituisce valore public void stampaModello() { System.out.println("Il modello dell'auto è: " + modello); } // Metodo pubblico che mostra dettagli public void mostraDettagli() { System.out.println("Dettagli Automobile:"); mostraModello(); // Chiamata a metodo privato } // Metodo privato, accessibile solo all'interno della classe private void mostraModello() { System.out.println("Modello: " + modello); } } ‘’’ Parametri dei Metodi in Java I metodi possono essere definiti senza parametri o con uno o più parametri. I parametri fungono da input al metodo e influenzano il suo comportamento.   ‘’’Java public class Automobile { String modello; // Costruttore con parametro public Automobile(String modello) { this.modello = modello; } } ‘’’ In questo esempio, modello è un parametro del costruttore che viene utilizzato per inizializzare la variabile di istanza.   Questo modo di organizzare il contenuto dovrebbe rendere i concetti più chiari e diretti, facilitando la comprensione e lo studio delle strutture di programmazione in Java.

9

Programmazione ad oggetti e Java

9.1

Gli oggetti in Java

Arriviamo ora ad una delle parti più ostiche per i neofiti: nella programmazione informatica, e più specificatamente in un linguaggio orientato agli oggetti come Java, un oggetto è un'entità che rappresenta un'istanza di una classe. In termini più concreti, puoi pensare a una classe come a un "blueprint" o un progetto, e ad un oggetto come a una costruzione reale basata su quel progetto. Capire gli oggetti in Java Ecco alcuni punti chiave per comprendere meglio il concetto di oggetto in Java: Analogia: Un modo per pensare agli oggetti è di immaginarli come oggetti nel mondo reale. Per esempio, se consideri la classe "Auto" come un progetto generico per un'automobile, allora un oggetto sarebbe una specifica automobile (come una Ferrari rossa del 2022) che è stata costruita seguendo le specifiche di quel progetto. Stato: Gli oggetti hanno uno "stato", che è rappresentato da attributi o variabili di istanza. Nell'esempio dell'auto, lo stato potrebbe includere cose come il modello, il colore, il numero di chilometri percorsi, ecc. Comportamento: Gli oggetti hanno anche "comportamento", che è rappresentato dai metodi della classe. I comportamenti sono le azioni che un oggetto può eseguire o che possono essere eseguite su di esso. Per l'auto, comportamenti potrebbero essere 'accelerare', 'frenare', 'girare', ecc. Identità: Ogni oggetto ha un'identità unica, che in un ambiente di programmazione come Java viene gestita attraverso il riferimento all'oggetto. Anche se due oggetti hanno lo stesso stato, essi sono distinti e l'operazione su uno non influenzerà l'altro. Incapsulamento Gli oggetti incapsulano (o nascondono) i dettagli di implementazione e espongono solo ciò che è necessario all'esterno. Questo significa che possono avere dati privati che possono essere letti o modificati solo attraverso metodi pubblici (getter e setter). Supponiamo di avere una classe "Auto":   public class Auto { private String modello; private String colore; private int anno; public Auto(String modello, String colore, int anno) { this.modello = modello; this.colore = colore; this.anno = anno; } public void accelera() { System.out.println(modello + " sta accelerando!"); } } Per creare un oggetto "Auto" in Java, useresti: Auto miaFerrari = new Auto("Ferrari", "Rosso", 2022); ‘miaFerrari’ è un oggetto che ha uno stato (modello, colore, anno) e comportamenti (come accelera). È un'istanza concreta della classe "Auto" che risiede in memoria, e puoi interagire con essa usando i suoi metodi.   Ma proviamo ora con la pratica a sviluppare un esercizio su queste tematiche. “In una biblioteca, abbiamo diversi libri disponibili per il prestito e membri che desiderano prenderli in prestito. Ogni libro e ogni membro possono essere rappresentati come oggetti con i loro attributi e metodi specifici. Ad esempio, un libro può avere attributi come il titolo, l'autore e uno stato che indica se è attualmente in prestito o meno. Similmente, un membro della biblioteca può avere un nome e la capacità di prendere in prestito o restituire libri.”   La classe Libro rappresenta un libro nella biblioteca   public class Libro { private String titolo; private String autore; private boolean inPrestito; public Libro(String titolo, String autore) { this.titolo = titolo; this.autore = autore; this.inPrestito = false; } public String getTitolo() { return titolo; } public String getAutore() { return autore; } public boolean isInPrestito() { return inPrestito; } public void presta() { if (!inPrestito) { inPrestito = true; System.out.println(titolo + " è stato prestato."); } else { System.out.println(titolo + " è già in prestito."); } } public void restituisci() { if (inPrestito) { inPrestito = false; System.out.println(titolo + " è stato restituito."); } else { System.out.println(titolo + " non è in prestito."); } } } La classe Membro rappresenta un membro della biblioteca   public class Membro { private String nome; public Membro(String nome) { this.nome = nome; } public void prestaLibro(Libro libro) { libro.presta(); } public void restituisciLibro(Libro libro) { libro.restituisci(); } } Ora, usiamo queste classi per creare oggetti e gestire i prestiti dei libri.   public class BibliotecaDemo { public static void main(String[] args) { // Creazione di oggetti Libro Libro libro1 = new Libro("Il Signore degli Anelli", "J.R.R. Tolkien"); Libro libro2 = new Libro("1984", "George Orwell"); // Creazione di un oggetto Membro Membro membro1 = new Membro("Mario Rossi"); // Mario Rossi prende in prestito "Il Signore degli Anelli" membro1.prestaLibro(libro1); // Verifichiamo se "Il Signore degli Anelli" è in prestito System.out.println("Il Signore degli Anelli è in prestito? " + libro1.isInPrestito()); // Mario Rossi tenta di prendere in prestito "1984" membro1.prestaLibro(libro2); // Mario Rossi restituisce "Il Signore degli Anelli" membro1.restituisciLibro(libro1); // Verifichiamo nuovamente lo stato del prestito di "Il Signore degli Anelli" System.out.println("Il Signore degli Anelli è in prestito? " + libro1.isInPrestito()); } } Questo esercizio mostra come gli oggetti possono essere utilizzati per rappresentare elementi come libri e membri in una biblioteca e per gestire le operazioni tra di loro, come prendere in prestito e restituire libri. Gli oggetti forniscono un modo intuitivo e flessibile per modellare concetti del mondo reale in un programma software.   L'output conferma il comportamento atteso:   Il membro "Mario Rossi" ha preso in prestito il libro "Il Signore degli Anelli", e il sistema ha confermato che il libro è stato prestato. È stato verificato che "Il Signore degli Anelli" è effettivamente in prestito. "Mario Rossi" ha, poi, preso in prestito il libro "1984", e il sistema ha registrato anche questo prestito. Successivamente, "Mario Rossi" ha restituito "Il Signore degli Anelli", e il sistema ha confermato che il libro è stato restituito. Alla verifica finale, "Il Signore degli Anelli" non è più in prestito.
9.2

Costruttori in Java

Nella programmazione orientata agli oggetti, i costruttori hanno un ruolo fondamentale. Essi sono metodi speciali che vengono chiamati al momento della creazione di un oggetto (istanza) di una classe. Il compito principale di un costruttore è di inizializzare l'oggetto, impostando un valore iniziale per ogni attributo o eseguendo qualsiasi impostazione preliminare necessaria.   Caratteristiche dei Costruttori in Java Nome: Un costruttore deve avere lo stesso nome della classe in cui è definito. Non ha un tipo di ritorno: A differenza di altri metodi, un costruttore non ha un tipo di ritorno, nemmeno void. Automatizzazione: Viene automaticamente chiamato quando si crea un nuovo oggetto tramite l'operatore new.   Tipi di Costruttori in Java Costruttore Predefinito: Se non si definisce un costruttore per una classe, Java fornisce automaticamente un costruttore predefinito senza parametri che non fa nulla se non chiamare il costruttore della superclasse. Costruttore Senza Parametri: È un costruttore definito dall'utente senza argomenti. Serve per creare un oggetto senza passare valori espliciti e spesso assegna valori di default agli attributi. Costruttore Con Parametri: Questo tipo di costruttore permette di passare argomenti al momento della creazione di un oggetto, consentendo di inizializzare immediatamente lo stato dell'oggetto con valori specifici.   Esempio Pratico Immaginiamo di avere una classe Libro per la nostra biblioteca:   public class Libro { private String titolo; private String autore; private int annoPubblicazione; // Costruttore senza parametri che inizializza i valori di default public Libro() { this.titolo = "Sconosciuto"; this.autore = "Sconosciuto"; this.annoPubblicazione = 0; } // Costruttore con parametri per impostare titolo, autore e anno di pubblicazione public Libro(String titolo, String autore, int annoPubblicazione) { this.titolo = titolo; this.autore = autore; this.annoPubblicazione = annoPubblicazione; } // Metodo principale (main) che è il punto di ingresso dell'applicazione public static void main(String[] args) { // Crea una nuova istanza di Libro con parametri specificati Libro mioLibro = new Libro("Il Nome della Rosa", "Umberto Eco", 1980); // Stampa i dettagli del libro System.out.println("Dettagli Libro:"); System.out.println("Titolo: " + mioLibro.getTitolo()); System.out.println("Autore: " + mioLibro.getAutore()); System.out.println("Anno di Pubblicazione: " + mioLibro.getAnnoPubblicazione()); } // Metodi getter per accedere agli attributi privati public String getTitolo() { return titolo; } public String getAutore() { return autore; } public int getAnnoPubblicazione() { return annoPubblicazione; } } Utilizzando il costruttore senza parametri: Libro libroMisterioso = new Libro(); Utilizzando il costruttore con parametri: Libro libroFamoso = new Libro("Il Signore degli Anelli", "J.R.R. Tolkien", 1954); I costruttori forniscono un mezzo per garantire che un oggetto sia in uno stato valido non appena viene creato. Incoraggiano un codice più pulito e più sicuro, evitando oggetti parzialmente inizializzati o in uno stato inconsistente.
9.3

Ereditarietà in Java

Ci sono 3 concetti principali che fungono da pilastri fondamentali della programmazione orientata agli oggetti (OOP) e forniscono un framework per creare strutture di codice complesse e flessibili: ereditarietà, polimorfismo e incapsulamento. Scopriamo insieme, anche nei capitoli a seguire, come funziona ognuno di loro.   Ereditarietà in Java L'ereditarietà è un meccanismo che permette ad una classe di ereditare attributi e metodi da un'altra classe. La classe che eredita è chiamata "sottoclasse" o "classe derivata", mentre la classe da cui eredita è chiamata "superclasse" o "classe base".   Caratteristiche dell'ereditarietà Promuove il riutilizzo del codice permettendo alle nuove classi di utilizzare metodi e variabili della classe base. Crea una relazione gerarchica tra classi. Supporta la creazione di nuove classi che estendono o modificano il comportamento di classi esistenti. La classe Veicolo contiene attributi e metodi comuni a tutti i tipi di veicoli.   public class Veicolo { private String marca; private String modello; public Veicolo(String marca, String modello) { this.marca = marca; this.modello = modello; } public void stampaDettagli() { System.out.println("Marca: " + marca + ", Modello: " + modello); } } La classe Auto estende Veicolo e aggiunge un attributo specifico per l'auto, come il numero di porte.   public class Auto extends Veicolo { private int numeroPorte; public Auto(String marca, String modello, int numeroPorte) { super(marca, modello); // Chiama il costruttore della superclasse this.numeroPorte = numeroPorte; } @Override public void stampaDettagli() { super.stampaDettagli(); // Chiama il metodo della superclasse System.out.println("Numero di porte: " + numeroPorte); } } La classe Moto estende anche Veicolo e può avere attributi specifici per le moto, come la presenza di un bauletto.   public class Moto extends Veicolo { private boolean haBauletto; public Moto(String marca, String modello, boolean haBauletto) { super(marca, modello); // Chiama il costruttore della superclasse this.haBauletto = haBauletto; } @Override public void stampaDettagli() { super.stampaDettagli(); // Chiama il metodo della superclasse System.out.println("Ha bauletto: " + (haBauletto ? "Sì" : "No")); } } Di seguito un esempio di come potresti creare istanze di Auto e Moto e stampare i loro dettagli:   public class TestEreditarieta { public static void main(String[] args) { Auto miaAuto = new Auto("Fiat", "500", 4); Moto miaMoto = new Moto("Ducati", "Monster", true); miaAuto.stampaDettagli(); // Stampa dettagli dell'auto miaMoto.stampaDettagli(); // Stampa dettagli della moto } } Questo esempio mostra come l'ereditarietà permetta di definire una superclasse Veicolo con comportamenti comuni, che possono essere poi estesi e specializzati dalle sottoclassi Auto e Moto. Le sottoclassi ereditano gli attributi e i metodi della superclasse e possono anche avere i loro metodi e attributi specifici.
9.4

Polimorfismo in Java

Il polimorfismo, che letteralmente significa "molte forme", è la capacità di un oggetto di assumere molte forme diverse. In OOP, si riferisce alla capacità di una classe di fornire diverse implementazioni di metodi che sono definiti in una classe base o in un'interfaccia.   Ci sono due tipi principali di polimorfismo   Polimorfismo di Overloading: Si verifica quando due o più metodi nella stessa classe hanno lo stesso nome ma parametri diversi. Polimorfismo di Overriding: Si verifica quando una sottoclasse fornisce una specifica implementazione di un metodo che è già fornito dalla sua classe base. Esempio per polimorfismo di Overloading public class Animale { public void faiRumore() { System.out.println("Questo animale fa un suono."); } public void faiRumore(String suono) { System.out.println("Questo animale fa: " + suono); } } In questo caso, la classe Animale ha due metodi faiRumore(), uno senza parametri e l'altro con un parametro String.   Esempio per polimorfismo di Overriding   Partiamo dalla classe base ‘Animale’ public class Animale { public void faiRumore() { System.out.println("Questo animale fa un suono."); } } Sottoclassi ‘Cane’ e ‘Gatto’ public class Cane extends Animale { @Override public void faiRumore() { System.out.println("Il cane fa: Bau Bau!"); } } public class Gatto extends Animale { @Override public void faiRumore() { System.out.println("Il gatto fa: Miao Miao!"); } } Ora creiamo istanze di Cane e Gatto e le usiamo polimorficamente come Animale. public class TestPolimorfismo { public static void main(String[] args) { Animale animale1 = new Cane(); Animale animale2 = new Gatto(); animale1.faiRumore(); // Stampa: Il cane fa: Bau Bau! animale2.faiRumore(); // Stampa: Il gatto fa: Miao Miao! } } In questo esempio, anche se animale1 e animale2 sono di tipo Animale, il metodo faiRumore() che viene chiamato è quello definito nella classe dell'oggetto effettivo (Cane o Gatto), non quello nella classe Animale. 
9.5

Incapsulamento in Java

L'incapsulamento è la pratica di nascondere i dettagli interni di un oggetto e di esporre solo ciò che è necessario. Questo è di solito realizzato con l'uso di modificatori di accesso per nascondere gli attributi (rendendoli privati) e fornendo metodi pubblici (getter e setter) per accedere a quegli attributi.   Benefici dell'incapsulamento in Java:   Protegge lo stato interno di un oggetto. Controlla come i dati possono essere modificati o accessibili. Riduce la complessità e aumenta la riutilizzabilità.   Esempio di una classe ‘Persona’ che incapsula l'età della persona.   public class Persona { private int eta; // Età della persona, accessibile solo all'interno della classe // Costruttore che inizializza l'età della persona public Persona(int etaIniziale) { setEta(etaIniziale); } // Metodo setter che imposta l'età della persona // Verifica che l'età sia ragionevole prima di impostarla public void setEta(int nuovaEta) { if (nuovaEta >= 0 && nuovaEta <= 120) { eta = nuovaEta; } else { System.out.println("Età non valida. Per favore inserisci un valore tra 0 e 120."); } } // Metodo getter che ritorna l'età della persona public int getEta() { return eta; } } Eco un utilizzo della classe ‘Persona’ public class TestPersona { public static void main(String[] args) { Persona persona = new Persona(30); // Crea una persona di 30 anni System.out.println("Età della persona: " + persona.getEta()); // Stampa: Età della persona: 30 persona.setEta(35); // Imposta una nuova età System.out.println("Nuova età della persona: " + persona.getEta()); // Stampa: Nuova età della persona: 35 persona.setEta(-5); // Prova a impostare un'età non valida // Stampa: Età non valida. Per favore inserisci un valore tra 0 e 120. } } In questo esempio, il campo ‘eta’ è privato, quindi non può essere accessibile o modificato direttamente dall'esterno della classe Persona. Invece, devi usare i metodi ‘setEta’ e ‘getEta’ per modificare e leggere l'età della persona. Questo assicura che l'età possa essere impostata solo a valori validi, proteggendo il campo da assegnazioni non valide o dannose.

10

Librerie, scrittura e lettura in Java

10.1

Cos’ è una libreria in Java

Le librerie in Java sono insiemi di classi preesistenti che forniscono una vasta gamma di funzionalità pronte all'uso. Dalle operazioni matematiche di base alle complesse funzioni di rete, le librerie consentono agli sviluppatori di sfruttare codice standardizzato senza doverlo riscrivere da zero. Questo non solo accelera il processo di sviluppo ma assicura anche che il codice sia affidabile e ottimizzato, avendo beneficiato di test approfonditi e di miglioramenti continui da parte della comunità di sviluppatori.   Le librerie Java possono riguardare qualsiasi cosa, dalla gestione delle interfacce utente (come Swing o JavaFX) alla connettività di rete, dall'elaborazione di immagini e file multimediali alla crittografia e sicurezza. Utilizzando le librerie, i programmatori possono concentrarsi sulla logica specifica del loro programma, senza dover reinventare la ruota.   Librerie Java VS framework Il concetto di libreria appena espresso potrebbe suonarti familiare: ricordi i framework? In effetti, le librerie in Java sono simili ai framework, ma ci sono delle differenze significative tra i due concetti. Abbiamo detto che le librerie sono raccolte di classi e metodi predefiniti che forniscono funzionalità specifiche, progettate per essere riutilizzate in diversi contesti e importate e utilizzate nei tuoi progetti Java.   I framework, d'altra parte, sono strutture più ampie che forniscono un'architettura e un set di regole per lo sviluppo di applicazioni. Oltre a fornire librerie, i framework definiscono anche un flusso di lavoro e delle convenzioni per lo sviluppo. Spesso, i framework Java includono librerie interne, ma vanno oltre offrendo strumenti per gestire aspetti come la gestione degli eventi, la sicurezza, il routing e molto altro ancora. Esempi di framework Java sono Spring Framework, Hibernate e JavaServer Faces (JSF).   In sintesi, mentre le librerie offrono funzionalità specifiche che possono essere utilizzate come parte di un progetto, i framework forniscono una struttura più ampia per lo sviluppo di applicazioni, inclusi strumenti e convenzioni per semplificare lo sviluppo e migliorare la manutenibilità del codice.
10.2

Lettura di file in Java

Per leggere un file in Java, si utilizza la libreria java.io o java.nio, che fornisce varie classi per leggere i dati. Le classi più comuni per la lettura di file sono FileReader per i file di testo, e FileInputStream per i file binari. Con Java 8, è stata introdotta la classe Files che offre metodi semplici per leggere tutte le linee di un file di testo con una singola chiamata.   Ecco un esempio di lettura di un file di testo con Files:   import java.nio.file.Files; import java.nio.file.Paths; import java.io.IOException; import java.util.List; public class FileLettura { public static void main(String[] args) { try { List<String> lines = Files.readAllLines(Paths.get("esempio.txt")); for (String line : lines) { System.out.println(line); } } catch (IOException e) { e.printStackTrace(); } } } Questo esempio mostra come leggere un file di testo e stampare ogni linea sulla console. La gestione delle eccezioni è fondamentale quando si lavora con file per gestire situazioni come file non trovati o errori di input/output.
10.3

Scrittura di File in Java

Abbiamo esplorato, nel capitolo precedente, la lettura di file in Java; la scrittura su file in Java è altrettanto semplice. Le classi FileWriter e BufferedWriter sono comunemente usate per scrivere testo, mentre FileOutputStream è usata per i file binari. Con la classe Files, si può anche scrivere in un file in modo efficiente.   Ecco un esempio di come scrivere su un file di testo usando BufferedWriter:   import java.io.BufferedWriter; import java.io.FileWriter; import java.io.IOException; public class FileScrittura { public static void main(String[] args) { try (BufferedWriter writer = new BufferedWriter(new FileWriter("esempio.txt"))) { writer.write("Questo è un esempio di scrittura su file."); } catch (IOException e) { e.printStackTrace(); } } } Questo frammento crea un file chiamato "esempio.txt" e vi scrive una singola linea di testo. L'uso del costrutto try-with-resources assicura che il BufferedWriter sia chiuso automaticamente alla fine del blocco try, anche se si verificano eccezioni.

11

Collections in Java

11.1

Cos'è una collection in Java

Nel mondo della programmazione Java, la gestione di gruppi di oggetti è un'operazione così comune che il linguaggio fornisce una libreria dedicata a questo scopo: il framework delle collections (ricordiamo che un framework è insieme di strumenti e librerie predefiniti che forniscono una struttura di base per lo sviluppo e la distribuzione di applicazioni in un particolare ambiente). Questo framework non solo offre modi per raggruppare gli oggetti, ma fornisce anche diversi modi per manipolarli efficacemente.    Cos'è una collection in Java Una collection in Java (o collezione) è un oggetto che rappresenta un gruppo di oggetti, noto anche come una struttura dati. Il framework delle collezioni fornisce diverse classi e interfacce che consentono ai programmatori di lavorare con queste strutture in modo più semplice ed efficiente. Le collezioni sono utilizzate per memorizzare, recuperare, manipolare e comunicare dati aggregati.   Le strutture di dati principali fornite dal framework delle collezioni includono:   Liste: per gestire sequenze ordinate che possono contenere duplicati. Set: per gestire insiemi unici di elementi, senza duplicati. Mappe: per gestire coppie chiave-valore, consentendo una rapida ricerca e recupero.   Nei prossimi capitoli analizzeremo, una ad una, queste 3 strutture di dati fornite dalle collections in Java.
11.2

Listing Java: ArrayList, LinkedList

Le liste in Java sono collezioni ordinate che possono contenere elementi duplicati. Sono equivalenti alle matrici dinamiche in altri linguaggi di programmazione. `ArrayList` e `LinkedList` sono due implementazioni comuni di questa interfaccia.   Arraylist Java ArrayList è implementato come un array tridimensionabile. Fornisce un accesso rapido agli elementi tramite indici, ma può essere lento per le operazioni che richiedono spostamenti di elementi, come l'inserimento o la rimozione di elementi da qualsiasi posizione che non sia la fine dell'array.   List<String> frutti = new ArrayList<>(); frutti.add("Mela"); frutti.add("Banana"); frutti.add("Arancia"); LinkedList Java LinkedList invece, è implementato come una lista doppiamente collegata. È ottimale per le operazioni di inserimento e rimozione, poiché non richiedono uno spostamento di elementi, ma l'accesso agli elementi non è altrettanto rapido come in `ArrayList`.   List<String> animali = new LinkedList<>(); animali.add("Cane"); animali.add("Gatto"); animali.add("Cavallo");
11.3

Set: HashSet Java, TreeSet Java

Nel linguaggio Java, un Set è un tipo di collezione che enfatizza l'unicità: ogni elemento può esistere nella collezione solo una volta, senza duplicati. Questa caratteristica è simile al concetto matematico di un insieme, che è una raccolta di oggetti distinti. HashSet Java HashSet è una collezione che utilizza una tabella hash. Offre tempi di accesso costanti per le operazioni di base, assumendo che la funzione hash disperda gli elementi adeguatamente tra i bucket.   Set<Integer> numeri = new HashSet<>(); numeri.add(1); numeri.add(2); numeri.add(3); TreeSet Java TreeSet è implementato come un albero rosso-nero, una struttura di dati di tipo albero bilanciato. Mentre `TreeSet` ordina gli elementi in base al loro valore, è generalmente più lento di `HashSet` per le operazioni di aggiunta e ricerca.   Set<String> alberi = new TreeSet<>(); alberi.add("Quercia"); alberi.add("Pino"); alberi.add("Faggio");
11.4

Mappe: HashMap Java, TreeMap Java

Le Mappe nel linguaggio Java sono strutture di dati basate sul concetto di coppie chiave-valore. Ogni elemento in una mappa consiste in una chiave unica e un valore associato a quella chiave. Le mappe sono ideali per situazioni in cui vuoi associare un valore identificativo univoco, come un ID utente, a un oggetto specifico, come i dettagli dell'utente.   HashMap Java HashMap è una mappa basata su tabelle hash. Come `HashSet`, fornisce un accesso efficiente alle sue voci.   Map<String, Integer> capitalePopolazione = new HashMap<>(); capitalePopolazione.put("Roma", 2873000); capitalePopolazione.put("Parigi", 2148000); TreeMap Java TreeMap implementa una mappa con un albero rosso-nero. Ordina le sue chiavi in modo naturale o tramite un `Comparator` fornito alla creazione della mappa. Mentre mantiene l'ordine delle chiavi, le operazioni di inserimento e ricerca possono essere più lente rispetto a `HashMap`.   Map<String, String> dizionario = new TreeMap<>(); dizionario.put("ciliegia", "Un piccolo frutto rosso e dolce");
11.5

Esercizio con Java Collections

Per sedimentare i concetti espressi in questa sezione dedicata alle collections in Java proviamo a fare un esercizio insieme! Pronto? Cominciamo! Esercizio guidato: gestione di una biblioteca con Java Collections Obiettivi: Creare una lista ArrayList di libri nella biblioteca. Gestire una lista LinkedList di utenti in attesa di un libro. Utilizzare un HashSet per memorizzare gli ID univoci dei libri già in prestito. Utilizzare un TreeSet per ordinare gli utenti in base al loro cognome. Creare una mappa HashMap per associare ogni libro al suo ID unico. Utilizzare una mappa TreeMap per tenere traccia del prestito dei libri agli utenti.   import java.util.*; public class Biblioteca { public static void main(String[] args) { // Parte 1: Liste List<String> libri = new ArrayList<>(); libri.add("Il Signore degli Anelli"); libri.add("1984"); libri.add("Il Grande Gatsby"); libri.add("Moby Dick"); libri.add("Don Chisciotte"); LinkedList<String> listaAttesa = new LinkedList<>(); listaAttesa.add("Mario Rossi"); listaAttesa.add("Luigi Bianchi"); listaAttesa.add("Anna Verdi"); // Parte 2: Set Set<Integer> libriInPrestito = new HashSet<>(); libriInPrestito.add(1); libriInPrestito.add(2); libriInPrestito.add(3); TreeSet<String> utentiOrdinati = new TreeSet<>(); utentiOrdinati.addAll(listaAttesa); // Parte 3: Mappe Map<Integer, String> catalogoLibri = new HashMap<>(); for (int i = 0; i < libri.size(); i++) { catalogoLibri.put(i + 1, libri.get(i)); // Assegniamo ID ai libri partendo da 1 } TreeMap<Integer, String> prestitiLibri = new TreeMap<>(); prestitiLibri.put(1, "Mario Rossi"); prestitiLibri.put(2, "Luigi Bianchi"); // Nota: Il libro con ID 3 è in prestito, ma non sappiamo a chi, quindi non lo aggiungiamo qui // Domande: // 1. Verifica se un libro è disponibile o in prestito. int idLibro = 1; // Sostituire con l'ID del libro da controllare if (libriInPrestito.contains(idLibro)) { System.out.println("Il libro con ID " + idLibro + " è in prestito."); } else { System.out.println("Il libro con ID " + idLibro + " è disponibile."); } // 2. Assegna un libro dalla lista d'attesa se diventa disponibile. if (!libriInPrestito.contains(idLibro) && !listaAttesa.isEmpty()) { String utente = listaAttesa.poll(); // Rimuove il primo utente dalla lista d'attesa prestitiLibri.put(idLibro, utente); libriInPrestito.add(idLibro); System.out.println("Il libro è stato assegnato a: " + utente); } // 3. Aggiungi un nuovo utente alla utentiOrdinati mantenendo l'ordine. utentiOrdinati.add("Sofia Neri"); System.out.println("Utenti ordinati: " + utentiOrdinati); // 4. Verifica a quale utente è stato prestato un particolare libro. String utentePrestito = prestitiLibri.get(idLibro); if (utentePrestito != null) { System.out.println("Il libro con ID " + idLibro + " è stato prestato a: " + utentePrestito); } else { System.out.println("Il libro con ID " + idLibro + " non è attualmente in prestito."); } } } Questo codice mostra come potresti gestire una piccola biblioteca usando il framework delle collezioni di Java   Quando si esegue questo codice, le seguenti operazioni saranno simulate:   Verifica della disponibilità di un libro. Assegnazione di un libro a un utente in attesa. Aggiunta di un nuovo utente e mantenimento dell'ordine alfabetico. Controllo dell'utente a cui è stato prestato un libro.

12

Java e sviluppo di applicazioni Web

12.1

Introduzione ai servlet in Java

Lo sviluppo di applicazioni web utilizzando Java si basa principalmente su due tecnologie robuste e ben consolidate: i servlet e le JavaServer Pages (JSP).   Cos’ è un servlet in Java Per iniziare, un servlet è una classe Java che vive sul lato server e risponde alle richieste dei client, come un browser web. È come un intermediario che prende la richiesta di un utente, esegue del codice (come accedere a un database o calcolare qualcosa), e poi dà indietro una risposta, che potrebbe essere una pagina web completa, un documento, un'immagine o semplicemente del testo.   Quando parliamo di servlet, immagina che tu stia chiamando un numero per ordinare la pizza. Il servlet è come la persona che risponde al telefono: prende il tuo ordine (la richiesta HTTP), lo trasmette alla cucina (il server e le sue risorse), e poi ti assicura che ricevi la tua pizza (la risposta HTTP).   Caratteristiche dei Servlet in Java Stateless: i servlet sono tipicamente senza stato. Non mantengono informazioni tra le richieste (stateless), a meno che non siano usate sessioni o altre tecniche di gestione dello stato. Multithreading: i servlet gestiscono le richieste in modo concorrente, utilizzando diversi thread, il che li rende efficienti nell'elaborare molteplici richieste simultaneamente. Configurabili: attraverso il file di deployment descriptor (web.xml) o le annotazioni, i servlet possono essere configurati per rispondere a specifici pattern URL o per inizializzare parametri. Sicurezza: possono integrarsi con i meccanismi di sicurezza Java EE per l'autenticazione e l'autorizzazione degli utenti.   Cosa sono le JavaServer Pages Ora, passiamo alle JavaServer Pages, o JSP. Queste sono un po' come un template o un modello che puoi riempire con contenuti diversi ogni volta. Funzionano in maniera simile ai servlet, ma sono più focalizzate sulla parte visuale dell'applicazione web, ovvero quello che gli utenti vedono sul loro schermo. Se i servlet sono la persona al telefono, il JSP è come il menu da cui scegli la tua pizza, che mostra diverse opzioni e prezzi, e può cambiare ogni giorno con nuove offerte o pizze speciali.   Entrambe queste tecnologie, servlet e JSP, sono essenziali per costruire applicazioni web interattive con il linguaggio Java. Elaborano le richieste degli utenti, interagiscono con altre parti del server, come database e applicazioni, e alla fine, producono una risposta che il browser dell'utente può visualizzare. Imparare a lavorare con i servlet e le JSP è fondamentale per qualsiasi sviluppatore Java che voglia creare siti web che non solo appaiano bene ma che siano anche funzionali e reattivi.
12.2

Ciclo di vita di un Servlet in Java

Il ciclo di vita di un servlet è definito da tre fasi principali: inizializzazione, elaborazione delle richieste e distruzione. Esploriamole insieme!   Fase 1: inzializzazione Caricamento: Il contenitore servlet carica la classe servlet, di solito al primo accesso, ma può anche essere caricata all'avvio del contenitore se configurato in questo modo. Creazione dell'istanza: Viene creata un'istanza del servlet. Chiamata al metodo init(): Viene eseguito una sola volta e serve per inizializzare risorse che il servlet può necessitare durante il suo ciclo di vita.   import javax.servlet.*; import javax.servlet.http.*; import java.io.IOException; public class SimpleServlet extends HttpServlet { // Metodo init() per l'inizializzazione del servlet public void init() throws ServletException { // Codice di inizializzazione va qui System.out.println("Servlet is being initialized"); } // Metodo service() per gestire le richieste GET e POST protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Logica di servizio va qui System.out.println("Servlet is servicing a request"); // Per esempio, rispondiamo con un semplice messaggio response.getWriter().println("Hello from the servlet!"); } // Metodo destroy() per pulizia prima della distruzione del servlet public void destroy() { // Codice di pulizia va qui System.out.println("Servlet is being destroyed"); } } Il metodo init() è dove si mette il codice di inizializzazione del servlet. Questo metodo viene chiamato una sola volta quando il servlet viene inizialmente caricato. Il metodo service() gestisce le richieste dei client. Il contenitore servlet chiama questo metodo ogni volta che il servlet riceve una richiesta (GET, POST, ecc.). In questo esempio, il servlet semplicemente stampa un messaggio nella console e risponde al client con "Hello from the servlet!". Il metodo destroy() contiene codice che viene eseguito prima che il servlet venga distrutto, come la liberazione delle risorse. Questo metodo viene chiamato una sola volta quando il servlet viene rimosso dal servizio.   Fase 2: Elaborazione delle richieste Chiamata al metodo service(): Per ogni richiesta HTTP, il contenitore invoca il metodo service() del servlet, che poi delega la richiesta al metodo doGet(), doPost(), doPut(), doDelete(), ecc., a seconda del tipo di richiesta HTTP. Questi metodi sono responsabili per l'elaborazione della richiesta e la produzione della risposta.   import javax.servlet.*; import javax.servlet.http.*; import java.io.IOException; public class RequestHandlingServlet extends HttpServlet { // Metodo doGet() per gestire le richieste GET protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Qui va la logica per gestire una richiesta GET response.setContentType("text/html"); response.getWriter().println("<h1>Response from doGet method</h1>"); } // Metodo doPost() per gestire le richieste POST protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Qui va la logica per gestire una richiesta POST response.setContentType("text/html"); response.getWriter().println("<h1>Response from doPost method</h1>"); } // Metodo doPut() per gestire le richieste PUT protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Qui va la logica per gestire una richiesta PUT // ... } // Metodo doDelete() per gestire le richieste DELETE protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Qui va la logica per gestire una richiesta DELETE } // Il metodo service() non è necessario sovrascriverlo in questo caso // perché la superclasse HttpServlet fornisce già un'implementazione di service() // che delega a doGet(), doPost(), ecc. in base al tipo di richiesta HTTP. } Il metodo doGet() viene invocato quando il servlet riceve una richiesta HTTP GET. Tipicamente, questo metodo è utilizzato per leggere dati. Il metodo doPost() viene invocato in risposta a una richiesta HTTP POST. Questo tipo di richiesta è spesso usato per inviare dati al server, ad esempio, quando si invia un modulo. I metodi doPut() e doDelete() possono essere implementati per gestire rispettivamente le richieste HTTP PUT e DELETE, che sono meno comuni ma utilizzate per operazioni come l'aggiornamento o l'eliminazione di risorse.   Fase 3: distruzione Chiamata al metodo destroy(): Prima che il contenitore servlet termini l'esecuzione del servlet, chiama il metodo destroy() che permette al servlet di rilasciare risorse o eseguire altre operazioni di pulizia prima che l'oggetto servlet sia eliminato.   import javax.servlet.*; import javax.servlet.http.*; import java.io.IOException; public class CleanupServlet extends HttpServlet { // Altre implementazioni del servlet (init, doGet, doPost, ecc.) // Metodo destroy() per la pulizia public void destroy() { // Rilascio delle risorse e operazioni di pulizia System.out.println("Servlet is being destroyed, releasing resources"); } }
12.3

Applicazioni dei Servlet in Java

Ma quali potrebbero essere le applicazioni concrete dei serverlet Java? Scopriamone alcune: Generazione dinamica di contenuti:  I servlet possono generare contenuti HTML dinamici, leggere dati da un database e presentarli all'utente in forma di HTML o altri formati come JSON e XML. Gestione di form: Possono processare e rispondere ai dati inviati dagli utenti attraverso form HTML. Integrazione con altre tecnologie Java EE: i servlet possono essere utilizzati in combinazione con JSP, framework MVC come Spring, servizi web RESTful, EJB e altre tecnologie per costruire applicazioni enterprise complesse. Ad esempio, questo servlet risponde a richieste HTTP GET generando una semplice pagina HTML che visualizza un messaggio di benvenuto:   import javax.servlet.*; import javax.servlet.http.*; import java.io.IOException; import java.io.PrintWriter; // La dichiarazione di un Servlet deve estendere la classe HttpServlet @WebServlet("/welcome") // Annotazione per la configurazione del servlet public class WelcomeServlet extends HttpServlet { // Metodo init() per eseguire la configurazione iniziale @Override public void init() throws ServletException { super.init(); // Codice di inizializzazione qui, se necessario log("WelcomeServlet inizializzato con successo"); } // Metodo doGet() per gestire le richieste GET @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Imposta il tipo di contenuto della risposta a HTML response.setContentType("text/html; charset=UTF-8"); // Ottieni l'oggetto PrintWriter per inviare la risposta PrintWriter out = response.getWriter(); try { // Invia il testo HTML come risposta out.println("<!DOCTYPE html>"); out.println("<html>"); out.println("<head>"); out.println("<title>Benvenuto</title>"); out.println("</head>"); out.println("<body>"); out.println("<h1>Benvenuto nel mondo dei Servlet Java!</h1>"); out.println("</body>"); out.println("</html>"); } finally { // Assicurati che l'oggetto PrintWriter sia sempre chiuso alla fine out.close(); } } // Metodo destroy() per rilasciare le risorse @Override public void destroy() { log("Pulizia del servlet WelcomeServlet"); // Codice di pulizia qui, se necessario }
12.4

JavaServer Pages (JSP) ed Espressioni JSP

Le pagine JSP usano una varietà di tag personalizzati e standard per incorporare logica Java nelle pagine HTML:   Direttive JSP (<%@ ... %>): Permettono di dare istruzioni al container JSP, come l'importazione di pacchetti o la configurazione della pagina. Scriptlet (<% ... %>): Sono frammenti di codice Java inseriti direttamente nella pagina JSP. Possono definire variabili o eseguire logica al momento della richiesta. Espressioni JSP (<%= ... %>): Valutano un'espressione Java e inseriscono il risultato nella pagina. Sono utilizzate per stampare il valore delle variabili o espressioni direttamente nel markup. Dichiarazioni JSP (<%! ... %>): Permettono di dichiarare metodi o variabili a livello di classe che possono essere utilizzati da altre parti della pagina JSP. Ecco un semplice esempio di codice JSP che dimostra l'utilizzo di direttive, scriptlet, espressioni e dichiarazioni:   <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page import="java.util.Date" %> <!DOCTYPE html> <html> <head> <title>Simple JSP Example</title> </head> <body> <%-- Direttiva JSP: Importazione di classi Java --%> <%@ page import="java.util.*" %> <%-- Scriptlet: Codice Java che esegue all'interno della pagina JSP --%> <% // Definizione di una variabile Java String saluto = "Ciao, Mondo!"; Date data = new Date(); %> <%-- Espressione JSP: Stampa di variabili Java nella pagina --%> <p><%= saluto %></p> <p>Oggi è: <%= data %></p> <%-- Dichiarazione JSP: Definisce variabili e metodi a livello di classe --%> <%! // Metodo definito nella pagina JSP public String getSaluto() { return "Benvenuto nella pagina JSP!"; } %> <p><%= getSaluto() %></p> </body> </html> Le Direttive JSP sono utilizzate per fornire istruzioni al container JSP su come gestire la pagina, come l'importazione di classi Java (<%@ page import="java.util.*" %>).  Gli Scriptlet contengono codice Java che può eseguire qualsiasi operazione come definizione di variabili o esecuzione di logica (<% String saluto = "Ciao, Mondo!"; %>). Le Espressioni JSP sono utilizzate per inserire il risultato di un'espressione Java direttamente nel markup della pagina (<%= saluto %>). Le Dichiarazioni JSP permettono di dichiarare variabili e metodi che possono essere utilizzati all'interno della JSP (<%! public String getSaluto() { return "Benvenuto nella pagina JSP!"; } %>). In questo modo, JSP fornisce un potente mezzo per incorporare logica di programmazione all'interno di una pagina web, permettendo la creazione di contenuti web interattivi e dinamici.   Vantaggi dell'Uso di JavaServer Pages Separazione tra logica di presentazione e business: JSP permette di mantenere separati il codice HTML dalla logica applicativa, facilitando la manutenzione e lo sviluppo. Riduzione del codice Java nelle pagine web: Con i tag JSP, c'è meno bisogno di scrivere codice Java esplicito, rendendo le pagine più leggibili e più facili da gestire. Supporto per i tag personalizzati: Gli sviluppatori possono creare propri tag JSP personalizzati, che possono essere riutilizzati attraverso diverse pagine JSP, aumentando il riutilizzo del codice.   Limitazioni e Considerazioni di JavaServer Pages Mentre JSP semplifica lo sviluppo web, l'uso eccessivo di scriptlet è scoraggiato poiché complica la separazione tra la logica di presentazione e quella di business e può rendere il codice difficile da testare e mantenere. Inoltre, con l'avvento di framework moderni come Spring MVC, l'uso diretto di JSP è diminuito, ma rimangono una risorsa preziosa per molti progetti Java EE.

13

Java ed API

13.1

Creazione di API RESTful con Java

Cos'è un'API Le API, o Application Programming Interfaces, sono insiemi di regole che consentono a diversi software di interagire tra loro. Fungono da intermediari che permettono a due applicazioni di comunicare, inviando richieste e ricevendo risposte.   Immagina che tu voglia prenotare un volo tramite un'applicazione di viaggio sul tuo telefono. L'applicazione si connette all'API della compagnia aerea, che funge da intermediario, per accedere alle informazioni sui voli disponibili. Quando inserisci le date e la destinazione, l'applicazione invia queste informazioni all'API, che poi interroga il database della compagnia aerea e restituisce i voli disponibili. L'applicazione di viaggio quindi mostra queste opzioni affinché tu possa scegliere e prenotare il volo desiderato.   Ecco alcuni esempi di API comuni:   API per i social media: le API fornite da piattaforme come Twitter, Facebook o Instagram permettono agli sviluppatori di creare applicazioni che possono postare aggiornamenti, accedere a feed di notizie, o analizzare le tendenze dei social media. Google Maps API: questa API consente di integrare Google Maps in siti web o applicazioni, consentendo funzionalità come la ricerca di luoghi, la navigazione e l'integrazione di mappe personalizzate. API di pagamento: piattaforme come PayPal o Stripe offrono API che facilitano l'integrazione di sistemi di pagamento sicuri nelle applicazioni, permettendo agli utenti di effettuare transazioni finanziarie online. API del tempo: Servizi come OpenWeatherMap forniscono API che permettono di accedere a previsioni meteo, dati storici e informazioni climatiche da incorporare in altre applicazioni. Le API possono essere pubbliche (aperte a tutti gli sviluppatori), private (limitate a specifici partner commerciali) o interne (utilizzate all'interno di un'organizzazione). Sono un componente essenziale per la costruzione di software moderni e connessi, permettendo l'interoperabilità e la creazione di ecosistemi di applicazioni che possono lavorare insieme.   Alcuni punti focali sull'uso delle API nello sviluppo:   Integrazione di servizi: Le API consentono agli sviluppatori di integrare facilmente funzionalità come pagamenti, mappe, comunicazioni in tempo reale, e altro, fornite da terze parti, nelle loro applicazioni. Automazione: Le API possono essere usate per automatizzare processi. Ad esempio, una API può consentire a uno script di aggiornare automaticamente database o archivi quando si verificano determinati eventi. Microservizi: Nell'architettura a microservizi, le API sono essenziali per consentire la comunicazione tra i diversi servizi che compongono un'applicazione, mantenendo un accoppiamento leggero e una coesione elevata. Sviluppo Web: Le API RESTful sono uno standard de facto per il web e sono utilizzate per costruire interazioni tra client web e server in modo semplice e standardizzato.   Come creare un API RESTful con Java La creazione di una semplice API RESTful nel linguaggio Java si svolge generalmente attraverso l'utilizzo di framework che facilitano lo sviluppo di servizi web, come Spring Boot o Jersey. Questi framework seguono il principio REST (Representational State Transfer), che è un tipo di architettura software per sistemi ipertestuali distribuiti come il World Wide Web. Vediamo come creare un’API RESTful utilizzando Spring Boot.   1) Crea un nuovo progetto Spring Boot Utilizza Spring Initializr per generare e scaricare un progetto base. Scegli Maven come sistema di build e aggiungi le dipendenze Spring Web e Spring Data JPA. Estrai il progetto scaricato e aprilo nel tuo IDE. Utilizza Spring Initializr per generare un progetto con le seguenti dipendenze: Spring Web, Spring Data JPA, H2 Database.   Clicca su “add dependencies”   Clicca su “”Spring web” e, infine, generate   Su eclipse clicca su import   A questo punto clicca su "existing Maven Projects". Importa, ora, il file che hai generato dopo averlo estratto dal rar. chiamato springboot-first-app   Fai click sul pulsante Finish   Segui il percorso springboot-fist-app/src/main/java/com/springboot/app e avvia la classe che chiameremo WelcomeController   2) Sviluppa il controller RESTful Crea una classe annotata con @RestController. Definisci metodi con annotazioni come @GetMapping, @PostMapping, @PutMapping, e @DeleteMapping per gestire le varie richieste HTTP. Inietta il repository nel controller per interagire con il database.   Ricordiamo che:   @RestController: Indica che una classe è un "Controller REST", una componente centrale in un'applicazione Spring Boot per gestire le richieste HTTP. Fa in modo che i dati restituiti dai metodi del controller siano scritti direttamente nel corpo della risposta HTTP, anziché restituire una vista. @GetMapping: Utilizzata per mappare le richieste HTTP GET su specifici metodi del controller. Comunemente usata per recuperare dati dal server. @PostMapping: Utilizzata per mappare le richieste HTTP POST su specifici metodi del controller. Tipicamente usata per inviare dati al server per creare una nuova risorsa (ad esempio, un nuovo record in un database). @PutMapping: Utilizzata per mappare le richieste HTTP PUT su specifici metodi del controller. Solitamente usata per aggiornare dati esistenti sul server. @DeleteMapping: Utilizzata per mappare le richieste HTTP DELETE su specifici metodi del controller. Usata per eliminare risorse dal server.   // Definisce il pacchetto in cui si trova questa classe. // È una pratica standard in Java per organizzare il codice in modo logico e gestibile. package com.springboot.springbootfirstapp; // Importazioni necessarie dalle librerie di Spring Framework. // Queste dichiarazioni permettono di utilizzare le classi e le annotazioni fornite da Spring senza dover specificare il loro percorso completo. import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; // Annotazione @RestController // Indica che questa classe è un controller Spring, specificamente un controller REST. // Questa annotazione fa sì che le risposte dei metodi siano direttamente mappate al corpo della risposta HTTP, senza dover utilizzare @ResponseBody. @RestController public class WelcomeController { // Annotazione @GetMapping // Specifica che il metodo sottostante gestirà le richieste GET inviate all'URL "/welcome". // Questo è un esempio di mappatura di endpoint REST in Spring Boot. @GetMapping("/welcome") public String welcome() { // Il corpo del metodo // Quando questo endpoint viene chiamato, restituisce semplicemente una stringa. // In un'applicazione reale, qui potresti avere logiche più complesse, come interrogare un database o chiamare altri servizi. return "Welcome to the Spring Boot app"; } } Quando eseguirai la tua applicazione Spring Boot, Spring Framework configurerà automaticamente un server web e mappa gli endpoint definiti nei tuoi controller. In questo caso, se il server viene eseguito in locale sulla porta predefinita (8080), puoi accedere a questo endpoint visitando http://localhost:8080/welcome in un browser o utilizzando un client HTTP come Postman. Riceverai una risposta che contiene il testo "Welcome to the Spring Boot app".   3) Testa il risultato! Il risultato che otterrai sarà: Questo esempio basilare è solo l'inizio. Partendo da qui, puoi esplorare funzionalità più avanzate e realizzare applicazioni web complete. Ad esempio:   Gestione completa del CRUD: utilizzando annotazioni come @PostMapping, @PutMapping, e @DeleteMapping, puoi creare un'API che supporta operazioni complete di CRUD (Create, Read, Update, Delete) su entità del database. Validazione dei dati: implementare la validazione dei dati in ingresso nelle tue API, garantendo che le informazioni ricevute siano corrette e complete prima di procedere con l'elaborazione o l'aggiornamento del database. Autenticazione e autorizzazione: integrare meccanismi di sicurezza come JWT (JSON Web Tokens) o OAuth per gestire l'autenticazione e l'autorizzazione degli utenti, assicurando che solo gli utenti autorizzati possano accedere a determinate funzionalità dell'API. Interazione con database: collegare il tuo controller a un database utilizzando Spring Data JPA o altri strumenti ORM (Object-Relational Mapping), permettendo un'interazione sofisticata e efficiente con il database. Integrazione con Servizi Esterni: chiamare e integrare servizi web esterni, come API di terze parti, per arricchire le funzionalità della tua applicazione.

14

Come testare il tuo codice Java

14.1

Cos'è un test unitario

Nel mondo dello sviluppo software, testare il proprio codice non è solo una buona pratica, è una necessità assoluta. Per gli sviluppatori Java, il test unitario rappresentano la prima linea di difesa contro i bug e assicurano che il codice funzioni come previsto.   Cos'è un test unitario Il test unitario è una fase cruciale nello sviluppo del software. Per approfondire, discutiamo cos'è, perché è importante, e come implementarlo concretamente in Java utilizzando JUnit. Un test unitario in Java è un test che verifica il corretto funzionamento di una specifica "unità di codice", che di solito si riferisce a un metodo. Questi test sono scritti e eseguiti dagli sviluppatori per assicurarsi che una determinata parte del software funzioni come previsto.   Perché i test unitari sono importanti? Il test unitario in Java è importante per diverse motivazoni: Identificare i bug precocemente nel ciclo di sviluppo, risparmiando tempo e risorse. Facilitare il refactoring del codice, permettendo di modificare il codice esistente con maggiore sicurezza. Verificare che il codice soddisfi i requisiti funzionali. Documentare il comportamento del codice. Fornire un feedback rapido sui cambiamenti apportati al codice.
14.2

JUnit per il test del tuo codice java

Esempio Pratico con JUnit Supponiamo di avere una semplice classe `Calculator` che fornisce una funzione di addizione. Qui sotto è illustrato come potrebbe essere implementata e come si potrebbe scrivere un test unitario per il metodo `add`.   Implementazione della Classe Calculator public class Calculator { public int add(int number1, int number2) { return number1 + number2; } // Altri metodi... } Scrittura del Test Unitario con JUnit   import static org.junit.Assert.*; import org.junit.Test; public class CalculatorTest { @Test // Annotazione che indica che questo metodo è un test public void testAdd() { Calculator calculator = new Calculator(); int result = calculator.add(2, 3); // Assert che verifica se il risultato atteso è uguale a quello ottenuto assertEquals(5, result); } } In questo test, `assertEquals` è una dichiarazione che verifica se i due valori (atteso e risultato) sono uguali. Se non lo sono, JUnit segnalerà il test come fallito.   Esecuzione dei Test Unitari Per eseguire il test, puoi utilizzare l'ambiente di sviluppo integrato (IDE) che, in genere, ha funzionalità dedicate per eseguire e visualizzare i risultati dei test JUnit. In alternativa, puoi eseguire il test dalla linea di comando se stai utilizzando un sistema di build come Maven o Gradle.   Best Practices per i Test Unitari Isolamento: Ogni test deve essere indipendente dagli altri. Nomi Significativi: I metodi di test dovrebbero avere nomi che descrivono ciò che stanno verificando. Ripetibilità: I test dovrebbero fornire lo stesso risultato ogni volta che vengono eseguiti. Rapidità: I test unitari dovrebbero essere veloci da eseguire. Automatizzazione: I test dovrebbero essere eseguiti automaticamente, ad esempio, come parte della build o tramite script.

15

Design patterns in Java

15.1

Design patterns in Java

Lo sviluppo di software è una disciplina tanto artistica quanto tecnica, che richiede non solo capacità di codifica ma anche una progettazione attenta. I design patterns, o "modelli di progettazione", sono soluzioni consolidate a problemi ricorrenti nello sviluppo software. Questi modelli offrono un linguaggio comune tra gli sviluppatori e servono come strumenti per creare software in modo efficiente e mantenibile. In questo articolo, esploreremo due dei design patterns più fondamentali in Java: il Singleton e il Factory. Cos'è un design pattern? I design pattern sono una componente fondamentale nell'architettura del software, fornendo una struttura ripetibile e ottimizzata per risolvere problemi di progettazione comuni. I pattern emergono dalle esperienze di sviluppo di software nel corso di molteplici contesti e progetti. Essi aiutano a evitare soluzioni ad hoc che possono essere meno efficienti e più difficili da comprendere e mantenere.   Classificazione dei Design Patterns I design patterns sono tipicamente divisi in tre categorie principali: Creazionali:Si concentrano sulla logica di creazione degli oggetti. Mirano a rendere un sistema indipendente da come gli oggetti in esso sono creati, composti e rappresentati. Esempi includono il Singleton, Factory Method, Abstract Factory, Builder e Prototype. Strutturali: Riguardano la composizione di classi e oggetti per formare strutture più grandi. Questi pattern facilitano la progettazione assicurando che le modifiche in una parte del sistema richiedano modifiche minime in altre parti. Esempi includono Adapter, Decorator, Facade, Composite e Flyweight. Comportamentali: Si concentrano sull'assegnazione di responsabilità tra gli oggetti. Cosa fa un sistema non dipende solo da come gli oggetti sono composti, ma anche da come comunicano tra loro. Esempi includono Strategy, Observer, Command, Iterator e State.   Esempi di Design Patterns in Java   Singleton Il Singleton, come accennato, garantisce che una classe abbia solo una singola istanza e fornisce un punto di accesso globale a questa istanza.   Esempio di Singleton in Java: public class DatabaseConnector { private static DatabaseConnector instance; private DatabaseConnector() { // Inizializzazione della connessione al database } public static synchronized DatabaseConnector getInstance() { if (instance == null) { instance = new DatabaseConnector(); } return instance; } // Metodi per interagire con il database } Il pattern Singleton è utile in diverse situazioni, ma deve essere usato con cautela perché può portare a problemi di design se usato impropriamente. Ecco alcune situazioni in cui l'uso di Singleton potrebbe essere giustificato: Controllo delle Risorse Condivise: Quando si ha bisogno di un controllo rigoroso su una risorsa condivisa. Ad esempio, la gestione della connessione a un database può essere un buon candidato per il pattern Singleton per evitare l'apertura di connessioni multiple inutilmente. Configurazione Globale: Il Singleton può essere utilizzato per memorizzare la configurazione che deve essere accessibile globalmente e rimanere consistente in tutta l'applicazione, come le impostazioni di configurazione caricate da un file. Logging: Un logger è spesso implementato come un Singleton per evitare la sovrapposizione o la duplicazione dei messaggi di log quando diverse parti dell'applicazione cercano di accedere allo stesso file di log. Driver Hardware o Gestione delle Connessioni: Se l'applicazione necessita di un punto di accesso unico a un driver hardware o a una connessione (come una connessione seriale a un dispositivo fisico), il Singleton può garantire che il driver non sia inizializzato più volte. Pool di Oggetti: Per la gestione di un pool di risorse, come thread o connessioni, dove è necessario un punto di accesso centralizzato e controllato. Servizi di Cache: Quando si implementano servizi di cache, il Singleton può essere utile per mantenere un unico punto di riferimento per la cache, assicurandosi che la cache sia unica e globale. Servizi di Factory con Stato: In alcuni casi, potrebbe essere necessario che una factory mantenga uno stato durante la creazione di oggetti; un Singleton potrebbe essere adatto per conservare tale stato.   Factory Method Il Factory Method offre un modo per delegare la logica di istanziazione agli eredi della classe base. La classe base in un modello factory non crea direttamente oggetti; invece, chiama un metodo factory per farlo.   Esempio di Factory Method in Java: public abstract class Animal { public abstract String speak(); } public class Dog extends Animal { @Override public String speak() { return "Bark"; } } public class Cat extends Animal { @Override public String speak() { return "Meow"; } } public class AnimalFactory { public Animal createAnimal(String type) { if (type.equalsIgnoreCase("dog")) { return new Dog(); } else if (type.equalsIgnoreCase("cat")) { return new Cat(); } else { throw new IllegalArgumentException("Unknown animal type"); } } } Questo pattern è particolarmente utile quando ci sono interfacce comuni per diversi compiti, ma è necessaria la flessibilità nella creazione concreta degli oggetti.   L’uso è consigliato: Quando la Creazione di Oggetti Richiede Logica Complessa: Se la creazione di un oggetto non è solo una questione di inizializzazione ma richiede una logica decisionale, il Factory Method permette di isolare questa logica all'interno di una classe o metodo dedicato. Quando c'è Bisogno di Flessibilità nella Creazione di Oggetti: Se il tuo codice deve essere estensibile, in modo che possa aggiungere nuove classi di prodotti con piccole modifiche o senza alcuna modifica al codice cliente, il Factory Method è una buona scelta.   Adapter L'Adapter consente di interfacciare due sistemi incompatibili. L'Adapter converte l'interfaccia di una classe in un'altra interfaccia attesa dai client.   Esempio di Adapter in Java: public interface EuropeanPlug { void fitEuropeanSocket(); } public class AmericanPlug { public void fitAmericanSocket() { System.out.println("Fitting American plug into American socket."); } } public class PlugAdapter implements EuropeanPlug { private AmericanPlug americanPlug; public PlugAdapter(AmericanPlug americanPlug) { this.americanPlug = americanPlug; } @Override public void fitEuropeanSocket() { americanPlug.fitAmericanSocket(); } } L'Adapter funge da ponte tra il codice client e le classi che hanno interfacce diverse, traducendo le richieste del codice client in un formato che la classe adattata può comprendere.   Quando Usare l'Adapter Integrazione di Terze Parti: Quando il codice deve interagire con una libreria esterna o un sistema che ha un'interfaccia incompatibile con quella attuale del sistema. L'Adapter può tradurre le chiamate tra il sistema e la libreria di terze parti. Riuso del Codice Esistente: Se c'è del codice esistente o una libreria che fa quello che serve, ma la sua interfaccia non corrisponde a quella che il sistema corrente si aspetta, un Adapter può essere usato per riusare questo codice senza riscriverlo. Refactoring: Quando stai rifattorizzando parti di un sistema che non possono essere modificate direttamente, forse perché sono state distribuite in librerie o perché altre parti del sistema dipendono da esse, puoi usare un Adapter per fornire l'interfaccia richiesta senza toccare il codice originale. Transizione tra Diverse Piattaforme: Quando stai migrando o integrando sistemi da piattaforme diverse e hai bisogno di standardizzare le comunicazioni tra di essi, gli Adapter possono fornire un mezzo per uniformare le interazioni.   Observer L'Observer è un pattern comportamentale che definisce una dipendenza uno-a-molti tra oggetti in modo che quando un oggetto cambia stato, tutti i suoi dipendenti vengono notificati e aggiornati automaticamente.   Esempio di Observer in Java: import java.util.ArrayList; import java.util.List; public class NewsAgency { private String news; private List<Channel> channels = new ArrayList<>(); public void addObserver(Channel channel) { this.channels.add(channel); } public void removeObserver(Channel channel) { this.channels.remove(channel); } public void setNews(String news) { this.news = news; for (Channel channel : this.channels) { channel.update(this.news); } } } public interface Channel { void update(String news); } public class NewsChannel implements Channel { @Override public void update(String news) { System.out.println("NewsChannel received news: " + news); } } Questo pattern è particolarmente utile per creare un sistema di comunicazione efficiente e ben organizzato tra componenti che necessitano di rimanere sincronizzati.   Quando Usare l'Observer Quando si Hanno Diverse Rappresentazioni dello Stesso Stato: Se hai molteplici forme di rappresentazione dello stesso stato e queste devono essere aggiornate simultaneamente, l'Observer è la scelta ideale. Quando il Cambiamento di Uno Stato Richiede Cambiamenti in Altri Oggetti: Se un oggetto deve essere in grado di notificare altri oggetti senza assumere nulla riguardo a chi siano questi oggetti, l'uso dell'Observer è appropriato. Quando l'Accoppiamento è da Evitare: L'Observer è utile per ridurre l'accoppiamento tra classi che altrimenti sarebbero strettamente legate. I soggetti non hanno bisogno di conoscere i dettagli dei loro osservatori.

16

Interfaccia grafica con Java

16.1

JavaFX

Nel vasto ecosistema di Java, JavaFX emerge come una potente libreria per lo sviluppo di interfacce grafiche utente (GUI). Mentre le precedenti API di Java come Swing e AWT hanno giocato il loro ruolo, JavaFX offre un approccio moderno e ricco di funzionalità per creare GUI ricche e reattive. In questo articolo, esploreremo JavaFX, le sue caratteristiche principali e perché gli sviluppatori scelgono di utilizzarlo per i loro progetti GUI.   Cos'è JavaFX? JavaFX è un set di pacchetti grafici che consente agli sviluppatori Java di progettare, creare, testare, debuggare e distribuire applicazioni client ricche che operano in modo coerente su diverse piattaforme. Lanciato per la prima volta da Sun Microsystems nel 2008, JavaFX è stato inteso come il successore moderno di Swing, con una serie di vantaggi e miglioramenti.   Caratteristiche di JavaFX Architettura Moderna: JavaFX è costruito su un'architettura moderna che sfrutta hardware accelerato per il rendering grafico, consentendo animazioni fluide e una resa grafica di alta qualità. Flessibilità e Potenza: Con JavaFX, gli sviluppatori possono costruire GUI con componenti standard, personalizzati e anche 3D. Supporta l'incorporamento di grafica web e multimediale, inclusi audio e video. Scenegraph: Al centro di JavaFX c'è il concetto di scenegraph, una struttura ad albero che rappresenta tutti gli elementi della GUI, dai bottoni ai pannelli, che semplifica la gestione e la trasformazione degli elementi della GUI. Binding e Proprietà: JavaFX introduce un potente sistema di binding che permette agli sviluppatori di collegare facilmente l'interfaccia utente alla logica dell'applicazione, assicurando che la UI rifletta lo stato corrente dell'applicazione. Styling CSS: Gli sviluppatori possono usare linguaggio CSS per personalizzare l'aspetto dell'interfaccia utente, offrendo una separazione tra la presentazione e la logica dell'applicazione simile a quella trovata nello sviluppo web. FXML: JavaFX utilizza FXML, un linguaggio di markup basato su XML, per definire l'interfaccia utente, permettendo agli sviluppatori di progettare GUI in modo dichiarativo e separare il design dell'interfaccia dalla logica dell'applicazione.   Perché Scegliere JavaFX? Portabilità tra Piattaforme: JavaFX mantiene la promessa di Java di "scrivere una volta, eseguire ovunque", rendendo le applicazioni trasportabili su Windows, macOS e Linux senza modifiche al codice sorgente. Comunità e Supporto: Dopo la sua adozione in OpenJDK, JavaFX ha una comunità attiva e il supporto di contributori esterni, assicurando che la piattaforma continui a evolversi. Integrazione con IDE e Strumenti: JavaFX è ben supportato dagli ambienti di sviluppo integrato (IDE) come IntelliJ IDEA, Eclipse e NetBeans, che offrono strumenti di design visuale per costruire GUI drag-and-drop. Applicazioni Enterprise: Per applicazioni enterprise che richiedono una GUI robusta e reattiva con piena integrazione con il backend Java, JavaFX è una scelta eccellente che fornisce coerenza e affidabilità.   Nel prossimo capitolo scopriamo come realizzare un'interfaccia grafica utilizzando JavaFX: pronto a cominciare?
16.2

Come creare un'interfaccia utente con JavaFX

Creare un'interfaccia utente (UI) con JavaFX può essere un processo piuttosto diretto e piacevole. JavaFX fornisce un framework ricco per il design di UI che può essere sia esteticamente gradevole che funzionalmente complesso. Ecco una guida passo-passo per creare una semplice UI con JavaFX.   Prerequisiti Prima di iniziare, assicurati di avere l'ultimo JDK installato sul tuo sistema e che il tuo IDE (come IntelliJ IDEA, Eclipse o NetBeans) supporti JavaFX.  Una volta constatato, segui pure questo tutorial!   Passo 1: Creazione del Progetto Crea un nuovo progetto JavaFX nel tuo IDE. Seleziona JavaFX come framework e imposta il tuo progetto. La maggior parte degli IDE moderni offrirà un template di progetto JavaFX.   Passo 2: Struttura dell'Applicazione Un'applicazione JavaFX tipica segue questa struttura: Main Class - Estende Application e sovrascrive il metodo start(Stage stage). FXML (opzionale) - File XML per definire la struttura della UI. Controller Class - Gestisce la logica di interazione dell'interfaccia utente. CSS (opzionale) - Per lo styling dell'UI.   Passo 3: Definizione dell'Interfaccia Utente Puoi definire l'UI in due modi: programmaticamente o usando FXML. Creazione dell'UI Programmatically Nel tuo metodo start, puoi creare elementi dell'UI come Button, Label, e TextField, e aggiungerli a un layout, come StackPane o VBox.   // Metodo sovrascritto della classe Application di JavaFX. @Override public void start(Stage primaryStage) { // Creazione di un nuovo bottone. Button btn = new Button(); // Impostazione del testo visualizzato sul bottone. btn.setText('Say "Hello World"'); // Impostazione dell'azione da eseguire quando il bottone viene cliccato. // In questo caso, stampa "Hello World!" sulla console. btn.setOnAction(event -> System.out.println("Hello World!")); // Creazione di un layout a colonna singola. VBox root = new VBox(); // Aggiunta del bottone al layout. root.getChildren().add(btn); // Creazione di una scena con il layout root come contenuto, // e impostazione delle sue dimensioni a 300x250 pixel. Scene scene = new Scene(root, 300, 250); // Impostazione del titolo della finestra. primaryStage.setTitle("Hello World!"); // Assegnazione della scena al palcoscenico (finestra principale dell'applicazione). primaryStage.setScene(scene); // Visualizzazione del palcoscenico. primaryStage.show(); } Passo 4: Aggiungere Styling con CSS Creare un file CSS per definire lo stile degli elementi dell'UI. .button { -fx-background-color: #ff0000; -fx-text-fill: #ffffff; } Collega il file CSS alla tua scena:   scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm()); Passo 5: Esecuzione dell'Applicazione Dopo aver definito la UI e associato eventuali eventi di gestione, puoi eseguire l'applicazione. Il Stage principale mostrerà la UI e gli utenti potranno interagire con essa.   Se ti è piaciuto provare a realizzare un’interfaccia utente con JavaFX e vuoi approfondire la tematica per realizzare UI uniche e performanti, non perderti la nostra guida di web design per programmatori! 
16.3

Eventi e gestione degli eventi in JavaFX

Gli eventi in JavaFX sono rappresentati da oggetti che estendono la classe Event. Questi oggetti contengono informazioni sull'evento, come il tipo di evento, lo stato del tasto o del mouse, e altre informazioni pertinenti.   Gestione degli Eventi Per gestire gli eventi in JavaFX, si seguono questi passaggi: Definire un EventHandler: Un EventHandler è un'interfaccia che definisce il metodo handle, che verrà invocato quando si verifica un evento. Puoi implementare questa interfaccia nella tua classe controller o utilizzare un'espressione lambda per gestire l'evento direttamente.   // Creazione di un nuovo oggetto Button con il testo "Press Me" mostrato sul bottone. Button btn = new Button("Press Me"); // Impostazione di un gestore di eventi per il bottone. // Questo gestore risponderà alle azioni dell'utente, come il click sul bottone. btn.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { // Questo è il codice che viene eseguito quando il bottone viene premuto. // In questo caso, stampa "Button Pressed!" sulla console. System.out.println("Button Pressed!"); } }); Oppure con un'espressione lambda:   btn.setOnAction(event -> System.out.println("Button Pressed!")); Registrare l'EventHandler: Dopo aver definito un EventHandler, devi registrarlo con il componente che genererà l'evento, utilizzando uno dei metodi forniti dal componente, come setOnAction per i bottoni. Gestire l'Evento: Quando si verifica l'evento, il metodo handle dell'EventHandler viene chiamato automaticamente. All'interno di questo metodo, puoi definire come l'applicazione dovrebbe rispondere.   Eventi Comuni e Loro Gestori in JavaFX ActionEvent: Comune per i componenti come i bottoni; si verifica, per esempio, quando un utente clicca su un pulsante. MouseEvent: Si verifica quando ci sono azioni correlate al mouse, come click, movimenti o trascinamenti. KeyEvent: Si verifica in risposta a interazioni con la tastiera, come la pressione o il rilascio di un tasto.   Propagazione degli Eventi in JavaFX In JavaFX, gli eventi seguono un percorso attraverso la scena che inizia dalla "target node" (il nodo obiettivo dell'evento) e si propaga verso il nodo radice (Scene o Stage). Questo percorso può essere intercettato e gestito da altri nodi lungo il cammino, consentendo un controllo fine sulla gestione degli eventi.   Consumo degli Eventi in JavaFX Se un evento è stato gestito e non si desidera che continui a propagarsi, si può chiamare il metodo consume sull'oggetto evento per "consumarlo". Ciò previene la propagazione dell'evento ai gestori di eventi successivi.

17

Prospettive future di Java

17.1

Java e l'evoluzione dell'ecosistema di sviluppo

Java e il suo futuro nell'ecosistema di sviluppo software si prospettano luminosi e pieni di evoluzioni. La costante innovazione di Java è testimoniata dal recente rilascio di Java SE 21 LTS, che ha introdotto funzionalità avanzate come i Virtual Threads e il Pattern Matching migliorato. Queste innovazioni si rivolgono direttamente alle sfide moderne dello sviluppo, come la concorrenza e la gestione delle risorse.   Funzionalità Java SE21 Al momento della stesura di questa guida, dunque, Java SE 21 è l'ultima versione con supporto a lungo termine (LTS); Vediamo alcun delle funzionalità più rilevanti che ha introdotto:   String Templates: Ti permettono di creare testi con vari dati inseriti più facilmente. Generational ZGC: Migliora il sistema che libera la memoria del computer non più necessaria, facendolo in modo più intelligente. Record Patterns: Ti consentono di lavorare con i dati strutturati in modo più semplice. Pattern Matching for switch: Rende più facile e sicuro scegliere tra diverse opzioni nel codice. Unnamed Patterns and Variables: Per scrivere codice più pulito e con meno restrizioni. Virtual Threads: Per creare programmi che fanno molte cose contemporaneamente, ma in modo più efficiente. Unnamed Classes and Instance Main Methods: Ti lascia creare parti di codice senza dover dare loro un nome specifico. Scoped Values: Introduce variabili speciali che esistono solo in certe parti del programma. Vector API: Migliora come Java gestisce le operazioni che lavorano con molti dati alla volta. Deprecate the Windows 32-bit x86 Port: Indica che la versione di Java per i vecchi computer Windows non sarà più supportata. Prepare to Disallow the Dynamic Loading of Agents: Fa in modo che non si possano più aggiungere certi tipi di codice a Java dopo che è già in esecuzione. Key Encapsulation Mechanism API: Fornisce strumenti per proteggere e gestire le informazioni sicure come le password.   Prospettive future per Java Guardando al futuro, Java sembra concentrarsi sul migliorare l'integrazione con sistemi cloud-native e le prestazioni nel campo dell'elaborazione di dati massivi e dell'intelligenza artificiale. Gli sviluppatori possono aspettarsi che Java continui a sviluppare API più robuste e a migliorare la gestione della memoria e le prestazioni con nuove release. Il supporto per l'interoperabilità con altre lingue e sistemi è un altro ambito in cui Java potrebbe espandersi, permettendo una maggiore flessibilità e apertura.   La comunità Java sta lavorando su progetti come Project Loom, che mira a rivoluzionare il modello di concorrenza in Java, rendendolo più scalabile e performante. Con questi continui aggiornamenti, Java dimostra la sua capacità di adattarsi e rimanere pertinente, promettendo un futuro dove continua a essere uno dei pilastri dell'ingegneria del software.

Sei indeciso sul percorso? 💭

Parliamone! Scrivici su Whatsapp e risponderemo a tutte le tue domande per capire quale dei nostri corsi è il più adatto alle tue esigenze.

Oppure chiamaci al 800 128 626