"Perché C non era abbastanza traumatico"
La serie delle "GUIDE DEFINITIVE" è un progetto creato in parte per scherzo e in parte per aiutare chi ne ha bisogno. Se riscontri problemi, errori o simili, puoi inviarmi un messaggio su Telegram tramite (@clipwav), cercherò di rispondere il prima possibile!
Java è un linguaggio di programmazione orientato agli oggetti che ti permette di creare applicazioni software, web e mobili. In questa guida, imparerai le basi di Java e alcune delle sue caratteristiche più importanti.
Per programmare in Java, hai bisogno di installare il JDK (Java Development Kit), che contiene il compilatore e le librerie necessarie per eseguire il codice Java. Puoi scaricare il JDK dal sito ufficiale di Oracle. Segui le istruzioni per la tua piattaforma (Windows, Mac o Linux) e verifica che l’installazione sia andata a buon fine digitando java -version
nel terminale. Dovresti vedere qualcosa del genere:
Se non vedi questo output, significa che qualcosa è andato storto.
Forse hai dimenticato di impostare la variabile d’ambiente JAVA_HOME
o di aggiungere il percorso del JDK al PATH
… Non preoccuparti, succede anche ai migliori
Ora che hai installato il JDK, sei pronto per scrivere il tuo primo programma Java.
Apri il tuo editor di testo preferito (o un IDE come Eclipse o IntelliJ IDEA se sei un tipo sofisticato) e crea un file chiamato HelloWorld.java. In questo file, scrivi il seguente codice:
Questo codice definisce una classe chiamata HelloWorld che contiene un metodo chiamato main.
📚 Dizionario: Cos'è una classe?
Una classe in Java è un modello o una “struttura” che definisce le caratteristiche e i comportamenti comuni a tutti gli oggetti di quel tipo.
Un oggetto è un’istanza di una classe, cioè un’entità che ha dei valori specifici per le caratteristiche e i modi di eseguire i comportamenti definiti dalla classe
🆘 N.B. Se il concetto non ti è subito chiaro, non ti preoccupare, andremo a vedere cosa sono le classi nello specifico più avanti, ci saranno anche esempi!
Il metodo main
è il punto di ingresso di ogni applicazione Java e accetta come parametro un array di stringhe chiamato args.
Il metodo main
stampa a schermo il messaggio “Hello World”
usando il metodo println
della classe System
.
Per eseguire il tuo programma, devi prima compilarlo.
Apri il terminale nella cartella dove hai salvato il file HelloWorld.java
e digita il comando:
Questo comando invoca il compilatore Java (javac
) e genera un file chiamato HelloWorld.class
, che contiene il codice bytecode del tuo programma.
📚 Dizionario: cos'è il bytecode?
Il bytecode è un linguaggio intermedio che può essere eseguito da una macchina virtuale Java (JVM).
Per eseguire il tuo programma, digita il comando:
Questo comando invoca la JVM (java) e passa come parametro il nome della classe da eseguire (HelloWorld
). Dovresti vedere l’output del tuo programma:
💡 Info: JVM? Macchina virutale?
la JVM (Java Virtual Machine) è un programma che permette a un computer di eseguire applicazioni scritte in Java o in altri linguaggi che sono compilati in bytecode Java.
Si tratta di un’astrazione di una macchina reale, come il server su cui gira il tuo programma, pensa ad una macchina virtuale senza interfaccia la cui sola funzione è eseguire i programmi java.
Se invece hai incontrato qualche errore, non demoralizzarti. Gli errori sono parte integrante della programmazione e ti aiutano a imparare. Cerca di capire cosa hai sbagliato e correggi il tuo codice.
Una variabile è un contenitore che memorizza un valore.
In Java, ogni variabile ha un tipo che determina quali valori può contenere e quali operazioni può eseguire.
I tipi primitivi sono i tipi più semplici e sono:
byte
: un numero intero a 8 bit, da -128 a 127short
: un numero intero a 16 bit, da -32768 a 32767int
: un numero intero a 32 bit, da -2147483648 a 2147483647long
: un numero intero a 64 bit, da -9223372036854775808 a 9223372036854775807float
: un numero reale a 32 bit, con una precisione di circa 7 cifre decimalidouble
: un numero reale a 64 bit, con una precisione di circa 15 cifre decimalichar
: un carattere Unicode a 16 bit, da ‘\u0000’ a ‘\uffff’boolean
: un valore logico, che può essere true o falsePer dichiarare una variabile, devi specificare il suo tipo e il suo nome.
Puoi anche assegnarle un valore iniziale usando l’operatore =. Per esempio:
Puoi usare le variabili per eseguire operazioni aritmetiche, logiche e di confronto. Per esempio:
Oltre ai tipi primitivi, Java offre anche dei tipi riferimento.
Sono classi che rappresentano oggetti più complessi.
Alcuni esempi di tipi riferimento sono:
String
: una sequenza di caratteri, come “Hello World”Array
: una collezione di elementi dello stesso tipo, come [1, 2, 3]ArrayList
: una lista dinamica di elementi di qualsiasi tipo, come [1, “Hello”, true]Scanner
: un oggetto che permette di leggere l’input da tastiera o da filePer dichiarare una variabile di tipo riferimento, devi specificare il nome della classe e il nome della variabile.
Puoi anche creare un’istanza della classe usando l’operatore new e il costruttore della classe. Per esempio:
La programmazione orientata agli oggetti (OOP) è un paradigma di programmazione che si basa sul concetto di oggetto.
Un oggetto è un'entità che ha delle proprietà (attributi) e dei comportamenti (metodi).
Per esempio, una persona è un oggetto che ha delle proprietà come il nome
, l'età
, il sesso
, e dei comportamenti come camminare
, parlare
e mangiare
.
In Java, gli oggetti sono creati a partire da delle classi, che sono dei modelli che definiscono le caratteristiche e i comportamenti comuni a tutti gli oggetti di quel tipo.
Per esempio, la classe Persona
può definire le proprietà nome
, età
, sesso
e i metodi camminare
, parlare
, mangiare
.
Ogni istanza della classe Persona
è un oggetto che ha i suoi valori per le proprietà e i suoi modi di eseguire i metodi.
📚 Dizionario: Cos'è l'istanza di una classe
un’istanza di una classe è un oggetto che è stato creato usando quella classe come modello.
Esempio:
Per definire una classe in Java, si usa la parola chiave class
seguita dal nome della classe. Il corpo della classe è racchiuso tra parentesi graffe e contiene le dichiarazioni degli attributi e dei metodi. Per esempio:
Questa classe definisce tre attributi: nome
, eta
e sesso
, che sono di tipo String
, int
e char
. Questi attributi sono accessibili solo all’interno della classe (di base hanno il modificatore di accesso default
).
In base al modificatore di accesso usato, si può controllare quali altre classi possono accedere a un elemento del codice.
In Java ci sono quattro modificatori di accesso:
La classe definisce anche un costruttore, che è un metodo speciale che viene invocato quando si crea un’istanza della classe.
Il costruttore ha lo stesso nome della classe e non ha un tipo di ritorno, in questo caso, accetta tre parametri: nome
, eta
e sesso
, che vengono usati per inizializzare gli attributi dell’oggetto.
La parola chiave this
si riferisce all’oggetto corrente e serve per distinguere gli attributi dai parametri.
La classe definisce infine tre metodi: camminare
, parlare
e mangiare
, che sono di tipo void
(non restituiscono nulla). Questi metodi stampano a schermo un messaggio che indica l’azione compiuta dall’oggetto.
Per creare un’istanza della classe Persona, si usa l’operatore new seguito dal nome della classe e dai parametri da passare al costruttore. Per esempio:
Un concetto fondamentale dell'OOP è l'ereditarietà, che permette di definire una relazione tra due classi in cui una classe (la sottoclasse) eredita le caratteristiche e i comportamenti di un'altra classe (la superclasse). Questo permette di evitare la duplicazione di codice e di creare una gerarchia di classi.
Per esempio, si può definire una classe Studente che estende la classe Persona, ereditando i suoi attributi e metodi e aggiungendo altri attributi e metodi specifici per gli studenti.
Per indicare che una classe estende un'altra classe, si usa la parola chiave extends
seguita dal nome della superclasse.
Per esempio:
Questa classe definisce due attributi:
matricola e corso, che sono di tipo String.
Questi attributi sono specifici per gli studenti e non sono presenti nella classe Persona
.
La classe definisce anche un costruttore, che accetta cinque parametri: nome
, eta
, sesso
, matricola
e corso
. Il costruttore invoca il costruttore della superclasse usando la parola chiave super e passando i primi tre parametri. Poi inizializza gli attributi della sottoclasse con gli ultimi due parametri.
La classe definisce infine due metodi: studiare
e sostenereEsame
, che sono di tipo void
. Questi metodi stampano a schermo un messaggio che indica l’azione compiuta dall’oggetto.
Per creare un’istanza della classe Studente
, si usa lo stesso modo della classe Persona
.
Per esempio:
Per accedere agli attributi e ai metodi di un oggetto di tipo Studente
, si usa lo stesso modo della classe Persona
. Per esempio:
Un oggetto di tipo Studente
può accedere anche agli attributi e ai metodi ereditati dalla classe Persona
. Per esempio:
Le condizioni e i cicli sono strutture di controllo che permettono di alterare il flusso di esecuzione del programma in base a delle condizioni o a delle ripetizioni.
Le condizioni sono istruzioni che eseguono un blocco di codice solo se una certa espressione booleana è vera. In Java, si usano le parole chiave if
, else if
ed else
per creare delle condizioni. Per esempio:
Questo codice controlla il valore della variabile x
e stampa un messaggio diverso a seconda che sia positivo, negativo o zero. Il blocco di codice dopo l’if
viene eseguito solo se l’espressione x > 0
è vera.
Il blocco di codice dopo l’else if
viene eseguito solo se l’espressione x < 0
è vera e quella precedente è falsa
.
Il blocco di codice dopo l’else
viene eseguito solo se tutte le espressioni precedenti sono false.
I cicli sono istruzioni che ripetono un blocco di codice finché una certa condizione è vera.
In Java, si usano le parole chiave while
, do-while
e for per creare dei cicli. Per esempio:
Questo codice stampa i numeri da 1 a 10 usando un ciclo while
. Il blocco di codice dopo il while
viene ripetuto finché l’espressione i <= 10
è vera. Ad ogni iterazione, il valore di i
viene incrementato di 1 con l’operatore ++
.
Questo codice fa la stessa cosa del precedente, ma usando un ciclo do-while
. La differenza è che il blocco di codice dopo il do
viene eseguito almeno una volta, prima di controllare la condizione dopo il while
.
Questo codice fa la stessa cosa dei precedenti, ma usando un ciclo for
.
Il ciclo for
ha tre parti:
l’inizializzazione, la condizione e l’incremento.
Lo switch-case è un'altra struttura di controllo che permette di eseguire un blocco di codice in base al valore di una variabile. In Java, si usa la parola chiave switch
seguita da una variabile di tipo int, char, String o enum. Il corpo dello switch è racchiuso tra parentesi graffe e contiene dei casi (case
) che corrispondono ai possibili valori della variabile. Ogni caso termina con una parola chiave break
che serve a uscire dallo switch. Si può anche aggiungere un caso default
che viene eseguito se nessun altro caso corrisponde. Per esempio:
Questo codice controlla il valore della variabile x e stampa un messaggio diverso a seconda del caso.
case 1
viene eseguito solo se x vale 1.case 2
viene eseguito solo se x vale 2.case 3
viene eseguito solo se x vale 3.default
viene eseguito solo se x non vale nessuno dei casi precedenti.Le funzioni sono dei blocchi di codice che possono essere riutilizzati più volte per eseguire una determinata operazione. In Java, le funzioni sono chiamate metodi e sono definite all’interno delle classi. Un metodo ha un nome, un tipo di ritorno, una lista di parametri e un corpo. Per definire un metodo in Java, si usa la seguente sintassi:
Il tipo indica il tipo di dato che il metodo restituisce al termine della sua esecuzione. Se il metodo non restituisce nulla, si usa il tipo void. Il nome indica il nome del metodo, che deve seguire le regole per i nomi delle variabili. I parametri sono delle variabili che vengono passate al metodo quando viene invocato e servono a fornire dei dati al metodo. I parametri sono racchiusi tra parentesi tonde e separati da virgole. Il corpo del metodo è racchiuso tra parentesi graffe e contiene le istruzioni da eseguire.
Per invocare un metodo, si usa il nome del metodo seguito dai parametri effettivi da passare al metodo. I parametri effettivi devono corrispondere ai parametri formali per numero, ordine e tipo. Se il metodo restituisce un valore, si può assegnare questo valore a una variabile o usarlo in un’espressione. Per esempio:
Questo codice definisce un metodo chiamato somma che accetta due parametri di tipo int
(a
e b
) e restituisce un valore di tipo int c
. Il metodo calcola la somma di a
e b
e usa la parola chiave return per restituire il valore di c
.
Il codice invoca poi il metodo somma con due parametri effettivi x
e y
) e assegna il valore restituito a una variabile z
. Il codice stampa poi il valore di z
.
La ricorsione è una tecnica di programmazione che consiste nel far chiamare un metodo se stesso per risolvere un problema più piccolo, richiede due elementi:
un caso base, che è la condizione che termina la ricorsione
e un caso ricorsivo, che è la chiamata al metodo se stesso con parametri diversi.
Per esempio:
Questo codice definisce un metodo chiamato fattoriale che accetta un parametro di tipo int (n)
e restituisce un valore di tipo int. Il metodo calcola il fattoriale di n, che è il prodotto di tutti i numeri interi da 1 a n. Il metodo usa la ricorsione per calcolare il fattoriale: se n è 0, restituisce 1 (caso base); altrimenti, restituisce n moltiplicato per il fattoriale di n - 1
(caso ricorsivo).
Il codice invoca poi il metodo fattoriale con un parametro effettivo (x) e assegna il valore restituito a una variabile y.
Il codice stampa poi il valore di y.
La ricorsione può essere utile per risolvere problemi che hanno una struttura ripetitiva o frattale, come le torri di Hanoi, il triangolo di Tartaglia o la serie di Fibonacci. Tuttavia, la ricorsione ha anche degli svantaggi, come l’uso elevato di memoria e il rischio di incorrere in uno stack overflow se la ricorsione non termina mai.
In Java, ci sono diverse classi che permettono di leggere e scrivere i dati da e verso i file.
Queste classi fanno parte del package java.io e devono essere importate nel codice per poterle usare.
Inoltre, la lettura e la scrittura di file possono generare delle eccezioni che devono essere gestite con il costrutto try-catch
o con la clausola throws
.
La classe File
rappresenta un file o una directory nel sistema operativo e permette di creare, eliminare, ricercare o rinominare un file. Per esempio:
La classe File ha diversi metodi per gestire i file, tra cui:
exists()
[boolean
]: restituisce true se il file esiste, false altrimenti.createNewFile()
[boolean
]: crea effettivamente il file sul disco.delete()
[boolean
]: elimina il file dal disco.isFile()
[boolean
]: restituisce true se si tratta di un file, false altrimenti.isDirectory()
[boolean
]: restituisce true se si tratta di una directory, false altrimenti.FileReader
e FileWriter
permettono di leggere e scrivere i caratteri contenuti in un file di testo uno alla volta. Per esempio:La classe BufferedReader ha il metodo readLine()
che permette di leggere una linea intera di testo invece di un singolo carattere. La classe BufferedWriter ha il metodo flush()
che serve a scaricare il contenuto del buffer nel file.
Le classi Scanner e PrintWriter sono altre classi utili per leggere e scrivere i dati da e verso i file. La classe Scanner permette di leggere i dati in modo strutturato, separando i token in base a un delimitatore (di default lo spazio bianco). La classe PrintWriter permette di scrivere i dati in modo formattato, usando i metodi print()
e println()
. Per esempio:
La classe Scanner ha diversi metodi per leggere i token in base al loro tipo, come nextInt()
, nextDouble()
, nextBoolean()
ecc.
Le eccezioni sono degli eventi che si verificano durante l’esecuzione di un programma e che interrompono il flusso normale delle istruzioni.
Per esempio, un’eccezione può essere causata da un errore di sintassi, da un accesso a una variabile non inizializzata, da una divisione per zero o da un tentativo di aprire un file inesistente.
In Java, le eccezioni sono rappresentate da degli oggetti di tipo Throwable
o di una delle sue sottoclassi. Le principali sottoclassi di Throwable
sono Exception
e Error
. Le eccezioni di tipo Exception
sono quelle che possono essere gestite dal programmatore, mentre quelle di tipo Error
sono quelle che dipendono dal sistema e non possono essere recuperate.
Per gestire le eccezioni in Java, si usa la struttura try-catch-finally
, che ha la seguente sintassi:
Il blocco try
contiene il codice che può lanciare un’eccezione.
Se si verifica un’eccezione, il flusso del programma passa al blocco catch
corrispondente, che contiene il codice per gestire l’eccezione.
Il blocco finally
contiene il codice che viene eseguito sempre, anche se non si verifica nessuna eccezione o se si usa la parola chiave return nel blocco try
o catch
. Il blocco finally
è opzionale e può essere omesso se non necessario.
Per esempio:
Questo codice prova a dividere due numeri interi e stampare il risultato.
Tuttavia, siccome il divisore è zero, si genera un’eccezione di tipo ArithmeticException
.
Il blocco catch
cattura questa eccezione e stampa un messaggio di errore.
Il blocco finally
stampa un messaggio di fine del programma.
Se il blocco try
può generare più tipi di eccezioni, si possono usare più blocchi catch
, uno per ogni tipo di eccezione da gestire. I blocchi catch
devono essere ordinati dal più specifico al più generico, altrimenti si può avere un errore di compilazione.
Per esempio:
Questo codice prova a leggere un numero intero da un file chiamato “test.txt” e stamparlo.
Tuttavia, se il file non esiste o se il file non contiene un numero intero, si generano delle eccezioni di tipo FileNotFoundException
o InputMismatchException
.
I blocchi catch catturano queste eccezioni e stampano dei messaggi di errore. Il blocco finally
stampa un messaggio di fine del programma.
Questa guida rapida a Java ti ha introdotto ai concetti fondamentali del linguaggio, come le variabili, i tipi, le condizioni, i cicli, le classi, gli oggetti e i metodi.
Ovviamente, Java ha molto altro da offrire, come le eccezioni, le interfacce, l’ereditarietà multipla, i generics, le lambda expressions e le stream.
Per approfondire questi argomenti e altri ancora, ti consiglio di consultare altre fonti online o libri.
Spero che questa guida ti sia stata utile e ti abbia fatto apprezzare Java.
Come diceva un famoso filosofo: “Java è per tutti”. O forse era “Java è ovunque”. Non ricordo bene.
In ogni caso, buona programmazione! 👋
L’ereditarietà è un concetto che permette di definire una relazione tra due classi in cui una classe (la sottoclasse) eredita le caratteristiche e i comportamenti di un’altra classe (la superclasse).
Questo permette di evitare la duplicazione di codice e di creare una gerarchia di classi.
Per esempio, si può definire una classe Animale che ha un attributo nome e un metodo emettereSuono.
Poi si può definire una classe Cane che estende la classe Animale e aggiunge un attributo razza e un metodo abbaiare.
La classe Cane eredita l’attributo nome e il metodo emettereSuono dalla classe Animale e può usarli come se fossero suoi
L’incapsulamento è il concetto che consiste nel nascondere i dettagli interni di una classe e fornire solo dei metodi pubblici per interagire con essa.
Questo permette di proteggere i dati della classe da accessi o modifiche indesiderate e di rendere il codice più facile da mantenere e testare.
Per esempio, si può definire una classe ContoBancario che ha un attributo privato saldo e dei metodi pubblici deposita, preleva e getSaldo. Questi metodi sono l’unico modo per accedere o modificare il saldo del conto bancario, garantendo così la sua integrità
Il polimorfismo è il concetto che permette a un oggetto di assumere "forme" diverse a seconda del contesto in cui viene usato.
In Java, il polimorfismo si manifesta in due modi: l’overloading e l’overriding.
L’overloading è il fatto di definire più metodi con lo stesso nome ma con parametri diversi. Questo permette di usare lo stesso nome per eseguire operazioni simili ma con dati diversi.
Per esempio, si può definire una classe Calcolatrice che ha due metodi somma:
A seconda dei parametri passati al metodo somma, verrà invocato il metodo appropriato.
Esempio:
L’overriding è il fatto di ridefinire un metodo ereditato da una superclasse in una sottoclasse.
Questo permette di modificare il comportamento del metodo in base al tipo dell’oggetto che lo invoca.
Per esempio, si può definire una classe Animale che ha un metodo emettereSuono e una classe Cane che estende la classe Animale e ridefinisce il metodo emettereSuono.
Se si crea un oggetto di tipo Animale e uno di tipo Cane e si invoca il metodo emettereSuono su entrambi, si otterranno risultati diversi.
Le liste sono delle strutture dati che permettono di memorizzare e manipolare una sequenza di elementi.
In Java, le liste sono implementate da diverse classi che fanno parte del framework delle collezioni, come ArrayList, LinkedList e Vector.
Le liste hanno dei metodi comuni per aggiungere, rimuovere, accedere e modificare gli elementi, oltre a dei metodi specifici per ogni tipo di lista.
Per esempio, si può definire una lista di stringhe usando la classe ArrayList e usare i suoi metodi per gestire gli elementi della lista.
Ecco i metodi più comuni:
int
] size(): per ottenere il numero di elementi nella lista.boolean
] isEmpty(): per controllare se la lista è vuota o no.boolean
] contains(Object o): restituisce true se questa lista contiene l’elemento specificato.Iterator<E>
] iterator(): restituisce un iteratore sugli elementi di questa lista in ordine corretto.Object []
] toArray(): restituisce un array contenente tutti gli elementi di questa lista in ordine corretto.Esempio:
E se invece ho una lista bi-dimensionale?
Per operare con una lista bidimensionale, devi usare una lista di liste, cioè una lista che contiene altre liste come elementi.
Per esempio, puoi creare una lista bidimensionale di interi così:
Per accedere agli elementi di una lista bidimensionale, devi usare due indici: il primo per la lista esterna e il secondo per la lista interna. Per esempio:
Per modificare gli elementi di una lista bidimensionale, devi usare il metodo set con due indici: il primo per la lista esterna e il secondo per la lista interna. Per esempio:
E se invece voglio creare una LinkedList?
Per creare una LinkedList in Java, devi importare la classe java.util.LinkedList
e usare l’operatore new per creare un’istanza di essa. Per esempio:
Una LinkedList
è una struttura dati che consiste in una sequenza di nodi, ognuno contenente un elemento e un riferimento al nodo successivo e precedente.
Una LinkedList
permette di inserire e cancellare rapidamente gli elementi in qualsiasi posizione, ma di accedere lentamente a un elemento specifico per indice.
Una LinkedList
implementa anche le interfacce List, Deque e Queue, che forniscono dei metodi per aggiungere, rimuovere, accedere e modificare gli elementi in modi diversi.
Per esempio: