capitolo 2 - scrivere ed eseguire un programma java · java è un linguaggio di programmazione...

18
2 Scrivere ed eseguire un programma Java Contenuto 2.1 Scrittura, compilazione ed esecuzione 2.2 La compilazione 2.3 L’esecuzione 2.4 I package e la direttiva import 2.5 Cosa può andare male? 2.6 Esercizi Nel capitolo precedente abbiamo spiegato che un algoritmo può essere codificato in un programma scritto in un certo linguaggio formale, e quindi può essere mandato in esecuzione su di un calcolatore. Vediamo ora concretamente come si può scrivere ed eseguire un primo semplice programma Java: questo ci permetterà di introdurre alcuni concetti elementari del linguaggio. 2.1 Scrittura, compilazione ed esecuzione Java è un linguaggio di programmazione orientato agli oggetti, progettato e realizzato agli inizi degli anni novanta da James Gosling e da un gruppo di sviluppatori della Sun Microsystems (acquisita nel 2010 da Oracle). Un programma Java contiene in generale una o più classi. Ogni classe, identificata da un nome, è una raccolta di proprietà e funzionalità correlate. Ciascuna funzionalità viene realizzata da un meto- do. Ogni metodo ha un nome e contiene una sequenza di istruzioni la cui esecuzione implementa la corrispondente funzionalità. Per i primi programmi che presenteremo, identificheremo il programma con una classe avente lo stesso nome e contenente un metodo di nome main. La funzionalità del programma sarà quella realizzata dal metodo main. Per esempio, il listato che segue mostra il programma Hello, il nostro primo programma Java che analizzeremo in dettaglio in questo capitolo. /* Un programma che chiede il tuo nome e ti saluta */ (C) 2011 Apogeo

Upload: votram

Post on 20-Sep-2018

226 views

Category:

Documents


0 download

TRANSCRIPT

ii

ii

ii

ii

2 Scrivere ed eseguireun programma Java

Contenuto2.1 Scrittura, compilazione ed esecuzione

2.2 La compilazione

2.3 L’esecuzione

2.4 I package e la direttiva import

2.5 Cosa può andare male?

2.6 Esercizi

Nel capitolo precedente abbiamo spiegato che un algoritmo può essere codificatoin un programma scritto in un certo linguaggio formale, e quindi può essere mandatoin esecuzione su di un calcolatore. Vediamo ora concretamente come si può scrivereed eseguire un primo semplice programma Java: questo ci permetterà di introdurrealcuni concetti elementari del linguaggio.

2.1 Scrittura, compilazione ed esecuzione

Java è un linguaggio di programmazione orientato agli oggetti, progettato e realizzatoagli inizi degli anni novanta da James Gosling e da un gruppo di sviluppatori dellaSun Microsystems (acquisita nel 2010 da Oracle). Un programma Java contiene ingenerale una o più classi. Ogni classe, identificata da un nome, è una raccolta diproprietà e funzionalità correlate. Ciascuna funzionalità viene realizzata da un meto-do. Ogni metodo ha un nome e contiene una sequenza di istruzioni la cui esecuzioneimplementa la corrispondente funzionalità.

Per i primi programmi che presenteremo, identificheremo il programma con unaclasse avente lo stesso nome e contenente un metodo di nome main. La funzionalitàdel programma sarà quella realizzata dal metodo main. Per esempio, il listato chesegue mostra il programma Hello, il nostro primo programma Java che analizzeremoin dettaglio in questo capitolo.

/*Un programma che chiede il tuo nome e ti saluta

*/

(C) 2011 Apogeo

ii

ii

ii

ii

14 Scrivere ed eseguire un programma Java

import jbook.util.Input;

public class Hello {public static void main(String [] args){

System.out.print("Come ti chiami? "); // stampaString persona;persona = Input.readString (); //leggeSystem.out.println("Ciao " + persona + '!'); // stampa

}}

Listato 2.1: Il programma Hello

Per scrivere il programma possiamo utilizzare un qualunque editor di testi dispo-nibile sul calcolatore. La classe dovrà essere contenuta in un file avente lo stessonome della classe, seguito dal suffisso .java; quindi il programma Hello deve esserescritto in un file chiamato Hello.java. Per eseguire il programma, come anticipatonella Sezione 1.1, abbiamo bisogno sul nostro calcolatore di un compilatore e di uninterprete per Java. Esistono molte versioni di questi programmi: noi useremo quel-li contenuti nel Java Standard Edition Development Kit (JDK) che si può scarica-re liberamente dal sito http://www.oracle.com/technetwork/java/javase/downloads/index.html

Prima di compilare il programma Hello, è necessario copiare nella directory in cuisi trova il file Hello.java la directory jbook e tutto il suo contenuto: questa directorypuò essere scaricata dal sito web del libro. Fatto questo, per compilare il programmaHello possiamo eseguire il comando

$ javac Hello.java

in un interprete di comandi, come una shell sotto Linux o un interprete DOS sottoWindows.1 Se il programma è stato scritto correttamente, la compilazione produceun file chiamato Hello.class che contiene la traduzione del programma Hello inbytecode, cioè in un linguaggio di programmazione di livello intermedio.

A questo punto possiamo mandare in esecuzione il programma con il comando$ java Hello

Il comando java lancia la Java Virtual Machine, cioè l’interprete che è in grado dieseguire i programmi scritti in bytecode. L’esecuzione del programma Hello provo-ca un’interazione con l’utente. Come prima cosa, il programma scrive sul terminaleCome ti chiami? e si mette in attesa di un input da parte dell’utente. Per prose-guire l’utente deve scrivere da tastiera una sequenza di caratteri, per esempio Maria,terminata da un ritorno a capo. A questo punto il programma completa l’esecuzionescrivendo Ciao Maria!. Rappresentiamo l’interazione come segue, dove evidenziamoin corsivo l’input da tastiera dell’utente:

1Con il carattere $ indichiamo genericamente il prompt dell’interprete di comandi, cioè i caratteri da essostampati per indicare che è pronto per leggere e interpretare il prossimo comando.

(C) 2011 Apogeo

ii

ii

ii

ii

2.1 Scrittura, compilazione ed esecuzione 15

� �Come ti chiami? MariaCiao Maria!� �

Dando uno sguardo al programma Hello è abbastanza semplice capire che le istru-zioni System.out.print(...) e System.out.println(...) permettono di scrivere del-le parole sullo schermo, mentre Input.readString() permette di leggere una parolada tastiera. Nel resto del capitolo descriveremo in dettaglio non solo il significatodelle linee di codice che costituiscono il programma, ma anche l’effetto dei comandijavac Hello.java e java Hello.

Gli ambienti integrati di sviluppo

Nel descrivere come si può compilare ed eseguire un programma Java, abbiamo as-sunto di avere a disposizione sul nostro calcolatore un ambiente di sviluppo minimale,cioè un editor di testi (per la scrittura dei programmi), un compilatore e un interpreteper Java, e naturalmente un interprete di comandi fornito dal sistema operativo checi consenta di lanciare l’esecuzione del compilatore e dell’interprete con i comandijava e javac, come descritto sopra.

In alternativa, per lo sviluppo di programmi Java si può usare un ambiente in-tegrato di sviluppo, chiamato più brevemente IDE, acronimo di Integrated Develo-pment Environment. Un IDE normalmente fornisce un editor guidato dalla sintassiin grado di individuare alcuni errori sintattici già durante la scrittura, e consente dicompilare ed eseguire un programma selezionando opportuni pulsanti (o comandi dimenu, o combinazioni di tasti) dell’interfaccia grafica. In alcuni IDE la compilazionedel programma avviene in maniera totalmente automatica, ogni volta che il sorgen-te viene modificato tramite l’editor. Oltre a integrare editor, compilatore e ambientedi esecuzione, molti IDE forniscono agevolazioni quali il completamento automaticodel codice, l’indentazione automatica, l’accesso alla documentazione del linguaggioe delle librerie, la navigazione ipertestuale dei problemi riscontrati, e l’integrazionedi potenti strumenti per il collaudo e il debugging. Queste caratteristiche facilitanonotevolmente la scrittura di programmi Java e grazie a numerose altre funzionalità di-sponibili nell’ambiente permettono di gestire in modo efficace lo sviluppo di progettiJava di grandi dimensioni.

La Figura 2.1 mostra l’aspetto di Eclipse, un IDE che si sta affermando co-me uno standard di fatto sia in ambito accademico che industriale, al termine del-la scrittura e dell’esecuzione del programma Hello: oltre al programma nella fi-nestra dell’editor, al centro, si può intravedere in basso l’output risultante dall’in-terazione con il programma. Eclipse può essere scaricato liberamente dalla URLhttp://www.eclipse.org/ .

(C) 2011 Apogeo

ii

ii

ii

ii

16 Scrivere ed eseguire un programma Java

Figura 2.1: L’ambiente integrato di sviluppo Eclipse

2.2 La compilazione

La traduzione di un programma Java nel corrispondente programma in bytecode ef-fettuata dal compilatore javac è un’operazione alquanto complessa. Durante la gene-razione dei bytecode il compilatore analizza il programma sorgente effettuando unaserie di controlli. Alcuni di questi controlli servono per verificare la correttezza sin-tattica, cioè che il programma sia effettivamente una sequenza di simboli generatadalla grammatica di Java. Altri controlli sono relativi alla cosiddetta semantica stati-ca, e servono a verificare che il programma rispetti alcune regole del linguaggio chepotrebbero essere violate anche da un programma sintatticamente corretto.

Per esempio, con l’analisi di semantica statica si controllano regole come ognivariabile deve essere dichiarata prima di essere usata, oppure il tipo dell’espressio-ne a destra dell’operatore di assegnamento deve essere compatibile con il tipo dellavariabile alla sua sinistra. Se tutte le regole rilevanti sono soddisfatte, il compilatorecompleta la traduzione del programma generando il codice intermedio in bytecode.Altrimenti la traduzione viene interrotta e il compilatore scrive un messaggio descri-vendo gli errori individuati. Le regole di semantica statica saranno discusse ampia-mente nei prossimi capitoli, man mano che introdurremo i costrutti del linguaggio aiquali esse si applicano.

(C) 2011 Apogeo

ii

ii

ii

ii

2.2 La compilazione 17

2.2.1 L’analisi lessicale e l’analisi sintattica

Come accennato sopra, l’analisi sintattica controlla che il programma sia effettiva-mente generabile dalla grammatica del linguaggio Java. In realtà la “grammatica” diJava, come quelle di tutti i linguaggi ad alto livello, è formata da due grammatichedistinte: la grammatica lessicale definisce i token o elementi lessicali del linguag-gio, mentre la grammatica sintattica definisce i costrutti veri e propri del linguaggiousando i token come simboli terminali. Di conseguenza l’analisi sintattica è costi-tuita da due fasi principali: l’analisi lessicale, che legge la sequenza di caratteri checostituisce il programma e la trasforma in una sequenza di token, e l’analisi sintatti-ca propriamente detta che analizza la sequenza di token risultante e controlla che siacorretta, cioè che sia generata dalla grammatica sintattica.

Lo studio degli algoritmi che il compilatore utilizza per verificare che un program-ma sia sintatticamente corretto è un argomento che va al di là degli obiettivi di questolibro. Presenteremo però in modo sistematico le due grammatiche che definisconoJava: in questo modo il lettore non solo potrà verificare che gli esempi di program-mi che proponiamo siano sintatticamente corretti, ma avrà anche a disposizione unostrumento per poter generare esempi originali dei vari costrutti del linguaggio.

Le produzioni delle grammatiche di Java presentate, talvolta in modo incre-mentale, nel corso dei prossimi capitoli sono raccolte nella forma più generalenell’Appendice A.

2.2.2 I commenti e la formattazione

Durante l’analisi lessicale il compilatore legge la sequenza di caratteri che costitui-sce il programma e la trasforma in una sequenza di token. Durante questo procedi-mento, il compilatore ignora i commenti, che sono frammenti di codice che hannoprincipalmente la funzione di documentare il programma per gli esseri umani.

Un commento si può estendere su più linee, compreso tra i delimitatori /* e */come nelle prime linee del file Hello.java:

/*Un programma che chiede il tuo nome e ti saluta

*/

Oppure si estende dal delimitatore // fino alla fine della linea in cui esso compare,come nella parte finale della seguente linea del programma:

System.out.println("Ciao " + persona + '!'); // stampa

Quando scriviamo un programma, per facilitarne la lettura possiamo inserire a piace-re commenti, spazi e caratteri di tabulazione, e possiamo spezzare e indentare le lineea piacimento (tranne che all’interno delle costanti letterali di tipo String, introdottenella Sezione 4.5.2). Per capire l’importanza di una buona formattazione, si confronti

(C) 2011 Apogeo

ii

ii

ii

ii

18 Scrivere ed eseguire un programma Java

il seguente programma con il Listato 2.1: per il compilatore essi sono del tutto equi-valenti, poiché differiscono solo per commenti e caratteri non significativi, ma per unprogrammatore hanno un grado di leggibilità drammaticamente diverso.

import jbook.util.Input; public class Hello {public staticvoid main(String [] args){ System.out.print("Come ti chiami? ");String persona;persona=Input.readString (); System.out.println("Ciao "+persona+'!');}}

Nei programmi che proporremo verrà usato uno stile standard di formattazione eindentazione del codice, seguendo le convenzioni reperibili alla URL http://www.oracle.com/technetwork/java/codeconv-138413.html :2 invitiamo ancheil lettore a seguire questo stile.

2.2.3 I token

Una volta eliminati dal programma i commenti e gli altri caratteri non rilevanti, l’a-nalisi lessicale procede con l’esame della sequenza di caratteri risultante, e individuaal suo interno i token. Ci sono cinque categorie sintattiche di token in Java, comedecritto dalla seguente produzione della grammatica lessicale:

Token ::= ParolaChiave | Separatore | Operatore |CostanteLetterale | Id

Le parole chiave sono parole riservate: hanno un significato predefinito nel linguag-gio e non possono essere usate in altro modo. Esse sono elencate nella seguenteproduzione:3

ParolaChiave ::=abstract | assert | boolean | break | byte |case | catch | char | class | const |continue | default | double | do | else |enum | extends | finally | final | float |for | goto | if | implements | import |instanceof | interface | int | long | native |new | package | private | protected | public |return | short | static | strictfp | super |switch | synchronized | this | throws | throw |transient | try | void | volatile | while

I separatori e gli operatori del linguaggio sono sequenze di uno o più caratterigenerate dalle seguenti produzioni:

2Ci concederemo piccole eccezioni per chiarezza espositiva o per presentare gli esempi in modo più compatto.3Come curiosità, const e goto non sono usate in Java, anche se sono riservate.

(C) 2011 Apogeo

ii

ii

ii

ii

2.2 La compilazione 19

Separatore ::= ( | ) | [ | ] | { | } | ; | , | .Operatore ::== | > | < | ! | ˜ | ? | : | == | <= |>= | != | && | || | ++ | -- | + | - | * |/ | & | | | ˆ | % | << | >> | >>> | += |-= | *= | /= | &= | |= | ˆ= | %= | <<= | >>= |>>>=

Spiegheremo il significato dei vari operatori nel Capitolo 5. Le costanti letterali sonosequenze di caratteri che rappresentano valori di un certo tipo di dati.

CostanteLetterale ::= LettIntero | LettVirgolaMobile | LettCarattere |LettStringa | true | false | null

Descriveremo in dettaglio la sintassi delle costanti letterali nel Capitolo 4, quando in-trodurremo i tipi di dati corrispondenti. Solo a titolo di esempio, sono costanti letteralicorrette gli interi 345 e -12345678, i numeri in virgola mobile 34.56 e 3456e3, i caratteri'c', '\n' e '\u0041', e la stringa (cioè una sequenza di caratteri) "Come ti chiami? ".Si noti che oltre alle parole chiave, le uniche altre parole riservate di Java sono lecostanti letterali booleane true e false, e la costante letterale null.

L’ultima categoria di token sono gli identificatori (non-terminale Id). Un identi-ficatore è una sequenza di lunghezza arbitraria composta da lettere, cifre (i caratterida 0 a 9) e i caratteri $ e _ (underscore), che non inizi con una cifra e che sia di-versa da una parola riservata. Java è un linguaggio case sensitive, ovvero distinguetra maiuscole e minuscole: ciò vuol dire, per esempio, che gli identificatori pippo ePippo sono considerati diversi, e che True (diverso dalla costante letterale true) è unidentificatore ammissibile.

Anche se qualunque sequenza di caratteri che rispetti le regole appena elencate èriconosciuta come un identificatore dal compilatore, in pratica esistono varie conven-zioni nella scelta degli identificatori che i programmatori sono tenuti a seguire. Peresempio, il carattere $ andrebbe usato solo in programmi generati automaticamente,mentre _ andrebbe usato solo in nomi di costanti composti da più parole (vedi Sezio-ne 3.5). Vedremo altre convenzioni che riguardano l’uso di maiuscole e minuscolequando introdurremo le variabili, le costanti, i metodi e le classi.

Le lettere che possono comparire in un identificatore comprendono non solo lelettere maiuscole e minuscole dell’alfabeto inglese, ma anche le lettere di numerosialtri alfabeti di tutto il mondo codificati nello standard UNICODE. Comunque, pergarantire una migliore portabilità dei sorgenti dei nostri programmi, noi useremo sololettere dell’alfabeto inglese evitando le lettere accentate del nostro alfabeto: questoperché il supporto fornito dagli editor di testo a queste lettere non sempre è affidabile.

(C) 2011 Apogeo

ii

ii

ii

ii

20 Scrivere ed eseguire un programma Java

2.2.4 L’analisi sintattica

Per favorire la leggibilità, nel seguito useremo per i listati dei programmi alcune sem-plici convenzioni tipografiche. Per esempio, il programma Hello del Listato 2.1 saràvisualizzato così:

1 /*2 Un programma che chiede il tuo nome e ti saluta3 */4

5 import jbook.util.Input;6

7 p u b l i c c l a s s Hello {8 p u b l i c s t a t i c v o i d main(String [] args){9 System.out.print("Come ti chiami? "); // stampa

10 String persona;11 persona = Input.readString (); //legge12 System.out.println("Ciao " + persona + '!'); // stampa13 }14 }

Quindi le linee di codice sono numerate a sinistra, le parole riservate di Java sono ingrassetto, mentre i commenti sono resi in corsivo.

Analizziamo in dettaglio la struttura sintattica del programma. Procederemo inmodo informale, introducendo la terminologia e i concetti necessari per descrivere icostrutti del linguaggio che incontreremo: la presentazione completa di tali costrutti,con le relative produzioni della grammatica, verrà fatta nei capitoli successivi.

La prima linea significativa del programma è la direttiva di importazione

5 import jbook.util.Input;

Questa direttiva comunica al compilatore l’intenzione di usare nel programma laclasse Input del package jbook.util, come in effetti avviene nella linea 11. Appro-fondiremo il ruolo dei package, delle clausole di importazione e della classe Inputnelle Sezioni 2.4 e 3.4.1.

Le linee 7–14 contengono la dichiarazione della classe Hello. La sua intestazione,costituita dalla settima linea, comprende la parola chiave class seguita da un identi-ficatore, il nome della classe, e preceduta dal modificatore di visibilità public di cuiparleremo nel Capitolo 8. Il nome della classe è seguito da una parentesi graffa cheviene chiusa nella linea 14: queste parentesi delimitano il corpo della classe.

Il corpo della classe Hello contiene un solo membro: la dichiarazione del metododi nome main, che ha una struttura simile a quella della classe: l’intestazione delmetodo nella linea 8 è seguita dal corpo del metodo nelle linee 9–12, delimitato dalleparentesi graffe nelle linee 8 e 13. Nell’intestazione, il nome del metodo è precedutoda alcune parole chiave, ed è seguito dalla lista dei parametri formali racchiusa traparentesi: illustreremo tutto questo nel Capitolo 8.

Il (corpo del) metodo main contiene tre istruzioni o comandi e una dichiarazione:

(C) 2011 Apogeo

ii

ii

ii

ii

2.2 La compilazione 21

9 System.out.print("Come ti chiami? "); // stampa10 String persona;11 persona = Input.readString (); //legge12 System.out.println("Ciao " + persona + '!'); // stampa

Le istruzioni delle linee 9 e 12 sono due invocazioni o chiamate di metodi: ven-gono chiamati i metodi print e println dell’oggetto System.out, passando comeargomenti delle stringhe, e precisamente la costante letterale "Come ti chiami? " el’espressione "Ciao "+ persona + '!'. La linea 10 è una dichiarazione di variabile:viene dichiarata una variabile chiamata persona, il cui tipo è la classe String. Infinela linea 11 contiene un comando di assegnamento, costituito dalla variabile personaseguita dall’operatore di assegnamento =, seguito a sua volta dell’invocazione delmetodo readString() della classe Input.

La dot notation

Nel listato del programma Hello ci sono diverse occorrenze del separatore punto,cioè del carattere “.”, con significati diversi. La dot notation, ovvero l’uso del punto(dot in inglese) come “selettore”, è tipica dei linguaggi di programmazione e deiformalismi orientati agli oggetti mentre è usata meno spesso in altri paradigmi: essanecessita quindi di una breve spiegazione. In generale, la notazione

nome1.nome2

dove sia nome1 che nome2 sono identificatori, rappresenta la “proprietà” o “entità”nome2 nel contesto del significato di nome1. Il significato preciso dipende da cosarappresentano gli identificatori.

Per esempio, Input.readString() denota il metodo readString() contenuto nel-la classe Input. Analogamente, System.out denota la variabile out contenuta nellaclasse System. Infine jbook.util denota il sottopackage util contenuto nel packagejbook (si veda la Sezione 2.4).

La dot notation può essere iterata. Per esempio, System.out.println() denotail metodo println() dell’oggetto denotato da System.out, mentre jbook.util.Inputdenota la classe Input del package jbook.util.

Nel descrivere alcuni metodi offerti dalle API di Java tenderemo a specificare se sitratta di metodi statici o di metodi d’istanza. La differenza tra i due concetti sarà chia-rita nei Capitoli 7 e 10, quando i temi della programmazione orientata a oggetti saran-no approfonditi. Per il momento ci basti sapere che i metodi statici vengono invocatiusando come prefisso un nome di classe (per esempio jbook.util.Input.readInt()invoca il metodo statico readInt() della classe jbook.util.Input) mentre quellid’istanza seguono un oggetto che di fatto costituisce un parametro implicito delmetodo (System.out.println() invoca il metodo d’istanza println() sull’oggettoSystem.out).

(C) 2011 Apogeo

ii

ii

ii

ii

22 Scrivere ed eseguire un programma Java

La notazione per i metodi

Java permette di definire e usare più varianti di metodi che hanno lo stesso nomema che differiscono per il numero e/o tipo dei parametri che ricevono. Questa ca-ratteristica, chiamata overloading di simboli, è molto importante e verrà discussa piùapprofonditamente nel Capitolo 8. Come verrà spiegato nella Sezione 8.3, la firmadi un metodo consiste del nome del metodo e della lista dei tipi dei suoi argomen-ti. Quindi l’overloading permette la coesistenza di metodi con lo stesso nome mafirma diversa. A livello di notazione, nel riferire un metodo tramite il nome faremouso delle seguenti convenzioni sintattiche: (1) quando intendiamo riferire un parti-colare metodo indicheremo la firma completa (per esempio readInt(String)); (2)quando è utile riferire esplicitamente nel testo un particolare argomento del metodoinseriremo anche un identificatore per ciascun parametro (per esempio il nome msgin readInt(String msg)); (3) quando intendiamo riferire l’intera famiglia di metodi,indipendentemente dal numero e tipo dei parametri useremo semplicemente il nomedel metodo seguito dalla coppia di parentesi tonde, come nel caso del metodo senzaparametri (per esempio readInt()).

2.3 L’esecuzione

Lanciando il comando java Hello viene mandata in esecuzione la JVM che interpre-ta i bytecode del file Hello.class generato dal compilatore. Lo studio del formatoe del significato dei bytecode esula dagli obiettivi di questo libro, e quindi seguire-mo la prassi comune di descrivere l’esecuzione del programma facendo riferimentodirettamente al codice sorgente della classe Hello.java.

L’interprete java inizia l’esecuzione dal metodo main della classe passata comeargomento. Se tale classe non avesse un metodo main (con intestazione analoga aquella della linea 8 di Hello.java) verrebbe segnalato un errore. L’esecuzione delmetodo consiste, nel caso in esame, nell’esecuzione sequenziale delle linee 9–12. Ilprimo comando,

9 System.out.print("Come ti chiami? "); // stampa

invoca una funzionalità fornita dalle librerie di Java, e ha l’effetto di scrivere in unaopportuna finestra dello schermo la stringa� �

Come ti chiami?� �Più precisamente, System.out (la variabile out contenuta nella classe System) rap-

presenta lo standard output, che normalmente è una finestra sullo schermo del calco-latore. L’esecuzione del metodo print() invocato su di esso ha l’effetto di scriveresullo standard output la stringa passata per argomento, cioè racchiusa tra parentesidopo il nome del metodo.

(C) 2011 Apogeo

ii

ii

ii

ii

2.4 I package e la direttiva import 23

La dichiarazione di variabile della linea 10 e il successivo comando diassegnamento

10 String persona;11 persona = Input.readString (); //legge

hanno il seguente effetto. Dapprima viene riservata una zona di memoria cui viene as-sociato il nome persona, di dimensione adatta a contenere un riferimento a una stringa(un oggetto di tipo String). Successivamente viene invocato il metodo readString()della classe Input che ha l’effetto di leggere una stringa terminata da un ritorno a ca-po dallo standard input, che tipicamente vuol dire dalla tastiera, e di restituirla comerisultato; infine viene messo un riferimento a questa stringa nella zona di memoriaassociata al nome persona. Per concludere, il comando

12 System.out.println("Ciao " + persona + '!'); // stampa

per prima cosa valuta l’espressione "Ciao "+ persona + '!'. In Java l’operatore +applicato a due stringhe ne restituisce la concatenazione, cioè una stringa costituitadai caratteri della prima seguiti dai caratteri della seconda. Pertanto in questo caso ilrisultato è la stringa ottenuta concatenando le stringhe "Ciao ", quella memorizzatanella variabile persona e il singolo carattere '!'. Infine viene stampata questa stringasullo standard output passandola come argomento al metodo System.out.println().Quindi se l’utente ha scritto da tastiera Maria seguita da invio, l’interazione completacon il programma risulta essere la seguente:� �

Come ti chiami? MariaCiao Maria!� �

2.4 I package e la direttiva import

Anche i più semplici programmi Java hanno bisogno, per essere compilati ed eseguiti,di altre classi, oltre naturalmente a quella che contiene il metodo main. Per esempio,il programma Hello usa direttamente le classi System, String e Input. Durante lacompilazione di una classe, quando il compilatore incontra il nome di un’altra classeesso controlla che la classe esista, e se non è già compilata la compila a sua volta.

Le classi di Java sono organizzate in package, i quali formano uno spazio di nomigerarchico: un package può contenere classi e/o altri package. Per semplicità si puòpensare ai package come alle cartelle di un file system, ma questa analogia è soloconcettuale: concretamente i package e le classi in essi contenute potrebbero ancheessere memorizzati in un database oppure in un singolo file (come un archivio .jar).

Per le regole sintattiche del linguaggio il nome di un package può essere un qua-lunque identificatore ammissibile (si veda la Sezione 2.2.3), ma normalmente si usa-no solo lettere minuscole. Il nome completo di un package è formato dalla sequenza

(C) 2011 Apogeo

ii

ii

ii

ii

24 Scrivere ed eseguire un programma Java

dei nomi dei package che lo contengono, separati dal carattere '.'. Per esempio,java.util.concurrent è il nome completo del package concurrent, sottopackage delpackage util, a sua volta contenuto nel package java.

La distribuzione di Java comprende centinaia di classi organizzate in decine dipackage. Queste classi forniscono un ricco insieme di librerie e di tipi di dati chepossono essere usati liberamente da ogni programmatore per rendere i propri pro-grammi più compatti ed efficienti: non c’è bisogno di riprogrammare funzionalitàgià offerte nelle classi della distribuzione, e comunque sarebbe difficile realizzarle inmodo più efficiente.

Ogni classe Java appartiene a un package, che deve essere dichiarato con una op-portuna direttiva prima della dichiarazione della classe, e prima di eventuali direttivedi importazione. Per esempio, il file Input.java del package jbook.util inizia nelseguente modo:

1 package jbook.util;2

3 import java.io.BufferedReader;4 import java.io.InputStreamReader;5 import java.io.IOException;6 import java.util.Vector;7

8 /**9 Una semplice classe per leggere stringhe e numeri

10 dallo standard input.11 */12 p u b l i c c l a s s Input{

La direttiva package jbook.util; dichiara appunto che la classe appartiene al packagejbook.util. Questa direttiva può mancare, come nel caso della nostra classe Hello:in tal caso la classe è considerata appartenere a un package senza nome (unnamed).

Le classi contenute in uno stesso package devono avere nomi diversi, ma si pos-sono avere classi con lo stesso nome in package diversi. Un programma può farriferimento a un’altra classe usando il suo nome semplice solo se essa si trova nel suostesso package oppure nel package java.lang, le cui classi sono parte integrante delladefinizione del linguaggio Java (come String e System, usate nell’esempio). Altri-menti occorre usare il nome completo della classe, che comprende anche il packageche la contiene, oppure si può usare una direttiva di importazione.

Nel caso del programma Hello, la presenza della direttiva di linea 5 permette diinvocare il metodo readString() della classe jbook.util.Input semplicemente comeInput.readString() in linea 11. Alternativamente si potrebbe eliminare la direttiva,a patto di sostituire la chiamata del metodo con jbook.util.Input.readString().

(C) 2011 Apogeo

ii

ii

ii

ii

2.5 Cosa può andare male? 25

2.5 Cosa può andare male?

Quando si scrive un programma, è del tutto normale commettere degli errori, che pos-sono essere di varia natura e gravità. Gli errori sintattici e quelli di semantica staticasono rilevati dal compilatore: se il programma non è generabile dalla grammaticadi Java oppure non rispetta tutte le regole di semantica statica, allora la compilazio-ne fallisce, il bytecode non viene generato, e il compilatore stampa una sequenza dimessaggi indicando gli errori individuati.

È estremamente importante imparare a leggere i messaggi di errore del compilato-re, per individuare e correggere rapidamente gli errori. Per questo motivo quasi tuttii capitoli di questo libro contengono una sezione come la presente, in cui vengonopresentati alcuni messaggi di errore tipici, rilevanti per i concetti introdotti nel capi-tolo stesso. Questi messaggi, anche se differiscono a seconda del compilatore che siusa, sono in genere molto precisi, e indicano sia la natura dell’errore che il punto delprogramma dove esso si è verificato.

Noi descriveremo alcuni messaggi di errore prodotti dal compilatore della distri-buzione JDK 7.0 di Java. Data la complessità del linguaggio e del compilatore, sa-rebbe impossibile presentare tutte le possibili tipologie di messaggi di errore: ci li-miteremo quindi a considerare quelle che, nella nostra esperienza, un programmatoreincontra più frequentemente.

Nome di file errato. Supponiamo di aver salvato il programma Hello in un filechiamato Ciao.java. La compilazione del file produce il seguente output:� �

$ javac Ciao.javaCiao.java :7: error: class Hello is public , should be

declared in a file named Hello.javapublic class Hello {

^1 error� �

Il messaggio di errore ci ricorda che la classe Hello è pubblica, e deve essere di-chiarata in un file chiamato Hello.java. Questa regola vale per tutte le classi pubbli-che, cioè quelle la cui dichiarazione comincia con la parola chiave public: parleremodi questo nel Capitolo 10.

Errata dichiarazione del metodo main. Nella Sezione 2.3 abbiamo accennato alfatto che l’interprete inizia l’esecuzione con il metodo main della classe passataglicome argomento, e che questo metodo deve avere l’intestazione simile a quella dellalinea 8 del programma Hello, cioè:

8 p u b l i c s t a t i c v o i d main(String [] args){

(C) 2011 Apogeo

ii

ii

ii

ii

26 Scrivere ed eseguire un programma Java

Senza anticipare il significato delle varie parti dell’intestazione che saranno di-scusse successivamente, da un punto di vista puramente sintattico l’intestazione delmetodo main deve soddisfare le seguenti condizioni:

• main deve essere preceduto da void;

• void deve essere preceduto da public e da static, in qualunque ordine;

• il contenuto delle parentesi dopo main deve cominciare con String, seguito daun coppia di parentesi quadre ([]) o da tre punti (...), e infine un qualunqueidentificatore. L’identificatore può anche precedere le parentesi quadre.

Se una classe sintatticamente corretta non contiene un metodo main che soddisfitutte queste condizioni, quando si lancia l’esecuzione l’interprete segnalerà un errore:� �

$ java ProvaError: Main method not found in class Prova , please

define the main method as:public static void main(String [] args)� �

Omissione di separatori. Supponiamo ora che scrivendo il programma Hello siastato omesso il separatore ; alla fine della direttiva import di linea 5:

1 /*2 Un programma che chiede il tuo nome e ti saluta3 */4

5 import jbook.util.Input6

7 p u b l i c c l a s s Hello {

In questo caso la compilazione del programma Hello produce il seguente output:� �Hello.java :5: error: ';' expectedimport jbook.util.Input

^1 error� �

La prima linea del messaggio di errore comunica all’utente in quale linea di codiceè stato individuato l’errore (Hello.java:5, cioè la quinta linea del file Hello.java),nonché la tipologia dell’errore: ';'expected, cioè ci si aspettava il carattere ';'. Ledue linee successive indicano precisamente dove, all’interno della linea incriminata, èstato individuato l’errore: prima viene riportata integralmente la linea Hello.java:5,e poi l’unico carattere della linea successiva, il caret ^, si trova esattamente sotto laposizione dove dovrebbe trovarsi il ; . Naturalmente per correggere l’errore bastamettere un ; dove richiesto.

(C) 2011 Apogeo

ii

ii

ii

ii

2.5 Cosa può andare male? 27

Errori di battitura in parole chiave o identificatori. Supponiamo di aver scrit-to Public invece di public in linea 7, nell’intestazione della classe. L’output delcompilatore sarà� �

Hello.java :7: error: class , interface , or enum expectedPublic class Hello {^1 error� �

confermando il fatto che Java è case sensitive: nella posizione indicata è illegalela presenza del token Public, che viene interpretato come un identificatore, mentresarebbe stata legittima un’occorrenza dalla parola chiave public.

Un altro errore sintattico molto frequente consiste nello scrivere in modo erratoun identificatore, che può essere il nome di una classe, di una variabile, di un metodo,eccetera. In questo caso, il compilatore non trova una definizione associata all’iden-tificatore. Per esempio, il seguente messaggio di errore viene ottenuto compilando ilprogramma Hello dopo aver cambiato readString() in redString():� �

Hello.java :11: error: cannot find symbolpersona = Input.redString (); //legge

^symbol: method redString ()location: class Input

1 error� �Nella prima linea del messaggio di errore il compilatore segnala la linea di codice

rilevante e il tipo di errore: cannot find symbol, non trovo il simbolo. Nelle duelinee successive spiega qual è il simbolo che non trova, cioè il metodo redString(), edove dovrebbe trovarsi il metodo cercato, cioè nella classe jbook.util.Input. Infineil consueto ^ indica la posizione dove è stato trovato il simbolo non riconosciuto.

Errori multipli. In base a quanto abbiamo visto nella Sezione 2.2, possiamo divi-dere concettualmente il lavoro del compilatore in tre fasi: l’analisi sintattica, l’analisidi semantica statica e la generazione del bytecode.

In generale la compilazione non si ferma appena trova un errore, ma procede finoalla fine della fase corrente cercando di individuare eventuali altri errori, e stampandoalla fine un messaggio per ognuno degli errori individuati. Ne segue che una fase dicompilazione non viene iniziata se vi sono degli errori nella fase precedente.

Vediamo un esempio. Supponiamo di aver modificato Hello.java in modo che lelinee 10 e 11 siano:

10 String persona11 person = Input.readString () //legge

(C) 2011 Apogeo

ii

ii

ii

ii

28 Scrivere ed eseguire un programma Java

Manca il separatore ; alla fine delle linee 10 e 11, due errori sintattici, e l’identi-ficatore person in linea 11 non è dichiarato, perché è diverso dalla variabile personadella linea 10, un errore di semantica statica. Compilando otteniamo:� �

Hello.java :10: error: ';' expectedString persona

^Hello.java :11: error: ';' expected

person = Input.readString () //legge^

2 errors� �Quindi vengono riportati solo i due errori sintattici. Se li correggiamo entram-bi e ricompiliamo, otteniamo come previsto la segnalazione che person non èriconosciuto:� �

Hello.java :11: error: cannot find symbolperson = Input.readString (); //legge^

symbol: variable personlocation: class Hello

1 error� �Nel caso il compilatore segnali più errori, una buona prassi consiste nel cercare di

individuare e correggere gli errori nell’ordine in cui vengono segnalati, ricompilandofrequentemente il programma. Infatti alcuni errori segnalati potrebbero essere solouna conseguenza di errori precedenti.

Purtroppo non sempre i messaggi di errore del compilatore risultano chiari e in-formativi come quelli visti sopra. Per esempio, se nel solito programma Hello dimen-tichiamo la parentesi graffa aperta alla fine dell’intestazione del metodo main in linea8, l’output della compilazione segnalerà ben 10 errori tra cui i seguenti:� �

Hello.java :8: error: ';' expectedpublic static void main(String [] args)

^Hello.java :11: error: <identifier > expected

persona = Input.readString (); //legge^

Hello.java :12: error: <identifier > expectedSystem.out.println("Ciao " + persona + '!'); // stampa

^...Hello.java :14: error: class , interface , or enum expected}^10 errors� �

(C) 2011 Apogeo

ii

ii

ii

ii

2.6 Esercizi 29

In questo caso solo il primo messaggio di errore è significativo, ma solo in parte. Ilprogrammatore dovrà riconoscere che alla fine della linea 8 manca in realtà un {, enon un ; come indicato: correggendo e ricompilando si vedrà che anche gli altri errorispariscono.

2.6 Esercizi

Esercizio 2.1 Dire quali delle seguenti affermazioni sono vere e quali false:

– javac è un compilatore di programmi – javac è un editor di programmi– javac è un interprete di programmi – java è un sistema operativo– java è un IDE – java è un interprete di programmi– eclipse è un IDE – eclipse è un sistema operativo

Esercizio 2.2 Si scriva, compili ed esegua la classe Hello presentata nel testo, e sene verifichi il funzionamento come atteso.

Esercizio 2.3 Si modifichi la classe Hello presentata nel testo in modo che essa chie-da all’utente, separatamente, nome e cognome, e saluti con un più formaleBuongiorno seguito da nome e cognome. Si compili ed esegua poi il programmaper verificarne il funzionamento.

Esercizio 2.4 Si identifichino nel seguente frammento di codice Java i vari to-ken, distinguendoli in parole chiave, separatori, operatori, costanti letterali eidentificatori.

public static void uno() { if (x == 1)System.out.println("x vale 1");

else { x--; flag = true; } return ;}

Esercizio 2.5 Si riscriva il codice dell’esercizio 2.4 con la formattazione adeguata,seguendo lo stile degli altri esempi presentati nel testo.

Esercizio 2.6 Si identifichino i vari token (distinti in parole chiave, separatori,operatori, costanti letterali, identificatori) nel sorgente della classe Hello:

/*Un programma che chiede il tuo nome e ti saluta

*/

import jbook.util.Input;

public class Hello {public static void main(String [] args){

System.out.print("Come ti chiami? "); // stampa

(C) 2011 Apogeo

ii

ii

ii

ii

30 Scrivere ed eseguire un programma Java

String persona;persona = Input.readString (); //leggeSystem.out.println("Ciao " + persona + '!'); // stampa

}}

Esercizio 2.7 Per ciascuna delle seguenti sequenze di caratteri, si dica se si tratta diun identificatore legale in Java oppure no, giustificando la risposta:

pippo Pippo PIPPO pIppO

"pippo" 'pippo' _pippo_ <pippo>

paolo3 p1n0 3ug3n10 B0

int program Else null

Pino&Laura Pino e Laura Pino_e_Laura Pino_<3_Laura

Carlo_Motta CarloMotta Carlo.Motta Carlo-Motta

$HOME $_$_$_$ dubbio...atroce pagina_____6

Esercizio 2.8* 4 Si identifichino, nel codice seguente della classe Errori.java, tut-ti gli errori di sintassi. Si provi dapprima a farlo su carta, senza l’aiuto delcompilatore, e si verifichi poi il risultato provando a compilare il programma.

import java.util.Input:*/ un programma che contiene

molti errori /*public class Errori {

pubblic static void Main(String {} args) {String a;a = Input ,readString (); / leggeSting b = Imput.readString ();system.out.println(a + b) // stampareturn;

}

Esercizio 2.9* L’ordine delle istruzioni è importante. Quali permutazioni delle linee9-12 che compongono il programma Hello sarebbero accettate dal compilatore?Quali avrebbero senso?

4Gli esercizi marcati con un asterisco sono di maggiore difficoltà rispetto ad altri; è raccomandabile affrontarlidopo aver acquisito sufficiente familiarità con l’oggetto del capitolo.

(C) 2011 Apogeo