professionale

Java: Arte e tecnica
Sintassi e struttura del linguaggio-Parte II

Dario de Judicibus

 

Dopo aver descritto parole, tipi di operatori, separatori e commenti; illustrato cos’Φ un’espressione, continuiamo la nostra analisi dalla trattazione delle istruzioni.

Paragonando un linguaggio di programmazione a una vera lingua possiamo affermare che l’insieme delle parole chiave e degli operatori utilizzati ne rappresentano il vocabolario, mentre i simboli di raggruppamento e separazione corrispondono alla punteggiatura. Il modo poi di combinare le parole riservate e i vari simboli con gli identificatori e gli oggetti definiti dal programmatore forma quella che si pu˜ a ragione definire la sintassi del linguaggio.

Lo scorso numero abbiamo descritto le parole riservate a Java, i vari tipi di operatori, i separatori e i commenti. Abbiamo illustrato cos’Φ un’espressione evidenziando come la sintassi di questo linguaggio sia fortemente caratterizzata dal fatto che Java ha l’architettura di un linguaggio interpretato, per cui, non esistendo il concetto di risoluzione dei collegamenti (link-edit), non prevede l’esistenza di costrutti che non possono essere risolti durante l’esecuzione. Abbiamo anche iniziato a trattare le istruzioni; ne esistono di tre tipi: le dichiarazioni, le espressioni e le istruzioni di controllo del flusso. Propriamente, perchΦ un’espressione possa essere considerata un’istruzione, essa deve terminare con un punto e virgola. Pi∙ istruzioni racchiuse fra parentesi graffe formano un blocco di istruzioni, o se vogliamo un’istruzione composta. In seguito useremo il termine istruzione per indicare tanto la singola quanto un blocco di istruzioni, a meno che non sia altrimenti specificato.

Le istruzioni per il controllo del flusso sono praticamente le stesse del C e del C++, ovverosia i vari tipi di cicli condizionati (for, while, do-while) e i costrutti di scelta (if-else, switch-case-default), come riportato nelle tabelle 1 e 2.

Istruzioni, etichette, tipi, classi

Esistono, inoltre, due istruzioni di salto incondizionato (break, continue) che possono essere utilizzate per uscire dai cicli in modo pulito, in accordo con le regole della programmazione strutturata. L’istruzione break pu≥ essere utilizzata anche per uscire dal costrutto di scelta plurima switch. Θ anche possibile associare a un’istruzione un’etichetta, cioΦ un identificativo seguito dal carattere due punti. Le etichette possono essere applicate a qualunque istruzione, ma di fatto servono solo nel caso di cicli annidati, quando si vuole indicare esplicitamente da quale ciclo il salto incondizionato deve uscire. In Java non esistono le istruzioni per il preprocessore come in C++, quali, per esempio, #include o #define e non Φ permesso il goto. Inoltre, non esistono le strutture (struct), le unioni (union) e le enumerazioni (enum), perchΦ tutte queste collezioni di dati possono essere sostituite in modo pi∙ pulito da opportune classi. Infine, non si usa pi∙ il qualificatore const, sostituito dalla parola chiave final. Un altro elemento molto usato in C e C++ ma proibito in Java Φ typedefs, dato che esso pu≥ generare confusione, problemi di portabilitα ed Φ sempre possibile ridisegnare un programma in modo da evitarlo. In effetti, chi ha disegnato Java ha cercato di semplificare al massimo il linguaggio evitando costrutti ridondanti o che, per loro natura, potevano indurre in errore lo sviluppatore. Nei prossimi numeri descriveremo come e perchΦ Java non abbia neanche le funzioni e i puntatori. In Java esistono solo quattro tipi semplici, e cioΦ gli interi, i numeri reali a virgola mobile, i booleani e i caratteri, pi∙ tre tipi composti, e precisamente le matrici, le classi e le interfacce. Al contrario dello SmallTalk, tuttavia, i tipi semplici non sono classi, anche se esistono librerie standard di Java che permettono di rivestirli con classi vere e proprie (wrapper). Il motivo per cui non si sono trasformati tutti i tipi in classi, come in altri linguaggi orientati agli oggetti, Φ che tale approccio avrebbe inutilmente appesantito il linguaggio creando seri problemi di prestazioni. Java Φ infatti un linguaggio pensato per la rete, dove la cura nell’utilizzo delle risorse Φ fondamentale per evitare di saturare la larghezza di banda. Effettivamente, anche l’architettura da linguaggio interpretato affonda le sue radici nel carattere prettamente sequenziale del trasferimento di dati in rete. Se ci pensate, la maggior parte delle variabili e degli oggetti di un programma appartiene a uno dei quattro tipi semplici, per cui trasformarli in classi avrebbe comportato un notevole aumento delle dimensioni del programma; una classe anche minimale necessitα comunque di molti pi∙ byte di un singolo dato. I vari tipi sono riportati in tabella 3.

Da notare che tutti i numeri sono con segno, che esistono quattro diversi tipi di interi e due tipi di numeri a virgola mobile, e che ogni tipo ha una lunghezza in byte ben definita indipendente dalla piattaforma su cui gira la macchina virtuale Java, cioΦ il motore che interpreta il programma e lo esegue. In Java non pu≥ succedere come in C e C++ che il tipo int occupi 16 bit in un sistema e 32 in un altro. Il codice Java non esistono le librerie di oggetti come in C, ma i cosiddetti pacchetti (package). Un pacchetto non Φ altro che un insieme di classi raggruppate logicamente. La struttura dei pacchetti Φ di tipo gerarchico, e cos∞ la loro nomenclatura. Per esempio, tutti i pacchetti che contengono le classi standard di Java iniziano con il nome java seguito da un punto. Abbiamo cos∞ java.lang e java.awt, tanto per citarne due. Se vogliamo referenziare una classe specifica di un pacchetto, faremo precedere al nome della classe il nome del pacchetto che la contiene, introducendo un punto di separazione. Avremo quindi nomi come java.lang.String, java.awt.Graphics e java.applet.Applet. Dato che sarebbe alquanto scomodo usare sempre i nomi delle classi nella loro forma estesa, cosa che renderebbe alquanto pesante leggere il codice sorgente, Java permette di usare un’istruzione particolare chiamata import, da posizionare in testa al file che contiene il codice. Questa istruzione pu≥ essere considerata simile all’#include del C, con la differenza che permette di importare tanto una singola classe quanto l’insieme di tutte le classi di un pacchetto. Se, per esempio, ci serve usare solo la classe Applet del pacchetto java.applet, allora scriveremo in testa al nostro file import java.applet.Applet;

Se invece ci servono un po’ tutte le classi grafiche necessarie a disegnare un’interfaccia interattiva, scriveremo import java.awt.*;

Da notare come import rappresenti un’istruzione a tutti gli effetti, al contrario della #include, e quindi richiede il punto e virgola di chiusura. L’unico pacchetto che non richiede l’uso di istruzione di importazione Φ java.lang, perchΦ automaticamente importato dal compilatore. Se volete crearvi un vostro pacchetto, dovrete usare l’istruzione package in testa a ogni file contenente una classe del pacchetto. Tenete presente che ogni file pu≥ contenere una sola classe pubblica, cioΦ visibile all’esterno del pacchetto.

Descrizioni, proprietα e regole

E veniamo alle dichiarazioni. Esse sono utilizzate per definire le proprietα di una classe, di un metodo, di una variabile, di una costante e cos∞ via. La loro sintassi dipende da cosa stiamo dichiarando. Nel descrivere le varie dichiarative, useremo le seguenti regole:

  1. le parole riservate, i separatori e le parentesi sono riportate in grassetto.
  2. gli elementi da sostituire con parole riservate o identificativi sono in corsivo.
  3. le parti opzionali sono racchiuse da una coppia di parentesi quadre.
  4. le liste sono indicate da tre puntini.

Fate quindi attenzione a non confondere le parentesi che fanno parte della sintassi, riportate in grassetto ([ e ]), da quelle usate per descriverla ([ e ]). La barra verticale (|) viene infine utilizzata per separare scelte mutuamente esclusive.

Consideriamo prima la dichiarazione delle variabili. La sintassi Φ la seguente [accesso] [static] [final] [transient] [volatile]

tipoVar nomeVar [= valoreIniziale];  dove tipoVar Φ il tipo di variabile e nomeVar Φ l’identificativo assegnato dal programmatore. Il tipo pu≥ essere semplice, oppure il nome di una classe o una matrice. Nel primo caso Java assegna effettivamente alla variabile un’area di memoria. Nel secondo caso, la dichiarazione non fa altro che informare Java che si intende creare un oggetto appartenente a quella classe. L’allocazione vera e propria dovrα essere fatta con il metodo di classe new, come vedremo in uno dei prossimi numeri. Questo punto Φ importante. Come accennato poco sopra, in Java non esistono i puntatori, come del resto anche in SmallTalk. Un identificatore pu≥ quindi rappresentare un oggetto oppure un riferimento ad esso a seconda che sia di tipo semplice o composto. Questo vuol dire tra l’altro che in Java mancano gli operatori di indirizzo (&) e di deriferimento (*). In tabella 4 sono riportati alcuni esempi. Ci sono poi cinque attributi opzionali posti davanti al tipo. Il primo serve a indicare quali altre classi hanno accesso alla variabile in questione. I vari tipi di accesso sono riportati nella tabella 5 insieme al loro significato.

Degli altri quattro attributi due sono utilizzati sin dalla prima versione di Java, mentre gli altri due sono riservati a versioni future del linguaggio. Il primo, static, indica che la variabile Φ una variabile di classe, cioΦ Φ condivisa da tutti gli oggetti appartenenti alla stessa classe. Il secondo, final, indica che la variabile Φ in realtα una costante e non pu≥ essere modificata durante l’esecuzione del programma. Il terzo, transient, identifica quelle variabili che non fanno parte dello stato persistente dell’oggetto. Questo vuol dire che se l’oggetto Φ salvato su memoria di massa per poter essere successivamente ricaricato da un programma, le variabili di questo tipo non sono memorizzate. Il quarto, volatile, si riferisce a variabili che possono essere modificate in modo asincrono da pi∙ threa in esecuzione contemporanea. Questo permette a Java di assicurarsi che la variabile abbia sempre un valore coerente all’interno di ciascuna thread.

Il valore iniziale della variabile pu≥ essere specificato contestualmente alla dichiarativa sia che si tratti di un valore fisso (literal) sia che si tratti di una espressione. Da notare che in Java, a differenza del C, tutte le variabili dichiarate hanno un valore predefinito. Questo riduce la possibilitα di errori dovuti a variabili non inizializzate. Tutti i numeri interi e reali sono inizializzati a zero, i caratteri al carattere nullo (\u0000), i booleani a false e i riferimenti a un qualunque oggetto a null. Inoltre, Φ possibile assegnare a una costante il proprio valore solamente nella dichiarazione della variabile. Una matrice si dichiara come una variabile, utilizzando una coppia di parentesi quadre per ogni dimensione vettoriale. Per cui la dichiarazione sarα

[accesso]

[static] [final] [transient] [volatile]

tipoMatr[][[]...] nomeMatr [= valoreIniziale];

In realtα le parentesi quadre possono essere posizionate tanto dopo il tipo quanto dopo il nome della matrice. Da notare inoltre che, al contrario del C, la dichiarazione non contiene il numero di elementi per ogni dimensione. Tale informazione dovrα essere fornita solo quando la matrice sarα effettivamente allocata. La dichiarazione di una classe segue invece il modello

[accesso] [abstract|final] class NomeClasse

[extends SuperClasse] [implements NomeInterfaccia...]

{

dichiarazioni delle variabili di classe

dichiarazioni dei metodi

}

dove NomeClasse Φ il nome della classe. L’unico modificatore di accesso valido per una classe Φ public. Se specificato, vuol dire che la classe pu≥ essere utilizzata da classi appartenenti ad altri pacchetti, altrimenti essa potrα essere utilizzata solo da altre classi dello stesso pacchetto. Una classe di tipo abstract non pu≥ essere "istanziata", non Φ possibile cioΦ creare oggetti di quella classe. Essa pu≥ essere utilizzata solo come superclasse da cui derivare altre classi. Una classe final Φ proprio l’opposto: essa non pu≥ essere "sottoclassata". Ovviamente i due modificatori si escludono a vicenda. Se non viene specificata una superclasse utilizzando la chiave extends, allora la classe in questione Φ una sottoclasse della classe Object. Se inoltre la classe implementa una o pi∙ interfacce, sarα utilizzata la parola chiave implements.

Un’interfaccia Φ simile a una classe ma non pu≥ essere istanziata. Le interfacce sono utilizzate come meccanismo di ereditarietα multipla. In pratica sono un po’ come una classe astratta, dato che contengono solo costanti e metodi non implementati. Non hanno cioΦ nΘ variabili, nΘ istruzioni all’interno dei metodi. La dichiarazione Φ la seguente:

[accesso] [abstract] interface NomeInterfaccia

[extends AltraInterfaccia...]

{

dichiarazioni delle costanti dell’interfaccia

dichiarazioni dei metodi (vuoti)

}

Anche in questo caso l’unica chiave di accesso ammessa Φ quella pubblica, mentre il modificatore abstract Φ permesso ma di fatto inutile, in quanto implicito nella definizione d’interfaccia. La dichiarazione dei metodi all’interno di una classe Φ, infine,

[accesso]

[abstract|final] [static] [native] [synchronized]

 

risultato NomeMetodo (argomenti...)

[throws eccezioni...]

{

dichiarazioni delle variabili di metodo

istruzioni

}

dove nomeMetodo Φ il nome del metodo e i modificatori di accesso sono gli stessi delle variabili, e cioΦ public, protected e private, mentre risultato Φ il tipo della variabile il cui valore Φ restituito dal metodo, se esiste. I metodi astratti non hanno un’implementazione, sono cioΦ vuoti, e possono essere definiti solo nelle classi astratte. Inoltre, non possono essere nΘ privati, nΘ statici o costanti. Un metodo finale non pu≥ essere ridefinito in una sottoclasse. I metodi nativi, invece, sono dipendenti dal sistema operativo e sono sviluppati con un linguaggio differente da Java, come il C o il C++. Del meccanismo di sincronizzazione tratteremo un’altra volta, cos∞ come delle eccezioni e della parola chiave throws. In quella sede parleremo anche di un altro costrutto, ovverosia try-catch.

(ddejudicibus@tecnet.it)

 

 

 

Tabelle
Tabella 1 - Istruzioni di controllo del flusso: scelte
Istruzione Descrizione
if (espressione)

istruzione

else

istruzione

Se l’espressione in parentesi Φ vera, viene eseguita la prima istruzione, altrimenti viene eseguita la seconda istruzione.
if (espressione)

istruzione

else if (espressione)

istruzione

else

istruzione

Se l’espressione in parentesi Φ vera, viene eseguita la prima istruzione, altrimenti viene valutata la seconda espressione. Se questa risulta vera viene eseguita la seconda istruzione, altrimenti viene eseguita la terza istruzione.
Switch

{

case espressione-costante:

istruzione

case espressione-costante:

istruzione

default:

istruzione

}

Le varie espressioni sono valutate dall’alto in basso, in sequenza. Se un’espressione Φ vera, viene eseguita l’istruzione corrispondente, altrimenti si passa all’espressione successiva. Le varie espressioni devono essere costanti e il flusso passa attraverso i vari blocchi a meno che non venga incontrata un’istruzione di salto incondizionato prima dell’espressione successiva (break).

 

Tabella 2 - Istruzioni di controllo del flusso: cicli e salti
Istruzione Descrizione
while (espressione)

istruzione

L’istruzione viene eseguita fintanto che l’espressione in parentesi Φ vera. L’istruzione pu≥ non venire mai eseguita se l’espressione Φ falsa fin dall’inizio.
Do

Istruzione

while (espressione);

L’istruzione viene eseguita fintanto che l’espressione in parentesi Φ vera. L’istruzione viene comunque eseguita almeno una volta.
for (espr1; espr2; espr3)

istruzione

Equivale al costrutto

espr1;

while (espr2)

{

istruzione

espr3;

}

break;

break etichetta;

Provoca l’uscita incondizionata da un ciclo (for, while, do-while) oppure dall’istruzione di scelta plurima switch. ╚ possibile specificare un’etichetta per identificare il ciclo nel caso di costrutti annidati.
continue;

continue etichetta;

Provoca il salto incondizionato al ciclo successivo nelle istruzioni for, while e do-while. ╚ possibile specificare un’etichetta per identificare il ciclo nel caso di costrutti annidati.

 

Tabella 3 - Tipi semplici, detti anche primitivi

 

Tipo Lunghezza Formato e valori
byte 8 bit Intero con segno da -128 a 127
short 16 bit Intero con segno da –32.768 a 32.767
int 32 bit Intero con segno da –2.147.483.648 a 2.147.483.647
long 64 bit Intero con segno da –9.223.372.036.854.775.808 a 9.223.372.036.854.775.807
float 32 bit Reali a singola precisione da 32 bit nel formato IEEE 754.

Limite positivo superiore: 3.4028234668528860e+38

Limite positivo inferiore: 1.40129846432481707e-45

double 64 bit Reali a doppia precisione da 64 bit nel formato IEEE 754.

Limite positivo superiore: 1.79769313486231570e+308

Limite positivo inferiore: 4.94065645841246544e-324

char 16 bit Carattere UNICODE da due byte
boolean n.s. True o false.

 

Tabella 4 - Esempi di dichiarazioni di variabili e matrici
Variabili
private MenuBar barra;

private boolean visibile = true;

protected final String NOME = "Dario";

double d = 6.723401;

public Agenda personale = null;

Matrici
public static int [ ] vettore = new int [100];

int x [ ], y [ ];

private float [ ] matrice [ ];

private Point estremi [ ] = linea.estremi();

 

Tabella 5 - Modificatori di accesso
Modificatore Descrizione
public L’elemento Φ accessibile da qualunque classe di qualsiasi pacchetto
protected L’elemento Φ accessibile da qualunque classe dello stesso pacchetto e da qualsiasi sua sottoclasse ovunque essa sia.
private L’elemento Φ accessibile solo dalla classe nella quale Φ definito
package Questo Φ l’accesso di default se non Φ specificato altrimenti. L’accesso Φ dato a tutte le classi nello stesso pacchetto, compresa ovviamente la classe in cui l’elemento Φ definito.
Accesso Classe Sottoclasse Pacchetto Qualunque
private U      
protected U U (1) U  
public U U U U
package U   U  
(1) L’accesso Φ ammesso solo su oggetti della sottoclasse, non della classe base.






Internet News Φ un mensile della Casa Editrice Tecniche Nuove S.p.A.
⌐ 1995/96/97. Tutti i diritti sono riservati.
Internet News non risponde di eventuali errori e omissioni.
Commenti a: inews@tecnet.it