Impara Java da zero

Quindi, vuoi programmare in Java? È fantastico, e sei arrivato nel posto giusto. La serie Java 101 fornisce un'introduzione autoguidata alla programmazione Java, partendo dalle basi e coprendo tutti i concetti fondamentali che è necessario conoscere per diventare uno sviluppatore Java produttivo. Questa serie è tecnica, con molti esempi di codice per aiutarti a cogliere i concetti mentre procediamo. Presumo che tu abbia già una certa esperienza di programmazione, ma non in Java.

Questo primo articolo introduce la piattaforma Java e spiega la differenza tra le sue tre edizioni: Java SE, Java EE e Java ME. Imparerai anche il ruolo della Java virtual machine (JVM) nella distribuzione delle applicazioni Java. Ti aiuterò a configurare un Java Development Kit (JDK) sul tuo sistema in modo che tu possa sviluppare ed eseguire programmi Java e ti farò iniziare con l'architettura di una tipica applicazione Java. Infine, imparerai come compilare ed eseguire una semplice app Java.

Aggiornato per Java 12 e il nuovo JShell

Questa serie è stata aggiornata per Java 12 e include una rapida introduzione al nuovo jshell: uno strumento interattivo per l'apprendimento di Java e la prototipazione del codice Java.

download Ottieni il codice Scarica il codice sorgente per applicazioni di esempio in questo tutorial. Creato da Jeff Friesen per JavaWorld.

Cos'è Java?

Si può pensare a Java come a un linguaggio generico orientato agli oggetti che assomiglia molto a C e C ++, ma che è più facile da usare e consente di creare programmi più robusti. Sfortunatamente, questa definizione non ti dà molte informazioni su Java. Nel 2000, Sun Microsystems (creatore della piattaforma Java) ha descritto Java in questo modo: 

Java è un linguaggio per computer semplice, orientato agli oggetti, esperto di rete, interpretato, robusto, sicuro, indipendente dall'architettura, portatile, ad alte prestazioni, multithreading e dinamico.

Consideriamo separatamente ciascuna di queste definizioni.

Java è un linguaggio semplice . Java è stato inizialmente modellato su C e C ++, meno alcune caratteristiche potenzialmente confuse. I puntatori, l'ereditarietà di implementazioni multiple e il sovraccarico degli operatori sono alcune funzionalità C / C ++ che non fanno parte di Java. Una funzionalità non obbligatoria in C / C ++, ma essenziale per Java, è una funzione di garbage collection che recupera automaticamente oggetti e array.

Java è un linguaggio orientato agli oggetti . L'attenzione orientata agli oggetti di Java consente agli sviluppatori di lavorare sull'adattamento di Java per risolvere un problema, piuttosto che costringerci a manipolare il problema per soddisfare i vincoli del linguaggio. Questo è diverso da un linguaggio strutturato come C. Ad esempio, mentre Java ti consente di concentrarti sugli oggetti dei conti di risparmio, C richiede di pensare separatamente allo stato del conto di risparmio (tale saldo) e ai comportamenti (come deposito e prelievo).

Java è un linguaggio esperto di reti . L'ampia libreria di rete di Java semplifica la gestione dei protocolli di rete TCP / IP (Transmission Control Protocol / Internet Protocol) come HTTP (HyperText Transfer Protocol) e FTP (File Transfer Protocol) e semplifica l'attività di creazione di connessioni di rete. Inoltre, i programmi Java possono accedere agli oggetti su una rete TCP / IP, tramite URL (Uniform Resource Locator), con la stessa facilità con cui si accederà a essi dal file system locale.

Java è un linguaggio interpretato . In fase di esecuzione, un programma Java viene eseguito indirettamente sulla piattaforma sottostante (come Windows o Linux) tramite una macchina virtuale (che è una rappresentazione software di una piattaforma ipotetica) e l'ambiente di esecuzione associato. La macchina virtuale traduce i bytecode del programma Java (istruzioni e dati associati) in istruzioni specifiche della piattaforma tramite interpretazione. L'interpretazione è l'atto di capire cosa significhi un'istruzione bytecode e quindi scegliere equivalenti istruzioni specifiche della piattaforma "predefinite" da eseguire. La macchina virtuale esegue quindi quelle istruzioni specifiche della piattaforma.

L'interpretazione semplifica il debug di programmi Java difettosi perché in fase di esecuzione sono disponibili più informazioni in fase di compilazione. L'interpretazione consente inoltre di ritardare la fase di collegamento tra le parti di un programma Java fino al runtime, il che accelera lo sviluppo.

Java è un linguaggio robusto . I programmi Java devono essere affidabili perché vengono utilizzati in applicazioni sia consumer che mission-critical, che vanno dai lettori Blu-ray ai sistemi di navigazione per veicoli o di controllo aereo. Le caratteristiche del linguaggio che aiutano a rendere affidabile Java includono dichiarazioni, controllo del tipo duplicato in fase di compilazione e runtime (per evitare problemi di mancata corrispondenza della versione), true array con controllo automatico dei limiti e omissione di puntatori. (Vedere "Funzionalità elementari del linguaggio Java" per iniziare con i tipi di linguaggio Java, i letterali, le variabili e altro.)

Un altro aspetto della robustezza di Java è che i cicli devono essere controllati da espressioni booleane invece che da espressioni intere dove 0 è falso e un valore diverso da zero è vero. Ad esempio, Java non consente un ciclo in stile C, ad esempio while (x) x++;perché il ciclo potrebbe non terminare dove previsto. Invece, devi fornire esplicitamente un'espressione booleana, come while (x != 10) x++;(il che significa che il ciclo verrà eseguito fino a quando è xuguale a 10).

Java è un linguaggio sicuro . I programmi Java vengono utilizzati in ambienti di rete / distribuiti. Poiché i programmi Java possono migrare ed essere eseguiti su varie piattaforme di una rete, è importante salvaguardare queste piattaforme da codice dannoso che potrebbe diffondere virus, rubare informazioni sulla carta di credito o eseguire altri atti dannosi. Le funzionalità del linguaggio Java che supportano la robustezza (come l'omissione dei puntatori) funzionano con funzionalità di sicurezza come il modello di sicurezza sandbox Java e la crittografia a chiave pubblica. Insieme, queste funzionalità impediscono a virus e altri codici pericolosi di provocare il caos su una piattaforma ignara.

In teoria, Java è sicuro. In pratica, sono state rilevate e sfruttate varie vulnerabilità di sicurezza. Di conseguenza, Sun Microsystems e Oracle ora continuano a rilasciare aggiornamenti di sicurezza.

Java è un linguaggio indipendente dall'architettura . Le reti collegano piattaforme con diverse architetture basate su vari microprocessori e sistemi operativi. Non ci si può aspettare che Java generi istruzioni specifiche della piattaforma e che queste istruzioni siano "comprese" da tutti i tipi di piattaforme che fanno parte di una rete. Invece, Java genera istruzioni bytecode indipendenti dalla piattaforma che sono facili da interpretare per ciascuna piattaforma (tramite la sua implementazione della JVM).

Java è un linguaggio portatile . La neutralità dell'architettura contribuisce alla portabilità. Tuttavia, c'è di più nella portabilità di Java rispetto alle istruzioni bytecode indipendenti dalla piattaforma. Considera che le dimensioni dei tipi interi non devono variare. Ad esempio, il tipo intero a 32 bit deve essere sempre firmato e occupare 32 bit, indipendentemente da dove viene elaborato l'intero a 32 bit (ad esempio, una piattaforma con registri a 16 bit, una piattaforma con registri a 32 bit o una piattaforma con registri a 64 bit). Anche le librerie di Java contribuiscono alla portabilità. Laddove necessario, forniscono tipi che collegano il codice Java con funzionalità specifiche della piattaforma nel modo più portatile possibile.

Java è un linguaggio ad alte prestazioni . L'interpretazione produce un livello di prestazioni che di solito è più che adeguato. Per scenari applicativi ad altissime prestazioni, Java utilizza la compilazione just-in-time, che analizza le sequenze di istruzioni interpretate in bytecode e compila le sequenze di istruzioni interpretate di frequente in istruzioni specifiche della piattaforma. I successivi tentativi di interpretare queste sequenze di istruzioni bytecode determinano l'esecuzione di istruzioni specifiche della piattaforma equivalenti, con conseguente aumento delle prestazioni.

Java è un linguaggio multithread . Per migliorare le prestazioni dei programmi che devono eseguire più attività contemporaneamente, Java supporta il concetto di esecuzione a thread . Ad esempio, un programma che gestisce una GUI (Graphical User Interface) in attesa di input da una connessione di rete utilizza un altro thread per eseguire l'attesa invece di utilizzare il thread GUI predefinito per entrambe le attività. Ciò mantiene la GUI reattiva. Le primitive di sincronizzazione di Java consentono ai thread di comunicare in modo sicuro i dati tra di loro senza corrompere i dati. (Vedere la programmazione a thread in Java discussa altrove nella serie Java 101).

Java è un linguaggio dinamico . Poiché le interconnessioni tra il codice del programma e le librerie avvengono dinamicamente in fase di esecuzione, non è necessario collegarle esplicitamente. Di conseguenza, quando un programma o una delle sue librerie si evolve (ad esempio, per una correzione di bug o un miglioramento delle prestazioni), uno sviluppatore deve solo distribuire il programma o la libreria aggiornati. Sebbene il comportamento dinamico si traduca in meno codice da distribuire quando si verifica una modifica di versione, questa politica di distribuzione può anche portare a conflitti di versione. Ad esempio, uno sviluppatore rimuove un tipo di classe da una libreria o lo rinomina. Quando un'azienda distribuisce la libreria aggiornata, i programmi esistenti che dipendono dal tipo di classe non funzioneranno. Per ridurre notevolmente questo problema, Java supporta un tipo di interfaccia, che è come un contratto tra due parti. (Vedere interfacce, tipi e altre funzionalità del linguaggio orientato agli oggetti discusse altrove nella serie Java 101).

Disimballare questa definizione ci insegna molto su Java. Ancora più importante, rivela che Java è sia un linguaggio che una piattaforma. Ulteriori informazioni sui componenti della piattaforma Java, ovvero la macchina virtuale Java e l'ambiente di esecuzione Java, più avanti in questo tutorial.

Tre edizioni di Java: Java SE, Java EE e Java ME

Sun Microsystems ha rilasciato il kit di sviluppo software Java 1.0 (JDK) nel maggio 1995. Il primo JDK è stato utilizzato per sviluppare applicazioni desktop e applet e successivamente Java si è evoluto per comprendere la programmazione di server aziendali e dispositivi mobili. Memorizzare tutte le librerie necessarie in un unico JDK avrebbe reso il JDK troppo grande per essere distribuito, soprattutto perché la distribuzione negli anni '90 era limitata da CD di piccole dimensioni e velocità di rete lente. Poiché la maggior parte degli sviluppatori non necessitava dell'ultima API (uno sviluppatore di applicazioni desktop difficilmente avrebbe bisogno di accedere alle API Java aziendali), Sun ha scomposto Java in tre edizioni principali. Questi alla fine divennero noti come Java SE, Java EE e Java ME:

  • Java Platform, Standard Edition (Java SE) è la piattaforma Java per lo sviluppo di applicazioni lato client (che vengono eseguite sui desktop) e applet (che vengono eseguite nei browser Web). Tieni presente che per motivi di sicurezza le applet non sono più ufficialmente supportate.
  • Java Platform, Enterprise Edition (Java EE ) è la piattaforma Java costruita su Java SE, che viene utilizzata esclusivamente per sviluppare applicazioni server orientate all'impresa. Le applicazioni lato server includono i servlet Java , che sono programmi Java simili agli applet ma eseguiti su un server anziché su un client. I servlet sono conformi all'API del servlet Java.
  • Anche Java Platform, Micro Edition (Java ME) è basato su Java SE. È la piattaforma Java per lo sviluppo di MIDlet , che sono programmi Java che vengono eseguiti su dispositivi informatici mobili, e Xlet , che sono programmi Java che vengono eseguiti su dispositivi incorporati.

Java SE è la piattaforma di base per Java ed è il fulcro della serie Java 101. Gli esempi di codice saranno basati sulla versione più recente di Java al momento della scrittura, Java 12.

La piattaforma Java e JVM

Java è sia un linguaggio di programmazione che una piattaforma per l'esecuzione di codice Java compilato. Questa piattaforma è costituita principalmente dalla JVM, ma include anche un ambiente di esecuzione che supporta l'esecuzione della JVM sulla piattaforma (nativa) sottostante. La JVM include diversi componenti per il caricamento, la verifica e l'esecuzione del codice Java. La Figura 1 mostra come viene eseguito un programma Java su questa piattaforma. 

Jeff Friesen

Nella parte superiore del diagramma c'è una serie di file di classe di programma, uno dei quali è indicato come file di classe principale. Un programma Java è costituito almeno dal file di classe principale, che è il primo file di classe da caricare, verificare ed eseguire.

La JVM delega il caricamento della classe al suo componente di caricamento classi. I programmi di caricamento classi caricano file di classe da varie origini, come file system, reti e file di archivio. Isolano la JVM dalle complessità del caricamento di classe.

Un file di classe caricato viene archiviato in memoria e rappresentato come un oggetto creato dalla Classclasse. Una volta caricato, il verificatore del bytecode verifica le varie istruzioni del bytecode per assicurarsi che siano valide e non compromettano la sicurezza.

Se i bytecode del file di classe non sono validi, la JVM termina. Altrimenti, il suo componente interprete interpreta il bytecode un'istruzione alla volta. L'interpretazione identifica le istruzioni bytecode ed esegue istruzioni native equivalenti.

Alcune sequenze di istruzioni bytecode vengono eseguite più frequentemente di altre. Quando l'interprete rileva questa situazione, il compilatore just-in-time (JIT) della JVM compila la sequenza bytecode in codice nativo per un'esecuzione più rapida.

Durante l'esecuzione, l'interprete incontra tipicamente una richiesta per eseguire il bytecode di un altro file di classe (appartenente al programma o ad una libreria). Quando ciò accade, il classloader carica il file di classe e il verificatore bytecode verifica il bytecode del file di classe caricato prima che venga eseguito. Inoltre, durante l'esecuzione, le istruzioni bytecode potrebbero richiedere alla JVM di aprire un file, visualizzare qualcosa sullo schermo, emettere un suono o eseguire un'altra attività che richiede la cooperazione con la piattaforma nativa. La JVM risponde utilizzando la sua tecnologia bridge JNI (Java Native Interface) per interagire con la piattaforma nativa per eseguire l'attività.