1. Java: un linguaggio di programmazione per applicazioni multimediali e multiambiente


1.5 Veduta d'insieme delle caratteristiche di Java

Affinità con il C e il C++

La similitudine sintattica con il più diffuso linguaggio di programmazione permette un più rapido apprendimento di Java da parte dei programmatori di C e di C++. Per esempio, le istruzioni for, if, else sono del tutto analoghe a quelle del C. Inoltre, per rendere il linguaggio più semplice e lineare, sono state eliminate in Java alcune caratteristiche del C e del C++, come le funzioni di allocazione e di liberazione della memoria e l'ereditarietà multipla. Altra caratteristica di Java sono le dimensioni ridotte del software, in modo che esso possa funzionare anche su sistemi molto piccoli. L'interprete, le classi e le "librerie" Java di base non superano nel loro complesso i 250 Kbytes.

Semplicità

Il linguaggio Java è stato progettato per essere semplice. I programmatori della Sun ritengono che il C++ sia un linguaggio ridondante. Esso infatti deriva dal C, che non è orientato all'oggetto e a cui sono state aggiunte le classi al fine di creare un linguaggio orientato all'oggetto. La conseguenza di ciò è che inevitabilmente il C++ possiede caratteristiche che appartengono al C e che non rientrano nella filosofia dei linguaggi orientati all'oggetto. Per questa ragione, tutte le caratteristiche del C++ ritenute ridondanti sono state tolte da Java, in modo che quest'ultimo potesse divenire un linguaggio di programmazione più snello e facile da leggere. L'elenco che segue puntualizza le caratteristiche del C++ che mancano in Java.

Linguaggio orientato all'oggetto (Object-oriented)

Per essere considerato orientato all'oggetto, un linguaggio di programmazione in linea di principio deve possedere almeno questi quattro requisiti:

  1. Incapsulamento: è possibile rendere riservata o astratta parte dell'informazione.
  2. Polimorfismo: gli stessi messaggi inviati a oggetti differenti producono effetti diversi a seconda dell'oggetto coinvolto.
  3. Ereditarietà: si possono definire classi sulla base di altre classi.
  4. Collegamento dinamico: gli oggetti possono provenire da qualsiasi luogo, compresa la rete. Deve essere possibile mandare messaggi agli oggetti senza conoscerne il loro tipo. Ciò è molto utile soprattutto durante l'esecuzione del programma.

Java soddisfa piuttosto bene a queste quattro esigenze. Infatti esistono le classi, le istanze delle quali costituiscono gli oggetti. Le variabili istanziate mantengono l'informazione di stato dei vari oggetti. Si può rendere riservata l'informazione attraverso la dichiarazione private, oppure la si può rendere astratta tramite la dichiarazione abstract (incapsulamento). Esiste comunicazione tra gli oggetti. I metodi posseduti da un oggetto stabiliscono il comportamento dell'oggetto stesso e manipolano le variabili istanziate della classe. In classi distinte possono esistere metodi diversi con lo stesso nome. Ogni oggetto risponde ai messaggi ricevuti secondo le caratteristiche che esso possiede (comportamento polimorfico). Da una classe possono essere create sottoclassi, le quali possiedono i metodi della classe da cui sono derivate (ereditarietà). Le classi derivate possono aggiungere nuovi metodi alla classe da cui derivano oppure possono sostituire ad essa alcuni metodi.

Infine, la neutralità all'architettura di Java permette di usare classi provenienti da tutta la rete. Siccome poi Java è un linguaggio interpretato, la fase di "link" tra le classi avviene in fase di esecuzione dei programmi (dinamic binding), consentendo una elasticità notevole per quanto riguarda l'utilizzo degli oggetti.
Il fatto che Java sia un linguaggio interpretato, però, costituisce un serio svantaggio in termini di velocità di esecuzione. Ciò può essere accettato quando si opera in ambiente WWW, poiché il caricamento e il collegamento di classi in fase di esecuzione consente un effettivo utilizzo di risorse distribuite su tutta le rete. Però, come linguaggio indipendente dal WWW, Java dovrebbe essere fornito pure di un compilatore in grado di produrre programmi direttamente eseguibili. Per quanto riguarda gli aspetti esteriori, la programmazione a oggetti in Java ricalca da vicino quella del C++. A differenza del C++, però, in Java qualsiasi cosa a cui si può fare riferimento è una classe. Fanno eccezione a questo solo pochi elementi strutturali come gli interi (int), i caratteri (char), i numeri in doppia precisione (double), etc. che non sono classi.
Oltre al C++, Java ha ereditato alcune fra le caratteristiche più rilevanti degli altri linguaggi di programmazione ad oggetti: Eiffel (garbage collection), Objective C (assenza di sovraccaricamento degli operatori, assenza di ereditarietà multipla), Smalltalk (il capostipite dei linguaggi orientati all'oggetto sviluppato dalla Xerox nel 1972; esso possiede ampie librerie di classi).

Gestione automatica della memoria

In ogni programma Java è sempre attivo lo automatic garbage collection, un thread a bassa priorità che provvede ad analizzare la memoria e a liberare quella che non è utilizzata. In Java quindi esiste soltanto la possibilità di allocare memoria attraverso la creazione di oggetti mediante l'istruzione new, similmente al C++. E' assente invece la funzione free, come pure le funzioni malloc, calloc e realloc. Una volta che un oggetto è stato creato, il garbage collection in fase di esecuzione osserva la vita dell'oggetto e provvede a liberare la memoria occupata quando essa non viene più utilizzata dall'oggetto in questione. Per esempio, il garbage collection provvede a liberare la memoria occupata da un oggetto creato in un metodo una volta che il medesimo metodo termina la sua esecuzione. Siccome si può allocare memoria solo con oggetti, il controllo degli oggetti è sufficiente a gestire con buona efficienza la memoria stessa. Ciò solleva il programmatore dal provvedere alla gestione della memoria, compito questo che in C e C++ conduce spesso all'introdurre errori che si verificano in fase di esecuzione dei programmi.

Neutralità all'architettura

La neutralità dell'architettura rappresenta una caratteristica importante in un mondo informatico sempre maggiormente popolato dai più svariati sistemi hardware e sistemi operativi. Con la fortissima crescita delle reti telematiche questa esigenza è divenuta ancora più pressante. Inoltre, per sviluppare prodotti software per il WWW, la neutralità dall'architettura costituisce un parametro assolutamente indispensabile.
Al fine di raggiungere la neutralità dall'architettura, Java affida l'esecuzione dei suoi programmi ad una macchina virtuale. Più specificatamente, un sorgente Java viene tradotto dal compilatore Java (Javac) non in istruzioni macchina reali, ma in istruzioi macchina virtuali, i bytecodes. Sarà compito poi della macchina virtuale Java (l'interprete Java) di leggere ed eseguire queste istruzioni tramite la generazione "on the fly" del codice macchina reale specifico per il sistema su cui il programma Java viene eseguito.

Sorgente Java => compilatore Java => bytecodes => interprete Java => codice macchina specifico

I programmi Java quindi non hanno bisogno di alcun riadattamento quando vengono portati su sistemi differenti da quelli su cui sono stati sviluppati. I bytecodes rappresentano un codice macchina virtuale di elevata astrazione in modo che possano trovare una rappresentazione reale su ogni macchina. I programmi Java compilati in bytecodes possono viaggiare attraverso reti telematiche ed essere eseguiti sulle macchine provviste dell'interprete Java. Quest'ultimo è incluso nelle ultime versioni di Netscape Navigator (release 2.0 o superiore), il quale è quindi in grado di interpretare particolari programmi Java (applet) e visualizzare l'uscita di questi programmi nella propria finestra.

Linguaggio robusto

L'assenza di puntatori, il fatto che stringhe e array siano oggetti e la gestione automatica della memoria conferiscono ad Java una robustezza particolare. Oltre a ciò, il compilatore Java individua molte situazioni in cui un programma potrebbe provocare problemi.

Dinamicità

Essendo Java un linguaggio interpretato, le classi di Java vengono caricate e "linkate" durante l'esecuzione. Ciò permette di risolvere alcuni fastidiosi problemi presenti nel C++, come quello chiamato problema della "Superclasse fragile". Quando in C++ si aggiorna una classe, per esempio introducendo in essa nuove variabili, tutte le altre classi che fanno riferirimento alla classe modificata devono essere ricompilate. Java, invece, permette di aggiornare le "librerie" di classi senza costringere gli utenti alla ricompilazione dei programmi. Alle classi in "libreria" si possono infatti aggiungere metodi e variabili senza che questo abbia effetto sui vecchi programmi. Ciò è possibile grazie al fatto che ogni classe in Java ha anche una rappresentazione "runtime" (bytecodes), la quale permette di individuare comunque il tipo di oggetti che l'utente richiede (in C++ per accedere ad un oggetto, è necessario non solo conoscere il puntatore a quell'oggetto, ma anche sapere di che oggetto si tratta; in Java, invece, l'utente possiede anche un modulo "runtime" della classe, il quale permette di accedere all'oggetto).
Il caricamento e il "linkaggio" dinamico delle classi risultano pure molto utili in un ambiente distribuito, poiché durante l'esecuzione di un programma gli oggetti possono essere richiesti da tutta la rete.
Ovviamente, il fatto che Java sia interpretato comporta anche degli svantaggi (vedi il praragrafo precedente "Linguaggio orientato all'oggetto").

Sicurezza

Siccome Java è stato progettato per funzionare in ambienti distribuiti o di rete, la sicurezza è un parametro molto importante. Si pensi per esempio alla possibilità che qualche malintenzionato scriva in Java programmi che arrecano danni al sistema, come virus informatici. Nell'ambiente del WWW ciò risulterebbe ancor più pericoloso, dal momento che gli applet vengono scaricati automaticamente durante la navigazione in Internet.
In fatto di sicurezza, Java offre innanzitutto la sue caratteristiche strutturali: gestione automatica della memoria in fase di esecuzione dei programmi e assenza di puntatori. Oltre a ciò, Java è stato dotato di altre caratteristiche che provvedono ad aumentarne la sicurezza. L'elenco che segue puntualizza queste caratteristiche.

Infine, come estremo mezzo per garantire la sicurezza, si può provvedere a implementare una autenticazione dei bytecodes attraverso metodi di crittografia a chiave pubblica.

Linguaggio distribuito

Java possiede molte librerie (class java.net.*) che permettono facilmente l'accesso a risorse remote tramite protocolli basati sul TCP/IP, come lo HTTP. Tramite URL (class java.net.URL) una applicazione Java può accedere a oggetti remoti come se essi fossero collocati su file presenti nel sistema ove essa funziona.

Linguaggio multi-threated

Una delle classi che fanno parte delle librerie standard di Java è la classe Thread. Essa possiede tutti i metodi necessari per far partire contemporaneamente più thread in un unico programma Java, nonché di arrestare gli stessi thread, di farli ripartire e fornire indicazioni sul loro stato. Java gestisce i vari thread in maniera preventiva (pre-emptive), ovvero essi possono essere controllati completamente. Un thread può essere interrotto in qualsiasi momento e al suo posto può partire un altro thread. A seconda del sistema su cui sono implementati, i thread possono essere anche regolati dal time-slice. Per aumentare la velocità di esecuzione in compiti particolarmente gravosi, è possibile anche sospendere il controllo di un thread per mezzo del metodo yield.
Attraverso l'istruzione synchronized è possibile mandare in esecuzione più metodi che accedono alle stesse variabili istanziate senza che vi sia il pericolo che il valore delle variabili a cui un metodo in esecuzione sta accedendo sia cambiato da un altro metodo. Più in generale, attraverso l'istruzione synchronized si impone che lo stato di un oggetto sia cambiato solo da un metodo alla volta. Buona parte delle caratteristiche multi-thread di Java sono state fornite dai linguaggi di programmazione Mesa e Cedar (un superset del Mesa, sempre implementato dalla Xerox).

Ampie librerie di classi di base Il sistema Java di base fornisce le librerie di classi adatte a sviluppare numerose applicazioni di qualsiasi genere. I principali gruppi delle classi fondamentali sono:

Interazione con i browser per Internet

Java possiede una classe apposita (class applet) che permette di inserire in un documento HTML riferimenti ad applicativi (applet) che possono essere eseguiti a distanza da un browser adatto, come lo specifico HotJava della Sun, o, ultimamente, anche Netscape. Il fatto che anche la società Netscape abbia adottato Java dà garanzie sulla diffusione in futuro del nuovo linguaggio di programmazione sviluppato dalla Sun. Essendo il browser Netscape il più usato in tutto il mondo, Java potrebbe divenire uno standard per inserire nei documenti HTML programmi eseguibili di ogni tipo che generano la loro uscita direttamente nella finestra del browser.
Si noti che, a differenza dei programmi richiamati tramite la tradizionale interfaccia CGI, i programmi applet sono eseguiti dal client e non dal server. Ciò consente di scrivere programmi che richiedono anche tempi di esecuzione lunghi (come animazione, o calcoli matematici), poiché la macchina impegnata è quella del client e quindi il server si accolla solo la parte del lavoro che riguarda la trasmissione dei bytecodes (programma Java compilato). Tutto ciò rappresenta pure una garanzia di sicurezza per il server, che si limita ad inviare solo dei dati (i bytecodes), e non concede a client remoti di eseguire programmi sul server stesso. Inoltre, l'uscita di un programma applet viene visualizzata direttamente nella finestra del browser. Ciò è molto importante, poiché essa non è limitata, come nell'interfaccia CGI, ad essere di tipo MIME allo scopo di essere trasmessa con il protocollo HTTP. Si ricordi infine che per la manipolazione di un tipo MIME, spesso il browser si deve appoggiare ad altri applicativi (come avviene per filmati MPEG e file audio di vario formato). La classe applet invece provvede già a fornire i metodi necessari per implementare l'animazione e la manipolazione dei messaggi sonori da parte del browser.

Prestazioni

Sebbene la velocità del linguaggio interpretato Java sia sufficiente per molte applicazioni, può essere necessario ottenere una velocità maggiore. Se i bytecodes vengono trasformati completamente in codice macchina, i ricercatori della SUN assicurano che si ottengono prestazioni analoghe a quelle fornite dai programmi implementati in C e C++.