+ All Categories
Home > Documents > Corso_java.pdf

Corso_java.pdf

Date post: 29-Nov-2015
Category:
Upload: dandy75
View: 49 times
Download: 5 times
Share this document with a friend
Description:
un'introduzione.
43
Java 1 a lezione A partire da questo numero vi proponiamo un corso di programmazione per prin- cipianti assoluti nel quale si farà uso del linguaggio Java per tutti gli esempi e gli approfon- dimenti. La ragione della scelta di ta- le linguaggio va ricercata nelle sue peculiarità, che lo rendono idoneo per lo scopo e che ri- sulteranno evidenti via via du- rante il corso. Per il momento ci limitere- mo a citare il suo amplissimo campo d’impiego, l’estrema fa- cilità di reperire documenta- zione ed esempi di buon livello, il costo nullo, la possibilità di eseguire uno stesso program- ma su una vasta gamma di di- spositivi. Nel corso non si darà per scontata alcuna conoscenza specifica in tema di program- mazione, mentre si assumerà una normale dimestichezza nell’utilizzo generico del PC e in particolare di Windows e delle sue applicazioni. Il corso è organizzato per te- mi principali che saranno af- frontati in sequenza, mante- nendo un livello di comples- sità omogeneo, negli articoli mensili. Nel primo articolo introdur- remo i concetti di base e la ter- minologia da conoscere e trat- teremo un primo semplice ca- so pratico seguendo un meto- do generale, arrivando a un programma Java funzionante sul vostro sistema. Chi ha già A scuola con PC Open Programmare Java è per tutti 1 Programmare: le basi Inizia da questo mese un nuovo corso dedicato alla programmazione, rivolto ai neofiti. La scelta è caduta su Java, linguaggio molto versatile e diffuso e la cui documentazione è facile da reperire Lezione 1 Rudimenti di programmazione - Terminologia essenziale - Dall’algoritmo al programma - Preparazione dell’ambiente Java - Il primo programma Lezione 2 Elementi di programmazione a oggetti e grafica - Programmazione a oggetti - Creare una GUI - Programmazione a eventi - Gestione di immagini Lezione 3 File - Salvare i dati - Elaborare i dati Lezione 4 Networking - Programmi client-server - Interazione con risorse Web IL CALENDARIO DELLE LEZIONI di Mar co Mussini una certa esperienza in questo campo può saltare le sezioni che affrontano argomenti ele- mentari; tuttavia noi ne consi- gliamo ugualmente una rapida lettura, magari per far emerge- re eventuali convinzioni impre- cise. Esecutori e algoritmi Ogniqualvolta si intende far compiere un determinato com- pito a un esecutore è necessa- rio descriverglielo in una forma a lui comprensibile. È quindi fondamentale, nel farlo, immedesimarsi nell’ese- cutore e nel suo modo di pen- sare e di agire. Nel caso particolare in cui l’esecutore sia umano, per la descrizione del compito si può usare il linguaggio naturale, a schemi e a gesti, e fare affida- mento entro certi limiti nella capacità di intuizione e di imi- tazione. Naturalmente è sem- pre preferibile cercare di non abusarne e fare invece in modo di spiegarsi in un modo che ri- sulti: - inequivocabile: non ci devo- no essere ambiguità riguardo a ciò che si deve compiere, quan- do, o come. - completo: tutte le situazioni che possono presentarsi du- rante l’esecuzione del compito devono essere previste e una opportuna azione (o reazione) deve essere sempre precisata. Non si deve dare per scontata alcuna conoscenza o iniziati- va. Se la descrizione del compi- to è fatta rispettando tali vin- coli, essa viene tecnicamente chiamata algoritmo. Se le prescrizioni sopra elen- cate appaiono scontate nel ca- so di esecutore umano, non stupirà certo il fatto che esse rimangano assolutamente vali- de quando l’esecutore è una macchina, del tutto priva com’è di capacità intuitive e di intelligenza, a dispetto della sua potenza di calcolo tipica- mente molto elevata (donde la locuzione, spesso usata, “idio- ta veloce”). Vi è naturalmente un ulterio- re requisito non proprio tra- scurabile, l’algoritmo deve an- che essere: - comprensibile: ci si deve esprimere in una lingua o for- malismo che l’esecutore cono- sca. A questo riguardo vale la pe- na di sottolineare che così co- me uno stesso algoritmo (per esempio: la procedura per ad- dizionare due numeri interi) può essere spiegato a esecuto- ri umani di diverse nazionalità nelle loro rispettive lingue na- turali, anche per i computer esiste una pluralità di linguaggi nei quali esprimere algoritmi. Un algoritmo espresso (in gergo: implementato) in un de- terminato linguaggio di pro- grammazione è detto pro- gramma.
Transcript
Page 1: Corso_java.pdf

Java 1a lezione

Apartire da questo numerovi proponiamo un corso diprogrammazione per prin-

cipianti assoluti nel quale sifarà uso del linguaggio Java pertutti gli esempi e gli approfon-dimenti.

La ragione della scelta di ta-le linguaggio va ricercata nellesue peculiarità, che lo rendonoidoneo per lo scopo e che ri-sulteranno evidenti via via du-rante il corso.

Per il momento ci limitere-mo a citare il suo amplissimocampo d’impiego, l’estrema fa-cilità di reperire documenta-zione ed esempi di buon livello,il costo nullo, la possibilità dieseguire uno stesso program-ma su una vasta gamma di di-spositivi.

Nel corso non si darà perscontata alcuna conoscenzaspecifica in tema di program-mazione, mentre si assumeràuna normale dimestichezzanell’utilizzo generico del PC ein particolare di Windows edelle sue applicazioni.

Il corso è organizzato per te-mi principali che saranno af-frontati in sequenza, mante-nendo un livello di comples-sità omogeneo, negli articolimensili.

Nel primo articolo introdur-remo i concetti di base e la ter-minologia da conoscere e trat-teremo un primo semplice ca-so pratico seguendo un meto-do generale, arrivando a unprogramma Java funzionantesul vostro sistema. Chi ha già

� A scuola con PC Open

ProgrammareJavaè per tutti

1 Programmare: le basi

Inizia da questo mese un nuovo corso dedicato alla programmazione, rivolto aineofiti. La scelta è caduta su Java, linguaggio molto versatile e diffuso e la cuidocumentazione è facile da reperire

Lezione 1Rudimenti di programmazione- Terminologia essenziale- Dall’algoritmo al programma- Preparazione dell’ambienteJava- Il primo programma

Lezione 2Elementi di programmazione a oggetti e grafica- Programmazione a oggetti- Creare una GUI

- Programmazione a eventi- Gestione di immagini

Lezione 3File- Salvare i dati- Elaborare i dati

Lezione 4Networking- Programmi client-server- Interazione con risorse Web

IL CALENDARIO DELLE LEZIONI�

di Marco Mussini

una certa esperienza in questocampo può saltare le sezioniche affrontano argomenti ele-mentari; tuttavia noi ne consi-

gliamo ugualmente una rapidalettura, magari per far emerge-re eventuali convinzioni impre-cise.

Esecutori e algoritmiOgniqualvolta si intende far

compiere un determinato com-pito a un esecutore è necessa-rio descriverglielo in una formaa lui comprensibile.

È quindi fondamentale, nelfarlo, immedesimarsi nell’ese-cutore e nel suo modo di pen-sare e di agire.

Nel caso particolare in cuil’esecutore sia umano, per ladescrizione del compito si puòusare il linguaggio naturale, aschemi e a gesti, e fare affida-mento entro certi limiti nellacapacità di intuizione e di imi-tazione. Naturalmente è sem-pre preferibile cercare di non

abusarne e fare invece in mododi spiegarsi in un modo che ri-sulti:- inequivocabile: non ci devo-no essere ambiguità riguardo aciò che si deve compiere, quan-do, o come.- completo: tutte le situazioniche possono presentarsi du-rante l’esecuzione del compitodevono essere previste e unaopportuna azione (o reazione)deve essere sempre precisata.Non si deve dare per scontataalcuna conoscenza o iniziati-va.

Se la descrizione del compi-to è fatta rispettando tali vin-coli, essa viene tecnicamente

chiamata algoritmo.Se le prescrizioni sopra elen-

cate appaiono scontate nel ca-so di esecutore umano, nonstupirà certo il fatto che esserimangano assolutamente vali-de quando l’esecutore è unamacchina, del tutto privacom’è di capacità intuitive e diintelligenza, a dispetto dellasua potenza di calcolo tipica-mente molto elevata (donde lalocuzione, spesso usata, “idio-ta veloce”).

Vi è naturalmente un ulterio-re requisito non proprio tra-scurabile, l’algoritmo deve an-che essere:

- comprensibile: ci si deve

esprimere in una lingua o for-malismo che l’esecutore cono-sca.

A questo riguardo vale la pe-na di sottolineare che così co-me uno stesso algoritmo (peresempio: la procedura per ad-dizionare due numeri interi)può essere spiegato a esecuto-ri umani di diverse nazionalitànelle loro rispettive lingue na-turali, anche per i computeresiste una pluralità di linguagginei quali esprimere algoritmi.

Un algoritmo espresso (ingergo: implementato) in un de-terminato linguaggio di pro-grammazione è detto pro-gramma.

Page 2: Corso_java.pdf

Java 1a lezione

Linguaggi di programmazioneI linguaggi di programmazio-

ne sono vecchi quasi quanto icomputer e, come questi, han-no subito una lunga storia evo-lutiva, attraversando un certonumero di “epoche” e “mode”con mutamenti a volte notevo-li nella loro concezione di base.Per questo spesso si parla, conuna semplificazione “cronolo-gica” un po’ forzata, di “lin-guaggi di prima, seconda, ...., N-esima generazione”. Nella se-zione Linguaggi di programma-zione, troverete alcuni ap-profondimenti.

Nell’accingersi a implemen-tare un algoritmo, conviene te-nere ben presente che qualun-que sia il linguaggio di pro-grammazione, l’hardware, il si-stema operativo e la potenzadella macchina, le operazioniche un computer “classico” èin grado di eseguire possonoessere fatte rientrare in pochecategorie fondamentali:

1. Ricordare. Memorizzaredati; creare copie di dati (even-tualmente con cambiamento diformato); sostituire o cancella-re dati memorizzati.

2. Calcolare. Innanzituttocalcoli aritmetici (le quattrooperazioni), logici (algebra diBoole: And, Or, Not, Xor) e bit abit (per esempio: “inverti l’N-esimo bit del K-esimo byte...”),ma anche tutti i calcoli piùcomplessi basati su questi, in-cluse, per esempio, funzioni tri-gonometriche e trascendenti.

3. Decidere. Confrontare da-ti fra loro oppure con valori diriferimento e (importantissi-mo) in base al risultato del con-fronto fare una cosa oppureun’altra. Sono possibili anchedecisioni a scelta multipla.

4. Ripetere o saltare. Reite-rare una certa operazione, ogruppo di operazioni, per uncerto numero di volte, o fino ache non si verifica qualcosa diprestabilito; oppure saltare uncerto gruppo di operazioni epassare direttamente a un altropunto dell’algoritmo.

5. Comunicare. Scambiaredati e informazioni con l’ope-ratore umano o con altri com-puter o con periferiche dellostesso computer: per esempio:leggere da tastiera, mostrare avideo, scrivere e leggere da di-sco o dalla rete, “sentire” i mo-vimenti del mouse, emetteresuoni.

Tutte le operazioni comples-se e tutti i programmi sono

sempre esprimibili come un in-sieme ordinato, più o menogrande, di operazioni elemen-tari appartenenti a queste ca-tegorie.

È importante sottolineareche queste capacità sono co-muni a tutti i linguaggi di pro-grammazione, anche se espres-se in forme spesso molto di-verse. Tenendone conto, pos-siamo esprimere un algoritmoin una sorta di “linguaggio ge-nerico”, scrivendo quello chesi chiama “pseudo codice”, cherisulterà poi facile tradurre nellinguaggio particolare che de-cideremo poi di adottare perl’implementazione vera e pro-pria.

Formulazione dell’algoritmo:un caso pratico

Per vedere un caso pratico,consideriamo un gioco, “indo-vina il numero”, per due gioca-tori, che descriveremo così:

Prima descrizionequalitativaIl giocatore A inventa unnumero a caso; il giocatoreB deve scoprirlo andando pertentativi. Per ogni tentativo diB, A risponde annunciandose ha indovinato (nel qualcaso la partita finisce e siproclama il numero ditentativi impiegati) o se ilnumero segreto è maggioreo minore.

Questa descrizione dovreb-be risultare perfettamentecomprensibile per qualsiasiesecutore umano di lingua ita-liana che sia dotato di normalicapacità intuitive e di iniziativaautonoma. Sfortunatamenteesso risulta incomprensibileper un computer: per questo ri-chiede varie trasformazioni.Per esempio:• Deve essere reso esplicito

tutto ciò che è implicito. Peresempio, si deve precisareche cosa si sottintenda inespressioni come “per ognitentativo di B” o “annunciarese ha indovinato”.

• Si devono eliminare indica-zioni inutili come lo scopodel gioco (“il giocatore B de-ve scoprirlo”) o descrizioniqualitative del modo di pro-cedere, come “andando pertentativi”.• Si deve precisare come si

possa raggiungere unoscopo: per esempio (“siproclama il numero di ten-

tativi impiegati”) che im-plica di aver svolto un con-teggio durante la partita.

Esso deve cioè diventare unalgoritmo, prima ancora che sipossa tentare di tradurlo in unvero e proprio programma scri-vendolo in un particolare lin-guaggio.

Volendo riformulare la de-scrizione del gioco come un al-goritmo e facendo giocare alcomputer nel ruolo di A, unesempio più corretto (dal pun-to di vista di A) è riportato nelriquadro A2.

Questa nuova formulazionedifferisce dalla precedente siaper il tono sia per i contenuti.Non si descrivono obiettivi daraggiungere, ma si enuncianooperazioni da eseguire. Tutti icasi possibili sono previsti etrattati esplicitamente, in mo-do esaustivo e senza lasciarespazio a interpretazioni. Si notianche che tutte le operazioniindicate, che sono veramenteelementari, appartengono allecinque categorie elencate piùsopra.

Variabili, costanti e inizializzazioni

Per la verità l’algoritmo nonè ancora perfetto: si parla di uncontatore N, che viene ora in-crementato, ora scritto, manon si stabilisce quale debbaessere il suo valore iniziale.

È invece necessario precisa-re che inizialmente tale conta-tore deve avere valore 1.

Per questo è necessario ag-giungere all’inizio dell’algorit-mo un altro punto come que-sto:0. Ricorda il contatore N eassegnagli 1 come valore iniziale

Questo tipo di operazione sichiama inizializzazione ed ècomunissima negli algoritmi enei programmi. Ogni calcolodovrebbe essere fatto su valoriben precisi; non si può dire “in-crementa il contatore N” trala-sciando di stabilire chiaramen-te da quale valore iniziale sidebba partire.

Entità come T e N, destinatea contenere dei dati che duran-te l’esecuzione dell’algoritmopotranno cambiare, sono defi-nite variabili. Al contrario, en-tità come X, in cui vengono ri-cordati valori fissati una voltaper tutte, sono definite costan-ti. Sia le variabili sia le costantidovrebbero essere inizializzateprima dell’uso o comunque inoccasione del primo uso.

Successive operazioni di at-tribuzione di un valore ad unavariabile non si chiamano piùinizializzazioni, ma assegna-menti. Le costanti, invece (coe-rentemente con il loro nome...),dopo essere state inizializzatenon possono più ricevere asse-gnamenti.

Decisioni, ripetizioni e saltiIn un paio di punti dell’algo-

ritmo sopra riportato si pre-scrive di “tornare al passo 2”.Lo scopo di queste indicazioniè evidentemente quello di farripetere (iterare, reiterare) unaporzione dell’algoritmo, chenel nostro esempio è quella incui si riceve un tentativo e lo siconfronta con il numero segre-to. Istruzioni di questo tipo sichiamano costrutti iterativi.

In altri punti compaiono in-dicazioni del tipo “se.... alloraesegui da 5 a 7 altrimenti passaa 8”. In questo caso quello che

Algoritmo in formulazione sequenziale1. Scegli un numero casuale, che non potrà cambiare, e ricordalo:

chiama X questo numero.2. Ricevi dall’esterno un numero (il tentativo) e ricordalo: chiama T

questo numero.3. Confronta T con X:4. Se T è maggiore di X, allora esegui da 5 a 7, altrimenti passa

a 8.5. scrivi “Il numero segreto è minore”, 6. incrementa il contatore N,7. torna al passo 2.8. Se T è minore di X, allora esegui da 9 a 11, altrimenti passa

a 12.9. scrivi “Il numero segreto è maggiore”,10. incrementa il contatore N,11. torna al passo 2.12. scrivi “Hai indovinato. Numero di tentativi impiegati: “13. scrivi il contatore N,14. termina

A1

A2

Page 3: Corso_java.pdf

Java 1a lezione

si punta a ottenere è eseguireuna sezione dell’algoritmo se esolo se una certa condizione èvera. Di qui il nome di costrut-ti condizionali.

In un caso così semplice,con un algoritmo brevissimoed una sola sezione ripetuta,indicazioni del tipo “vai al pas-so 2” (tecnicamente definite“salti”) o “esegui da 5 a 7” ri-sultano ancora comprensibilie facili da seguire. Tuttavia,qualora l’algoritmo venissemodificato con l’inserimento dinuovi passi, la numerazione diquelli esistenti ne risulterebbealterata; per esempio, il “passo2” potrebbe diventare il “passo3” e tutti i punti in cui si fa rife-rimento ad esso andrebberomodificati di conseguenza.Inoltre, in presenza di algoritmipiù complessi e con un alto nu-mero di sezioni ripetute, maga-ri anche contenute (“innestate”o “nidificate”) le une nelle altre,la notazione numerica comin-cerebbe a diventare scomoda.

Per queste ragioni i costrut-ti iterativi e i costrutti condi-zionali vengono abitualmentedescritti con una notazione piùcomprensibile, come mostratonel riquadro A3.

Questo tipo di presentazio-ne dell’algoritmo risulta assaipiù leggibile nonostante la nu-merazione dei passi sia stata ri-mossa. Un notevole contributoviene dall’espediente di farrientrare il margine sinistro deltesto per meglio evidenziare igruppi di istruzioni e il loro ap-parentamento con istruzionicondizionali (“se... allora”) oiterative (“ripeti.... fino a....”):questo accorgimento si chiamacomunemente indentazione e iblocchi di righe interessati so-no detti indentati.

È opportuno accennare an-che a un altro tipo di rappre-

sentazione per gli algoritmi. Sitratta di una notazione graficadefinita “diagramma di flusso”o flow chart.

Questa rappresentazione hail vantaggio di rendere imme-diatamente riconoscibile il fun-zionamento generale dell’algo-ritmo, la presenza di cicli e de-cisioni e il modo in cui questisono organizzati. Sebbene sia-no previsti simboli standardper molti tipi di operazioni ele-mentari, nella pratica comunepossono bastare tre simbolifondamentali: il rettangolo(operazione generica), il rombo(decisione) e la freccia (flussodi esecuzione).

A titolo di esempio, con que-sta notazione il nostro algorit-mo può essere rappresentatocome in figura 1.

Algoritmi strutturatiLa formulazione strutturata

dell’algoritmo ne aumenta ilgrado di leggibilità al puntoche tutti i linguaggi di pro-grammazione più recenti con-sentono (o addirittura impon-gono) di scrivere i programmiin questa forma. Sono quindibandite istruzioni poco espres-sive e scomode sia da scriveresia da leggere, come “vai alpasso 12064”, “esegui da 9324 a10022”, “torna al passo 6750”,ed esistono invece costruttisintattici che descrivono l’os-satura logica del programmaricalcando esattamente le for-me espressive usate nella for-mulazione strutturata degli al-goritmi. Qui di seguito ne ri-portiamo un elenco:• Esecuzione condizionale • Selezione alternative • Ripetizione semplice a condi-

zione iniziale o a condizionefinale

• Ripetizione con operazioni diconteggio e controllo

Per approfondimenti, il ri-quadro Strutture di Controllo(ultima pagina di questo arti-colo) mostra in dettaglio la cor-rispondenza fra pseudocodice,sintassi in Java e simbologiagrafica.

Dallo pseudocodice a JavaA questo punto possiamo fi-

nalmente vedere come si pre-senta il nostro algoritmo tra-dotto da pseudo codice a codi-ce Java vero e proprio. Riqua-dro A4.

Il programma contiene an-cora dettagli sintattici e istru-zioni che risulteranno chiarisolo nelle prossime lezioni.Tuttavia nella sua struttura ri-sulta “immersa”, fedelmente ri-prodotta, la struttura logicadell’algoritmo del riquadro A3.

Vi raccomandiamo ora dipredisporre l’ambiente per la-vorare in Java sul vostro PC.Fate innanzitutto riferimentoalle procedure indicate nellasezione Preparazione dell’am-biente Java.

Se non ne avete già uno divostro gradimento, dovrete an-che scegliere un text editor perimmettere il programma, peresempio fra quelli gratuiti danoi segnalati.

Oppure potete optare findall’inizio per un ambiente in-tegrato di lavoro, che consentedi operare in modo più pratico:per esempio, le istruzioni perinstallare JCreator LE (gratui-to) si trovano nell’apposito ri-quadro.

A installazione completataprovate a scrivere, compilareed eseguire il programma del

riquadro A4 seguendo le pro-cedure di compilazione – ese-cuzione mostrate per il caso“editor + compilazione a lineadi comando” o per il caso diJCreator LE, a seconda dellostrumento per il quale aveteoptato.

Da notare che il listato Javacontiene commenti in molte li-nee: i commenti sono compresifra “//” e la fine della linea epossono essere omessi per ri-sparmiare tempo di digitazio-ne. In alternativa, naturalmen-te, trovate i listati sul CD ROMallegato a PC Open.

Se non saranno stati com-messi errori di trascrizione dellistato, il programma in esecu-zione apparirà come mostratonella figura sottostante.

Segnaliamo in particolareche alla richiesta di un tentati-vo, in caso di immissione di unvalore non interpretabile comenumerico il programma reagiràsegnalando errore.

Come esercizio vi proponia-mo di analizzare il programmaper cercare di individuare checosa determina tale comporta-mento.

Riformulazione dell’algoritmo in forma strutturata• Scegli un numero casuale, che non potrà cambiare, e ricordalo:

chiama X questo numeroRipeti quanto segue fino a diversa indicazione:• Ricevi dall’esterno un numero (il tentativo) e ricordalo:

chiama T questo numero• Confronta T con X:• Se T è maggiore di X, allora:

- scrivi “Il numero segreto è minore”, incrementa il contatore N• Altrimenti, se T è minore di X, allora:

- scrivi “Il numero segreto è maggiore”, incrementa il contatore NAltrimenti:• scrivi “Hai indovinato. Numero di tentativi impiegati: “• scrivi il contatore N• interrompi le ripetizioni

A3

L’algoritmo del riquadro A3rappresentato come diagramma diflusso

1

Ecco come appariràil programma in esecuzione

2

Page 4: Corso_java.pdf

Java 1a lezione

2 Preparazionedell’ambiente Java

L’algoritmo implementato in Java

// Preannunciamo al compilatore che useremo alcune classi di libreriaimport java.util.Random; // generatore di numeri pseudocasualiimport java.io.InputStreamReader; //lettura a caratteri da flusso di input abyteimport java.io.BufferedReader; //lettura a righe di testo da flusso di input acaratteri

// Classe principale (e unica) del programmapublic class Indovina{// Metodo di innesco standard del programma e unico metodo della classepublic static void main(String [] args){

// predisponi un contatore di tentativi 'n' con valore iniziale pari a 0int n=0;// Scegli un numero casuale e ricordalo nella variabile 'x'Random r=new java.util.Random();int x=r.nextInt(100)+1;// (condizione iniziale: numero non ancora indovinato)boolean indovinato=false;//(predisponi flusso in lettura per righe di testo e collegalo alla tastiera)BufferedReader br=new BufferedReader

(new InputStreamReader (System.in));

// Ripeti quanto segue fintantoche il numero non viene indovinato// (v. condizione 'while' a fine ciclo)do{

// (predisponi la cattura e gestione di eventuali errori verificatisi// nella decodifica del tentativo immesso)try{

// Ricevi dall'esterno il tentativo e ricordalo come 't'System.out.println("Tentativo? "); // invita a inserire un valoreString tentativo=br.readLine(); // leggi una stringa da tastieraint t=Integer.parseInt(tentativo); //converti stringa in num. int.

if(t>x) // Se t e' maggiore di x...{

// stampa messaggio...System.out.println("Il numero segreto e' minore");n=n+1; // incrementa contatore di tentativi

}else if(t<x) // Se t e' minore di x...{

// stampa messaggio...System.out.println("Il numero segreto e' maggiore");n=n+1; // incrementa contatore di tentativi

}else // Per esclusione: t uguale a x - numero indovinato !{

// stampa messaggio...System.out.println("Hai indovinato!");// stampa numero di tentativi...System.out.println("Numero di tentativi impiegati:"+n);indovinato=true; // segnala di interrompere le ripetizioni

}}catch(Exception e) // Gestione degli errori catturati nel blocco try{

System.out.println("Tentativo non valido - ripetere, prego");}} while(indovinato==false); // Il ciclo termina solo quando

'indovinato' vale true} // fine metodo main

} // fine classe Indovina

A4

Prima di poter scrivere edeseguire programmi inJava è necessario dotarsi

dei “ferri del mestiere”. Ve-diamo quindi quali sono eche cosa occorre fare per in-stallare un ambiente di lavo-ro completo nel sistema.

Il Text EditorSi tratta dello strumento

di uso quotidiano per il pro-grammatore e la sua sceltanon andrebbe mai compiutadistrattamente. Un buon edi-tor facilita il lavoro, aiuta aprevenire gli errori, rendemeno stancante l’attività epuò ridurre anche di molto iltempo necessario per scrive-re, provare e correggere i pro-grammi. Ricordiamo qui diseguito alcune delle funzionipiù importanti.

Supporto specifico per il linguaggio usato

L’editor può conoscere lasintassi del linguaggio ado-perato e avvalersi di questaconoscenza per fare un con-trollo di validità sintattica delcodice sorgente mentre lo siscrive. Inoltre, per aumentar-ne la leggibilità, può eviden-ziare con colori diversi le pa-role chiave del linguaggio, inomi di variabili e costanti, ivalori, i commenti e così via(syntax highlighting). Alcunieditor, quando riconosconoche l’utente sta iniziando ascrivere un costrutto del lin-guaggio, inseriscono automa-ticamente la punteggiaturanecessaria e le eventuali pa-rentesi (completamento au-tomatico). Molto comoda,specie con linguaggi la cuisintassi è ricca di parentesi edi blocchi con indentazione,è la funzione che, quando ilcursore è posizionato su unaparentesi, riconosce ed evi-denzia in colore la parentesiad essa associata (bracketmatching).

Indentazione del codiceautomatica o assistita.

Durante la digitazione, l’e-ditor può riconoscere che sista scrivendo un blocco dicodice e provvedere automa-

ticamente alla sua indenta-zione. Oppure, nel caso in cuiil file sia stato scritto in pre-cedenza, può offrire comandiper indentare o de-indentaregruppi di linee selezionati.

Copie di backupautomatiche

Una funzione molto utilenel deprecato caso in cui l’e-ditor vada in crash, rischian-do di far perdere il lavoro.

Undo potenziato Rispetto all’undo normale

che “ricorda” tutt’al più quel-lo che è stato fatto dopo l’ul-timo salvataggio, l’undo di unbuon editor dovrebbe esserein grado di ricordare e annul-lare anche modifiche prece-denti (unlimited undo/redocapability).

Funzioni per lavorare suiblocchi

I migliori text editor con-sentono operazioni di taglia eincolla non solo su sezioni ditesto contiguo, ma anche surettangoli di testo e colonne.

Comode funzioni di movimento

È essenziale che la “diteg-giatura” del programmatoresull’editor sia aiutata al mas-simo da una opportuna scel-ta di tasti (che dovrebberoessere ben disposti sulla ta-stiera e non troppo diversi daquelli degli altri programmi)e da speciali combinazionidedicate allo spostamentofra righe, parole e blocchi.

Numerazione delle linee di codice

I compilatori segnalano glierrori dando come riferimentoil numero della linea del sor-gente in cui essi si trovano. Sel’editor non fornisce l’indica-zione dei numeri di linea, o senon prevede almeno una fun-zione “Go-to” per posizionarsidirettamente a una certa lineadel sorgente, il lavoro diventaveramente arduo.

Visualizzazione gerarchicadei costrutti sintattici

Specialmente in file sor-

Page 5: Corso_java.pdf

genti di notevole lunghezza econ costrutti sintattici profon-damente annidati può succe-dere di “perdersi” cercando diseguire su schermo il flusso diesecuzione del programma.Questa funzione consente dicollassare o espandere unblocco di cui momentanea-mente non interessa vedere ilcontenuto.

Integrazione con gli altristrumenti di programmazione

L’attività del programmato-re è una continua ripetizionedel ciclo “scrittura sorgente –compilazione – test”. Risultacomodo poter lanciare la com-pilazione direttamente dall’in-terno dell’editor, dal momentoche è tipico che vengano se-gnalati errori e che questi deb-bano essere corretti nell’editorprima di ritentare la compila-zione. Strumenti che integranoin modo molto spinto tutti itool di programmazione sonospesso chiamati IDE (Integra-ted Development Environment).

Vi segnaliamo i seguenti edi-tors per Windows completa-mente gratuiti e adatti all’usocon programmi Java: vi consi-gliamo di provarli e di selezio-nare quello che preferite. Litrovate sul CD Guida 2 allegatoa PC Open.• ConTEXT• CPad• Crimson Editor• Source Edit• SYN

Per chi cercasse un ambien-te completo anzichè un sem-plice editor, segnaliamo invecel’interessante JCreator LE, unIDE potente, “leggero” e gratui-to. Dal momento che questo ti-po di prodotti si integra con ilJDK, l’installazione è legger-mente più complessa di quelladi un semplice editor; chi fosseinteressato può fare riferimen-to all’apposito riquadro Instal-lazione di JCreator.

Il Java Development KitPer lo sviluppo di program-

mi Java è disponibile gratuita-mente il Java Development Kit(JDK) un kit comprendente tut-

ti gli strumenti di base neces-sari (ad eccezione del text edi-tor, da procurarsi a parte). Almomento in cui scriviamo ilJDK è arrivato alla versione1.5.0 Update 1; è possibile sca-ricarlo dal sitohttp://java.sun.com, oppurepotete trovarlo sul CD Guida 2(CD2, PDF, Corsi, Java).

Fra gli strumenti dell’am-biente Java, quelli di utilizzoquotidiano (a parte, natural-mente, l’editor) sono i seguen-ti (figura 3):

• javac.exe – il compilatoreJava, che traduce i sorgenti Ja-va (files con estensione .java)in bytecode (files con esten-sione .class). Deve essere uti-lizzato dopo ogni modifica alprogramma, per aggiornare laversione eseguibile.

• java.exe – l’interprete dibytecode, usato per eseguire iprogrammi Java dopo che so-no stati compilati conjavac.exe.

• jar.exe – lo strumento perconfezionare dei files .jar (JavaARchive), che raccolgono tuttele classi e i file di risorse ne-cessari per il funzionamentodi un programma. Risulta quin-di possibile distribuire un pro-gramma Java, inclusi file diconfigurazione, help, file grafi-ci e sonori, semplicementeconsegnando un singolo file.

InstallazioneL’installazione completa del

JDK richiede circa 190 MB,mentre l’installazione della ric-chissima documentazione (vi-vamente consigliata) ne richie-de circa altri 250. Vediamo laprocedura da seguire per l’in-stallazione e configurazione.

Iniziamo lanciando il pro-gramma di installazione delJDK. Per prima cosa ci viene ri-chiesto di accettare l’accordodi licenza (figura 4).

Nella seconda fase viene of-ferta la possibilità di selezio-nare i componenti da installa-re. Consigliamo vivamente unainstallazione completa. (figura5). Prima di passare alla scher-mata successiva, consigliamodi cambiare il percorso di in-

Java 1a lezione

Installazione di JCreator LEAlcuni programmatori sono abituati a scrivere il programma in uneditor “puro” ed eseguire le compilazioni a linea di comando da unafinestra DOS, con un pieno controllo su opzioni e processo digenerazione. Altri invece preferiscono lavorare in un ambienteintegrato (IDE) che offra innanzitutto un facile accesso a tutte lefunzioni.Vediamo dunque la procedura da seguire per l’installazione diJCreator LE, un buon IDE freeware specializzato nella gestione diprogetti in Java. Da notare che la procedura va eseguita a JDK e relativadocumentazione già correttamente installati.

Una volta lanciato il programma diinstallazione (lo trovate anche sulCD ROM allegato a PC Open) vieneinnanzitutto chiesto a quali tipi difile si voglia associare ilprogramma. I files .java sono isorgenti Java: grazieall’associazione, con un doppio clicverranno aperti direttamente inJCreator. Gli altri due tipi di filesono file di servizio del programma.Accettiamo i default e procediamo.

Nel secondo step ci viene chiestodi indicare sotto quale directory èstato installato il JDK. Questaindicazione è necessaria perchél’IDE ha la necessità di invocarejavac.exe o java.exe quando siselezionano le corrispondentiopzioni nei suoi menu. Nel nostrocaso il JDK è installato sottoc:\jdk150. Selezioniamo quindiquesto percorso e procediamo.

Nell’ultima fase dobbiamoinformare il programma diinstallazione su dove si trovi ladocumentazione di Java. Ciòpermetterà all’IDE di visualizzarlaquando richiesto dall’utente.Procediamo dopo quest’ultima fasee l’installazione avrà termine.

Ora che l’IDE è installato,verifichiamo il buon funzionamentodel sistema provando a immettereun programma di prova. Per prima cosa creare una cartellaC:\java. Una volta lanciatoJCreator, dal menu File selezionareNew. Nel dialog box che appare,scegliere la scheda Files, poil’icona Java File. Nella casellaFilename immettere Ciao.java ecome location indicare il percorsoC:\java. Premere OK e il file verràcreato e aperto nella finestra diediting.

3

Gli strumenti dell’ambiente Java di utilizzo quotidiano

Page 6: Corso_java.pdf

stallazione (usando il pulsanteChange). Quello proposto perdefault, infatti, è lungo e sco-modo da digitare. Meglio quin-di installare in una directorydal nome breve e posizionatadirettamente sotto la radicedel disco, come c:\jdk150. (fi-gura 6). Può ora avere iniziol’installazione vera e propriadel JDK, che richiede alcuniminuti anche su macchine dibuona potenza. (figura 7)

Dopo il JDK (Java Develop-ment Kit) viene installato il JRE(Java Runtime Environment).Anche in questo caso lasciamoinvariate le opzioni.

Stavolta non è importanteche la directory di installazio-ne sia breve, quindi si puòtranquillamente accettare ildefault (figura 8).

Al termine dell’installazionedel JRE viene richiesto di indi-care a quali browser dovrà es-sere agganciato il Java Plug-in,che consente l’esecuzione de-gli applet presenti nelle pagineWeb. Consigliamo di installarloin tutti i browser presenti (fi-gura 9).

A questo punto l’installazio-ne del JDK è completata. Dinorma non è richiesto un riav-vio dopo l’operazione, ma nelcaso venisse richiesto, lascia-mo fare (figura 10).

Possiamo verificare che l’in-stallazione sia andata a buonfine aprendo una finestra MS-DOS e digitando il comando ja-va –version.

Dovremmo ottenere un mes-saggio che riporta la versionedi Java installata, come mo-strato in figura (figura 11).

Dopo il JDK è ora il momen-to di installare la documenta-zione tecnica e di riferimento.Tutto quello che occorre fare èestrarre in una cartella a pia-cere l’intero contenuto del filejdk-1_5_0-doc.zip.

Noi abbiamo scelto diestrarlo in c:\jdk150docs. Ri-cordarsi di selezionare l’opzio-ne che mantiene la strutturadelle cartelle, altrimenti la do-cumentazione risulterà com-pletamente inutilizzabile (figu-ra 12).

CollaudoOra che l’installazione del

JDK è completa conviene veri-ficare che tutto sia regolar-mente funzionante provando aimmettere, compilare ed ese-guire un programma “minimo”.

Per prima cosa, utilizzando

Java 1a lezione

Nella finestra di editingdigitare il seguenteprogramma esattamentecome riportato in figura

Si procede ora alla compilazionedel programma. L’operazione è effettuabile senzanemmeno uscire dall’IDE. Dal menuBuild selezionare Compile file.

Se la compilazione è andataa buon fine, nel pannelloinferiore appariranno imessaggi riportati in figura

È interessante a questopunto verificare la situazionesul file system. Aprire unafinestra di Esplora risorse eportarsi su C:\java. Come sipuò notare, accanto al fileCiao.java è comparso il fileCiao.class: questo filecontiene il bytecodecorrispondente al sorgenteed è il risultato dellacompilazione.

A questo punto si può mandare inesecuzione il programma. Sempre dal menu Build, selezionareExecute File:

Il programma viene lanciato ed il suo output appare inuna finestra DOS, comeriportato in figura

4

5

6

7

8

Page 7: Corso_java.pdf

Java 1a lezione

un editor (per il momento puòbastare anche il sempliceBlocco Note di Windows, rag-giungibile da Start/Program-mi/Accessori), creare un file ditesto e digitare il programma diprova esattamente come mo-strato in figura (figura 16).

Salvare il file nella directoryC:\java dandogli il nomeCiao.java.

Assicurarsi (con Esplora Ri-sorse) che il file abbia ricevutoil nome Ciao.java e non Ciao.ja-va.txt; se necessario, corregge-re il nome in modo che sia esat-tamente Ciao.java.

Una premessa: per scrivereprogrammi Java sono assolu-tamente indispensabili due ca-ratteri non presenti sulle ta-stiere italiane: le parentesigraffe aperte e chiuse.

Ci sono tre modi per immet-terle: • Usare le combinazioni

SHIFT+ALT GR+[ eSHIFT+ALT GR+], rispettiva-mente, per ottenere “{“ e “}”.

• Attivare il Num Lock sul padnumerico della tastiera, do-podichè tenere premuto ALTe premere in successione itasti 0,1,2,3 del pad numeri-co. Questo farà apparire la

parentesi graffa aperta. Laparentesi graffa chiusa si ot-tiene in modo analogo, ma lasequenza da digitare è0,1,2,5.

• Dal Pannello di Controllo se-lezionare Opzioni Internazio-nali e della lingua, Lingue,Dettagli, Impostazioni, pre-mere Aggiungi, dall’elencoLingua scegliere Inglese (Sta-ti Uniti) e da Layout di tastie-ra scegliere Stati Uniti.Premere OK, OK, OK.Sulla barra Start, premere iltasto destro del mouse inuna zona libera.Nel sottomenu Barre degliStrumenti attivare l’opzioneBarra della lingua.Apparirà un’icona sulla barraStart. Questa icona consentedi commutare la lingua e illayout di tastiera fra inglese(EN) e italiano (IT).Ciò è possibile facendo clicsull’icona stessa oppure, piùsemplicemente, con la com-binazione di tasti ALT sini-stro + Maiuscole.Nel layout di tastiera inglese,le parentesi quadre si trova-no nello stesso punto in cuisi trovano sulla tastiera ita-liana (ma senza premere ALTGR), mentre gli stessi due ta-sti (+ Maiuscole) danno le pa-

rentesi graffe.Per compilare il programma

Ciao.java aprire una finestraMS-DOS (Prompt dei comandi)e immettere i seguenti coman-di:

cd c:\javaC:\jdk150\bin\javac Ciao.java

il primo comando serve perposizionarsi nella directory dilavoro; il secondo chiama ilcompilatore sul programmasorgente Ciao.java, per gene-rare il corrispondente file by-tecode, Ciao.class.

Se il programma non conte-neva errori e la compilazioneha avuto successo, si presen-terà una situazione come quel-la riportata in figura (figura17). Ora si può mandare in ese-cuzione il programma.

Per fare questo immettere ilcomando

java Ciao

Se non sono stati commessierrori il programma entrerà infunzione e stamperà “Ciao!” sulterminale, così: (figura18)

A questo punto possiamoconsiderare verificata la cor-retta installazione dell’ambien-te Java!

9

10

11

12

13

14

15

17

18

16

L’installazione del Java Development Kit (JDK) richiede circa 190 MB. Ecco alcunefasi dell’installazione e della configurazione

Page 8: Corso_java.pdf

Java 1a lezione

Il gran numero di linguaggiesistenti può disorientare chisi accosta per la prima volta

alla programmazione. Che differenza c’è fra Java e

Javascript? Perchè certi siti Web sono

realizzati in PHP e altri in Perl?È meglio un linguaggio inter-

pretato o compilato? Che cosa sono i linguaggi di

scripting? HTML può definirsiun linguaggio di programma-zione?

Che cosa significa program-mare a oggetti?

Qual è il linguaggio “miglio-re”, quello che conviene impa-rare? E così via.

Anche se per questo corsoabbiamo deciso di adottare illinguaggio Java, per ragioniche risulteranno sempre piùchiare strada facendo, è giustoe utile ricordare brevemente lecaratteristiche e le “attitudini”degli altri linguaggi di pro-grammazione, soprattutto persapersi meglio orientare nelleproprie scelte.

Certo, elencare tutti i lin-guaggi finora comparsi sullascena sarebbe lungo e sostan-zialmente inutile.

Qui ci limiteremo a proporreinnanzitutto due temi di rifles-sione utili come chiave di let-tura:

- l’obiettivo dell’introduzio-ne di linguaggi di programma-zione di nuovo tipo è tipica-mente stato quello di renderepiù facile e veloce la scritturadi programmi di complessitàcrescente, o per particolariclassi di problemi, riducendoper quanto possibile la possi-bilità di commettere errori.

- il semplice fatto che atutt’oggi continuino ad essereusati numerosi linguaggi diver-si per classi di applicazioni di-verse sembra dimostrare chenon esiste (ancora?) “il” lin-guaggio di programmazioneperfetto, ideale per ogni cam-po e situazione d’impiego.

D’altra parte, persiste unacerta tendenza a perseguire ilperfezionamento dei linguaggidi programmazione più attra-verso la continua introduzionedi nuovi linguaggi che attra-verso un processo di affina-mento di quelli esistenti.

I linguaggi di programmazio-

ne possono inoltre essere clas-sificati secondo le loro carat-teristiche. Le principali cate-gorie che è opportuno cono-scere sono le seguenti.

Linguaggi compilatiUna volta scritti, per poter

essere eseguiti i programmivengono tradotti una volta pertutte in linguaggio macchina,l’unica forma direttamentecomprensibile dall’hardware.Questa traduzione viene effet-tuata automaticamente da ap-positi programmi denominaticompilatori. Il programmacompilato non richiede più ul-teriori elaborazioni e può es-sere mandato direttamente inesecuzione tutte le volte cheoccorre.

Linguaggi interpretatiPer l’esecuzione del pro-

gramma viene usato un altroprogramma – denominato in-terprete – che lo esamina edesegue le operazioni corri-spondenti alle istruzioni chelo compongono.

L’interprete è così denomi-nato perchè da un lato leggeun certo linguaggio (per esem-pio: BASIC) e dall’altro “parla”all’hardware nella lingua chequesto è in grado di capire: illinguaggio macchina. Perquanto l’interprete possa es-sere ben progettato, i linguaggieseguiti in questo modo hannosempre prestazioni inferiori aquelle ottenibili con la compi-lazione.

In compenso un programmainterpretato può essere man-dato in esecuzione appenascritto, senza aspettare la com-pilazione (un’operazione cheper programmi di grandi di-mensioni può durare anche pa-recchi minuti).

Caratteristiche generaliPortabilità. Un linguaggio si

dice “portabile” quando i pro-grammi scritti con esso posso-no essere “portati” ed eseguitisu molti tipi diversi di compu-ter senza bisogno di adatta-menti complessi. I linguaggipiù portabili sono supportatianche su centinaia di architet-ture diverse. Questa proprietàaumenta evidentemente il va-lore del programma, in quanto

esso sarà compatibile con mol-ti tipi diversi di computer e po-trà fra l’altro essere vendutosu un mercato più ampio.

Tipizzazione. Sono detti “ti-pati” o “fortemente tipati” queiprogrammi nei quali prima dipoter utilizzare, immagazzina-re, elaborare, trasferire dati ènecessario dichiarare esplici-tamente di che tipo di dati sitratta.

Questo consente general-mente di prevenire un gran nu-mero di possibili errori di ese-cuzione dei programmi. Se il ti-po dei dati è sempre noto pri-ma ancora di iniziare l’esecu-zione, si parla di “tipi statici”,altrimenti si parla di “tipi dina-mici”.

Paradigma diprogrammazione

Linguaggi macchina. Si trat-ta di rudimentali linguaggi neiquali le istruzioni sono espres-se per mezzo di numeri binari.Possono essere compresi edeseguiti direttamente dal-l’hardware, senza modifiche nètraduzioni.

Per la loro “vicinanza” al li-vello dell’hardware e per il bas-so livello di astrazione checonsentono, sono detti spessolinguaggi di basso livello. Per ilfatto che questi linguaggi sonostati i primi a comparire sullascena, in contemporanea conle macchine, a volte sono an-che detti “linguaggi di I gene-razione”.

Linguaggi simbolici assem-blatori. Questi linguaggi (a vol-te detti “linguaggi di II genera-zione”) costituiscono un primotentativo di astrarsi dallo sco-modo “cifrario” dei linguaggimacchina: i programmi sonoancora composti di istruzioniche corrispondono quasi esat-tamente alle istruzioni macchi-na, ma questa volta la formaespressiva è più comprensibi-le, essendo testuale e general-mente simile alla lingua ingle-se.

Per esempio, l’istruzione disomma viene indicata come“ADD” anzichè come“01011101”. Lo stesso pro-gramma riscritto in Assemblersi presenta così.

Linguaggi procedurali. Inquesti linguaggi l’enfasi della

programmazione viene postaprimariamente sulla descrizio-ne della procedura da seguireper elaborare i dati.

I programmi non sono com-pletamente lineari e sequen-ziali, ma modulari, in quantopossono essere strutturati co-me un insieme di unità più pic-cole (funzioni e procedure)ognuna delle quali provvedead eseguire un compito sem-plice. Gli esempi più noti sonoil C e il Pascal o le più recentivarianti di BASIC.

Linguaggi a oggetti. Lin-guaggi nei quali al centro del-l’attività del programmatoreviene posto il progetto dei tipidi dato da trattare, dell’affinitàche li lega e delle operazionipossibili su di essi.

I dati trattati sono visti co-me oggetti sui quali agisconoparticolari procedure definitemetodi. Oggetti dello stesso ti-po sono caratterizzati dall’ap-partenenza a una certa classe,che descrive un insieme di pro-prietà e metodi che li accomu-nano.

La presenza di aspetti co-muni a più classi, ove presen-te, viene descritta dalla rela-zione di ereditarietà e sfruttataper semplificare la scrittura ela manutenzione dei program-mi.

C++ e Java sono i più noti ediffusi linguaggi di questo tipo.

Linguaggi funzionali. Inquesti linguaggi i programmisono visti come funzioni mate-matiche e l’esecuzione dei pro-grammi è di conseguenza assi-milata al calcolo di queste fun-zioni.

Questa formulazione si rive-la particolarmente espressivaper certi particolari tipi di pro-grammi, ma può risultare assaicriptica per molti altri. L’utiliz-zo della ricorsione, un mecca-nismo per mezzo del quale unafunzione è definita in termini dise stessa (per esempio: il fat-toriale di un numero N>1 è pa-ri a N moltiplicato per il fatto-riale di N-1...), è fra i costruttipiù spesso usati nei linguaggifunzionali per implementarealgoritmi complessi. Il Lisp è ilpiù noto rappresentante diquesta categoria.

Linguaggi di scripting. Laspecializzazione originaria di �

4 Linguaggi di programmazione

Page 9: Corso_java.pdf

Java 1a lezione

questa classe di linguaggi èquella dell’espressione di pro-cedure da usarsi per coordina-re e interpolare il lavoro di altriprogrammi già scritti.

Per rendere l’idea, se i mat-toni fossero scritti in C, C++ oJava, allora i linguaggi di scrip-ting sarebbero la calce, con unruolo complementare e nonantitetico.

Questi linguaggi (quasi sem-pre interpretati) solitamenteeccellono nell’elaborazionedei risultati intermedi di altriprogrammi, nel richiamo e ge-stione di programmi, nell’ese-cuzione in forma automatizza-ta e ripetitiva di gruppi (batch)di comandi normalmente im-messi manualmente dall’ope-ratore. In generale essi tendo-no a privilegiare la sinteticità el’immediatezza d’uso rispettoalle prestazioni, alla correttez-za, alla leggibilità e alla manu-tenibilità dei programmi.

Ferma restando questa im-pronta culturale di base, i piùrecenti linguaggi di scriptingsono stati dotati di potenzia-lità ormai equivalenti a quelledei linguaggi “classici”.

Fra i linguaggi di scripting siannoverano Tcl, Perl, Python,PHP, il linguaggio batch dellashell di MS-DOS (files .BAT), ilVBScript e così via.

Piccola antologia deilinguaggi

Ecco infine una breve pre-sentazione di alcuni fra i piùnoti linguaggi rappresentatividelle varie “generazioni” o ti-pologie che si sono succedute.Ometteremo di citare i lin-guaggi di scripting in quantoparzialmente estranei al focusdi questo corso.

A titolo di esempio è mo-strata l’implementazione diuno stesso programma (chestampa semplicemente il mes-saggio Hello, World!) in ognunodei linguaggi citati.

Linguaggio macchina Un programma che stampa

Hello, world! in linguaggio mac-china Intel 8086 sotto MS-DOSsi presenta così:

BA 0B 01 B4 09 CD 21 B4 4C CD21 48 65 6C6C 6F 2C 20 77 6F 72 6C 64 2113 10 24

Assembler Si tratta della rappresenta-

zione testuale dell’unico lin-

guaggio “capito” direttamentedall’hardware (linguaggio mac-china o codice macchina) eper questo definito “di bassolivello”.

Concetti ed istruzioni sonoin effetti estremamente ele-mentari. Scrivere programmicomplessi esprimendosi diret-tamente in questo linguaggio èoggettivamente scomodo e fa-ticoso, ma grazie al totale con-trollo che esso fornisce sul fun-zionamento dell’hardware èpossibile, con qualche accor-tezza, ottenere le massime pre-stazioni possibili con una de-terminata macchina.

start: mov dx,offset messagemov ah,09h int 21h

message db 'Hello World!',13,10,'$'

FORTRAN (FORmula TRANslator). Uno

dei primi linguaggi di grandesuccesso ad aver introdottoun livello di astrazione più ele-vato, specialmente per quantoriguarda le espressioni mate-matiche, e un adeguato siste-ma per l’input/output di datiformattati su schermo, tastie-ra, stampante e disco.

PROGRAM HelloWorldPRINT *, "Hello World"

END PROGRAM HelloWorld

LISP (LISt Processing) Il “linguag-

gio funzionale” per eccellenza.Qualunque algoritmo vieneespresso come una funzionematematica. I programmi ten-dono a essere eleganti e leggi-bili per certe classi di algoritmiche ben si prestano a tale no-tazione, e oltremodo cripticiper tutte le altre.

Tradizionalmente associato,più per ragioni di contempora-neità che per oggettiva specifi-cità, ad applicazioni di intelli-genza artificiale.

Nella sua epoca di massimapopolarità furono anche co-struite macchine (LISP machi-nes) il cui hardware era in gra-do di capire direttamente il LI-SP. Oggi è usato come linguag-gio di programmazione internodi certe applicazioni.

(DEFUN HELLO-WORLD () (PRINT(LIST 'HELLO 'WORLD)))

PROLOG (PROgrammazione LOGica).

Qualunque algoritmo vieneespresso come un teorema dadimostrare.

Assiomi, teoremi di reperto-rio e regole di deduzione sonoforniti dal programmatore; alresto (trovare la dimostrazio-ne) pensa il computer.

Come il LISP, rende eleganteed estremamente sintetica laprogrammazione di certe clas-si di applicazioni e farraginosaquella di tutte le altre.

Il Prolog è praticamente ca-duto in disuso.

?- write('Hello World'), nl.

PASCAL Un linguaggio procedurale,

fortemente tipato, inizialmenteproposto con intenti soprat-tutto didattici, che si è succes-sivamente imposto per il suorigore formale e sintattico eper la sua disponibilità su mi-crocomputer di ampia diffu-sione.

PROGRAM HelloWorld;BEGIN

WRITELN('Hello World');END

BASIC (Beginner’s All-purposeSymbolic Instruction Code)

Nato negli anni ’60 apparen-do come una sorta di “Pascalvolgarizzato”, non senza uncerto “retrogusto di Assem-bler”, inizialmente adottatocome linguaggio di program-mazione di base da tutti gli ho-me computers, col passare de-gli anni ne sono state proposteinnumerevoli varianti arricchi-te e potenziate.

Relativamente intuitivo econ una curva di apprendi-mento morbida, è ancora mol-to usato e conosciuto.

10 print "Hello World!"

C Nato negli anni ’70 agli AT&T

– Bell Labs, è il linguaggio incui furono scritte vere e pro-prie pietre miliari dell’informa-tica, fra cui UNIX, il TCP/IP e XWindows.

Assomiglia per certi versi alPascal, ma è meno rigoroso,consente “trucchi e scorcia-toie” discutibili da un punto divista teorico ma assai graditeai programmatori di sistemache vogliono un controllo sul-l’hardware simile all’assemblersenza rinunciare alle comoditàe alla “disciplina” di un lin-

guaggio di alto livello come ilPascal.

Per un lasso di tempo diquasi 20 anni è stato il linguag-gio “generico” più diffuso.

È ancora molto usato in am-biente UNIX, meno in ambientePC dove strumenti più moder-ni sono largamente disponibilida tempo.

#include <stdio.h>main() {

printf("Hello World\n");}

C++Arricchisce un linguaggio di

impiego generale e di enormediffusione, il C, con il concettodi programmazione orientataagli oggetti o alle classi (OOP)in precedenza proposta da lin-guaggi mai realmente uscitidall’ambiente accademico, co-me Eiffel, o di nicchia, comeSmalltalk.

Il tutto senza importante de-grado di prestazioni. Questecaratteristiche ne spiegano larapidissima adozione (curvadi apprendimento “non ripida”per chi proveniva dal C) e lalongevità (il paradigma OOP loha reso idoneo a trattare ap-plicazioni complesse e a sup-portare metodologie di proget-to moderne).

#include <iostream.h>void main(){

cout << "Hello World" << endl;}

JavaAbbastanza simile al C++

per quanto riguarda la sintassi,costituisce un importantissi-mo passo avanti dal punto divista della portabilità, della ric-chezza delle librerie a corredo,del supporto per il Web, l’in-ternazionalizzazione e la pro-grammazione concorrente,della prevenzione degli errori,della facilità d’uso, della rapi-dità con cui possono esserescritti programmi complessi.La contropartita è un certo de-grado di prestazioni rispetto alC++, avvertibile soprattuttocon applicazioni grafiche com-plesse.

class HelloWorld {public static void main

(String args[]) {System.out.print("Hello World ");}

}

Page 10: Corso_java.pdf

Java 1a lezione

5 Strutture di controlloEsecuzione condizionale (costrutto if)

Pseudocodice Esempio Java Flow chart

Se è vero che (condizione logica) if(a==4)allora esegui (blocco di operazioni 1) { /* ..operazioni 1.. */ }altrimenti esegui (blocco di operazioni 2) else

{ /* ..operazioni 2.. */ }

Selezione alternative (costrutto switch)

Pseudocodice Esempio Java Flow chart

Considera il valore di (variabile): switch(a){

Nel caso (valore1) esegui (gruppo operazioni 1) case 5: /* ..operazioni.. */break;

Nel caso (valore2) esegui (gruppo operazioni 2) case 71:... /* ..operazioni.. */

break;Nel caso (valoreN) esegui (gruppo operazioni N)

In tutti gli altri casi esegui default:(gruppo operazioni per gli altri casi) /* ..operazioni.. */

break;}

Ripetizione semplice – variante a condizione iniziale (costrutto while)

Pseudocodice Esempio Java Flow chart

Fintantochè risulta vero che while(a < 100)(condizione logica) ripeti (blocco operazioni) { /* .. operazioni .. */ }

Ripetizione semplice – variante a condizione finale (costrutto do..while)

Pseudocodice Esempio Java Flow chart

Esegui (blocco operazioni); se risulta vero doche (condizione logica) ripeti il tutto { /* .. operazioni .. */ }

while (a < 100);

Ripetizione con operazioni di controllo o conteggio (costrutto for)

Pseudocodice Esempio Java Flow chart

Esegui (inizializzazione); for(i=1; i<100; i=i+1)dopodichè: {se è vero (condizione logica) /* .. operazioni .. */allora esegui (blocco istruzioni); }poi esegui (aggiornamento valori);quindi ripeti quanto sopra eccetto inizializzazione

Page 11: Corso_java.pdf

Java 2a lezione

Nella prima puntata abbia-mo affrontato i problemiconcettuali di base della

programmazione introducen-do nel contempo la terminolo-gia essenziale e i costrutti lo-gici e sintattici di uso più fre-quente.

In parallelo abbiamo vistocome allestire ed utilizzare unambiente di programmazioneper Java, arrivando a realizza-re un primo semplice pro-gramma funzionante, seppureprivo di interfaccia grafica o diinterazione con rete e file.

L'obiettivo della seconda le-zione del corso, che vi propo-niamo questo mese, è quello dicompletare il bagaglio di con-cetti di base. Andremo a illu-strare concetti quali le dichia-razioni, gli identificatori, i tipi egli eventi per poi introdurre lenozioni fondamentali di pro-grammazione orientata agli og-getti e di programmazione aeventi, concludendo con unprogramma in Java dotato diinterfaccia grafica in grado divisualizzare immagini caricateda disco.

� A scuola con PC Open

ProgrammareJavaGli oggetti

1 Dichiarazioni, tipi e identificatori

In questo secondo appuntamento andremo ad approfondire i concettifondamentali del linguaggio per poi entrare nel vivo della programmazione: Java ci aiuterà a scoprire oggetti, eventi e interfacce grafiche

Lezione 1Rudimenti di programmazione- Terminologia essenziale- Dall’algoritmo al programma- Preparazione dell’ambienteJava

- Il primo programma

Lezione 2Elementi di programmazione a oggetti e grafica- Tipi e identificatori- Astrazione funzionale

- Programmazione a oggetti- Creare una GUI

Lezione 3File- Salvare i dati- Elaborare i dati

Lezione 4Networking- Programmi client-server- Interazione con risorse Web

IL CALENDARIO DELLE LEZIONI

di Marco Mussini

Abbiamo già avuto modo disottolineare che dalle cin-que categorie principali a

cui appartengono le operazio-ni eseguibili con un computer,tre (ricordare, calcolare, co-municare) riguardano in modoprevalente la manipolazione didati.

Molti linguaggi esigono cheil programmatore specifichiesplicitamente, attraverso unadichiarazione, la natura dei da-ti prima di usarli: questi lin-guaggi sono detti tipati o forte-mente tipati.

L’operazione di dichiarazio-ne ha lo scopo di attribuire allavariabile o costante:• un nome univoco (identifica-

tore) per potersi riferire adessa. Un identificatore è unaparola che obbedisce a benprecise regole. Esistono inol-tre delle convenzioni larga-

mente adottate che aiutano asceglierli in modo tale da mi-gliorare la leggibilità del pro-gramma. È caldamente rac-comandabile seguire questeprassi se si desidera che ipropri programmi risultinochiari e ben comprensibili (siveda più avanti il paragrafoIdentificatori: una scelta deli-cata per tutti i dettagli):

• un tipo, che implicitamentedetermina quali saranno leoperazioni che potranno es-servi effettuate;

• una quantità appropriata dispazio in memoria per ospi-tarne il valore. È evidente chequesta quantità dipende daltipo; se per un boolean puòbastare 1 bit, per un doublene servono 64.Quando si devono dichiara-

re più variabili dello stesso tipoè possibile raggrupparle in

un'unica dichiarazione "collet-tiva", su un'unica linea di codi-ce, in una lista separata da vir-gole.

Ecco alcuni esempi di di-chiarazioni semplici e multiple:

byte val_piccolo;short contatore;int a,i,j,k;long bigValue;float c, _valore;double totale;boolean stato, decisione;char b12;

Altri linguaggi (soprattutto ilinguaggi di scripting e, in misu-ra minore, certe varianti di Ba-sic) consentono al program-matore di prendersi la libertàdi usare i dati senza specificar-ne in anticipo il tipo: sarà ilcomputer a tentare di determi-nare in che modo vadano in-

terpretati i dati, in base allecircostanze. Per esempio, lastringa di testo “123” potrebbeessere trattata come un nume-ro se sommata al numero 456,oppure come una stringa di te-sto se si tentasse di concate-narla alla stringa “ABC”.

Nei linguaggi a oggetti esisteinfine, come vedremo, un’ulte-riore scenario: i tipi devono ob-bligatoriamente essere dichia-rati nel programma, ma duran-te l’esecuzione possono “cam-biare”.

Va sottolineato che il fattoche un linguaggio pretenda chei tipi vengano dichiarati nondeve essere considerato un fat-to negativo. Si tratta invece diun potente strumento per laprevenzione degli errori e perdefinire norme rigorose nelprogetto software. Infatti, in unlinguaggio che effettua “acro-

Page 12: Corso_java.pdf

Java 2a lezione

bazie automatiche” per adatta-re i tipi di dato alle circostanze,un errore di progettazione del-la struttura dati del program-ma potrebbe restare latente eprodurre conseguenze nefastemolto più tardi. Un linguaggiofortemente tipato, invece, ob-bliga il programmatore a unamaggior attenzione nel mo-mento in cui scrive il codice,ma in compenso è in grado diaccorgersi di errori e ambiguitàe li segnala immediatamente.

Per esempio, l’espressione“123”+456 potrebbe essere in-tesa come una somma aritme-tica il cui primo addendo è sta-to mal espresso, oppure comeuna operazione di concatena-mento fra due stringhe, in cuila seconda stringa è stata erro-neamente indicata come un nu-mero. Il compilatore in questocaso potrebbe segnalare che sistanno combinando tipi in-compatibili e pretendere che ilprogrammatore intervengaesplicitamente con una con-versione di tipo per otteneredalla stringa di testo “123” ilnumero 123 (se intendeva otte-nere una somma), oppure percostruire la stringa “456” a par-tire dal numero 456 (se punta-va invece a un concatenamen-to fra stringhe).

In questo esempio l’incom-patibilità di tipo è evidente per-chè i valori sono espliciti; se sitrattasse di variabili di tipi di-versi lo sarebbe molto meno(per esempio: nell’espressionex+y chi ci assicura, a prima vi-sta, che x e y siano di tipi com-patibili?)

Identificatori: una scelta delicata

Gli identificatori sono nomiunivoci utilizzati per riferirsi a

variabili, costanti, classi, fun-zioni, procedure. In Java, comein quasi tutti i linguaggi, unidentificatore può conteneresolo caratteri alfabetici o nu-merici più il carattere “under-score” (“_”), con l'ulteriore re-strizione che il primo caratterenon può mai essere un caratte-re numerico. La distinzione framinuscole e maiuscole è signi-ficativa.

Identificatori validi sono,per esempio:

a, b12 , contatore, listaDati, cal-cola_valore_numero, _temp ,a_71

Ortografia, sintassi e semantica

Oltre a queste regole impo-ste dal linguaggio, che se nonrispettate causano il rifiuto delprogramma da parte del com-pilatore, ve ne sono altre chesono "solo" raccomandazioni.Il loro scopo è quello di diffe-renziare anche a prima vista l'a-spetto degli identificatori de-stinati a impieghi diversi. Veditabella sottostante.

La questione lunghezzaÈ sempre raccomandabile

fare in modo che il nome di unmetodo (funzione o procedu-ra) dia l'idea di ciò di cui quelmetodo si occupa. Questo per-mette di interpretare facilmen-te il codice: per esempio

max=trova_valore_massimo(vettore);

è un'istruzione comprensibi-le anche senza conoscere neidettagli il funzionamento dellafunzione trova_valore_massi-mo, mentre a=elabora(b);

è talmente vaga da obbligareil lettore, per capire qualcosa,ad esaminare il tipo di a, il tipodi b e il codice della funzioneelabora.

D'altra parte non bisogna la-sciarsi prendere la mano esa-gerando con i dettagli: la pro-lissità è un boomerang

valore_massimo=scandisci_vettore_e_identifica_valore_massimo(vettore_dati)

Si deve sempre cercare uncompromesso fra espressivitàe leggibilità ed evitare comun-que di scegliere identificatoripiù lunghi di una ventina di ca-ratteri.

Generalmente è accettabileusare nomi corti e poco espres-sivi per variabili di interesse lo-cale, il cui senso può facilmen-te essere dedotto esaminando"i dintorni", mentre è impor-tante scegliere molto accurata-mente i nomi degli identificato-ri che saranno usati in lungo ein largo nel programma.

Al di là della questione lun-ghezza, per l'ortografia degliidentificatori lunghi e discorsi-vi esistono due alternativeclassiche:• Uso di underscore per sepa-

rare le parole: costruisci_vet-tore_dati

• Uso delle maiuscole per evi-denziare l'inizio di ogni paro-la: costruisciVettoreDati

Il primo metodo era preva-lente in ambito C e (in misuraminore) C++. Il secondo meto-do, che oltretutto produceidentificatori leggermente piùcorti, tende a essere preferitoin Java, anche perchè adottato

dalle librerie standard del lin-guaggio. Fra l'altro questa con-venzione lascia il carattere un-derscore meno inflazionato equindi libero per altri scopi (adesempio, usato in prima posi-zione può suggerire che la va-riabile è privata o di interesselocale).

Parole chiave riservateL'ultimo importante vincolo

che gli identificatori devono ri-spettare è quello di non coinci-dere con una delle parole chia-ve riservate del linguaggio, pernon creare ambiguità sintatticain fase di compilazione. Ve nesono circa 50, fra le quali ab-biamo già incontrato for, whi-le, do, if, else, switch, case,class, int, byte, short, long,float, double, char, boolean,void e così via; l'elenco com-pleto è a http://java.sun.com/docs/books/tutorial/java/nut-sandbolts/_keywords.html

Generalmente si può direche scegliendo identificatori inlingua italiana si riduce il ri-schio di collisioni (ma atten-zione a omonimi come case,super, private, volatile). Sicu-ramente gli identificatori conalmeno una maiuscola o un-derscore sono al riparo da que-sti rischi.

Tipi di datoI dati che un computer è in

grado di manipolare possonoessere usati per rappresentareinformazioni di qualunque ti-po. Anche se per l’elettronicadel computer si tratta sempre esolo di un insieme di bit, il mo-do di trattarli ed elaborarli èovviamente diverso se questibit servono per rappresentarenumeri, testo, immagini, suonio altro ancora.

Entità da denominare Prassi ortografica Sintassi raccomandata Semantica raccomandata Esempi di identificatoriCostante Scrittura tutto MAIUSCOLO sostantivo singolare entità PIGRECO, MAX_SIZE

Variabile, scalariIniziale minuscola sostantivo singolare risultato, entità, concetto

i, count, numValori,attributo, non booleani last_val, impiegatoparametro scalari attributo singolare, predicato situazione, stato, condizione; finito, trovato, vuoto, formale booleani Iniziale minuscola nominale, participio passato, azione compiuta o da compiere ha_dati, is_empty, (scalari e non imperativo must_exit, salta_bloccobooleani) array, sostantivo plurale o risultati, entità, concetti; dati, impiegati, elenco,

vettori Iniziale minuscola singolare collettivo caratteristica comune lista, scartati, trovati

Metodo sostantivo singolare; verbo calcola, azzera_tutto, (funzione Iniziale minuscola all'imperativo azione ricercaDato, salva_lavoro, o procedura) chiusura, inizializzazione

Classe Iniziale MAIUSCOLA sostantivo o locuzione concetto; ruolo all'interno DatiPersona, Applicazione, Interfaccia del programma GestoreConnessione

Page 13: Corso_java.pdf

Java 2a lezione

Generalmente è presenteuna distinzione tra i tipi di datoche il linguaggio riconosce etratta per... “capacità innata”(tipi base, detti anche primitivio scalari) e quelli, più comples-si, che l’utente definisce permodellare informazioni più ric-che ed articolate (tipi user-de-fined).

Tipi primitiviSolitamente la categoria dei

tipi base comprende tipi di da-to come i seguenti:• numeri, distinti essenzial-

mente fra numeri interi e nu-meri reali, con numerose va-rianti dipendenti dall’ampiez-za dell’intervallo di valorirappresentabile (identificateda prefissi quali short, long,long long, double, long dou-ble) e, per i numeri interi, dal-la presenza o assenza di se-gno (unsigned)

• caratteri e, in alcuni linguag-gi, sequenze (stringhe) di ca-ratteri. Generalmente i carat-teri sono internamente rap-presentati su 8 bit, mediante

il loro codice ASCII, ma in Ja-va è stata adottata la codificaUnicode che consente di rap-presentare anche caratteri dilingue non occidentali, comeil cinese, il giapponese, il co-reano e molti altri; la contro-partita è un’occupazione dimemoria esattamente doppia(16 bit per ogni carattere).

• Valori logici (booleani): valea dire: true o false. Questi valori servono nei co-strutti condizionali e iterativi,nei quali si devono prenderedecisioni logiche in base allaverità o falsità di un’espres-sione.

• Bit e byte, per consentire lamanipolazione diretta dei va-lori ospitati in memoria senzadoverli interpretare come i ti-pi sopra indicati.Come si può vedere nell’ap-

posito riquadro, in Java è pre-visto un insieme quasi comple-to di tipi primitivi. Sono previ-sti numeri interi e reali a varieprecisioni, oltre a byte, carat-teri e booleani. Vi sono però al-cune particolarità rispetto ai

tipi base offerti da linguaggimolto diffusi come C e C++:• i tipi interi rappresentano

sempre numeri relativi (nonsono cioè disponibili tipi in-teri unsigned);

• i caratteri, che adottano lacodifica Unicode a 16 bit, so-no trattati da un tipo specifi-co (char) che viene tenutoben distinto dal tipo intero a8 bit (byte);

• la dimensione in bit dei tipiinteri è definita rigidamentenella specifica del linguaggio;non esiste quindi il rischioche, sotto questo aspetto, im-plementazioni diverse delcompilatore facciano sceltediverse (in C, un int potrebbeessere a 16 bit negli ambientiDOS, a 32 bit negli ambientiWindows e UNIX e a 64 bit sualtri ambienti)

I literalIn un listato i valori su cui il

programma esegue calcoli,confronti e decisioni possonoesistere essenzialmente in treforme:

• valori modificabili contenutiin variabili.

• valori immutabili immagazzi-nati in costanti. Una costantedeve essere inizializzata conil valore che dovrà contene-re, ma da quel momento inpoi nel programma si men-zionerà solo il nome della co-stante. Il valore in essa con-tenuto rimarrà “nascosto”dentro alla costante.

• valori immutabili espressi let-teralmente nel programma intutti i punti in cui vengonousati (literal). A differenza di quanto avvie-ne per costanti e variabili,quello che compare nel lista-to è la “rappresentazioneesteriore” del valore: peresempio, il valore 10, e non ilnome di una costante xyz checontiene il valore 10.

Come in tutti i linguaggi, an-che in Java esistono regole pre-cise su come esprimere valoriliteral per ogni tipo di dato. Siveda la tabella " I literal in Java"per maggiori dettagli.

Tipi base disponibili in Java:le loro caratteristiche e alcuni esempi raccomandati di impiegoParola Descrizione Dimensione in Codifica Intervallo di valori Esempi di dati chiave Java memoria (bit) rappresentabile rappresentabili

byte Intero byte 8 complemento -128 .... +127 Contatori per iterazionicon segno a 2 Cardinalità di insiemi

Indici posizionali in vettoriMese; giorno del mesePercentuali senza parte frazionaria

short Intero short 16 complemento -32.768 .. +32.767 Contatori per iterazionicon segno a 2 Cardinalità di insiemi

Indici posizionali in vettori; anniPercentuali senza parte frazionaria

int Intero “normale” 32 complemento -2.147.483.648 ... Contatori per iterazionicon segno a 2 2.147.483.647 Cardinalità di insiemi

Indici posizionali in vettori; CAPImporti in denaro senza parte frazionaria

long Intero long 64 complemento -9.223.372.036.854.775.808 ... Contatori per iterazionicon segno a 2 +9.223.372.036.854.775.807 Cardinalità di insiemi

Indici posizionali in vettoriImporti in denaro senza parte frazionaria

float Numero reale 32 IEEE 754 Valori positivi o negativi con valore Importi in denaro con parte frazionaria(approssimato) in ass. compreso fra 1.401298*10-45 Percentuali con parte frazionariavirgola mobile a e 3.402823*10+38 Valori per calcoli scientifici singola prec.singola precisione Grandezze fisiche - Numeri molto grandi

double Numero reale 64 IEEE 754 Valori positivi o negativi con valore Importi in denaro con parte frazionaria(approssimato) in ass. compreso fra 4,9406564*10-324 Valori per calcoli scientifici di alta prec.virgola mobile a e 1,7976931*10+308 Grandezze fisiche ad alta precisionedoppia precisione Numeri estremamente grandi

char Carattere di testo 16 Unicode Qualsiasi singolo carattere della tab. TestiUnicode (www.unicode.org/) Codici fiscali

boolean Valore logico 1 - True o false Condizioni logiche - Risultati di decisioni

Page 14: Corso_java.pdf

Java 2a lezione

Esistono alcuni caratteri chehanno un effetto particolare:interruzione di linea, confinedi stringa, e così via. È chiaroche se sorge la necessità di ci-tare nel programma proprioquesti caratteri essi non pos-sono essere citati letteralmen-te, in quanto avrebbero appun-to ... il loro effetto speciale an-zichè essere interpretati come"caratteri citati".

Per evitare simili ambiguitàquesti caratteri sono rappre-sentabili in una particolare for-ma secondaria (detta, per ra-gioni storiche, escape sequen-ce). La tabella qui sotto riassu-me questi casi particolari.

Escape Caratteresequence\\ Barra inversa (Backslash)\" Virgoletta doppia

(Double quote)\' Virgoletta singola

(Single quote)\b Cancella a sinistra (Backspace)\f Avanzamento foglio (Form feed)\n A capo / Avanzamento linea

(Newline)\r Ritorno cursore (o carrello

stampante) a inizio linea (Carriage return)

\t Tabulazione (Horizontal tab)

Tipi user-defined: i costruttori di tipo

I tipi base messi a disposi-zione dei linguaggi, pur nume-rosi, sono ancora troppo ele-mentari per rappresentare ade-guatamente le informazioni delmondo reale. Questo per dueaspetti che caratterizzano in-variabilmente tali informazioni:• numerosità e affinità. Qualun-

que programma non banalenon si limita a effettuare cal-coli su una manciata di valo-ri. È invece normale che pereffettuare elaborazioni di unaqualche utilità pratica risultinecessario manipolare gran-di quantità di dati dello stes-so tipo (migliaia o milioni).Scrivere un programma chefaccia questo sarebbe prati-camente impossibile se perogni dato elementare da ge-stire si dovesse dichiarareuna variabile elementare conun nome specifico. Ogni qual-volta esista la necessità di ef-fettuare elaborazioni su gran-di insiemi di oggetti affiniemerge l’esigenza di poter de-notare ognuno di tali insiemicon un nome collettivo e dipoter poi accedere in modoparametrico alle informazionielementari ivi contenute.

• l’articolazione e la correlazio-ne. Si tratta di due facce dellastessa medaglia. Per esem-pio, in un archivio parco vet-ture la descrizione di un sin-golo autoveicolo si articola indiverse informazioni elemen-tari, quali il numero di telaio,il numero di targa, il modello,l’anno di immatricolazione, ilchilometraggio e così via. Ve-dendo la cosa da un’altra an-golazione, si può dire chequelle informazioni elemen-tari sono correlate dal fatto diriferirsi tutte a una stessa en-tità. Sta di fatto che nel mon-do reale i dati non banali ap-paiono come insiemi di infor-mazioni elementari legate fraloro, piuttosto che come mo-noliti isolati. Appare di con-seguenza comodo poterli ge-stire come gruppi, costituen-ti vere e proprie entità strut-turate.La soluzione messa a dispo-

sizione dei linguaggi consistenell’uso di speciali costruttori ditipo che applicati ai “mattonielementari” rappresentati daitipi base consentono al pro-grammatore di costruire tipi didato più articolati, adatti a rap-presentare in forma aggregatainformazioni più “ricche”. I co-

struttori di tipo più comunicomprendono:• vettori (array): consentono

di raggruppare vari elementidello stesso tipo formandoun insieme ordinato. L’insie-me potrà essere maneggiatocome se fosse un dato unico.

• strutture con campi indipen-denti (struct; record): consen-tono di raggruppare elementidi tipi diversi.

• strutture con campi in alter-nativa fra loro (union): con-sentono di ospitare in alter-nativa dati di tipo diverso inuna stessa zona di memoria.

Gli arrayUn array è un insieme ordi-

nato di dati dello stesso tipo. Ècaratterizzato da un nome e dauna dimensione (intesa comeampiezza o cardinalità), oltreche dal tipo degli elementi con-tenuti. Una dichiarazione po-trebbe suonare per esempiocosì: “costruisci un array di1000 elementi, tutti di tipo int, echiamalo archivio”.

L’accesso ai dati contenutinell’array si effettua indicandoil nome dell’array e la posizio-ne al suo interno (indice) del-l’elemento desiderato. Ci si ri-ferisce, per esempio, a “l’ele-

Tipo di dato, formato e precisione Regola Esempi di literal

Valori logici esistono solo due valori possibili, “vero” e “falso” true, false

espressi in notazione le cifre della rappresentazione decimale, 123, +123, -123decimale eventualmente precedute dal segno

Numeri interi int espressi in notazione le cifre della rappresentazione esadecimale esadecimale (indifferentemente maiuscole o minuscole) precedute da “0x” 0x1AF, 0xa4c

espressi in notazione ottale le cifre della rappresentazione ottale precedute da “0” 011, 047long Come sopra, ma con una lettera L (anche minuscola) aggiunta in coda. 123L, 0xA3FL, 011L

espressi in notazione le cifre della rappresentazione decimale, eventualmente 123.45, +123.45, -a singola decimale precedute dal segno. Il separatore delle cifre decimali è il punto 123.45precisione espressi in notazione Mantissa espressa come sopra, seguita da “E” 123.45E6,

Numeri reali decimale esponenziale (anche minuscola) e dall’esponente eventualmente -123.45E6, +123.45E-6,in base 10 preceduto dal segno 123.45E-6, ...

a doppia Come sopra, ma con una lettera D (anche minuscola) aggiunta in coda 123.45D precisione -123.45E-6D

Il carattere (o, per caratteri non stampabili, la relativa escapeCitati letteralmente (possibile sequence) delimitato con apici singoli

'a','7','X','\n'

solo per caratteri occidentali Nel caso particolare del carattere apice singolo esso Singoli caratteri e per alcuni caratteri speciali) va preceduto con backslash per evitare ambiguità. '\''

Nel caso particolare del carattere backslash esso va preceduto da un altro backslash. '\\'

Rappresentati dal loro Il codice Unicode espresso in notazione esadecimale a 4 cifre codice Unicode (indifferentemente con lettere minuscole o maiuscole) '\u0041'

e preceduto da \u, il tutto fra apici singoliLa sequenza di caratteri delimitata da apici doppi. È ammessa "ciao", "abcdef\n123",

Stringhe di caratteri la coabitazione di caratteri letterali, escape sequences e "apice\"doppio"codici Unicode. Il carattere apice doppio deve essere precedutoda backslash per evitare ambiguità.

Il valore null la parola null scritta in minuscolo, non delimitata da apici null

I literal in Java

Page 15: Corso_java.pdf

mento di posto 267 dell’arrayarchivio”.

Un array come quello usatoper questi esempi può essereimmaginato come una schieradi 1000 caselle, ognuna conte-nente un numero intero, dispo-ste in fila indiana. Topologica-mente questo equivale a unsegmento: un luogo monodi-mensionale, descrivibile conuna sola coordinata. Per l’ac-cesso è infatti sufficiente unsolo indice. Esistono però an-che array multidimensionali,che possono essere immagina-ti come schiere di informazionielementari disposte come suuna superficie rettangolare (ar-ray bidimensionali), oppure inun volume a parallelepipedo(array tridimensionali), e cosìvia. In generale, un array N-di-mensionale richiede, per l’ac-cesso alle informazioni, esatta-mente N indici (che rappresen-tano le “coordinate” delle celledell’array nell’N-spazio).

In Java gli indici degli arraysono “a base 0”: in un array mo-nodimensionale di 100 elemen-ti, il primo elemento ha indice 0e l’ultimo ha indice 99.

Nella maggior parte dei lin-guaggi gli array hanno dimen-sioni che vengono fissate al-l’atto della dichiarazione o del-l’allocazione. Non è general-mente possibile ingrandire oridurre un array in seguito, spe-cialmente se è già caricato condati.

Le struttureUna struttura (o record) è

un costrutto che provvede al-l’aggregazione di dati di tipopotenzialmente eterogeneo,ma correlati da un qualchenesso logico. La caratterizzanoun nome e l’elenco dei conte-nuti (che sono detti campi),per ognuno dei quali vengonospecificati un nome univoco eil tipo.

A seconda delle esigenze, lastruttura può essere trattatacome un’entità unica, oppureessere vista come contenitoredi dati, l’accesso ai quali vieneeffettuato per nome.

Le unionMeritano infine un cenno le

union. Si tratta di costrutti affi-ni alle strutture ma con una im-

portante sfumatura di signifi-cato.

Mentre nelle strutture i daticontenuti nei campi coesistono(in altre parole in memoria lastruttura ha dimensioni pari al-la somma degli ingombri deidati che contiene), nelle unioni campi, pur chiaramente di-stinti a livello di nome, posso-no essere usati solo in alterna-tiva, in quanto lo spazio è suffi-ciente per contenerne uno sol-tanto.

Questo tipo di strutture ri-sulta utile quando economizza-re memoria sia una priorità equando i dati da rappresentarecontengano effettivamente de-gli aspetti che possono ricorre-re solo in alternativa.

Per esempio, in una struttu-ra dati “dati autoveicolo” de-stinata a modellare sia auto-vetture sia autocarri, potrem-mo avere sia un campo intero“numero assi” (significativoper autocarri, superfluo per au-tomobili) sia un campo boolea-no “tetto apribile” (significati-vo per automobili, probabil-mente inutile per autocarri).

È evidente che riservare spa-

zio in memoria per entrambequeste informazioni sarebbeun inutile spreco: non potreb-bero essere mai presenti e si-gnificative allo stesso tempo.Utilizzando le union è possibileevidenziare questa circostan-za e sfruttarla per ottimizzarel’uso della memoria, dichiaran-do che questi due campi sonoalternativi e non “simultanei”.

Le strutture e le union sonodisponibili in molti linguaggi,fra cui C e C++, ma non in Java.Come vedremo più avanti, leclassi Java possono essere usa-te come sostituti delle struttu-re, mentre le union non sonodisponibili.

Combinazione di array e strutture

I costruttori di tipo appenaintrodotti, in particolare arraye strutture, supportano rispet-tivamente l’equivalente sui da-ti delle operazioni di iterazionee di selezione.

È possibile, anzi frequente,utilizzarli anche insieme perdefinire strutture dati ancorapiù articolate.

Per esempio, un array di 100

Java 2a lezione

Caratteristiche dell’array

Realtà modellata

Esempio di dichiarazionein Java

Esempio d’uso in Java

Array bidimensionaledi 1000x5 float

Il saldo (positivo o negativo) dei 1000conti correnti di ciascuna delle 5 filialidi una banca

float[][] conticorr;

// allocazione array // con unica istruzioneconticorr= new float[5] [1000];

// in alternativaconticorr=new float[5] [];conticorr[0]=new float[1000];conticorr[1]=new float[1000];conticorr[2]=new float[1000];conticorr[3]=new float[1000];conticorr[4]=new float[1000];

accredito 142.87 euro su conto n.671 della filiale n.3

conticorr[3][671] += 142.87;

addebito 0.5% (una tantum) su conto n.236 della filiale n.1

conticorr[1][236] *= 0.95;

Array tridimensionale di 12x31x5 float

Le temperature rilevate da una rete di 5 sensori

float[][][] rilevamenti;// allocazione "atomica"rilevamenti=new float[12][31][5];

// allocazione progressivarilevamenti=new float[12][][];rilevamenti[0]=new float[31][];rilevamenti[0][0]=new float[5];rilevamenti[0][1]=new float[5];// ecc...rilevamenti[0][30]=new float[5];rilevamenti[1][0]=new float[5];rilevamenti[1][1]=new float[5];// ecc... ecc...rilevamenti[11][30]=new float[5];

verificare se il 3(2) ottobre(9)la quinta(4) centralinaha rilevato una temperaturasuperiore a 16.4 gradi

if(rilevamenti[9][2][4]>16.4){// sopra la media stagionale!

}

Array quadridimensionale di 30x10x100x365 boolean

Lo stato “prestato/disponibile” dei libridi una biblioteca disposti in 30 sale con10 scaffali da 100 libri l’una, nei 365giorni dell’anno

boolean[][][][] stato_libri;stato_libri = new

boolean [30] [10] [100] [365];

il 212° giorno dell'anno è statoprestato l'88esimo libro del settimoscaffale della terza sala

stato_libri[2][6][87][211]=false;

Esempi di applicazioni di array

Page 16: Corso_java.pdf

strutture “dati personali” (ma-gari con l’aggiunta di un campo“numero_ matricola”) potreb-be rappresentare l’elenco deidipendenti di un’azienda; op-pure, in una struttura “dati delcontribuente” potrebbe figura-re un campo di tipo “array distringhe” contenente l’elencodei familiari a carico.

Nella figura qui a fianco ve-diamo un esempio di questo ti-po.

Supponendo definita unastruttura DatiDipendente, se nedichiara un array di 100 ele-menti.

Il tutto rappresenta un ar-chivio di dati personali acces-sibile come mostrato.

Java 2a lezione

// dichiarazione archivio // array di 100 struttureDatiDipendente[] archivioDipendenti = new DatiDipendente[100];

// caricamento datiarchivioDipendenti[73].nome = “Mario”archivioDipendenti[73].cognome = “Rossi”archivioDipendenti[73].numero_matricola = 16353// ecc..

Insieme di campi

Applicazione

Esempio di dichiarazionein Java

Esempio d’uso in Java

nome (stringa), cognome (stringa),età (intero), sesso (booleano),indirizzo (stringa)

Dati anagrafici di una persona

class DatiPersona{String nome;String cognome;int eta;boolean sesso;String indirizzo;

}

dichiara una variabile per i dati del direttoreDatiPersona direttore;

crea e inizializza un record per il direttoredirettore=new DatiPersona();direttore.nome="Mario";direttore.cognome="Rossi";direttore.eta=52;

oggi e' il compleanno del direttore...direttore.eta ++;

parte reale (float), parte immaginaria(float)

Numero complesso in coordinatecartesiane

class NumComplesso{float reale;float immaginaria;

}

dichiara due numeri complessiNumComplesso valore1,valore2;

crea e inizializza valore1valore1=new NumComplesso();valore1.reale=24.3f;valore1.immaginaria=5.03f;

somma valore2 a valore1valore1.reale += valore2.reale;

valore1.immaginaria += valore2.immaginaria;

cilindrata (intero), numero porte (byte),tipo alimentazione (carattere), targa(stringa)

Caratteristiche di un’autovettura

class Autovettura{int cilindrata;byte numero_porte;char tipo_alim;String targa;

}

Dichiara una variabileAutovettura x;

// (crea e inizializza..)

Determina una qualche tariffa in basealla cilindratafloat tariffa;if(x.cilindrata > 1600){tariffa=10f;}

else{tariffa=6.5f;}

Esempi di applicazioni di strutture

2 Astrazione funzionaleProcedure e funzioni

Come abbiamo visto nellaprima puntata, praticamentetutti i linguaggi di programma-zione offrono strutture sintatti-che che consentono di espri-mere con maggior chiarezza(anche visiva) gli algoritmi.

In particolare, i costrutti ite-rativi catturano e schematizza-no il concetto di ripetizione;quelli condizionali fanno altret-tanto per il concetto di deci-sione o selezione.

Questi accorgimenti agevo-lano soprattutto la lettura e lacomprensione di algoritmi dicomplessità medio-bassa, lacui espressione in linguaggio diprogrammazione è di limitataestensione.

Per algoritmi di grande com-plessità, tuttavia, essi non ri-sultano sufficienti per chiarireil senso del programma, chepotrebbe essere lungo decine ocentinaia di migliaia di linee:una dimensione troppo ampiaperchè la logica sottostantepossa essere colta dal lettore,anche facendo ricorso a inden-tazione e a strutture iterative econdizionali perfettamentecongegnate.

Quello che serve è un terzoaccorgimento che consenta leseguenti strategie di semplifi-cazione interpretativa edespressiva: • fornire dell’algoritmo una pri-

ma descrizione per grandi li-nee, confinando l’approfon-

dimento sui dettagli in sepa-rata sede ed eventualmenterimandandone l’esatta defini-zione, che in un primo mo-mento può essere data perscontata; (approccio topdown: dall’alto in basso)

• identificare eventuali opera-zioni identiche o molto similiche vengono ripetute in varipunti del programma e defi-nirle una sola volta, evitandoinutili ridondanze e consen-tendo di esprimere operazio-ni sempre più complesse intermini di operazioni piùsemplici definite in prece-denza, fino a creare le condi-zioni per poter esprimere ilprogramma vero e proprio(approccio bottom up: dal

basso verso l’alto)L’accorgimento che consen-

te entrambe le filosofie proget-tuali sopra richiamate si chia-ma astrazione funzionale ed icostrutti sintattici che lo tra-ducono in pratica sono dettifunzioni e procedure. Seppurequalcosa di equivalente esi-stesse già, da un punto di vistalogico, fin dai tempi del lin-guaggio macchina, è stato con ilinguaggi strutturati come Al-gol 68, C e Pascal che l’astra-zione funzionale diventò unavera e propria “architrave” del-la metodologia di progetto deiprogrammi: questa tecnica èstata sposata da questi lin-guaggi in modo così pervasivoche l’applicazione dei concetti

Page 17: Corso_java.pdf

Java 2a lezione

di procedura e funzione non èconfinata ad algoritmi di gran-de complessità, ma del tuttogeneralizzata. Tutti i program-mi C, per esempio, sono intera-mente decomposti in funzioni edeve sempre esistere una fun-zione con uno speciale nome(“main”) da cui inizia l’esecu-zione del programma.

SottoprogrammiGrazie all’astrazione funzio-

nale è possibile “catturare” unaparte ben identificata dell’al-goritmo complessivo in unaporzione del programma chia-ramente delimitata (sottopro-gramma), che è caratterizzatada un nome univoco e interagi-sce con il resto del programmaattraverso uno scambio di dati.I dati in ingresso sono detti pa-rametri e i dati in uscita valoridi ritorno.

Solitamente un sottopro-gramma che accetta parametri,ma non produce valori di ritor-no è detto procedura. Se sonoprevisti valori di ritorno si par-la invece di funzione.

In questo contesto il termine“funzione” è usato in assolutaanalogia con il concetto mate-matico di funzione, intesa co-me corrispondenza fra valoridelle variabili indipendenti(che corrispondono ai parame-tri presi dalla funzione) e valo-ri della variabile dipendente(che corrisponde al valore di ri-torno). Il sottoprogramma defi-nito “funzione” si occupa pre-cisamente di effettuare i calco-li necessari per determinare ilvalore di ritorno corrisponden-te ai valori ricevuti nei parame-tri.

Naturalmente parametri evalori di ritorno non sono altro

che dati, proprio come variabi-li e costanti: di conseguenzaanche per essi è necessariospecificare chiaramente il tipo.

La dichiarazione del tipo (edel significato) di parametri evalori di ritorno è di importan-za capitale per funzioni e pro-cedure, in quanto essi costitui-scono l’interfaccia di contattofra questi sottoprogrammi e ilresto del programma principa-le. Poter contare su una defini-zione chiara e inequivocabiledi questa interfaccia è quindiindispensabile per poter riuti-lizzare facilmente il sottopro-gramma in più occasioni, al li-mite anche in programmi di-versi, senza bisogno di esami-narlo ogni volta per compren-derne il funzionamento.

Possiamo paragonare que-sta esigenza di precisione a

quanto avviene in meccanica,per l’identificazione delle ca-ratteristiche di qualunquecomponente di un macchinariocomplesso: dal tipo di filettatu-ra di una vite alle dimensioni eall’angolo dei denti di un ingra-naggio, solo l’esistenza di unaesatta e schematica classifica-zione simbolica degli aspettiche determinano l’interazionecon il resto del sistema con-sente di progettare “in astrat-to”, velocemente e senza erro-ri.

Alcuni esempi di funzioni eprocedure sono riportati nel ri-quadro. Nelle funzioni si notil'uso della parola chiave returnper specificare qual è il valoredi ritorno della funzione. Quan-do l'espressione return vieneraggiunta, l'esecuzione dellafunzione termina e l'espressio-

ne che segue la parola returnviene calcolata e ritornata alchiamante come valore dellafunzione.

In realtà la parola return puòessere usata anche nelle pro-cedure, ma senza parametri.Le procedure infatti non ritor-nano valore. In questo caso re-turn ha solo l'effetto di far ter-minare immediatamente l'ese-cuzione della procedura, senzaattendere il normale decorsodella sequenza di istruzioni.

In Java le procedure si pre-sentano assai simili alle fun-zioni. Di fatto l'unica differen-za sta nel tipo di ritorno, cheper le procedure deve esseredichiarato del tipo fittiziovoid (il vuoto, il nulla), a si-gnificare che il chiamante nondeve attendersi alcun valoredi ritorno.

Esempi di sottoprogrammiEsempi di funzionicalcolo dell’area di un quadrato:float area_quadrato(float lato){

return lato * lato;}

calcolo (semplificato....) dell’imposta IRPEF:float calcola_IRPEF(float reddito_lordo, int n_figli_a_carico, float totale_detrazioni){

float ris = reddito_lordo*0.35 – 100*n_figli_a_carico – totale_detrazioni;if(ris > 0){return ris;}else{return 0;}

}

Esempi di procedureSalva il lavoro su disco (solo dichiarazione)void salva(String dati_da_salvare, String nome_file)

Invia una e-mail (solo dichiarazione)void invia(String email_destinatario, String titolo, String messaggio)

3 Elementi di programmazione a oggettiLa scorsa puntata si è con-

clusa con la presentazionedi un semplice programma

completo scritto in Java: un lin-guaggio che, come è stato ri-cordato, appartiene alla cate-goria dei linguaggi a oggetti.Prima di procedere a un’analisipiù approfondita dell’esempiopresentato introdurremo dun-que i concetti fondamentali diprogrammazione a oggetti.

I programmi scritti in lin-guaggi procedurali strutturati,

come il C e il Pascal, possonoconsiderarsi impostati sull’ar-chitrave logica rappresentatadall’algoritmo. L’aspetto mag-giormente enfatizzato è “come”agire piuttosto che “su qualidati” agire. I dati servono peralimentare l’algoritmo (input) ene costituiscono il risultato(output).

Se dovessimo scrivere inuno di questi linguaggi una li-breria (compendio tematico difunzioni) per la gestione di fi-

gure geometriche, questa po-trebbe essere costituita da tan-te funzioni separate che effet-tuano ognuna una certa opera-zione – solitamente identificatadal titolo della funzione stessa– lavorando sui parametri rice-vuti in ingresso. Per esempio:

float area_quadrato(float lato) {return lato*lato;}

float area_rettangolo(float base, float altezza) {return base*altezza;}

float area_triangolo(float base, float altezza) {return base*altezza/2;}

float risultato;risultato=area_quadrato(120.5,

67.1);come si vede, quello che esisteè un repertorio di funzioni checopre i casi da gestire, ma quel-lo che sfugge a questa impo-stazione è il concetto di figurageometrica di cui si vuole cal-colare l’area. Le funzioni effet-tuano il calcolo puro e sempli-

Page 18: Corso_java.pdf

Java 2a lezione

ce dell’area, ma esigono chevengano forniti i dati elementa-ri necessari per assolvere a ta-le compito.Il fatto che si tratti di figuregeometriche e del calcolo del-l’area è messo in evidenza solodal titolo delle funzioni: inrealtà le funzioni lavorano sunumeri puri e semplici, sui qua-li fanno solo calcoli aritmetici.

Questo ha un’altra implica-zione importante. Sta al pro-grammatore ricordarsi di chia-mare la funzione giusta (cheavrà, naturalmente, un suo no-me specifico) per ogni circo-stanza. Per esempio, per calco-lare l’area di un rettangolo do-vrà avere cura di chiamarearea_rettangolo e nonarea_triangolo, che ha anch’es-sa due parametri dello stessotipo e potrebbe essere confusacon la prima.

Questa situazione presentadegli inconvenienti. Innanzitut-to si chiede al programmatoreuno sforzo mnemonico aggiun-tivo: nonostante concettual-mente si stia sempre facendo lastessa cosa (calcolo di un’a-rea), di fatto il linguaggio ri-chiede di chiamare funzioni di-verse a seconda della figura datrattare. Saranno quindi neces-sarie decisioni del tipo "se è unrettangolo chiama la funzioneF1, se è un quadrato chiama lafunzione F2,...", di conseguenzail programma si allungherà di-ventando fra l'altro meno chia-ro. Nasce inoltre il rischio che ilprogrammatore commetta unerrore nella scelta della funzio-ne da chiamare caso per caso,compromettendo il buon fun-zionamento del programma.

Ma non è finita. Questa tec-nica non consente di cogliere esfruttare le affinità esistenti frale figure geometriche.

Per esempio, restando fra iquadrilateri, il rombo e il qua-drato hanno una legge ugualeper quanto riguarda il calcolodel perimetro, mentre per l’a-rea ciò vale per il rombo e il ret-tangolo.

Invece di scrivere 3x2=6 fun-zioni (una per il perimetro euna per l’area, per ognuna del-le 3 figure trattate) ne potreb-bero bastare di meno.

Certo si potrebbe impostarela libreria così:

float area_quadrato(float lato){return lato*lato;}

float area_rettangolo_oppure_rombo(float base, float altezza) {return base*altezza;}

float perimetro_quadrato_oppure_ rombo(float lato){return lato*4;}

float perimetro_rettangolo(float base, float altezza){return base*2+altezza*2;}

ma si intuisce subito che se leentità da trattare non fossero 3,ma 300, e se le affinità non fos-sero così poche ed evidenti,ma più numerose e sottili, que-sto approccio diventerebbe in-gestibile:• il numero delle funzioni da

scrivere si moltiplicherebbeesponenzialmente.

• i titoli delle funzioni divente-rebbero rapidamente “di-scorsi” illeggibili.

• al programmatore sarebbe ri-chiesto uno sforzo mnemoni-co inaccettabile per ricordarenomi e campo di applicazio-ne delle funzioni “polivalen-ti”.

• la percezione di quale siaesattamente l'insieme delle fi-gure trattate dal programmasi fa piuttosto confusa.

• l'aggiunta di un'eventualenuova figura, o qualche mo-difica alle caratteristiche diquelle esistenti, potrebbe ri-chiedere uno sforzo di ripen-samento generale del pro-gramma.

Un approccio orientato agli oggetti

Se si utilizza un linguaggio aoggetti il progetto prende piut-tosto l’avvio dalla modellizza-zione delle entità da trattare.Anzitutto si identificano affi-nità fra di esse; gruppi di entitàaccomunate da determinatecaratteristiche rilevanti ai finidell’elaborazione vengono ca-tegorizzati definendo una clas-se e dichiarandoli appartenen-ti a quella classe.

Gli esemplari di una certaclasse (istanze di quella clas-se) sono poi detti oggetti,ognuno dei quali ha la propriaidentità.

Gli oggetti sono poi caratte-rizzati da uno stato che descri-ve la condizione in cui si trova-no e (se è sicuramente univo-co) può essere anche usato peridentificarli. Sugli oggetti è inol-tre possibile effettuare delleoperazioni, il cui effetto prima-rio è spesso quello di cambiar-ne lo stato, ma può anche es-sere semplicemente quello di“esportare” informazioni basa-te sullo stato.

Ne segue che per definireuna classe di oggetti è neces-sario definire una forma di de-

scrizione dello stato (normal-mente questa consiste in un in-sieme di attributi) e un elencodi operazioni (metodi).

Istanze distinte di una stessaclasse sono entità indipenden-ti, ognuna delle quali, in gene-rale, potrà avere uno stato di-verso (in altre parole, gli attri-buti avranno un diverso insie-me di valori).

Normalmente ogni classe de-finisce uno speciale metodo,detto costruttore, che consen-te di creare un nuovo oggetto(istanza) appartenente a quellaclasse. Questo metodo non ri-chiede di dichiarare il tipo di ri-torno, nè di usare la parolachiave return. Il tipo del suo va-lore di ritorno è implicitamentela classe a cui appartiene e ilvalore ritornato è l'oggetto cheè stato creato. Come succedeper funzioni e procedure, an-che il costruttore può richiede-re dei parametri, che di solitosono usati per inizializzare lostato del "neonato" oggetto.

Se dovessimo riscrivere aoggetti la libreria sopra men-zionata, potremmo cominciareosservando che le figure datrattare nel programma sonoriconducibili a 4 categorie im-mediatamente riconoscibili:triangolo, quadrato, rombo erettangolo. Questo suggeriscesubito di definirle come classi.

C’è poi la questione di defi-nire che cos'è lo stato perognuna delle classi appena de-finite. Che cosa caratterizzacompletamente un quadrato(ed eventualmente lo differen-zia da altri quadrati)? Il valoredel lato. Nella classe quadrato,quindi, occorrerà un solo attri-buto, lato. Con quest’unicainformazione è possibile calco-lare area e perimetro.

Per un rettangolo e un rom-bo, per essere completamentedescritta la figura richiede dueinformazioni: base (che per ilrombo vale anche come lato) ealtezza. Per un triangolo occor-re anche conoscere la lunghez-za dei tre lati, altrimenti saràimpossibile calcolare il peri-metro.

Poiché, qualunque sia la fi-gura, è richiesto di poter cal-colare area e perimetro, le no-stre classi dovranno quindiavere due metodi ognuna: na-turalmente li chiameremmoarea e perimetro.

In totale scriveremmo quin-di 4 classi e 8 metodi (listato 1).

Questa modellizzazione hail pregio di separare chiara-

mente il codice che tratta ognifigura e la definizione dei datinecessari. Tuttavia non è pie-namente soddisfacente per-chè:• non dà conto del fatto che,

pur con le differenze esisten-ti, tutti questi poligoni sonofigure geometriche.

• alcuni metodi e alcuni attri-buti sono identici in più clas-si. L'ovvia parentela esisten-te, per esempio, tra un qua-drato e un rettangolo, o traun quadrato e un rombo, nonviene sfruttata nè per rispar-miare memoria nè per evitareripetizioni nell'implementa-zione dei metodi.È a questo punto che entra

in gioco uno strumento di im-portanza fondamentale nelprogetto dell'applicazione a og-getti: l'ereditarietà. Così comele classi colgono le caratteristi- �

Listato 1public class Geometria1{

class Quadrato{

float lato;public float area(){return lato*lato;}

public float perimetro(){return lato*4;}

}

class Rombo{

float base; // = latofloat altezza;public float area(){return base*altezza;}

public float perimetro(){return base*4;}

}

class Rettangolo{

float base;float altezza;public float area(){return base*altezza;}

public float perimetro(){return base*2 + altezza*2;}

}

class Triangolo{

float base; // = lato1float altezza;float lato2;float lato3;public float area(){return base*altezza/2;}

public float perimetro(){return base+lato2+lato3;}

}}

Page 19: Corso_java.pdf

Java 2a lezione

che comuni fra gli oggetti, la re-lazione di ereditarietà fra classicoglie le analogie fra le classi.

Così come il quadrato da 3x3cm e il quadrato da 92x92 cmappartengono tutti alla classedei quadrati, possiamo osser-vare che le classi Quadrato,Rettangolo e Rombo apparten-gono tutti alla "super-classe"dei Quadrilateri. Il ragiona-mento si può reiterare, osser-vando che le classi Quadrilate-ri e Triangoli appartengono al-la... "super-super-classe" deiPoligoni.

Questo tipo di situazione sipuò raffigurare in appositi dia-grammi come quello mostratoin (fig. 1). Le frecce indicanouna relazione di ereditarietà esono orientate dall'erede versol'antenato. Il codice corrispon-dente al diagramma è riportatonel listato

Con riferimento all'esempio,si dice che la classe Quadrila-tero "eredita dalla" (o "derivadalla", o "specializza la", o"estende la") classe Poligono,oppure che la superclasse del-la classe Quadrilatero è la clas-se Poligono, oppure che la clas-se Quadrilatero è una sotto-classe della classe Poligono. Di-re che una classe B eredita dauna classe A significa che tuttii metodi ed attributi definiti inA, che non siano stati dichiara-ti private, risultano disponibilianche in B, come se vi fosserostati trascritti, ma con il van-taggio di eliminare ripetizioniesplicite. Così, qualora fossenecessaria una modifica o unacorrezione, basterebbe farlasolo nella classe A per averlaautomaticamente anche in B ein qualunque altra classe ere-diti da A.

Naturalmente B è libera dispecializzare i metodi che ere-dita, ridefinendone in tutto o inparte il funzionamento, così co-me di fungere da estensionedella classe A, introducendometodi o attributi aggiuntivi.

La modellizzazione in fig. 1riesce a dare conto del fatto

che rombo, rettangolo e qua-drato sono quadrilateri. Que-sto permette di scrivere un al-goritmo molto generico per ilcalcolo del perimetro (comesomma di 4 lati) implementan-dolo nella classe Quadrilatero,e condividerlo nelle sottoclas-si. Si veda il listato 2.

Come si può notare le defini-zioni delle classi ricalcano ildiagramma mostrato in figura,mentre il metodo main serveper il collaudo: costruisce unoggetto di ciascuna classe e nerichiede il calcolo di area e pe-rimetro. Il risultato visualizzatodal programma è il seguente:

quadrato 100x100: a=10000.0,p=400.0

rettangolo 10x20: a=200.0, p=60.0

rombo 10, h5: a=50.0, p=40.0triangolo 30,40,50 h40: a=600.0,

p=120.0

Per quanto riguarda il calco-lo dell'area, per tutti i quadri-lateri considerati, quadratocompreso, può andar bene laregola "base x altezza", ma seper esempio vi fosse la pro-spettiva di introdurre la classeTrapezio, per il quale la formu-la da utilizzare è diversa, allorasi potrebbe prendere in consi-derazione uno schema diver-so, in cui venga introdottauna... linea dinastica dedicataal Trapezio, per meglio diffe-renziarlo, come in (figura 2)oppure in (figura 3), a secondadella relazione che interessaenfatizzare per il Quadrato. UnQuadrato è più un tipo parti-colare di Rombo (4 lati uguali)oppure di Rettangolo (4 angoliretti)? Non sempre emerge conevidenza una unica scelta "giu-sta"; in questo caso verrebbequasi spontaneo dire che ilquadrato è una specie di "in-crocio" tra un rombo e un ret-tangolo.

Purtroppo Java supportasolo il modello a ereditarietàsingola: ogni classe può eredi-tare da al più una classe. Altri

1

Listato 2public class Geometria2{

public static void main(String [] args){

Poligono p;p=new Quadrato(100f);System.out.println("quadrato 100x100: a="+p.area()+",

p="+p.perimetro());

p=new Rettangolo(10f,20f);System.out.println("rettangolo 10x20: a="+p.area()+",

p="+p.perimetro());

p=new Rombo(10f,5f);System.out.println("rombo 10, h5: a="+p.area()+",

p="+p.perimetro());

p=new Triangolo(30f,40f,50f,40f);System.out.println("triangolo 30,40,50 h40: a="+p.area()+",

p="+p.perimetro());}

}abstract class Poligono{

public abstract float area();public abstract float perimetro();

}class Quadrilatero extends Poligono{

float base, altezza, lato1, lato2, lato3, lato4;public float perimetro(){return lato1+lato2+lato3+lato4;}

public float area(){return base*altezza;}

}class Quadrato extends Quadrilatero{

public Quadrato(float lato){lato1=lato2=lato3=lato4=base=altezza=lato;}

public float area(){return base*base;} // specializzato

public float perimetro(){return base*4;} // specializzato

}class Rombo extends Quadrilatero{

public Rombo(float l, float h){base=lato1=lato2=lato3=lato4=l;altezza=h;}

public float perimetro(){return base*4;} // specializzato

}class Rettangolo extends Quadrilatero{

public Rettangolo(float b, float h){base=lato1=lato3=b;altezza=lato2=lato4=h;}

public float area(){return base*altezza;}

public float perimetro(){return base*2 + altezza*2;}

}class Triangolo extends Poligono{

float base; // = lato1float altezza;float lato2;float lato3;public Triangolo(float b, float l2, float l3, float h){base=b; altezza=h; lato2=l2; lato3=l3;}

public float area(){return base*altezza/2;}

public float perimetro(){return base+lato2+lato3;}

}

Page 20: Corso_java.pdf

Java 2a lezione

linguaggi a oggetti, come ilC++, consentono invece sche-mi di ereditarietà più articola-ti, in cui una classe può eredi-tare parte del proprio "patri-monio genetico" da due o piùclassi. In un caso semplice co-me il nostro una simile tecnicaappare perfino eccessiva, co-munque per completezza ri-portiamo in fig. 4 l'aspetto chepotrebbe avere il class dia-gram nel caso volessimo sfrut-tare l'ereditarietà multipla.

Come esercizio vi proponia-mo di provare a progettare leclassi necessarie per modella-re un parco automezzi compo-sto da Moto, Auto e Camion,evidenziando gli aspetti comu-ni con l'introduzione di even-tuali classi "antenate" e ge-stendo le differenze medianteopportune "diramazioni" nellalinea ereditaria.

Lo scopingAttributi e metodi sono ca-

ratteristiche proprie di unaclasse.

Se tale classe fosse comple-tamente autonoma rispetto alresto del programma ed alle al-tre classi, queste sue proprietàpotrebbero anche rimanere"segrete" per tutti. In realtàclassi e oggetti modellano con-cetti, ruoli ed entità che costi-tuiscono il programma e chesono gli attori del suo funzio-namento.

È quindi normale e necessa-rio un certo grado di interazio-ne fra essi.

Deve cioè esistere in unaclasse un certo insieme di ca-ratteristiche (metodi o attribu-ti) che risultino accessibili an-che ad altre classi, mentre al-tre caratteristiche potranno re-stare "segrete".

È vitale che le interfacce frale parti del programma restinoil più possibile stabili. In casocontrario, un cambiamentonell'interfaccia esposta da unaclasse A, usata dalle classi B eC, richiederebbe di adattarequeste ultime.

Ora, il modo in cui è espres-so lo stato interno di una clas-se è un dettaglio implementa-tivo che può essere necessariocambiare in occasione di unarevisione degli algoritmi diquella classe.

È quindi immediato ricono-scere che è preferibile che l'in-terfaccia esposta all'esternonon comprenda attributi, masoltanto metodi. Qualora fosse

necessario dare accesso agliattributi, sarà bene farlo soloindirettamente, attraverso deimetodi che consentano di leg-gere o scriverne il valore.

Questa impostazione con-sentirà, al limite, di intercetta-re la chiamata al metodo e in-gannare il codice esterno fa-cendogli credere che lo statosia espresso sempre allo stes-so modo, anche se in realtà al-l'interno tutto è cambiato.

Per esempio, la versione 1.0di una classe per la gestionedei numeri complessi potreb-be essere implementata incoordinate cartesiane. Se fa-cesse trasparire all'esterno ilproprio stato interno, la classeNumeriComplessi "abituereb-be" i programmatori delle altreclassi ad interagire con essa incoordinate cartesiane. Pro-grammi che usano questa clas-se verrebbero sviluppati sullabase di questo assunto. Sup-poniamo ora che il program-matore della classe Numeri-Complessi si pentisse dellascelta implementativa inizialee decidesse di reimplementarestato e algoritmi interni incoordinate polari.

Tutto il resto del program-ma non sarebbe più compati-bile con questa versione dellaclasse e andrebbe modificatoper adattarvisi!

Anche la scelta dei metodida esportare deve essere ocu-lata. Dovrebbero essere espor-tati solo i metodi strettamentenecessari.

Ogni metodo che vieneesportato è come un impegnoper il futuro: qualcuno potreb-be trovarlo utile e decidere diusarlo, e a quel punto sarem-mo "costretti" a supportarloanche in future revisioni dellaclasse, per non creare proble-mi ad altre parti del program-ma.

Per esempio, se avessimouna classe Stampante proget-tata inizialmente per gestireuna stampante laser, potrem-mo fare l'errore di esportare ilmetodo rimescolaToner() in-sieme al ben più significativoStampaPagina().

Passando a una stampante adiversa tecnologia, quel meto-do potrebbe non avere più sen-so, ma se per ipotesi fosse or-mai stato usato da altre partidel programma avremmo unproblema.

Che fare? Implementarlo"per finta"? Eliminarlo e rive-

dere le parti di programma chelo usavano? Un dilemma che sisarebbe potuto evitare facen-do maggior attenzione allascelta dei metodi da esportarecome nostra interfaccia ester-na.

Il meccanismo che consentedi stabilire l'ambito di visibilità(scope) di attributi e metodi diuna classe prevede in Javaquattro livelli di "riservatez-za", che limitano la popolazio-ne abilitata ad accedervi.• Public: accesso consentito

da codice di qualunque clas-se.

• Package: accesso consentitoda codice di questa classe odelle altre classi appartenen-ti al suo stesso package. Que-sto è il default. Attenzione: setutte le classi dell'applicativoappartengono a un unicopackage, questo default pro-duce lo stesso effetto di scri-vere "public" ovunque!

• Protected: accesso consenti-to da codice di questa classeo di sue sottoclassi.

• Private: accesso consentitosolo da codice di questa clas-se.

Metodi e attributi staticIl concetto di "stato" che ab-

biamo introdotto riguardava lasituazione in cui si trova ognisingolo oggetto, rappresentatadal valore degli attributi diquell'oggetto.

Possiamo però immaginareanche uno stato di una interacategoria di oggetti: un'infor-mazione che ha lo stesso valo-re e si applica allo stesso modoper ogni singolo oggetto dellaclasse. Per esempio: l'importoo il termine di pagamento in unarchivio di abbonati a un ser-vizio; il tasso d'interesse appli-cato indistintamente a tutti iclienti di una banca; i salari mi-nimi di categoria in un archiviodi dipendenti; e così via.

Se una di queste proprietà

condivise da tutti gli oggettidovesse essere cambiata sa-rebbe comodo poterla modifi-care con un'azione unica fattasulla classe, anzichè dover agi-re individualmente su tutti glioggetti di quella classe.

Questo concetto è suppor-tato in Java dal modificatorestatic che si applica ad attri-buti o metodi.

L'accesso ai membri staticdall'esterno si può effettuaredal nome della classe (come inClasse.azione1(...) ) anzichè dauna istanza particolare (comein oggetto.azione1(...)).

Gli attributi dichiarati staticvengono ovviamente allocatiuna sola volta, con notevole ri-sparmio di memoria (un cam-po di tipo int condiviso da unmilione di oggetti "costa" 4 by-tes se dichiarato static, 4 me-gabytes in caso contrario!).

2

3

4

Page 21: Corso_java.pdf

Java 2a lezione

4 Creazione di una GUI in Java

Le librerie di classi fornitecon Java comprendono unricco repertorio di stru-

menti per creare con relativasemplicità un'interfaccia grafi-ca (Graphical User Interface,GUI) di ottimo livello.

Come primo passo, in que-sta puntata introdurremo iconcetti fondamentali che è ne-cessario conoscere per poterusare tali librerie.

Programmazione a eventiIn Java il funzionamento di

un semplice programma dota-to di GUI segue generalmentequeste fasi:1. Inizializzazione. Questa fase

comprende le seguenti atti-vità fondamentali:• Creazione della finestra

principale• Definizione della disposi-

zione (layout) dei sotto-og-getti (detti controlli o wid-get) all'interno della fine-stra

• Creazione dei widget oc-correnti (pulsanti, liste, ta-belle, check box, radio but-ton, eccetera) e loro inseri-mento nella finestra secon-do la struttura impostata;

• Definizione delle azioni chedovranno essere intrapre-se per ogni evento saliente(pressione di un Button, se-lezione in una List, movi-mento del mouse,...). Leazioni sono implementatecome metodi di particolariclassi.

• Associazione di tali azioniai widget, attraverso la loro"nomina" ad "ascoltatori (Li-steners) di eventi" per i variwidget.

2. Fase di attesa eventi. Il pro-gramma entra in una condi-zione di "inattività vigile" incui resta in "ascolto" da ta-stiera e mouse.

3. Reazione all'evento. Quan-do viene mosso il mouse,premuto un pulsante, digita-to qualcosa sulla tastiera, ciòrappresenta un evento per ilprogramma. Da sottolineareche il programma reagiscesolo a eventi del mouse capi-tati entro la propria "giuri-sdizione", rappresentata dalperimetro della finestra. Leazioni del mouse avvenuteall'esterno di esso sarannocompetenza di altre applica-

zioni. Per quanto riguarda latastiera, questa viene "senti-ta" se la finestra è la finestraattiva, ossia, come si dicetecnicamente, "ha il focus".La finestra attiva è ricono-scibile perchè, oltre a esserevisibile in primo piano, è evi-denziata da Windows con undiverso colore della barradel titolo e con vari altri ac-corgimenti.

4. Una volta completata la rea-zione all'evento (reazioneche potrebbe anche com-portare la modifica dell'in-terfaccia grafica, con l'ag-giunta o rimozione di widgeto con la visualizzazione diuna nuova finestra con mes-saggi o altri controlli) si ri-torna al punto 2, la fase di at-tesa eventi. L'unica eccezio-ne a questa regola si haquando la reazione a unevento (per esempio: la chiu-sura della finestra) compor-ta l'uscita dal programma. Inquesto caso l'algoritmo ter-mina.Questo paradigma di pro-

grammazione, divenuto ormailo standard per lo sviluppo diinterfacce grafiche, è detto"programmazione a eventi". Inesso, come si vede, la logica se-guita dall'elaborazione non ècompletamente definita dall'al-goritmo: quello che avverrà arun time è determinato piutto-sto dalla combinazione di duefattori: da un lato le azioni eser-citate dall'esterno (eventi) edall'altro le reazioni che sonostate definite nel programmaper ognuna di esse.

È come se durante il periododi funzionamento dell'applica-zione venissero lanciati tantiprogrammini "di reazione", unoal verificarsi di ogni evento. Ènaturalmente importante cheogni reazione sia definita inmodo corretto rispetto all'azio-ne scatenante, ma anche che altermine della reazione l'appli-cazione venga riportata in unostato stabile e "normale", pron-ta per ricevere e gestire even-tuali altre azioni.

I widget che popolano la GUIdi un'applicazione trovano nelparadigma a oggetti una mo-dellizzazione estremamentepratica, che si articola in que-sto modo:• Viene definita una classe per

ogni tipo di widget (vedi ta-bella per alcuni esempi)

• Ogni particolare widget corri-sponde a un'istanza di una ditali classi.

• Viene sfruttata a fondo l'ere-ditarietà per mettere a fattorcomune caratteristiche ecomportamenti condivisi dadiversi tipi di widget. Peresempio, tutti i widget hannodelle coordinate, delle di-mensioni, un colore, una im-postazione di visibilità e unadi abilitazione, e così via. Al-cuni widget sono poi caratte-rizzati da una scritta, altri daun'immagine, e così via. Siveda il riquadro per un esem-pio reale (una piccola "fami-glia" di widget, quella dei pul-santi) tratto dalla libreria dicomponenti di Java.

Esempio di ereditarietà frawidget: la "dinastia" dei pulsanti

• AbstractButton: Il capostipitedella "famiglia", modella egestisce i comportamenti tipici ditutti i tipi di pulsante, inparticolare il lancio di unaprocedura quando il pulsanteviene premuto

- JButton: implementa unpulsante che si può premere erilasciare- JToggleButton: un pulsantedotato di stato, in grado cioè di"restare premuto" finchè nonviene premuto di nuovo- JCheckBox: casellina (di solitoquadrata) con segno di spuntache indica lo stato di selezione;funzionamento indipendente daaltre caselline- JRadioButton: come sopra, macon casellina rotonda e unfunzionamento in mutuaesclusione nell'ambito di ungruppo di caselline- JMenuItem: un pulsanteinserito nel contesto di un menu - JCheckBoxMenuItem: uncheckbox utilizzato nel contestodi un menu- JMenu: un menuItem che, sepremuto, fa apparire unsottomenu associato - JRadioButtonMenuItem: unradio button utilizzato nelcontesto di un menu.

L'elenco completo, con indi-ce visuale organizzato per ca-tegorie, di tutti i widget dispo-nibili nelle librerie Java è di-

sponibile on line all'indirizzohttp://java.sun.com/docs/books/tutorial/uiswing/compo-nents/components.html

Qui ci interessa innanzituttosuddividere le classi della li-breria GUI in alcune grandiaree:• I component (tutti i widget so-

no sottoclassi di una classecapostipite Component). So-no disponibili decine e deci-ne di widget diversi, dal sem-plice pulsante, all'albero, allatabella, al visualizzatore di fi-les HTML, al dialog box per lascelta dei colori o per la scel-ta di un file.

• I container. Come già accen-nato, i widget devono esserecollocati in una finestra o co-munque in un'area adatta acontenerli. Esistono vari tipidi queste aree; tutte sono mo-dellate da classi che derivanodalla classe Container. Alcuniwidget sono anche in gradodi contenere a loro volta altriwidget e quindi sono al tem-po stesso dei Component edei Container.

• I layout manager. Se i contai-ner forniscono l'area in cui di-sporre i component, i layoutmanager sono il "piano rego-latore" di quest'area. Esisto-no layout manager che gesti-scono la disposizione a gri-glia, in sovrimpressione, in fi-la monodirezionale, a bordi, ecosì via.

• Gli eventi. Anche gli eventisono modellati come oggettie quindi appartengono a clas-si. Ci sono classi che model-lano eventi del mouse, altreper gli eventi della tastiera,altri ancora per eventi dellefinestre, per il focus, ....

• Classi e interfacce di supporto(colori, listener, adapters).Alcune di queste classi gioca-no un ruolo molto importan-te nello sviluppo delle GUI,come vedremo.Per mettere in pratica i con-

cetti appena introdotti, vedia-mo un semplice esempio di GUIrealizzata in Java. Il program-ma proposto, Displayer, è un vi-sualizzatore di immagini GIF oJPG. Per prima cosa digitateloe compilatelo facendo riferi-mento alle procedure descrittenella prima lezione del corso.Una volta passata la compila-zione, per utilizzare il program-

Page 22: Corso_java.pdf

Java 2a lezione

ma basterà lanciarlo da riga dicomando di una finestra DOS(dopo essersi portati nella di-rectory in cui si trova il file Di-rectory.class risultato dellacompilazione) con la seguentesintassi:java Displayer nome-file-immagine

Il parametro nome-file-im-magine viene ricevuto dal pro-gramma come primo elementodel vettore di stringhe args[]. Ilfile immagine da caricare è in-somma specificato dalla strin-ga contenuta in args[0]. Dopoaver caricato in memoria l'im-magine e averla inserita in un'e-tichetta per la visualizzazione,il programma aprirà una fine-stra dimensionata in modo taleda poter contenere l'immaginepiù un pulsante OK da usareper uscire dall'applicazione. Ilnome del file immagine verràinoltre visualizzato nella barradel titolo (fig. 5). Per una primaspiegazione del funzionamentodel programma si vedano icommenti inseriti nel listato;alcuni aspetti saranno ripresi eapprofonditi nelle prossimepuntate. In chiusura vi propo-niamo, per esercizio, di modifi-care il programma in modo cheil pulsante OK appaia in alto(NORTH) anzichè in basso; chela scritta su di esso diventi"Exit Application"; e che, quan-do premuto, prima di far termi-nare l'applicazione, stampi unmessaggio "Exit Button Pressed"sulla console DOS.

Per approfondimentiPer una presentazione orien-

tata a Java dei concetti base del-la programmazione a oggetti:http://java.sun.com/docs/books/tutorial/java/concepts/

Per una introduzione com-pleta allo sviluppo di interfaccegrafiche in Java si veda l'appo-sita sezione del Java Tutorial:http://java.sun.com/docs/books/tutorial/index.html �

Listato programma Displayer

// importazione classi da librerie graficheimport java.awt.*;import java.awt.event.*;import javax.swing.*;

public class Displayer{// entry point del programmapublic static void main(String [] args){

// protestiamo se non viene fornito esattamente un parametroif(args.length!=1){

System.err.println("Sintassi: java Displayer <nomefile.gif>");return;

}// creazione finestra con titoloJFrame finestra=new JFrame("Displaying " + args[0]);

// lettura immagine e sua installazione in memoriaImageIcon immagine=new ImageIcon(args[0]);

// creazione di un widget etichetta (label) contenente l'immagineJLabel label=new JLabel(immagine);

// inserimento della label nella zona centrale della finestrafinestra.getContentPane().add(label);

// creazione di un pulsante "OK" per uscire dal programmaJButton termina=new JButton("OK");

// inserimento del pulsante nella zona inferiore della finestrafinestra.getContentPane().add(termina, BorderLayout.SOUTH);

// definizione di una reazione da associare al pulsanteActionListener azioneTermina=new ActionListener(){

public void actionPerformed(ActionEvent e){

// questo codice verra' chiamato quando si verifichera'// l'evento a cui sara' associata questa reazione.

// l'effetto sara' quello di far terminare il programma:System.exit(0);

}};

// associazione della reazione all'evento Action del pulsantetermina.addActionListener(azioneTermina);

// ingrandisce la finestra quanto basta per ospitare i widgetfinestra.pack();

// mostra la finestra ed entra in modo Attesa Eventifinestra.setVisible(true);

}}

5

ERRATA CORRIGESegnaliamo alcuni erroricomparsi nella primapuntata del corso Java.a pag, 95, nel testo inrosso, il valore perl'inizializzazione delcontatore non è 1 ma 0,conformemente aquanto riportato nellistato di pag. 97.a pag. 96, fig. 1,sostituire N con X nelleespressioni riportate neidue rombi gialli; inoltrela freccia di ritorno devearrivare a monte e non avalle del blocco "Ricevi iltentativo T".a pag. 103, nella figurache si riferisce alcostrutto switch,nell'ultimo rettangolo inbasso sostituire"operazioni N" con"eventuali operazionidefault".Ci scusiamo con i lettoriper l'inconveniente.

Page 23: Corso_java.pdf

Java 3a lezione

In questa terza lezione affron-teremo la gestione dei dati sufile in Java, accompagnando

la trattazione della teoria di ba-se con esempi presentati comefunzioni tratte da uno stessoprogramma.

Sarà finalmente l'occasioneper vedere concretamente inazione ed approfondire ulte-riormente diverse conoscenzeacquisite nelle prime due le-zioni. In particolare, per la gra-fica vedremo la creazione e l’u-so di alcuni nuovi widget, comei combobox, la struttura dei me-

nu, i pannelli per la visualizza-zione di testo HTML e i dialogbox; in tema di oggetti, tipi e co-struttori di tipo vedremo unesempio d’uso reale di array evari esempi di creazione di og-getti.

Per quanto riguarda le tema-tiche di programmazione intro-durremo invece il concetto difunzione ricorsiva e analizzere-mo la questione degli erroripresentando varie tecniche digestione degli errori, fra cuiquella basata sul concetto dieccezione.

� A scuola con PC Open

ProgrammareJavaGestione file

1 Archiviare e trasferire dati

Mettiamo finalmente alla prova le competenze acquisite nelle precedenti lezioni,sia per quanto riguarda la grafica (creazione di nuovi widget) sia per quantoriguarda gli oggetti, i tipi e i costruttori di tipo

Lezione 1Rudimenti di programmazione- Terminologia essenziale- Dall’algoritmo al programma- Preparazione di Java- Il primo programma

Lezione 2Elementi di programmazione a oggetti e grafica- Tipi e identificatori- Astrazione funzionale- Programmazione a oggetti

- Creare una GUI

Lezione 3File- Archiviare e trasferire i dati- Errori e gestione degli errori- La ricorsione- Un esempio

Lezione 4Networking- Programmi client-server- Interazione con risorse Web

IL CALENDARIO DELLE LEZIONI

di Marco Mussini

Iconcetti di file e di streamrappresentano il modello lo-gico per il trattamento di ar-

chivi e flussi di dati più larga-mente adottato nei linguaggi diprogrammazione, Java com-preso.

Prima di introdurre questiconcetti riteniamo opportunauna premessa sull'hardwareusato per la trasmissione e lamemorizzazione persistente didati. Infatti certe particolaritàdel modello potrebbero sem-brare poco giustificate, quasiarbitrarie, se non si tenessepresente, anche in una pro-spettiva "storica", la natura deidispositivi fisici usati per con-durre queste operazioni.

Il funzionamento della me-moria centrale è tale da con-sentire l'accesso ai dati in untempo approssimativamenteinvariante (e piccolo) qualun-

que sia la locazione in cui sitrovano e qualunque sia l'ordi-ne in cui si presentano gli indi-rizzi da cui è richiesta la letturadi dati: in altre parole, anche sela lettura dei dati venisse ri-chiesta in un ordine perfetta-mente casuale, il tempo perl'accesso resterebbe approssi-mativamente costante.

Per questa fondamentale pe-culiarità la si definisce di "me-moria ad accesso casuale", oRandom Access Memory(RAM). La presenza della ca-che in realtà modifica le pre-stazioni della RAM, miglioran-dole in caso di accesso se-quenziale o comunque con ca-sualità "prevedibile".

Se dunque per la memoriacentrale non fa praticamentenessuna differenza il particola-re ordine con cui i dati venganorichiesti dal programma, al

contrario, trasmettere dati ememorizzarli in forma persi-stente sono processi che risul-tano fondamentalmente orien-tati all'accesso in ordine se-quenziale, come i dispositiviche li supportano.

Sequenzialità nello scambio di dati

La capacità dei canali di co-municazione dati ha conosciu-to una storia di incrementi in-credibilmente veloci, con unritmo di crescita (anche un de-cuplicamento ogni 3 anni) cheultimamente ha addirittura su-perato quello delle capacità dicalcolo dei computer (all'incir-ca un raddoppio ogni 18 mesi).

Nonostante questi fenome-nali incrementi di prestazioni,la trasmissione dati è intrinse-camente (e probabilmente ri-marrà) un processo seriale.

Concettualmente i dati vengo-no inviati sul canale un bit allavolta; le tecnologie si differen-ziano per la velocità alla qualeriescono ad effettuare questaoperazione, o per il mezzo tra-smissivo adoperato, ma tuttoavviene in modo sequenziale.

In ambito microinformatico,per un immediato aumento del-la banda disponibile, si è spes-so fatto ricorso a canali paral-leli (per le periferiche esternel'interfaccia 8 bit Centronicsper la stampante; per quelle in-terne, il collegamento IDE, EI-DE, ATA 16 bit con i dischi; peril bus di sistema, gli standardISA, MicroChannel, PCI o AGP;per la memoria si è passati da30 pin a 100, 144, 168 pin, per ilprocessore basti pensare alSocket 940 di AMD).

L'introduzione del paralleli-smo è però finalizzata sola-

Page 24: Corso_java.pdf

Java 3a lezione

mente alla velocizzazione di untrasferimento di dati che vienecomunque come sequenziale.Semplicemente, il traffico, an-zichè sotto forma di una se-quenza di bit, avviene comeuna sequenza di byte o diword. È quindi un parallelismopuramente "tattico" in un qua-dro che rimane sostanzialmen-te seriale. Fra l'altro il continuoaumento delle frequenze di la-voro e il perfezionamento del-l'elettronica di controllo rendeormai possibile ottenere pre-stazioni estremamente elevatesenza bisogno di fare ricorso acanali paralleli. In parole pove-re, anzichè usare un cavo pa-rallelo a 32 bit e grossi connet-tori multipolari, se ne può usa-re uno seriale, più semplice edeconomico, che trasferisce unbit alla volta, ma lavora a unafrequenza 32 volte maggiore.Non a caso si assiste oggi a un"ritorno" delle soluzioni seriali:è il caso del Serial ATA per i di-schi, o dell'USB e del Firewireche hanno determinato l'uscitadi scena della Centronics per ilcollegamento alla stampante edella SCSI per i dischi esterni(almeno in sistemi PC).

Come vedremo, i costruttilogici messi a disposizione dalinguaggi e librerie per model-lare e gestire lo scambio di da-ti risentono di questa imposta-zione seriale.

Sequenzialità nelle memoriedi massa

Anche nelle memorie di mas-sa si sono registrati progressiquantitativi veramente consi-derevoli, soprattutto sul ver-sante della capacità (più esat-tamente, del rapporto tra ca-pacità e costo); ultimamente lacapacità di dischi e (soprattut-to) flash memory raddoppia aparità di prezzo ogni 12-18 me-si, anche in questo caso con rit-mi di crescita pari o superiori aquelli delle CPU.

Tuttavia, da un punto di vi-sta qualitativo, ancor oggi lamaggior parte dei sistemi dimemoria di massa sono o so-stanzialmente o strettamentesequenziali.

I nastri sono l'esempio piùevidente di memorie di massaseriali. Non si pensi che sianosolo dei "ruderi", ormai uscitidi scena: cartucce a nastro so-pravvivono ancor oggi in siste-mi dedicati di backup, ove ri-sultano un sistema assai com-petitivo in grado di offrire alta

capacità, alta velocità di trasfe-rimento dati e basso costo perbyte. La contropartita, natural-mente, è un alto tempo mediodi accesso alle informazioni (ilnastro deve venire avvolto eriavvolto per raggiungere ilpunto giusto), ma questo hascarsa rilevanza nei sistemi dibackup.

Le unità a disco hanno con-sentito di affrancarsi parzial-mente da un modello rigida-mente seriale di accesso ai da-ti: la testina è infatti libera disaltare da una traccia all'altra,anche se per farlo richiede untempo non trascurabile checomporta un decadimento del-le prestazioni rispetto all'ac-cesso sequenziale, che rimanela modalità di utilizzo preferitadal disco.

Il throughput istantaneo mas-simo, cache a parte, si ha evi-dentemente quando la testinarimane ferma su una traccia,mentre il disco (e con esso i da-ti) le ruota sotto a 7.200, 10.000o più giri al minuto.

A proposito di sequenzialitànei sistemi a disco, conviene ri-cordare che i compact disk(nati, ricordiamolo, per rim-piazzare i dischi in vinile in ap-plicazioni audio!) hanno unaunica, lunghissima, traccia aspirale e non diverse tracceconcentriche come gli hard di-sk e i floppy. L'accesso casualenei CD è, di fatto, tutto un sal-tare avanti e indietro nell'ambi-to di una unica traccia: un fe-nomeno che in linea di princi-pio sarebbe paragonabile al fa-st forward o al rewind nelleunità a nastro.

Le memorie di massa dalcomportamento più simile aquello della RAM, le uniche adessere poco o per nulla orien-tate a un accesso sequenziale,sono le flash memory, cheperò, per capacità, costo e ve-locità, non sono ancora in gra-do di prendere il posto dei di-schi magnetici e ottici se non inapplicazioni di nicchia (minia-turizzazione, basso consumo,resistenza agli urti, capacità re-lativamente basse).

Da osservare anche che, ol-tre all'incremento di capacitàed affidabilità, le tecnologiehardware delle memorie dimassa hanno perseguito l'otti-mizzazione e velocizzazione didue sole operazioni fondamen-tali: lettura e scrittura. L'orga-nizzazione logica dei dati, la lo-ro categorizzazione, le opera-

zioni di confronto e copiatura ecosì via, sono tipicamente a ca-rico del sistema operativo odelle applicazioni in esecuzio-ne sul computer.

Con queste premesse nonstupisce più di tanto constata-re che ancor oggi l'archiviazio-ne dei dati sia vista come unprocesso che, nella sua formapiù semplice, è modellato dalinguaggi e librerie come fon-damentalmente sequenziale edammette solo due operazioniprincipali (lettura e scrittura).È veramente raro, per esempio,trovare nelle librerie di I/O dibase operazioni per confronta-re o modificare in modo aggre-gato dei dati archiviati: peresempio, istruzioni per cercareun testo in un file, istruzioniper confrontare due file o istru-zioni per convertire in maiu-scolo tutto il testo in un file.Questo tipo di elaborazioni sirealizzano sempre in memoriacentrale; in generale, quindi, lasequenza di operazioni saràuna sequenza di questo tipo: • apertura del file; • lettura e/o elaborazione e/o

scrittura; • chiusura del file.

Per contro, il limite costitui-to da un accesso esclusiva-mente sequenziale agli archiviè apparso ben presto eccessi-vamente restrittivo, ragion percui sono state introdotte va-rianti al modello logico di baseche consentono di indirizzarele informazioni in modo più di-retto. Con esse, per leggere ilmillesimo elemento di un ar-chivio non è più necessario ar-rivarci leggendo prima (soloper scartarli) i 999 elementiche lo precedono, ma diventapossibile leggerlo direttamen-te, identificandolo semplice-mente come l'elemento di po-sto 1000. Oppure, per leggereuna porzione di un testo archi-viato, è possibile formulare unarichiesta del tipo "leggi 35 ca-ratteri a partire dalla posizione20561". Per questo tipo di ac-cesso si parla di file ad accessocasuale (random access files),in analogia con la memoriaRAM, ma, anche se Java li sup-porta con apposita classe emetodi, non ne parleremo oltrein questo articolo.

Si tenga presente che peruna visione dei dati pienamen-te astratta e orientata al loromodello logico piuttosto che astrutture fisiche, senza alcun

"residuo" di sequenzialità e conla possibilità di commissionareall'archivio stesso anche ela-borazioni sui dati, ci si dovreb-be piuttosto rivolgere ai data-base. Questi, pur dovendosibasare su dispositivi di memo-rizzazione che sono sempre glistessi, sono progettati per for-nire un'astrazione sui dati mol-to più ricca e più "pulita" diquanto non sia possibile farecon i soli file.

Vediamo dunque più da vici-no come funziona in Java l'in-put/output (I/O) sequenzialeda e verso file o altro. Si distin-guono nettamente due aree:l'I/O "grezzo", universale, gesti-to come un flusso di byte, equello orientato specificamen-te allo scambio di dati di testo.Java offre classi e servizi diver-si, specializzati per questi duetipi di flussi di dati.

Modello logico dell'I/Osequenziale a byte: i file e gli stream

In Java la questione dell'in-gresso/uscita di dati (input/output, I/O) è trattata da appo-site librerie di classi, riunitenel package java.io. Qui trovia-mo le modellizzazioni Java deidue concetti fondamentali checonsentono di rappresentare etrattare la memorizzazione per-sistente e il trasferimento didati: rispettivamente, il File egli Stream.

La classe File (java.io.File) inJava fornisce una rappresenta-zione astratta di un percorso(path o pathname) nel file sy-stem. Questo percorso potreb-be riferirsi a un file o anche, co-me caso particolare di file, auna directory. La classe Filenon contiene metodi per legge-re o scrivere dati sul file; trove-remo metodi per queste opera-zioni nelle classi Stream, comevedremo. I metodi della classeFile consentono invece di con-trollare a che tipo di entità (fileo directory) corrisponde ilpathname, di controllare i rela-tivi permessi di lettura o scrit-tura o la data di modifica, dicreare un file, un file tempora-neo o una directory con talepathname e in generale di ma-nipolare i pathname, estrarneparti, risalire alla directory su-periore e così via.

È essenziale sottolineare checreare un oggetto di classe Filenon implica affatto la creazionedi un file nel file system.

Per ottenere in più questo

Page 25: Corso_java.pdf

Java 3a lezione

secondo effetto occorre chia-mare appositi metodi. Gli og-getti di classe File, di per sè,rappresentano semplicementedei pathname.

Parte dei metodi della classeFile lavorano in astratto sulpathname in memoria, mentrealtri effettuano operazioni ocontrolli sul file system in rela-zione al file o directory identi-ficato dal pathname. Si vedanole note nella tabella a destra.

Una volta specificato il pathdel file da creare o da elabora-re creando un'opportuna istan-za della classe File, per poterleggere o scrivere su quel file ènecessario creare uno Stream(flusso) per modellare e gestirelo scambio di dati da e verso ilfile.

Vi è una distinzione esplicitafra gli stream che gestisconoflussi di dati in uscita (Output-Stream) e quelli che gestisconoflussi di data in entrata (Input-Stream). Sono disponibili varieclassi che pur rientrando inuna delle due categorie si ca-ratterizzano per la destinazio-ne o l'origine dei dati, oppureper qualche servizio a valoreaggiunto che forniscono conte-stualmente al transito dei dati.Tutte le informazioni sulle prin-cipali classi di queste due cate-gorie sono riportate in Tabella1 e in Tabella 2.

I/O sequenziale di testo: i Reader e i Writer

Se la classe File ci consentedi identificare e creare file e di-rectory del file system, di farviriferimento e di interrogare ilsistema sulle loro proprietà, ele varie classi della famiglia de-gli Stream servono per model-lare e gestire flussi di dati "grez-zi" da e verso file ed aree di me-moria, le classi che permetto-no di gestire con facilità un I/Odi testo sono altre. Si tratta del-le famiglie dei Reader e dei Wri-ter, i cui più importanti rappre-sentanti sono riportati nelle ta-belle 3 e 4.

I flussi di dati di testo hannoesigenze specifiche aggiuntiverispetto a quelle degli stream abyte. Per esempio, esiste unconcetto di righe di testo, inesi-stente nei file binari, e quindirisultano molto utili funzioniper leggere e scrivere righe ditesto (e non semplicementesingoli caratteri) oppure peremettere newline o per saltarlidurante la lettura.

Inoltre il testo può essere

� Principali metodi della classe Fileboolean canRead() Verificano se, in base ai permessi del file, l'applicazione può leggere o scrivere boolean canWrite() su di esso. Ritornano true o false. boolean createNewFile() Crea nel file system un nuovo file corrispondente al pathname specificato

dall'oggetto File su cui questo metodo viene chiamato. Ritorna un booleano, chevale false se un file con quel pathname esiste già e di conseguenza la creazione è fallita.

static File createTempFile Crea un file temporaneo in una directory arbitraria oppure nella directory che il (String prefix, String suffix) sistema definisce per tale scopo (per esempio potrebbe essere quella indicata

dalla variabile d'ambiente TEMP); il nome del temporaneo viene costruito usando static File createTempFile il prefisso e il suffisso specificati (per esempio: "MyApp" e ".tmp") più una parte(String prefix, String suffix, pseudocasuale, aggiunta dal metodo, che è usata per differenziare il file da quelli File directory) esistenti. - Ritornano un oggetto File che rappresenta il pathname del file creato

sul file system.boolean delete() Rimuove dal file system il file o la directory che corrispondono al pathname

specificato dall'istanza di File su cui questo metodo viene invocato. Ritorna un booleano che vale true se la cancellazione è riuscita, false se è fallita, qualunque sia la ragione.

void deleteOnExit() Una sorta di prenotazione di cancellazione automatica di un file. Il file verrà eliminato da Java appena il programma terminerà l'esecuzione. Utile per garantire che i file temporanei spariscano a fine algoritmo. La prenotazione, una volta fatta, non può essere annullata.

boolean equals(Object obj) Confronta questo oggetto File con un altro. N.B.: si tratta quindi di un confronto fra due pathname, non di un confronto sul contenuto di due file nel file system!

boolean exists() Verifica se sul file system esiste un file con un pathname uguale a quello specificato da questa istanza di classe File. Ritorna true o false.

File getAbsoluteFile() Vari metodi che consentono di estrarre le parti salienti di un pathname String getAbsolutePath() segnatamente, il path vero e proprio e il nome del file), di ottenerne la versioneFile getCanonicalFile() (assoluta e quella "canonica" (per esempio: ripulita di eventuali "." e ".." inutili, String getCanonicalPath() con la lettera dell'unità disco trasformata in maiuscolo, e cosi' via), di ricavareString getName() il path della directory superiore. A seconda dei metodi il risultato viene risultatoString getParent() come String o come un altra istanza di File.File getParentFile() Vedere i programmi esempio per una applicazione di getName() e getParent().String getPath() boolean isAbsolute() Ritorna true se questo File denota un pathname che risulta assoluto. In ambiente

DOS e Windows un pathname è assoluto se include come prefisso un identificatore di unità disco locale o di rete. In UNIX un pathname è assoluto se inizia con il carattere '/'.

boolean isDirectory() Questi metodi ritornano true se questo File denota un pathname che boolean isFile() corrisponde, nel file system, rispettivamente a una directory o a un semplice file.

Vedere i programmi esempio per una applicazione di questi metodi.boolean isHidden() Ritorna true se questo pathname corrisponde a un file nascosto.long lastModified() Ritorna la data alla quale è stato modificato per l'ultima volta il file o directory

denotato dal pathname espresso da questo oggetto File. La data è espressa come un intero long che misura i millisecondi dalla mezzanotte del 1 gennaio 1970. Per convertirla in un formato più maneggevole esistono apposite classi nel package java.util. (Date, Calendar)

long length() Ritorna la lunghezza del file.String[] list() Assumendo che questo pathname corrisponda nel file system a una directory e

non a un file, ritorna un array di stringhe che rappresentano i nomi di tutti i files contenuti in quella directory. Se il pathname corrisponde a una directory vuota ritorna un array di 0 elementi. Se il pathname non esiste nel file system, o corrisponde a un file, ritorna null.

String[] Come sopra ma consente in più di specificare un filtro che lavora sul nome dei list(FilenameFilter filter) files della directory. È possibile usare questa variante per ottenere l'elenco dei

soli files .txt presenti in una directory, oppure per ottenere l'elenco di quelli il cuinome soddisfa una determinata espressione regolare o contiene una sottostringa desiderata. Vedere i programmi esempio per una applicazione di questo metodo.

File[] listFiles() Come sopra, ma ritornano gli elenchi sotto forma di array di oggetti File anzichè File[] listFiles sotto forma di array di stringhe.(FilenameFilter filter) File[] listFiles Come sopra, ma il filtro può lavorare sulle proprietà dei file e non solo sul loro(FileFilter filter) nome. Ad esempio, il filtro potrebbe selezionare solo i file recenti, solo i file e

non le directory, solo i file sui quali è permessa la scrittura, e così via.static File[] listRoots() In un PC con più unità disco questo metodo consente di conoscerne l'elenco

completo. Ognuna di esse, che si può considerare la radice di un file system, viene rappresentata nell'array ritornato come un oggetto di tipo File.Vedere MyExplorer per una applicazione di listRoots().

boolean mkdir() Metodi per creare una directory. - Il primo, mkdir, va usato se tutti i livelli boolean mkdirs() superiori esistono già. Il secondo, mkdirs, si può sempre usare e provvede

automaticamente a creare eventuali livelli superiori presenti nel pathname richiesto, ma non ancora esistenti nel file system.Questi metodi ritornano true se la creazione è riuscita e false in caso contrario.

boolean renameTo(File dest) Effettua il rename di un file; ritorna false in caso di fallimento.Si noti che su alcuni sistemi (ad es. UNIX) il rename può implicare lo spostamento daun file system all'altro: ciò avviene se nell'operazione la radice del pathname viene cambiata.

boolean setLastModified Il metodo imposta sul file system la data di ultima modifica del file al valore (long time) desiderato.boolean setReadOnly() Revoca i permessi di scrittura sul file rappresentato da questo oggetto File.String toString() Trasforma il pathname in String. Vedere i programmi esempio.URL toURL() Ritorna questo pathname rappresentato sotto forma di URL.

Vedere i programmi esempio.�

Page 26: Corso_java.pdf

Java 3a lezione

rappresentato con numerosiformalismi (encoding) diversi.Fra questi il più comune è an-cora l'ASCII, ma si sta facendovelocemente largo Unicode,che ha il pregio di consentire iltrattamento omogeneo di unmix di caratteri occidentali edorientali da parte di uno stessoprogramma (per esempio: inuna stessa pagina Web).

Fra l'altro Unicode è l'unicarappresentazione interna usatada Java per i caratteri, mentrenell'I/O, in cui vi è la necessitàdi "piegarsi" alle consuetudinie alle esigenze del sistema cir-costante, Java supporta anchela trascodifica da e verso gli al-tri principali encoding, inclusiquelli giapponesi (JIS e SJIS), ci-nesi, coreani, e così via.

Risulta insomma chiaro chela gestione di file e flussi di da-ti di testo non può essere fattain modo efficace ragionando abyte e con primitive che con-sentono di "leggere i prossimi1.000 bytes in un array"; quelloche si desidera è piuttosto "leg-gi la prossima riga di testo con-vertendola da ASCII a Unicodee creando come risultato unastringa Java".

È proprio a questo tipo dielaborazioni e trasformazioniche sono deputate le classi del-le famiglie Reader e Writer. Sivedano le rispettive tabelle peri dettagli sulle operazioni mes-se a disposizione.

Costruire combinazioni di Reader, Writer e Stream

Una particolarità davveromolto comoda di questi sistemidi classi sta nel fatto che sonostati progettati per essere com-ponibili, proprio come i pezzi diun gioco di costruzioni.

In particolare, i Reader sonopensati per ricevere i dati o daaltri Reader o da un qualsiasi ti-po di InputStream, sui cui by-tes verrà effettuata al volo laconversione in caratteri. Sim-metricamente, i Writer sono ingrado di inviare i loro flussi didati verso altri Writer oppure,trascodificati in byte, verso de-gli OutputStream.

Per esempio, se volessimoleggere a righe da un file, po-tremmo combinare un Buffere-dReader (che supporta la let-tura per linee) con un FileRea-der (che supporta la lettura ditesto da file), come in Listato 1.

Supponendo di aver prepa-rato in C:\ un file ciao.txt comequello mostrato in figura Buffe-

redReader1.bmp, il codice so-pra riportato produrrebbe l'ef-fetto mostrato in figura 1.

Se invece volessimo scrivere

del testo accumulandolo in me-moria anzichè scriverlo su file,potremmo combinare unPrintWriter (che fornisce co-

modi metodi per scrivere varitipi di dato e gestire la scritturaper righe del testo) con un Out-putStreamWriter (che gestisce

� Tab 1 - Le principali classi della famiglia OutputStream, per la gestione in scrittura di flussi di dati a byte. Tutte le classi appartengono al package java.io

OutputStream Superclasse astratta delle classi della famiglia OutputStream. Rappresenta un gestore di flussi di dati a bytes in uscita. La destinazione e il trattamento dei flussi varia a seconda della particolare sottoclasse.

ByteArrayOutputStream Un OutputStream che invia i dati a un array di byte mantenuto internamente. È possibile ottenere accesso a questo array per utilizzare i dati immagazzinati. È anche disponibile un metodo per trasformare in String i bytes dell'array, secondo una codifica specificata. Vedere i programmi esempio.

FileOutputStream Questo OutputStream invia il flusso di byte su un file. Il file verrà aperto in scrittura,se necessario creandolo implicitamente.

FilterOutputStream Superclasse da usare per implementare varianti di OutputStream che effettuano elaborazioni sui dati in transito e li riversano poi su un altro OutputStream.

BufferedOutputStream OutputStream che ottimizza le prestazioni usando un buffer interno e raggruppando le scritture di dati.

DataOutputStream Un OutputStream che fornisce supporto di basso livello per la scrittura di dati di tutti i tipi base Java. I dati vengono scritti usando una rappresentazione (diversa da quella testuale) che ne consente la rilettura con un DataInputStream.

PrintStream Questa classe supporta la scrittura dei tipi base Java con metodi print e println similia quelli della classe PrintWriter. I dati escono però con una rappresentazione che può essere diversa da quella testuale.

ObjectOutputStream Questa classe lavora generalmente in tandem con ObjectInputStream e fornisce metodi per serializzare oggetti e grafi di oggetti e trasmetterli sotto forma di un flussodi byte che potrà essere inviato a un file, su rete o riletto per mezzo di un ObjectInputStream

Tab. 2 - Le principali classi della famiglia InputStream, per la gestione in lettura di flussi di dati a byte. Tutte le classi appartengono al package java.io

InputStream Rappresentazione generica di un flusso di dati in lettura. I dati sono visti unicamente comeuna sequenza di byte, senza particolare significato, e possono essere letti uno alla volta o a gruppi, ma solo sequenzialmente. È possibile solo saltare un numero specificato di byte, ma sempre "andando in avanti".

ByteArrayInputStream Un InputStream che trae i suoi dati da un vettore di byte in memoria.FileInputStream Un InputStream che legge byte da un File.FilterInputStream Un InputStream astratto che ne "avvolge" un altro, fornendo operazioni di lettura "a valore

aggiunto" comprendenti eventuali trasformazioni ed elaborazioni sui dati in passaggio.BufferedInputStream Un InputStream che mantiene un'area di memoria tampone (buffer) per ottimizzare le

prestazioni in lettura dei dati e per consentire di "tornare indietro" (entro limiti prefissati)nella lettura del file, attenuando la restrizione di una gestione totalmente sequenziale.

DataInputStream Un InputStream che offre la possibilità di leggere dallo stream i tipi di dato primitivi di Java (v. lezione II) anzichè semplici byte.

LineNumberInputStream Una classe che gestisce byte streams che rappresentano testi codificati in ASCII conseparatori di linea; fornisce la funzionalità di conteggio delle linee lette. L’uso è sconsigliatoin quanto esistono alternative più generiche fra le classi della famiglia Reader (v.)

PushbackInputStream Un InputStream che consente di leggere sequenzialmente e poi di... fare retromarcia (sempre sequenzialmente), reinserendo nello stream parte dei bytes letti (o anche dei bytes diversi da quelli letti).

ObjectInputStream Un InputStream in grado di leggere singoli oggetti o grafi di oggetti interpretando opportunamente i byte del flusso di dati. Per poter essere interpretati correttamente, i byte nello stream devono essere rappresentazioni codificate dalla classe gemella ObjectOutputStream.

PipedInputStream Un InputStream che in combinazione con la classe simmetrica PipedOutputStream può essere usato per costruire un canale di comunicazione seriale bufferizzato intra-applicazioneche appare a ciascuna delle due parti come un normale Stream. Per esempio, in un word processor, la funzione di Stampa potrebbe scrivere comandi grafici per la stampante sul PipedOutputStream mentre un thread Spooler potrebbe leggerli al ritmo corretto dalla partedel PipedInputStream.

SequenceInputStream Un InputStream che consente di modellare l'atto di leggere tutti i dati disponibili da uno stream e di passare poi a leggere quelli di un altro stream come una lettura da un unico stream virtuale contenente la sequenza dei dati dei due stream reali. Per esempio potrebbeessere usato su due FileInputStream per implementare facilmente l'Append di due file.

StringBufferInputStream Un InputStream che legge i suoi dati da un oggetto StringBuffer, che rappresenta un array dicaratteri arricchito da funzioni di manipolazione. Concetto simile a quello di ByteArrayInputStream che invece lavora su bytes "grezzi" non necessariamente riferiti a testo

Page 27: Corso_java.pdf

Java 3a lezione

la trascodifica da testo a byteverso un OutputStream) e ag-ganciare quest'ultimo a un By-teArrayOutputStream, che im-magazzina in un array di byte idati che riceve anzichè man-darli a una destinazione ester-na. Per esempio il listato 2 ge-nera i risultati mostrati in Figu-ra 2.

È inoltre possibile crearecollegamenti fra Reader e Wri-ter oppure fra InputStream eOutputStream, creando vere eproprie "tubazioni" tali per cuitutto il testo che viene scrittosu un Writer risulti leggibiledalla parte del Reader, o tutti ibyte che vengono inviati a unOutputStream sbuchino fuoridal lato dell'InputStream ad es-so collegato.

Non a caso, le classi che sup-portano questo tipo di "inter-scambio canalizzato" di datihanno il prefisso Piped (lette-ralmente intubato).

Queste tecniche risultanoutili, per esempio, quando unprogramma è progettato in mo-do tale da essere composto diparti indipendenti, in esecuzio-ne contemporanea (in threadseparati), le quali comunicanofra loro con flussi di dati seria-li interni all'applicativo. Peresempio, in un word processorpotrebbero essere presenti unthread (main) che gestisce l'im-paginazione e l'interfaccia

utente e un thread secondario(spooler) che si occupa di ge-stire la coda di stampa. L'avviodell'operazione di Stampa po-trebbe allora comportare lacreazione di un sistema internodi pipe attraverso cui il testoimpaginato e pronto da stam-pare viene inviato dal main allospooler. I due thread ricevono etrasmettono dati senza nem-meno accorgersi che la contro-parte si trova nella medesimaVirtual Machine.

I vantaggi per il riuso del codice

Grazie alla relazione di ere-ditarietà che lega queste classia poche classi radice (rispetti-vamente InputStream, Output-Stream, Reader e Writer) risul-ta molto facile riutilizzare in si-tuazioni nuove dei programmioriginariamente scritti per in-teragire con un certo tipo difonte o destinazione di dati.

Tab. 3 - Le principali classi della famiglia Writer, per gestire flussi di testo in uscita.Tutte appartengono al package java.io.

Writer Superclasse astratta delle classi che forniscono metodi per scrivere flussi di testo a carattere.BufferedWriter Classe che supporta metodi per la scrittura ottimizzata di flussi di testo. Le prestazioni

migliorano grazie a un meccanismo che immagazzina in un buffer i dati in uscita, raggruppando per quanto possibile le scritture elementari. Fornisce inoltre un metodo per inviare un newline come definito dal sistema operativo.

CharArrayWriter Un Writer che invia il flusso di dati di testo a un array di char.FilterWriter Superclasse astratta da usare per implementare dei Writer che effettuano trasformazioni

o elaborazioni sui dati in transito.OutputStreamWriter Un Writer in grado di indirizzare il flusso di testo in uscita verso un OutputStream che lavora

a bytes; gestisce automaticamente la conversione da caratteri a bytes, secondo la particolarecodifica scelta o secondo quella di default. Vedere i programmi esempio.

FileWriter La classe Writer che gestisce la scrittura di testo su file. Equivale alla combinazione fra un OutputStreamWriter e un FileOutputStream. Vedere i programmi esempio.

PrintWriter Un Writer che fornisce metodi per emettere sotto forma di testo dati di tutti i tipi elementari definiti in Java, con (println) o senza (print) emissione di newline a fine linea. Vedere i programmi esempio.

StringWriter Un Writer che immagazzina il flusso di testo in output in uno StringBuffer mantenuto internamente. In qualsiasi momento e' possibile richiedere l'accesso a questo StringBuffer e usarlo direttamente o costruire una String sulla base del suo contenuto.

Tab. 4 - Le principali classi della famiglia Reader, per gestire flussi di testo in entrata.Dal package java.io.Reader Superclasse astratta delle classi che forniscono il servizio di leggere flussi di caratteri.

A seconda delle classi, la fonte dei dati può essere a sua volta una fonte a caratteri (un altro Reader) oppure una fonte a bytes (v. InputStreamReader)

BufferedReader Classe di grande utilità che legge testo con un meccanismo di bufferizzazione interno che oltrea garantire un aumento di efficienza a run time ne consente una pratica gestione a righe. Vedere i programmi esempio.

LineNumberReader Classe che fornisce supporto per leggere per righe un file di testo mantenendo un conteggio delle righe lette.

CharArrayReader Un Reader che trae i suoi dati da un array di char (in analogia con ByteArrayInputStream che legge da un array di byte)

InputStreamReader Importantissimo Reader che supporta la lettura a caratteri di flussi di dati di testo aventi origineda uno Stream funzionante a bytes. Questa classe realizza quindi la decodifica di formato da bytes a Unicode, ASCII o altri formati.

FileReader Reader che legge flussi di dati di testo direttamente da file di testo, assumendo che la codifica usata per i caratteri sia quella di default. Vedere i programmi esempio.

StringReader Simile a CharArrayReader ma trae i suoi dati da una String.

Listato 1

Import java.io.*;public class FileExamples{

public static void main(String [] args){

try{BufferedReader br=new BufferedReader(new FileReader("c:\\ciao.txt"));System.out.println(br.readLine());}catch(Exception e){

e.printStackTrace();}

}}

1

Listato 2

import java.io.*;public class FileExamples{public static void main(String [] args){

try{

ByteArrayOutputStream baos=new ByteArrayOutputStream();PrintWriter pw=new PrintWriter(new OutputStreamWriter(baos));pw.print("Testo e numeri ");pw.print(10.0f/3.0f);pw.println(" seguiti da newline");pw.print("e infine da un'ultima stringa");pw.flush();//pw.close();System.out.println("Dal buffer interno:");System.out.println(baos.toString());

}catch(Exception e){

e.printStackTrace();}

}}

2

Page 28: Corso_java.pdf

Java 3a lezione

2 Errori e relativa gestione: le eccezioni

Una corretta gestione deglierrori è una questione diimportanza primaria per

qualsiasi programma non ba-nale o impegnato in attività im-portanti, dalle quali magari di-pende la sicurezza dei dati, lacontinuità di un servizio o l'in-columità delle persone. Tratta-re correttamente gli errori èpoi indispensabile specialmen-te in programmi che si occupa-no pesantemente di I/O ed èper questo che tratteremo l'ar-gomento in questa puntata.

In programmazione, con iltermine generico di errori ci sipuò tuttavia riferire fondamen-talmente a tre concetti ben di-versi.

Categorie di errori neiprogrammi

Innanzitutto vi sono gli erro-ri rilevabili prima di mandarein esecuzione il programma. Sitratta di:• errori puramente ortografici

o lessicali: parole scritte ma-le, maiuscole al posto di mi-nuscole, punteggiatura man-cante o errata.

• errori sintattici: espressionio costrutti mancanti di qual-che parte o scritte in un mo-do o in un ordine difforme daquello previsto dalla specifi-ca del linguaggio.

• errori semantici rilevabilistaticamente analizzando ilprogramma: per esempio,mancanza di dichiarazioni;mancanza di inizializzazioni;incompatibilità statica di tipoin un assegnamento; omoni-mia fra nomi di classi, varia-bili e metodi; incompatibilitàstatica fra tipo e numero deiparametri formali e attuali diuna chiamata a metodo; chia-mata a un metodo inesistenteper un oggetto di una certaclasse; istruzioni irraggiungi-bili (dead code) a causa di ci-

cli infiniti o di istruzioni di ri-torno anticipato incondizio-nato.Tutti questi errori risultano

immediatamente evidenti esa-minando il codice sorgente e ilcompilatore è in grado di rile-varli e segnalarli con opportu-na diagnostica, rifiutando di ul-timare la traduzione e di man-dare in esecuzione il program-ma. Perciò, durante l'esecuzio-ne (a run time) non dobbiamopreoccuparci di questa catego-ria di errori: si può assumereche siano stati tutti scoperti ecorretti in precedenza.

Di conseguenza non occor-rono (e infatti non esistono inalcun linguaggio) espedienti ecostrutti sintattici per rilevaree trattare a run time errori diquesto genere.

Vi sono poi gli errori logicicommessi dal programmatorenell'implementare un algorit-mo: si tratta di errori che evi-dentemente il compilatore, insede di analisi statica del codi-ce sorgente, non ha potuto ri-levare e segnalare. Non errorisintattici, quindi, e neppure er-rori semantici banali, ma erroriconcettuali. Per esempio: • usare una formula sbagliata

per un calcolo;• effettuare un numero scorret-

to di iterazioni in un ciclo;• prendere una decisione logi-

ca in base a una espressionesbagliata o effettuare con-fronti con valori di riferimen-to sbagliati;

• in un calcolo usare una varia-bile invece di un'altra (sep-pure di tipo compatibile, co-sicchè il compilatore non puòsegnalare il problema);

• dimenticare di effettuare unacerta elaborazione necessa-ria sui dati;

• passare a un metodo un pa-rametro di tipo giusto, ma divalore sbagliato;

...e così via. Errori di questotipo si manifestano solo in ese-cuzione e per essi il compilato-re può dare solo un aiuto con-sultivo e in una casistica estre-mamente ristretta. Del resto, èanche difficile pensare a co-strutti sintattici del linguaggioche siano realmente in grado diprevenire il verificarsi a run ti-me di questi errori o di conte-nerne le conseguenze.

Esistono invece tecniche,supportate anche da Java, cheaiutano a segnalare condizioniscorrette durante l'esecuzione,appena si verificano o, in qual-che caso, immediatamente pri-ma che si verifichino. Esse ri-chiedono un impegno supple-mentare da parte dello svilup-patore, il quale, dopo averscritto il programma, deve an-che "disseminarlo" con dellecondizioni logiche che devonoessere sempre vere (asserzio-ni: un concetto introdotto daHoare nel lontano 1968). Du-rante l'esecuzione Java verificase le asserzioni sono rispettateo no e in caso contrario bloccal'esecuzione e segnala il pro-blema, precisando quale asser-zione è stata violata.

Contro gli errori logici, in-somma, non esistono difeseche siano al tempo stesso mol-to economiche e molto efficaci.Per la loro prevenzione, il mixdi misure più realistico e credi-bile è una progettazione accu-rata del software, condotta daspecialisti esperti che adottinopratiche standard di ispezionecollegiale del codice, accom-pagnate da un uso intelligentee non smodato delle asserzionie di altri accorgimenti che nonabbiamo lo spazio di discuterequi.

Esiste però un'altra impor-tantissima categoria di condi-zioni anomale che si manife-stano anch'esse a run time ma

non sono in alcun modo impu-tabili al programmatore. Si trat-ta di problemi derivanti dalmalfunzionamento o dal rag-giungimento di limiti fisici otecnici del computer, delle sueperiferiche o dei canali di co-municazione verso altri com-puter, oppure dalla violazionedi una regola del sistema. Co-me vedremo, in molti casi sonocondizioni d'errore tipiche del-la gestione dell'I/O, dei file edella rete, e per questo ne par-leremo in questa puntata e nel-la prossima. Per esempio, soloper citare gli esempi più classi-ci:• Memoria esaurita• Il collegamento di rete è ca-

duto per congestione o gua-sto, l'host remoto non ri-sponde, la controparte hachiuso la comunicazione incorso.

• Un dispositivo con collega-mento wireless, in preceden-za raggiungibile, non rispon-de più

• Batteria scarica• Disco pieno• Disco danneggiato o illeggibi-

le• Esiste un file con lo stesso no-

me• Non si dispone di permessi

sufficienti per scrivere/legge-re un certo file o in una certadirectoryIn alcuni di questi casi (non

in tutti) sarebbe teoricamentepossibile verificare la situazio-ne prima di tentare l'operazio-ne: per esempio, misurare lamemoria libera o lo spazio sudisco, verificare se un file omo-nimo esiste già, interrogare ilsistema operativo per control-lare di quali permessi si dispo-ne. Tuttavia, molte operazionipossono fallire per diversi pos-sibili motivi, ed effettuare con-trolli multipli prima di eseguireognuna di esse aumenterebbe

Per esempio, un programmain grado di leggere righe di te-sto da un file e metterle in or-dine alfabetico potrebbe esse-re fatto lavorare invece sustringhe in memoria, semplice-mente passandogli uno Strin-gReader anzichè il solito File-Reader. Tutto quello che oc-

corre è che il programma siascritto per funzionare con ungenerico Reader; sarà allorapossibile passargli un’istanzadi una qualunque sottoclassedi Reader ed esso funzioneràcon quella, eseguendo il suonormale tipo di elaborazione,senza accorgersi della differen-

za della sorgente dei dati.Analogamente, un program-

ma abituato a salvare su discoi propri dati potrebbe essere ri-ciclato per lavorare in contestodi rete, ingannandolo in modotale che mandi i dati a una de-stinazione remota anzichè scri-verli su file. Questo è possibile

semplicemente passandogli unOutputStream connesso a unSocket anzichè un FileOutput-Stream aperto su un file. Per ilprogramma, se scritto in modotale da richiedere un genericoOutputStream e non necessa-riamente un FileOutputStream,non farà alcuna differenza.

Page 29: Corso_java.pdf

Java 3a lezione

troppo le dimensioni del pro-gramma e lo renderebbe prati-camente illeggibile.

In altri casi, per quegli even-ti il cui verificarsi è assoluta-mente imprevedibile dal puntodi vista del computer (ad esem-pio: disco rovinato e illeggibile;la rete cessa di funzionare; uncomputer remoto viene spentodurante una comunicazione; ecosì via) il controllo sarebbe in-trinsecamente impossibile.

Insomma, i possibili "inci-denti" che possono verificarsio sono talmente numerosi cheil loro controllo puntuale sa-rebbe troppo oneroso se effet-tuato in via preventiva, o sfug-gono al controllo del program-ma e del programmatore e perquesto semplicemente nonpossono essere prevenuti.

Per tutte queste considera-zioni si tende a preferire unagestione a posteriori di questaclasse di problemi: in sostanzasi tenta l'operazione senza ve-rifiche preliminari e poi si veri-fica "com'è andata" esaminan-do il risultato (exit code, errorcode, return status) segnalatodalla funzione o procedura.Questo può assumere una se-rie di ben precisi valori, a ognu-no dei quali corrisponde un di-verso tipo di problema o erroreverificatosi: per esempio, -1 po-trebbe significare "operazioneinterrotta", -2 potrebbe signifi-care "disco pieno", e così via.(Secondo una convenzioneadottata in molti linguaggi, ingenere gli errori sono indicaticon valori negativi, lo 0 signifi-ca "nessun errore" e valori po-sitivi possono rappresentarealtri tipi di risultati validi in as-senza di errori).

Così facendo, anzichè effet-tuare numerosi controlli primadi ogni singola operazione, sene effettua uno solo subito do-po di essa: lo stato che vienesegnalato darà conto di tutta laserie di possibili problemieventualmente verificatisi du-rante lo svolgimento dell'ope-razione.

Questa tecnica di gestionedegli errori garantisce sì una ri-duzione da N (controlli preli-minari) a 1 (controllo dell'esi-to), con una semplificazionenotevole per il programmatore,ma non è ancora ottimale. In-fatti un programma basato sulcontrollo degli errori a poste-riori appare come un'alternan-za continua fra "tenta di fare X"e "controlla com'è andata":

questa circostanza di certonon favorisce la leggibilità delcodice. Le istruzioni sono benpiù numerose di quelle che sa-rebbero realmente necessarieper "fare quello che si deve fa-re" se non ci fosse da preoccu-parsi degli errori dopo ognioperazione. Per esempio, inpseudocodice:

risultato=apri file("prova.txt", scrittura)

se risultato="disco pieno" allora segnala "spazio insufficiente!" e termina

altrimenti se risultato="permessi insufficienti" allora

segnala "impossibile creare il file!" e termina

altrimenti se risultato="settore difettoso" allora segnala "disco illeggibile. Vuoi formattarlo?"

se risposta=SI allora formatta disco; ripeti tutto

altrimentitermina

altrimenti ... procedi con il lavoro...

Per snellire i programmi econferire loro maggiore chia-rezza Java e altri linguaggi in-troducono anche una tecnicadiversa per il rilevamento e lagestione degli errori: la gestio-ne per eccezioni, che nelle li-brerie di Java (non solo in quel-le di I/O) è sicuramente il mec-canismo più usato (e può natu-ralmente essere usato anchedal programmatore per tratta-re i "suoi" errori applicativispecifici).

Con questa visione, comeimpostazione di base, si prefe-risce pensare al contesto diesecuzione del programma co-me a una sorta di "mondo idea-le" in cui non si verificano maierrori dovuti a cause esterneimprevedibili. La memoria è in-finita, la rete funziona sempre ei collegamenti sono stabili e ve-loci, il disco non finisce mai enon si guasta mai, e così via.

Potendo trascurare (in pri-ma istanza) tanti fastidiosi det-tagli, l'algoritmo può quindi ve-nire espresso in modo estre-mamente elegante e conciso,badando solo a realizzare l'im-plementazione più chiara ed ef-ficiente possibile. E gli errori,allora? Gli errori sono visti solocome eccezioni, che possonosì capitare, ma che vengonotrattati a parte, in separata se-de, per non rovinare la puliziagenerale del codice e per poter

mettere a fattor comune la ge-stione di errori dello stesso ti-po, benché capitati durante l'e-secuzione di istruzioni diverse.

La sezione di codice deputa-ta alla gestione delle eccezionipuò così essere anch'essa mol-to chiara e ordinata, dovendosioccupare esclusivamente ditentare un recupero delle con-dizioni di correttezza necessa-rie perchè il programma possaprocedere o perchè l'operazio-ne possa essere ritentata, o, sequesto non è proprio possibile,di provvedere a segnalare ilproblema e magari a far termi-nare il programma.

Il listato 3 riporta un esem-pio di programma che legge unfile di testo e ne produce un al-tro contenente lo stesso testodel primo, ma convertito tuttoin maiuscole.

Il programma gestisce i pos-sibili errori di I/O (e quelli suiparametri) come delle eccezio-ni, usando il costrutto Javatry.catch.

Il blocco try contiene un ar-bitrario numero di istruzioni(al limite anche l'intero algorit-mo) che vanno scritte come senon si verificassero mai errori.L'algoritmo è espresso quindiin forma molto chiara e facil-mente leggibile.

Al verificarsi di una eccezio-ne durante l'esecuzione di unaqualunque delle istruzioni delblocco try, il controllo passa alprimo blocco catch incontratoin grado di gestire o l'eccezionerilevata o una sua superclasse.Esiste una ereditarietà anchefra eccezioni; l'eccezione piùgenerica, quella da cui deriva-no tutte le altre, è la classe Ex-ception e in caso di clausolecatch multiple converrà mette-re in ultima posizione quellache la tratta, in modo tale dapermettere alle catch prece-denti di intercettare eccezionipiù specifiche.

Il blocco catch ha così l'op-portunità di stampare diagno-stici (come nel nostro esem- �

Listato 3

import java.io.*;public class FileExamples{

public static void main(String [] args){

try{

BufferedReader br=new BufferedReader(new FileReader(args[0]));

PrintWriter pw=new PrintWriter(new FileWriter(args[1]));

while(br.ready()){

pw.println(br.readLine().toUpperCase());}

br.close();pw.close();

}catch(ArrayIndexOutOfBoundsException a){

System.err.println("Numero di parametri errato!");

}catch(FileNotFoundException f){

System.err.println("File non trovato: "+f.getMessage());

}catch(Exception e){

System.err.println("Rilevata eccezione:");

e.printStackTrace();}

}}

Page 30: Corso_java.pdf

Java 3a lezione

pio), ma potrebbe anche tenta-re di riparare la situazione e ri-petere automaticamente l'ope-razione, per esempio grazie aun ciclo esterno.

Il costrutto prevede ancheuna clausola conclusiva opzio-nale finally, generalmente pocousata, che specifica azioni chedevono essere eseguite solodopo che il blocco try-catch èstato attraversato, con o senzaeccezioni.

Per generare una eccezione(che è un oggetto come qual-siasi altro, però appartenente auna classe che deriva da Ex-ception) è sufficiente creare unoggetto E di un qualche tipo dieccezione, eventualmente cor-redandolo con informazionisull'errore avvenuto, e poi se-gnalarlo, usando la primitivathrow(E).

Un metodo all'interno delquale vi siano generazioniesplicite di eccezioni oppurechiamate a metodi che dichia-rano di poter generare ecce-zioni deve obbligatoriamentesoddisfare ad almeno una delledue seguenti condizioni perognuna delle eccezioni chepossono verificarsi nel suo co-dice:• gestire in locale l'eccezione

con un opportuno blocco try-catch;

• dichiarare di poter sollevarea sua volta verso il chiamante(propagare) lo stesso tipo dieccezione. Questo equivale adichiararsi "incompetenti inmateria" e a passare la patatabollente a qualcuno, più com-petente o più generico, in gra-do di trattarla.Un metodo può dichiarare di

poter sollevare eccezioni ap-ponendo la clausola throws sul-la propria intestazione: peresempio, la dichiarazione:

public void combinaGuai(intparametro) throwsMiaEccezioneApplicativa,FileNotFoundException

segnala che questo metodopuò sollevare le due eccezionicitate e nessun'altra.

Chiunque faccia uso del me-todo "combinaGuai" deve ob-bligatoriamente gestire, oppu-re dichiarare a sua volta inclausola throws, tali due ecce-zioni.

Da sottolineare che i co-strutti try-catch sono annidabi-li: un intero sistema try-catchpotrebbe essere contenuto neltry di un sistema try-catch piùgrande. Se si verifica un'ecce-zione e nessun blocco catch èrisultato idoneo a gestirla, l'ec-cezione viene propagata all'e-ventuale struttura try-catchesterna, un po' come avvieneper la propagazione delle ecce-zioni fra le chiamate a metodi.

Segnaliamo inoltre che peralcune eccezioni non sussistel'obbligo di essere gestite o di-chiarate dai metodi. Si trattadelle eccezioni che derivanoda RuntimeException: si trattao di eccezioni che possono ca-pitare in così tante occasioniche se dovessero essere sem-pre gestite il programma di-venterebbe illeggibile, oppure,

viceversa, di eccezioni talmen-te insolite e di scarso interesseda non rendere vantaggiosopretendere che il programma-tore perda tempo a gestirle. Sipreferisce quindi lasciare fa-coltà di trattarle o di lasciareche accadano.

Infine, ricordiamo per com-pletezza che accanto alle Ex-ception esistono classi del tuttosimili e trattabili facoltativa-mente con costrutti try-catch(come le RuntimeException):gli Error.

Questi rappresentano situa-zioni di errore fatale, assoluta-mente infrequenti, irrimediabi-li e incontrollabili (per esem-pio: errore interno della Vir-tual Machine che sta eseguen-do il programma Java, oppure:bytecode Java alterato e incon-sistente), in ogni caso è previ-sto anche in questo caso unmeccanismo di segnalazione,di modo che il programma, unattimo prima del crollo genera-le, possa magari tentare qual-che azione protettiva, comequella di salvare automatica-mente il lavoro in corso.

Sia Exception che Error deri-vano da una classe super-gene-rica, Throwable, che è la capo-stipite di tutte le classi accetta-te dalla primitiva throws e che

fornisce metodi che supporta-no il trattamento degli errori,come il metodo printStackTra-ce() che consente di visualiz-zare su console la catena dichiamate fra metodi che haportato al verificarsi dell'ecce-zione, con l'indicazione del nu-mero di linea e modulo ovel'eccezione è stata sollevata.

L'effetto del trattamento de-gli errori nel programma diesempio è evidente in figura 3: • dapprima il programma viene

lanciato senza parametri, fa-cendo sì che l'array args[] siavuoto, cosicchè le espressioniargs[0] e args[1] risultano ille-gali e la loro esecuzione pro-voca un'eccezione ArrayIn-dexOutOfBoundsException

• successivamente viene spe-cificato un file di input inesi-stente; ciò provoca il verifi-carsi di un'eccezione File-NotFoundException quando ilcostruttore della classe File-Reader tenta di aprirlo in let-tura;

• viene poi eseguito corretta-mente il programma, passan-do un file da leggere esistentee un nome di file da scriverevalido; nessuna eccezione;

• infine viene specificato un fileda scrivere su unità floppy,ma senza che l'unità conten-ga alcun disco: viene visua-lizzato un dialog di errore diWindows; rispondendo An-nulla, il sistema operativo se-gnala il problema a Java, chelo riporta al programma co-me eccezione FileNotFoun-dException (anche se con"causale" diversa rispetto alcaso precedente).Per completezza, sempre in

figura, viene riportato il dumpdel file ciao.txt (in minuscolo) edel file ciao2.txt (in maiuscolo,generato dal programma).

3

3 La ricorsioneQuando si ha a che fare con

il file system ci si trovaimmediatamente alle pre-

se con la struttura gerarchicadelle directory. Queste forma-no un albero avente per radicel'unità disco, per rami le direc-tories stesse e per foglie (noditerminali) i files oppure le di-rectories vuote.

Se volessimo scrivere un al-goritmo che visiti il file systeme ne elenchi tutto il contenuto,

molto probabilmente arrive-remmo a una formulazione diquesto tipo (qui a fianco).

Come si può notare dall'ulti-ma riga, questo algoritmo in-voca sè stesso. Infatti il proble-ma di visualizzare l'albero sot-teso a un nodo X si può espri-mere decomponendolo in duesottoproblemi: (1) visitare lefoglie F attaccate al nodo e (2)stampare ognuno degli even-tuali sottoalberi Y1...Yn sot-

Algoritmo per la visita completa del file system

Visualizza directory(dir X)• Stampa il nome di X e vai a capo• Se X contiene dei files, allora

o per ogni file F contenuto direttamente sotto X- Visualizza il nome di F e vai a capo

• Se X contiene delle directory, allorao per ogni directory Y contenuta direttamente sotto X

- Visualizza directory(Y)

Page 31: Corso_java.pdf

Java 3a lezione

tesi al nodo. Quest'ultimo sot-toproblema (2) è uguale al pre-cedente, in quanto si tratta an-cora di visualizzare un albero.

Il fatto che l'algoritmo, inuno o più punti, sia espresso intermini di sè stesso, è chiama-to ricorsione e l'algoritmo conquesta caratteristica ricorsivo.

Un algoritmo ricorsivo hasempre le seguenti caratteristi-che: • tratta il caso generale ricon-

ducendolo a una combinazio-ne di casi più semplici

• tratta il caso banale (detto"base della ricorsione") inmodo immediato, senza più"rimandarlo".Problemi che implicano il

trattamento di strutture e mo-delli "autosimili", come questodegli alberi, trovano la lorotrattazione naturale in formaricorsiva. Altri esempi sono:• Elaborazione di liste. Una li-

sta si può vedere come l'ele-mento di testa + un'altra lista(tutta la coda). (caso banale:lista di un solo elemento)

• Calcolo del fattoriale. Il fatto-riale di un N positivo è pari aN moltiplicato per il fattorialedi N-1. (caso banale: N=1)

• Calcolo di potenza intera. Nintero positivo elevato a K in-tero positivo è pari a N molti-plicato per N elevato a K-1.(caso banale: K=1)

• Ricerca di un carattere in unastringa non nulla. Il carattereè presente se è in prima posi-zione, oppure se è presentenel resto della stringa. (casobanale: stringa di un solo ca-rattere)

• Casi molto più complessi, co-me la valutazione delle con-seguenze delle mosse in unprogramma che gioca a scac-chi o a dama: la valutazionedella bontà di una mossa puòessere effettuata ipotizzandodi aver compiuto la mossa ericonsiderando la situazioneche si verrebbe a creare, pro-vando una per una tutte lepossibili contromosse del-

l'avversario e riconsiderandoaltrettante situazioni e inognuna tutte le mie possibilicontro-contro-mosse, e cosìvia,... fino a una profondità dianalisi prefissata o finchè lamemoria non si esaurisce.E così via.Tutti questi problemi po-

trebbero essere trattati con al-goritmi iterativi (ossia con ci-cli), che fra l'altro in diversi ca-si risulterebbero di gran lungamigliori in termini di velocità diesecuzione e di memoria occu-pata, però la nostra mente tro-va spesso spontaneo esprime-re la strategia risolutiva in for-ma ricorsiva, "riduzionista":per risolvere un problema diffi-cile lo si riconduce a uno o piùsottoproblemi che si sanno im-mediatamente risolvere o checomunque risultano meno dif-ficili da risolvere.

Fra l'altro in qualche caso sa-rebbe veramente scomodo epoco chiaro esprimere un algo-ritmo iterativo per un proble-ma intrinsecamente ricorsivo;quando è così si preferisce farpassare in secondo piano laquestione della minor efficien-za di fronte al vantaggio dellachiarezza di esposizione.

Tornando al nostro algorit-mo di visita del file system, conun po' di attenzione ci rendere-mo conto che purtroppo l'out-put apparirebbe "piatto" e nonfornirebbe un'idea immediatadella relazione di contenimen-to esistente fra le directory.

Per questo occorre stampa-

re il testo con oppor-tuni rientri sul marginesinistro. L'entità delrientro deve natural-mente crescere con laprofondità della direc-tory nell'albero e quin-di deve essereun'informazione che"portiamo con noi", in-crementandola, nellechiamate ricorsive.

Per supportare que-

sto concetto l'algoritmo potràessere modificato come segue(vedi qui a sinistra).

Un’implementazione in Javadi questo algoritmo, limitata alsolo caso delle directories, è ri-portata in Listato 4.

Un piccolo estratto del suooutput, catturato mentre erain corso la visita dell'albero diinstallazione della documenta-zione di Java, è invece riporta-to in Figura 4.

Visualizza directory(dir X, rientro R)• Visualizza R spazi• Visualizza il nome di X• Vai a capo• Se X contiene dei files, allora

o per ogni file F contenuto direttamente sotto X- Visualizza R+1 spazi- Visualizza il nome di F - Vai a capo

• Se X contiene delle directory, allorao per ogni directory Y contenuta direttamente sotto X

- Visualizza directory(Y, R+1)

Listato 4

import java.io.*;

public class FileExamples{

public static void main(String [] args){

dump(new File("."),0);}

public static void dump(File path, int indent){

for(int i=0; i<indent; i++)System.out.print(" ");

System.out.println(path.getName());

File[] files=path.listFiles();for(int j=0; j<files.length; j++){

if(files[j].isDirectory()){

dump(files[j],indent+1);}

}}

}

4 Un esempio riepilogativoIl programma riepilogativo

che vi proponiamo in questapuntata dimostra l'applica-

zione di alcuni concetti sui filee riprende, sviluppandoli ulte-riormente, diversi temi discus-

si nella puntata precedente aproposito di interfacce grafi-che.

Il programma funziona co-me una sorta di mini File Ex-plorer e consente di muoversi

nell'albero delle directory ditutti i drive disponibili sul si-stema. Per alcuni tipi di file(HTML, TXT, JPG/JPEG e GIF) èinoltre in grado di funzionareda visualizzatore; per visionare

il file è sufficiente selezionarlocon il mouse.

Per ragioni di spazio vedre-mo qui solo una rassegna dibrani di codice che implemen-tano alcune delle funzioni del

4

Page 32: Corso_java.pdf

Java 3a lezione

programma. Il listato comple-to, MyExplorer.java, è presentesu CD Guida 2 allegato alla rivi-sta.

Creazione della finestraprincipale e dei menu

Per prima cosa è ovviamen-te necessario creare la finestra(un JFrame con titolo appro-priato). Si passa poi a creare laMenuBar, alla quale viene ag-giunto un menu File. In questomenu vengono poi inserite duevoci: Open e Exit, entrambe do-tate di acceleratori.

La MenuBar così creata vie-ne infine inserita sul JFrame.

L'operazione si conclude

con la definizione delle dimen-sioni iniziali e con la visualizza-zione della finestra principale.

Le relative istruzioni sono ri-portate nel riquadro A:

Poichè desideriamo inoltreche il programma termini chiu-dendo la finestra, e questocomportamento non è automa-tico, dobbiamo registrare unWindowListener che gestiscatale circostanza. Il codice chese ne occupa è nel riquadro B:

Per semplicità, questi spez-zoni di codice non comprendo-no la parte che inserisce i dueelenchi (directory e file) a sini-stra e a destra nel centro delJFrame. Vedere il listato com-

pleto per tutti i dettagli.

Un dialog box per muoversinel file system

Quando l'utente seleziona Fi-le/Open è necessario visualiz-zare un dialog box che gli con-senta di selezionare il file chedesidera aprire. Per farlo prov-vediamo a definire un ActionLi-stener che crea un'istanza diJFileChooser, esamina la deci-sione presa dall'utente e in ca-so positivo invoca il metododisplayFile che si occupa di vi-sualizzare il file scelto (riqua-dro C).

Visualizzare in finestra ilcontenuto di un file di testo o HTML

Se il file da visualizzare è unfile di testo o un file HTML, vie-ne impiegato un JEditorPane,uno speciale widget di Java, al

quale va indicata la URL del fileda aprire. Ecco come si proce-de: (f è il file da mostrare; il JE-ditorPane verrà poi inserito inun dialog box e visualizzato - ri-quadro D)

Il caso di file GIF o JPEG ètrattato in modo simile a quan-to presentato nel programmaDisplayer citato nella scorsapuntata.

Un elenco di file systemLa nostra applicazione deve

offrire la possibilità di selezio-nare il file system su cui si in-tende operare. Questo è per-messo da un JComboBox cari-cato con i nomi di unità discoritornati dal metodo listRootsdella classe File (riquadro E).

Il JComboBox è dichiarato fi-nal (costante) per poterlo refe-renziare nell'actionListener chegestisce l'evento di cambio se-lezione: quello che viene fattoquando questo si verifica è ag-giornare il path corrente e rige-nerare gli elenchi di file e di-rectory chiamando aggiornaE-lenchi (v.) (riquadro F).

Elencare files o directoriesUna volta creati due widget

JList per ospitare gli elenchi difile e directory il problema èquello di caricarli con i daticorretti. Di questo si occupa ilmetodo aggiornaElenchi().

Per esempio, per l'aggiorna-mento dell'elenco delle direc-tory sotto un certo path, siprovvede anzitutto a una visita

Riquadro AJFrame jfMain=new JFrame("MyExplorer"); // crea finestra con titoloJMenuBar jmbMenu=new JMenuBar(); // crea toolbar inizialmente vuotajfMain.setJMenuBar(jmbMenu); // aggancia menubar a finestraJMenu jmFile=new JMenu("File"); // crea menu File inizialmente vuotojmFile.setMnemonic(KeyEvent.VK_F); // imposta mnemonico (ALT)F per menu FilejmbMenu.add(jmFile); // aggancia menu File a menu barJMenuItem jmiOpen=new JMenuItem("Open...", KeyEvent.VK_O); // crea menuItem Open con mnemonico OjmFile.add(jmiOpen); // aggancia menuItem Open a menu FileJMenuItem jmiExit=new JMenuItem("Exit", KeyEvent.VK_E); // menuItem Exit, mnemonico EjmFile.add(jmiExit); // aggancia a menu File// imposta dimensione iniziale dell'area principale della finestra// 400x300 pixel per una finestra in rapporto di forma 4:3 alla nascitajfMain.getContentPane().setPreferredSize(new Dimension(400,300));jfMain.pack(); // consenti adattamento di geometrie e dimensionijfMain.setVisible(true); // mostra finestra e relativi contenuti

Riquadro B// registra listener che termina il programma // quando viene chiusa la finestrajfMain.addWindowListener(new WindowAdapter(){

public void windowClosing(WindowEvent w){

// Uscita dal programmaSystem.exit(0);

}});

Riquadro C// registra un listener per gestire l'azione Open.jmiOpen.addActionListener(new ActionListener(){

public void actionPerformed(ActionEvent e){

// Crea un File Open dialogJFileChooser chooser = new JFileChooser();

// Visualizza il dialog e ricevi la scelta dell'utenteint returnVal = chooser.showOpenDialog(jfMain);

// Se e' stato premuto Apri, visualizziamo il file sceltoif(returnVal == JFileChooser.APPROVE_OPTION) {

// chiamiamo la procedura di visualizzazione// passando come parametro il file selezionato nel dialogdisplayFile(chooser.getSelectedFile());

}}

});

Riquadro D// Costruisci un visualizzatore universale di files di testoJEditorPane jepPanel=new JEditorPane();

// Disabilita la possibilita' di modificare il testo visualizzatojepPanel.setEditable(false);

// Inserisci il visualizzatore nell'area centrale del dialog boxjdFileViewer.getContentPane().add(new JScrollPane(jepPanel));

// Preparati a gestire un errore di "URL non valida" o un errore// di I/Otry{

// Configura il visualizzatore per mostrare il file richiestojepPanel.setPage(f.toURL());

// Scegli una dimensione iniziale per il dialog boxinitialSize=new Dimension(800,600);

}catch(Exception e){

// In caso di errore stampa un report sul tipo di errore// e sul punto in cui si e' verificatoe.printStackTrace();

}

Page 33: Corso_java.pdf

Java 3a lezione

del suo contenuto (ottenutacon il metodo list()), visita nel-la quale si utilizza una classeche nella directory seleziona isoli "figli" che siano a loro voltadelle directories (riquadro G):

Una volta ottenuto l'elencodelle directory questo, se nonvuoto, può essere caricato(con setListData()) nella JListper farlo apparire su schermo(riquadro H).

Gestire la selezione in elencoQuando l'utente seleziona

una delle directory dell'elencodi sinistra o uno dei file dell'e-lenco di destra dobbiamo rea-gire opportunamente.

Per un file, in particolare,dobbiamo lanciare il visualiz-zatore invocando displayFile;per una directory dobbiamo in-vece aggiornare il path con il

nuovo valore e rigenerare tuttigli elenchi. Ecco come (riqua-dro I).

MyExplorer in funzionePer un approfondimento di

tutti gli aspetti rimandiamo al-l'esame del listato completodel programma, dettagliata-mente commentato.

Le figure mostrano il pro-gramma in azione in alcune si-tuazioni tipiche: la navigazionefra directory (immagine 4), l'a-pertura di un file HTML (imma-gine 6) e il dialog box di sele-zione del file da visualizzare(immagine 5).

Come esercizio conclusivovi proponiamo di modificare ilprogramma in modo tale chevisualizzi anche altri formati ditesto (come INI, INF o BAT) eperchè visualizzi, oltre al nomedei files, anche la loro dimen-sione in bytes.

Cosa ci aspettaNella prossima puntata con-

tinueremo lo sviluppo di questiconcetti, in particolare perquanto riguarda gli Stream, chesi riveleranno indispensabili,come vedremo, nello scambiodati via rete. �

Riquadro E// Ottiene dal sistema una lista dei file system disponibiliFile[] drives=File.listRoots();

// Crea un ComboBox per consentire la selezione del drivefinal JComboBox jcbDrives=new JComboBox(drives);

Riquadro F// Registra un listener per reagire all'azione di selezione // di una voce dell'elencojcbDrives.addActionListener(new ActionListener(){

public void actionPerformed(ActionEvent a){

// Aggiorna il path corrente con la nuova selezionecurrPath=(File)jcbDrives.getSelectedItem();

// aggiorna gli elenchi in funzione del nuovo pathaggiornaElenchi();

}});

Riquadro G// Classe incaricata di fare da filtro sui files.// Rifiuta qualsiasi file che non sia una directory.class AccettaSoloDirectories implements FilenameFilter{

public boolean accept(File path, String name){

// costruisce un File con path e nome forniti,// verifica se e' una directory e ritorna l'esito della verifica// (true o false)return new File(path,name).isDirectory();

}}

Riquadro H// sono presenti cartelle - tagliamo il prefisso // il metodo getName della classe File e' in grado// di ritornare la sola parte terminale di un path completofor (int i=0; i<folders.length; i++)

folders[i]=new File(folders[i]).getName();

// Visualizza il nuovo elenco nella lista delle directoriesjlFolders.setListData(folders);

Riquadro I/// registrazione event handler per reagire al cambio di cartellajlFolders.addMouseListener(new MouseAdapter(){

// Il metodo verrà chiamato quando il tasto del mouse// verra' rilasciatopublic void mouseReleased(MouseEvent me){

// Esclude il caso di selezione nullaif(jlFolders.getSelectedValue()!=null){

// Costruisci il path completo della nuova directorycurrPath=new File

(currPath,jlFolders.getSelectedValue().toString());

// Aggiorna elenchi in funzione della nuova directoryaggiornaElenchi();

}}

});

4

5

6

Un paio di situazioni tipichedurante il funzionamento diMyExplorer: la navigazionetra directory (in alto);il dialog box di selezione delfile da visualizzare (qui adestra) e l’apertura di un fileHTML (qui sotto).

Page 34: Corso_java.pdf

Java 4a lezione

Dopo un'introduzione suiconcetti fondamentali del-la rete TCP/IP vedremo

quali sono le classi e i metodiche a livello logico rappresen-tano in Java le varie entità ingioco. L'esposizione sarà ac-compagnata da esempi di codi-ce che useremo per dimostrarele tecniche presentate. Comesempre sarà l'occasione per ri-prendere anche alcuni dei temitoccati nelle puntate preceden-ti, come le classi di I/O e il sup-porto per la grafica, introdu-cendo qualche nuovo concettoanche su tali aree. Il nostro ri-sultato finale sarà nientemenoche un piccolo Web browser!

Java e Internet: unione perfetta

Possiamo dire che il potentesupporto per lo sviluppo di ap-plicazioni di rete è fra i princi-pali aspetti caratterizzanti del-la tecnologia Java.

Java è anzi stato sviluppatopensando ad applicazioni perla rete e nella rete. L'intenzionedei progettisti del linguaggio edelle sue librerie standard èsempre stata quella di renderepossibile lo sviluppo di appli-cazioni che fossero in grado diessere rapidamente scaricatevia rete e installate al volo sulclient, che potessero girare suqualsiasi hardware e fossero ingrado di operare in qualsiasicontesto linguistico, rimanen-do facili da scrivere, con unarobusta protezione contro glierrori applicativi e contro i ten-tativi di abuso come virus etrojan horse.

Questi requisiti di progetto,tutti finalizzati a fare di Java

una delle soluzioni d'elezioneper scrivere applicazioni di re-te, hanno avuto tutta una seriedi risposte tecnologiche cheoggi fanno parte del "patrimo-nio genetico" del linguaggio edelle sue librerie, offrendo allosviluppatore un notevole valo-re aggiunto.

Il bytecodeFra esse, una delle più rile-

vanti è l'utilizzo del bytecodecome formato "oggetto" per iprogrammi Java. Il bytecode èestremamente compatto, inol-tre non porta con sè le librerieusate dall'applicazione, inquanto si può sempre contaredi trovarle nell'ambiente runti-me. Di conseguenza, a secondadei casi, il codice applicativo diun programma scritto in Javapuò arrivare a essere anche 10-100 volte più piccolo che se lostesso programma fosse statosviluppato in un altro linguag-gio tradizionale. Applicazionidi piccole dimensioni ben siprestano a essere scaricate viarete, perché richiedono untempo relativamente breve peressere trasferite sul client perl'esecuzione. Questa proprietàè stata sfruttata in Java, fin dal1997, per proporre una formainnovativa di distribuzione inrete di programmi: l'applet. Unapplet è un'applicazione ("im-mersa" in una pagina Web) chenon risiede stabilmente sullapiattaforma di esecuzione, maviene scaricata e avviata "al vo-lo" ogni volta che serve. Nonserve installazione e non vieneoccupato spazio sul disco delclient. Inoltre, in caso di rila-scio di una nuova versione del-

� A scuola con PC Open

ProgrammareJavaComunicare in reteIn questa ultima puntata affrontiamo il tema Internet, con particolare enfasisulla programmazione in rete

Lezione 1Rudimenti di programmazione- Terminologia essenziale- Dall’algoritmo al programma- Preparazione di Java- Il primo programma

Lezione 2Elementi di programmazione a oggetti e grafica- Tipi e identificatori- Astrazione funzionale- Programmazione a oggetti

- Creare una GUI

Lezione 3File- Archiviare e trasferire i dati- Errori e gestione degli errori- La ricorsione - Un esempio

Lezione 4Networking- Programmare con i socket- Java e il TCP/IP- Web Browser - Un esempio

IL CALENDARIO DELLE LEZIONI

di Marco Mussini

l'applicazione, non sarà neces-saria alcuna azione ammini-strativa per aggiornare il client.Semplicemente, la prossimavolta che verrà aperta la pagi-na Web che contiene l'applet,questo verrà scaricato di nuo-vo (stavolta nella versione piùrecente) e l'utente noterà im-mediatamente la novità.

Altra conseguenza positivadell'utilizzo del bytecode co-me "linguaggio universale" edella Virtual Machine come"esecutore universale" è la qua-si perfetta portabilità di Java.Con questo termine si intendela possibilità di far girare pro-grammi su macchine di tipo di-verso senza doverle sottoporread adattamenti complicati. Nelcaso di Java, il bytecode è co-me una sorta di speciale lin-guaggio macchina che viene ri-conosciuto da un programma,la Virtual Machine appunto,che simula una CPU fisica. Il by-tecode può quindi essere ese-guito dovunque vi sia una Vir-tual Machine. E, come abbiamogià accennato all'inizio di que-

sto corso, esistono Virtual Ma-chine Java per dispositivi diogni genere, dal microchip im-merso in una smart card fino alpersonal computer, dal palma-re al controllore interno di unelettrodomestico, dal telefonocellulare ai più bizzarri gadgettecnologici. In un mondo in cuii dispositivi di tutte queste ca-tegorie (elettrodomestici com-presi, ormai!) vengono collega-ti a Internet per amplificarnel'utilità e il ventaglio di applica-zioni, la tecnologia Java è di-ventata una sorta di "linguafranca" grazie alla quale unastessa applicazione può esserericiclata su più piattaforme emercati; di conseguenza(aspetto da non sottovalutare)gli stessi sviluppatori Java pos-sono applicare le proprie co-noscenze e la propria espe-rienza a un amplissimo campoapplicativo.

Nel caso particolare delleapplicazioni Internet, la pro-prietà di Java di essere un lin-guaggio portabile è di estremaimportanza perché gli utenti di

Page 35: Corso_java.pdf

Java 4a lezione

1 Programmazione in rete con i socket

Internet si servono di termina-li di tipi molto diversi:• il sistema operativo può es-

sere una delle tante versioniesistenti di Windows, Macin-tosh, Linux, Unix;

• l'hardware può essere IntelIA32 compatibile (nel casodei PC), ma anche PowerMac(per i Mac), XScale/ARM/MI-PS (per i palmari), IA64/PA-RI-SC/SPARC (per worksta-tions), e così via.Un programma tradizionale,

compilato in codice macchina,può girare su una qualunque diqueste architetture, ma solo suquella. Per poterlo usare in unaltro ambiente deve essere ri-compilato apposta.

Java dà invece la possibilitàdi scrivere programmi come setutta questa diversità non esi-stesse. Le applicazioni vengo-no scritte una sola volta e ri-sultano poi eseguibili su prati-camente tutte le piattaformeclient per Internet.

Un'altra peculiarità di Inter-net e delle applicazioni per In-ternet sta nel fatto che a unostesso sito Web possono colle-garsi utenti di tutto il mondo edi tutte le lingue. Nel mondooccidentale le lingue utilizzanoquasi tutte l'alfabeto latino,"condito" da una varietà più omeno ampia di segni diacritici.Tuttavia gran parte della popo-lazione mondiale utilizza alfa-beti diversi: basti pensare al ci-

rillico utilizzato in Russia, all'e-braico, all'arabo, al sanscrito,al coreano, al cinese, al giappo-nese.

Risulta subito chiaro che unlinguaggio che come Java siproponga lo scopo di suppor-tare lo sviluppo di applicazioniInternet deve risolvere il pro-blema del trattamento di strin-ghe e messaggi scritti in tuttiquesti alfabeti. E infatti anchein questo comparto Java adot-ta, fin dal suo esordio, la solu-zione standard più potente ecompleta: il set di caratteri Uni-code, al posto dell'"eurocentri-co" ASCII. Grazie a questa scel-ta è possibile scrivere un pro-gramma una sola volta, lavo-rando con il proprio set di ca-ratteri abituale, e poi adattarload altri tipi di alfabeto senzacambiare il codice, ma tradu-cendo solo le stringhe e i mes-saggi: un lavoro che può fareun interprete, lasciando il pro-grammatore libero di concen-trarsi sul suo lavoro. Può sem-brare un’ovvia separazione deiruoli, ma prima dell'avvento diUnicode (e di Java che per pri-mo lo ha adottato) non era co-sì. Per "internazionalizzare"una applicazione pensata perlavorare con caratteri latini, edotarla di supporto per carat-teri asiatici, era sempre neces-saria una revisione profonda emeticolosa del codice, peradattare tutti i punti in cui ve-

nivano trattati dati di tipo te-stuale. Questo lavoro di adat-tamento, nei casi più comples-si, poteva richiedere uno sfor-zo dello stesso ordine di gran-dezza di quello speso per losviluppo dell'intera applicazio-ne in versione originale. Il van-taggio offerto da Java anchequi è evidente.

Un'ultima proprietà di Javache si rivela di grande impor-tanza per lo sviluppo di appli-cazioni in rete è il suo sofisti-cato sistema di protezione chegli conferisce una sorta di pre-ziosa "immunità" rispetto adattacchi e abusi come worm,virus e trojan horses.

Tutti sappiamo che questi"nemici", per potersi introdur-re nel nostro sistema, devonoessere eseguiti. Per questo lamaggior parte di essi si annidadentro gli eseguibili delle ap-plicazioni, dove di solito unbuon antivirus aggiornato è ingrado di scovarli.

Anche i programmi Java sca-ricati come applet o sotto altraforma provengono dalla rete eper questo devono sempre es-sere considerati sospetti fino aprova contraria.

Per assicurare la "disinfezio-ne" delle applicazioni Java èstata progettata una strategiapreventiva. Invece di cercare le"firme" dei virus noti all'internodell'applicazione, si effettuaun'analisi approfondita di tutte

le sue istruzioni, alla ricerca diqualsiasi istruzione considera-ta pericolosa per la sicurezzadel sistema o dei dati:• istruzioni non riconosciute o

dalla sintassi scorretta;• letture e scritture da disco

non precedute da esplicita ri-chiesta di autorizzazione;

• tentativi di collegarsi via retea siti diversi da quello dalquale l'applicazione è statascaricata;

• tentativo di cambiare impo-stazioni che si riflettono sututto il sistema.In sostanza, un applet Java

non può fare nulla di pericolo-so, a meno che non lo abbiaapertamente chiesto e sia statoautorizzato dall'utente.

Questo approccio in teoriagarantisce di scoprire non solovirus e codice pericoloso di ti-po già noto, ma anche quellinuovi.

Il tutto senza mai richiederedi aggiornare un database di"casi noti", come avviene con inormali antivirus.

È un po' come se Java fosse"vaccinato", all'origine e persempre, contro il rischio di fun-gere da veicolo di codice peri-coloso. La "tranquillità" che ta-le approccio garantisce all'u-tente Internet costituisce un ul-teriore, decisivo fattore di van-taggio di Java rispetto ad altresoluzioni per lo sviluppo di ap-plicazioni di rete.

Il modello di programmazionemesso a disposizione dalle li-brerie standard di Java per

realizzare programmi che co-municano in rete è il classicomodello a socket universal-mente utilizzato per lavorarecon il protocollo usato dalle piùimportanti applicazioni Inter-net, il TCP/IP (TransmissionControl Protocol/Internet Pro-tocol): è importante ricordaresinteticamente di che cosa sitratta, prima ancora di poterpresentare il concetto disocket.

Quando si parla di TCP/IP cisi riferisce di fatto a due distin-ti protocolli, così come due so-no gli aspetti affrontati per as-sicurare la possibilità di comu-nicare in rete: l'instradamento,di cui si occupa l'IP, e il traspor-

to, che è il compito del TCP.

Il protocollo IPL'Internet Protocol, che si

potrebbe a pieno titolo definirela "colla che tiene insieme In-ternet", si occupa del problemadi far arrivare pacchetti di datida un indirizzo origine a un in-dirizzo destinazione guidandoliattraverso la rete.

Ogni pacchetto contiene unheader IP nel quale sono ripor-tate, fra le altre, le informazionisull'indirizzo IP del mittente el'indirizzo IP del destinatario. Inbase a quest'ultima informazio-ne, i centri di smistamento/in-stradamento (routers) dissemi-nati nella rete possono decide-re che strada far fare a un pac-chetto per avviarlo alla sua de-stinazione finale.

Possiamo paragonare il pac-chetto di dati IP a un automobi-lista in viaggio da un luogo A aun luogo B che a ogni incrocio obivio si ferma per strada e chie-de a un passante P "da che par-te si va per B?". A seconda di B,il passante (che nell'esempiocorrisponde al router) indi-cherà la strada giusta da im-boccare per uscire dall'incrocioin direzione corretta. Esistonoquattro scenari possibili per larisposta. Supponiamo che l'in-dirizzo IP di B sia123.123.123.123.

Scenario 1: il passante ri-sponde: "È qui, lei è arrivato".Questa situazione corrispondeall'arrivo a destinazione delpacchetto: in sostanza P coin-cide con B, ossia è l'host desti-

nazione, non un router interme-dio. L'indirizzo IP di P è insom-ma 123.123.123.123.

Scenario 2: il passante ri-sponde: "Vada a destra, B è ilprossimo incrocio". In questocaso P è un router che noncoincide con la destinazione B,ma sa con precisione e in modospecifico come raggiungerla. SuP è stata cioè configurata unaregola del tipo: "per raggiunge-re l'indirizzo 123.123.123.123occorre far uscire i pacchettiper la scheda di rete n. 5".

Non è detto che P e B sianoadiacenti (collegati direttamen-te); potrebbero esserci altri "in-croci" lungo la strada, ma P co-nosce B in modo specifico epuò quindi fornire un'indicazio-ne puntuale.

Page 36: Corso_java.pdf

Java 4a lezione

� Scenario 3: la risposta è"Non conosco di preciso tuttala strada fino a B, ma sono si-curo che è per di qui, a destra".Ovviamente il passante nonpuò conoscere la posizione pre-cisa di tutte le città del mondo etutte le strade del mondo, mapur senza conoscere la partico-lare città B potrebbe essere al-meno in grado di dire se, al bi-vio, essa si trovi "verso destra"o "verso sinistra". Per esempio,se P sa almeno che B è una cittàdi un determinato Paese e sache per raggiungere quel Paeseoccorre andare a destra, potràcomunque fornire una rispostautile. Questo corrisponde, perun router, a conoscere una re-gola che si applica a un gruppodi indirizzi; se l'indirizzo di Brientra in quel gruppo di indi-rizzi, allora il router può instra-dare il pacchetto. Per esempio:"per raggiungere tutti gli indi-rizzi della sottorete 123.123.*.*occorre far uscire i pacchettiper la scheda di rete n. 5".

Scenario 4: "Non ho la piùpallida idea di dove si trovi B.Provi ad andare da questa par-te e chieda ancora". Se P non hala minima idea di dove possatrovarsi B, si suppone però chepossa almeno dare un suggeri-mento su dove recarsi per tro-vare qualcuno in grado di dareindicazioni. È il concetto di de-fault gateway: quando mancanoregole precise e perfino regoledi sottorete, come nei due sce-nari precedenti, allora deve esi-stere una "regola di default",per così dire ...l'ultima spiaggia,che possa comunque dareun'indicazione indirettamenteutile per mettere il pacchettosulla strada giusta. Lo si mandaallora verso un host o router,appunto il default gateway, cheè incaricato di "saperne di più"e di instradare lui al meglio ilpacchetto.

Il protocollo TCPSe il protocollo IP, come si è

visto, è un modo per fornire...indicazioni stradali ai pacchettiin viaggio per la rete, che aiu-tandoli a ogni "incrocio" assi-cura che non possano "sbaglia-re strada", va tenuto presenteche non vi è garanzia che neitratti di strada fra un incrocio el'altro un pacchetto non vada"perso". Nelle reti questo puòavvenire per motivi di conge-stione (se il traffico è eccessivoi pacchetti in surplus vengono

buttati via) oppure per guastoo interferenza alle linee di tra-smissione o alle schede di rete.Se un pacchetto va perso, è ne-cessario che qualcuno abbiamodo di accorgersene e se lofaccia rispedire, altrimenti saràcompromessa l'integrità delflusso di dati da A a B.

Inoltre, a seconda delle con-dizioni del traffico, i router po-sizionati presso gli "incroci" po-trebbero fornire ai pacchetti in-dicazioni diverse nel tempo. Diconseguenza, un flusso di pac-chetti tutti originati da A e di-retti a B potrebbero seguirestrade diverse per raggiungerela meta e anche ammettendoche non ne vada perso nessunopotrebbe accadere che le stra-de abbiano lunghezze diverse,col risultato che pacchetti par-titi prima potrebbero arrivarein ritardo rispetto a pacchettipartiti dopo, ma che sono statimessi su una strada più breve.All'arrivo, quindi, B ricevereb-be un flusso disordinato di pac-chetti. Se, come è quasi semprevero, l'ordine di arrivo dei dati èsignificativo, risulta necessariogarantire che esso non risultisconvolto da variazioni nellepolitiche di routing.

Il Transmission Control Proto-col (TCP) si occupa proprio diquesti problemi (oltre ad altrequestioni più sottili, ma nonmeno importanti, che non trat-teremo in questo articolo) e as-sicura che i pacchetti arrivinotutti a destinazione e nello stes-so ordine con cui sono partiti.In altre parole, si occupa del"trasporto", con garanzia di in-tegrità, dei dati.

Se un pacchetto va perso, il

TCP se ne accorge, grazie a unsistema di numerazione deipacchetti, e ne richiede la tra-smissione. Se invece rileva chel'ordine di arrivo non rispetta lanumerazione, accumula i pac-chetti arrivati in anticipo finchènon vede arrivare quelli che liprecedono nella sequenza; puòcosì ricostituire l'ordine corret-to e lasciar passare i pacchettiin sospeso.

Indirizzi e porteGrazie al TCP e al suo lavoro

di controllo e "riordino", quindi,la comunicazione attraverso larete, che nel modello IP appareun poco precaria, diventa affi-dabile. Da un punto di vista lo-gico è come se fra A e B venissecreata una strada apposita,senza incroci né possibilità distrade alternative né rischio diperdere pacchetti; una sorta di"tubo sigillato" all'interno delquale i dati fluiscono da A a Bsenza disordini e senza perdite.Questo "tubo" è detto "connes-sione TCP" e le sue due estre-mità, una sull'host A e una sul-l'host B, si dicono end points.Ognuna di esse è identificata dadue informazioni: l'indirizzodell'host e la porta. L'indirizzoha il familiare aspetto del tipo123.45.67.89 ed è costituito daun numero a 32 bit scomposto,per comodità di rappresenta-zione, in 4 byte. La porta è in-vece un numero intero positivoa 16 bit, compreso quindi fra 0 e65.535. Le porte sono parago-nabili a sottoindirizzi interninell'ambito dell'host: se para-goniamo l'indirizzo IP a "ViaVerdi, 1", le porte potrebberocorrispondere a "appartamen-

to n.234". Grazie al concetto diporta, un computer dotato diuna singola scheda di rete (equindi di un unico indirizzo IP)può comunque gestire fino a65.536 applicazioni distinte,ognuna delle quali utilizza unasua porta per scambiare i datisenza possibilità di ambiguitàcon le applicazioni "colleghe".

Gli indirizzi di origine e de-stinazione che sono riportatisui pacchetti in viaggio sulla re-te in effetti sono costituiti dal-l'indirizzo IP e da una porta.

Alcune porte (per la preci-sione, quelle il cui numero ècompreso fra 0 e 1.023) sono ri-servate per applicazioni o ser-vizi standard: per esempio, peri web server lo standard è laporta 80, mentre per FTP è laporta 21. Se dalla rete arriva unpacchetto, questo viene esami-nato innanzitutto per vedere aquale porta è destinato. Così,se la porta di destinazione è la80 il pacchetto viene mandatoal web server; se è 21, all'FTPserver. Se il numero di porta èsconosciuto (nel senso chenessuna applicazione in esecu-zione sull'host "abita" a quelnumero di porta) il pacchettoviene ignorato e se faceva partedi un tentativo di aprire unaconnessione TCP, tale tentativoviene respinto.

ConnessioniPossiamo paragonare le con-

nessioni alle telefonate. Per po-ter essere usata, una connes-sione deve essere innanzituttocreata. Per farlo occorre cheuna delle due parti provveda adalzare la cornetta e comporre ilnumero (l'insieme di prefisso e

Esempio telefonico Corrispondente concetto TCP/IP in Java

Prefisso internazionale, prefisso teleselettivo Indirizzo IP, rappresentato da una istanza della classee numero di base del centralino di un'azienda InetAddress

Numero dell'interno (Extension) Porta (numero intero)

Presa telefonica a muro ServerSocket per ricevere le chiamate; (associata a un numero telefonico interno) Socket per effettuarle

Apparecchio telefonico connesso alla presa Applicazione client o server

Microfono incorporato nella cornetta OutputStream ottenuto dal Socket

Altoparlante incorporato nella cornetta InputStream ottenuto dal Socket

Chiamare un numero Creare un Socket con l'indirizzo e porta desiderati

Attendere le chiamate davanti a un telefono Eseguire il metodo accept su un ServerSocket

Rispondere a una chiamata entrante Iniziare lo scambio dati sul Socket ritornato dal metodo accept

Page 37: Corso_java.pdf

Java 4a lezione

� numero telefonico corrispondeall'insieme di indirizzo IP e por-ta IP). Questo ruolo è definitoclient, initiator o caller. L'altraparte, che doveva essere pre-sente e in ascolto, allo squillaredel telefono deve alzare il rice-vitore: questo ruolo è definitoserver, responder o callee.

In seguito la conversazionesarà bidirezionale e chiunquedei due potrà chiuderla riag-ganciando il ricevitore.

I socketDa un punto di vista di pro-

grammazione, gli end point del-le connessioni TCP sono mo-dellati come delle entità con unciclo di vita, delle proprietà euna semantica ben definite: i

socket.Un socket è la rappresenta-

zione logica di una coppia "in-dirizzo+porta". Può essere lo-cale, se rappresenta una portadell'indirizzo locale del com-puter, oppure remoto, se rap-presenta una porta di un altrocomputer.

Una connessione TCP vistada un programma è semprecompresa fra due socket: unoè sempre locale, l'altro può es-sere remoto (è il caso più co-mune e corrisponde alla co-municazione fra due compu-ter) oppure essere anch'essolocale (in questo caso la co-municazione è fra due diversiprocessi che girano sullo stes-so computer).

Rifacendoci all'esempio del-la telefonata sopra proposto, ilsocket può essere di due tipi:client o server. Il client socket èquello lato initiator; è quello dacui si richiede un servizio (nel-l'esempio la telefonata; nel ca-so Internet, l'apertura di unaconnessione).

Il server socket si trova latoresponder ed è la posizione dacui si offre la disponibilità aerogare un servizio a chi nefaccia richiesta.

Non è tuttavia il serversocket a gestire direttamente laconnessione che viene a crear-si: il suo ruolo è solo quello dicaptare la richiesta di aperturadi una connessione. Quando ilserver socket riceve una simile

richiesta mentre si trova nellostato di attesa (accept), la con-nessione viene accettata e co-me end point lato server vieneinstallato un Socket fatto na-scere apposta.

Il server socket si libera co-sì dell'incombenza di trattarein prima persona il traffico da-ti e torna in attesa di altre ri-chieste di connessione.

Il client socket è modellatoin Java dalla classe Socket e of-fre, oltre a molti altri, il metodoconnect che corrisponde all'a-zione dell'initiator di tentaredi aprire la connessione.

Il server socket è invece mo-dellato dalla classe Server-Socket, che offre il già citatometodo accept.

2 Applicazione Java client-server con TCP/IPÈil momento di vedere Java

alle prese con il TCP/IP.Per farlo scriveremo una

prima semplice applicazionecon la classica struttura client-server. In sostanza l'applicazio-ne consiste in una coppia diprogrammi separati e indipen-denti, ma progettati per intera-gire attraverso uno scambio didati su una connessione di reteTCP/IP.

Si identifica con il termine diserver un programma in gradodi erogare un determinato ser-vizio a fronte di una richiestaesterna formulata in un modoprestabilito da un altro pro-gramma, identificato comeclient. Un punto fermo è chel'interazione tra client e serverparte sempre su iniziativa delclient. Quest'ultimo apre unaconnessione di rete verso ilserver mentre questo è in atte-sa; se l'operazione riesce, ilclient usa la connessione pertrasmettere la richiesta al ser-ver. L'arrivo della connessioneha "svegliato" il server dal suostato di attesa, che reagisce aquesto evento ricevendo la ri-chiesta ed eventualmente esa-minandola per stabilire esatta-mente il da farsi. Il passo suc-cessivo del server consiste nel-l'innescare l'elaborazione cheporterà alla determinazionedel risultato. Il processo si con-clude quando il server rispedi-sce al client il risultato dell'ela-borazione per mezzo dellastessa connessione di rete sul-

la quale il client ha inviato la ri-chiesta. A questo punto, a se-conda della logica di funziona-mento del server, la connessio-ne può venire chiusa oppureessere mantenuta aperta, pron-ta per le successive richieste.

Veniamo ora all'esempio, ini-ziando dall'esame del codicedel server (vedi riquadro "Ser-ver").

Il serverIl programma è molto sem-

plice: vi è una sola classe, Ser-ver, con un solo metodo, ilmain.

Il primo punto è la creazionedi un ServerSocket, che resteràin ascolto su una porta presta-bilita (la 9999) che deve esserenota sia al server sia al client.ServerSocket ss=new

ServerSocket(9999);Questa istruzione potrebbe

fallire per vari motivi, ragionper cui è stata posta in un bloc-co try..catch. Fra i possibili mo-tivi possiamo citare: assenzadella scheda di rete e protocol-lo TCP/IP non installato, porta9999 già occupata, permessonegato per motivi di sicurezza(per esempio dal firewall diWindows XP SP2). A questoproposito occorre ricordareche se sul PC è installato il Ser-vice Pack 2 di Windows XP, iltentativo del nostro server diriservare a sè la porta 9999 emettersi in ascolto su di essaverrà prudenzialmente blocca-to e segnalato con un dialog co-

me quello in figura 1. Sarà suf-ficiente rispondere Sblocca perinsegnare a Windows a fidarsidi Java tutte le volte successi-ve. Per esaminare ed eventual-mente annullare l'effetto del-l'impostazione Sblocca è suffi-ciente entrare in Pannello diControllo e scegliere Centro si-curezza PC, poi Windows Fi-rewall; dovrebbe presentarsiuna situazione simile a quellain figura 2 dove è presente unariga che segnala che d'ora inpoi si farà eccezione per Java.

Dopo aver creato il Server-Socket il server deve mettersi

in attesa che entrino delle con-nessioni originate dai client.Socket s=ss.accept();

1

2

Page 38: Corso_java.pdf

Java 4a lezione

Il metodo accept, applicatoall'oggetto ServerSocket, hal'effetto di attivare l'ascolto sul-la porta alla quale è stato ag-ganciato il ServerSocket in fasedi creazione. L'esecuzione nonprocede e resta bloccata in at-tesa su questa istruzione fino aquando non sarà rilevata unaconnessione entrante sullaporta 9999, qualunque sia laprovenienza. All'arrivo di taleconnessione verrà creato auto-maticamente un Socket (sem-plice) che avrà la funzione di"terminale" locale, lato server,della connessione stessa. TaleSocket viene ritornato come ri-sultato della funzione accept.

Una volta ottenuto il socketlocale, che da un punto di vistalogico rappresenta un'estre-mità di un tubo che ci collegadirettamente al client, dobbia-mo organizzarci per poter leg-gere e scrivere da/su questoSocket. Questo si fa servendosidi due metodi di questa classe:getInputStream(), che ritornaun InputStream da cui potremoleggere i dati in arrivo dalsocket, e getOutputStream(),che ritorna un OutputStreamche spedirà via socket i datiche scriveremo su di esso.

Come sappiamo dalla scorsapuntata, gli Stream sono adattia scambiare dati "grezzi" a by-te; poiché però nel nostroesempio il client e il server siparlano con messaggi di testo,dovremo usare delle classi Rea-der e Writer per manipolarlicon facilità.

Per la ricezione scegliamo dicostruire un BufferedReaderper poter leggere comodamen-te per righe i messaggi in arrivodal client. Ma i costruttori del-la classe BufferedReader richie-dono come parametro un Rea-der o una sua sottoclasse, nonun InputStream; per poterlicomporre, quindi, ci occorreinterporre un "adattatore", rap-presentato nell'esempio da unaistanza di classe InputStream-Reader.BufferedReader br=new

BufferedReader(new InputStreamReader

(s.getInputStream()));Per la trasmissione creiamo

un PrintWriter per poter man-dare testo per righe. Fortuna-tamente il PrintWriter può esse-re creato direttamente su unOutputStream:PrintWriter pw=newPrintWriter(s.getOutputStream());

Ora che tutto è pronto per

gestire dati di testo sul socketpossiamo partire con la primaoperazione: leggere il messag-gio (di una riga) in arrivo dalclient. String a=br.readLine();

Quando viene incontrataquesta istruzione, l'esecuzionesi blocca fino a quando non ri-sultano disponibili per la lettu-ra abbastanza dati per riempi-re una riga di testo (fino al new-line).

poiché il client invia al ser-ver la sua richiesta subito dopoaver aperto la connessione checi ha "risvegliato" dalla accept,l'attesa di questi dati è brevis-sima e in men che non si dica lavariabile "a" riceverà il valorericevuto dal client.

A questo punto la richiestadel client va esaminata per sta-bilire il da farsi ed elaboratapoi nel modo richiesto.

L'esame del messaggio en-trante è necessario in tutti i ca-si in cui il server sia in grado disvolgere varie funzioni diversea seconda della richiesta rice-vuta; ma poiché il nostro parti-colare server svolge sempre lostesso compito, ossia volgerein maiuscolo la stringa ricevu-ta, non occorre nessuna analisie si può procedere senz'altrocon l'elaborazione, che si rea-lizza con la funzione toUpper-Case della classe String; in so-stanza chiamiamo questo me-todo direttamente sulla varia-bile a che contiene la stringada volgere in maiuscolo.a=a.toUpperCase();

A questo punto il risultato èpronto e può essere spedito alclient semplicemente "stam-pandolo" sul PrintWriter cheabbiamo connesso con l'Out-putStream che conduce alclient:pw.println(a);

Questa istruzione può nongarantire che i dati escano su-bito. Il sistema, per motivi di ef-ficienza, può avere la politica diraggruppare i dati da spedire inrete e fare un'unica spedizione"grossa" (non appena si rag-giunge un livello di "accumulo"sufficiente) anzichè molte spe-dizioni "piccole", una ogni vol-ta che chiediamo di spedire po-chi dati. Noi però sappiamo dinon avere altri dati da spediree vogliamo che questi partanosubito. La cosa da fare allora èforzare lo svuotamento della..."sala d'attesa" utilizzando sulPrintWriter la funzione flush().pw.flush();

A questo punto il nostrocompito è terminato e possia-mo chiudere sia il PrintWriterin uscita sia il BufferedReaderin entrata, prima di terminare ilprogramma.pw.close();br.close();

Da notare infine che nelblocco catch abbiamo postouna istruzione che, qualora siverifichi un'eccezione, ci daràinformazioni su quello che èsuccesso visualizzando sulla

console opportuni messaggi:catch(Exception e){

e.printStackTrace();}vedremo più avanti alcuniesempi del risultato prodottoda questa istruzione.

Il clientVediamo ora come si svolgo-

no le cose dal punto di vista delclient. Anche qui la strutturadel programma è semplissima:

Riquadro "Server"// per le classi Socket e ServerSocketimport java.net.*;// per le classi di i/oimport java.io.*;

// Programma che ascolta sulla porta 9999, // riceve una stringa e risponde con la stessa// stringa trasformata in maiuscolopublic class Server{

// il punto di ingresso del programmapublic static void main(String [] args){

// il blocco try-catch cattura eventuali erroritry{

// Inizializza il socket verso il server// e implicitamente apre la connessioneServerSocket ss=new ServerSocket(9999);// Attende messaggi dal clientSystem.out.println("In attesa di messaggi");Socket s=ss.accept();System.out.println("Rilevata connessione entrante");// Predispone un PrintWriter per poter inviare// agevolmente stringhe sul socketPrintWriter pw=new PrintWriter(s.getOutputStream());// Costruisce un BufferedReader per leggere // a righe il flusso di testo ricevuto dal serverBufferedReader br=new BufferedReader

(new InputStreamReader(s.getInputStream()));// Legge la stringa inviata dal clientString a=br.readLine();// Visualizza il messaggio ricevutoSystem.out.println("Dal client: "+a);// Trasforma la stringa in maiuscoloa=a.toUpperCase();// Rimanda al client la stringa trasformatapw.println(a);// Forza la trasmissione immediatapw.flush();System.out.println("Risultato inviato");// Chiude le connessioni col serverpw.close();br.close();

}catch(Exception e){

e.printStackTrace();}

}}

Page 39: Corso_java.pdf

Java 4a lezione

una sola classe (Client) e un so-lo metodo (l'immancabilemain).

La prima cosa da fare è crea-re un Socket collegato alla por-ta e indirizzo dove ci aspettia-mo che sia in ascolto il Server:porta 9999 e host locale. InTCP/IP il nome universale perriferirsi all'host su cui si trova ilprogramma in esecuzione è "lo-calhost", corrispondente allospeciale indirizzo 127.0.0.1, cheviene anche chiamato loop-back address (ad indicare chele connessioni aperte versoquesto indirizzo saranno cometanti "autoanelli" che si richiu-dono sul client).

La creazione del Socket ver-

so il nostro server sarà quindiespressa in questo modo:Socket s=newSocket("localhost",9999);

Come già detto per il server,questa ed altre operazioni chehanno a che fare con la retepossono fallire per molti possi-bili motivi, corrispondenti alloscatenarsi di determinate ec-cezioni. Anche nel caso delclient, quindi, per catturare edeventualmente trattare questeeccezioni circondiamo il codi-ce "a rischio" con una strutturatry-catch, liberandoci così dalpensiero di dover controllare"come è andata" dopo ogniistruzione: ci penseremo nelblocco catch.

Se l'apertura del socket (conimplicita connessione al ser-ver) è riuscita, possiamo co-struire quello che ci servirà perlo scambio di messaggi di testocon il server. Anche in questocaso gli Input/Output Streammessi a disposizione dal socketnon ci vanno bene, quindiprovvediamo a "racchiuderli"(in gergo si usa il verbo ingleseto wrap, quasi stessimo "incar-tandoli") rispettivamente in unBufferedReader e in un PrintW-riter. Come al solito, per il Buf-feredReader occorre l'interpo-sizione di un InputStreamRea-der.

Le istruzioni che costruisco-no le due strutture che ci ser-vono sono queste:PrintWriter pw=new

PrintWriter(s.getOutputStream());BufferedReader br=new

BufferedReader(new InputStreamReader

(s.getInputStream()));Ora che tutto è pronto per la

comunicazione con il server(che nel frattempo, per inciso,si è risvegliato dalla accept incui era in attesa) possiamospedire la nostra richiesta: unastringa di testo che chiediamodi trasformare in maiuscolo.pw.println("Testo da trasformare");

Come al solito, per assicu-rarci che il messaggio parta su-bito forziamo la spedizione im-mediata con una flush():pw.flush();

L'ultima operazione da fare èpredisporsi per ricevere il ri-sultato che il server ci spedirà.L'esecuzione del client si sof-fermerà su questa istruzionefino a quando dal socket nonarriveranno dati sufficienti perriempire una riga di testo (new-line compreso). Allora talestringa verrà ricevuta e asse-gnata alla variabile stringa "a".String a=br.readLine();

Il sistema in funzioneSe entrambi i programmi so-

no stati già scritti e compilaticorrettamente (la compilazio-ne dà luogo a due files di byte-code, rispettivamente Client.class e Server.class) possiamoprovare a lanciarli.

Per fare questo apriamo duefinestre Prompt deicomandi(Start/Programmi/Ac-cessori/Prompt dei comandi) enella prima facciamo partire ilserver. Usando il comando cd,portiamoci nella directory do-ve si trovano i class file dell'ap-plicazione Server (per esem-

pio: c:\java), dopodichè diamoil comando java Server

Il server entrerà in esecuzio-ne e si metterà in attesa, comesegnalato da un apposito mes-saggio (figura 3).

Ora occupiamoci dell'altrolato. Nella seconda finestra, do-po esserci portati nella direc-tory c:\java, lanciamo il clientcon java Client

Questo dà il via a tutto il si-stema nel suo complesso, inquanto appena partito il clientsi collega al server, che senzaindugio elabora la richiesta ap-pena ricevuta, rispedisce alclient il risultato e termina.

Anche il client, appena rice-vuto il risultato, lo visualizza etermina. In pochi istanti il"duetto" giunge all'epilogo.Possiamo vedere che cosa suc-cede lato client in figura 4 e la-to server in figura 5.

Sequence diagramsAnche se i messaggi stampa-

ti dalle due applicazioni forni-scono indicazioni utili per ca-pire che cosa è successo e checosa "si sono dette", questatecnica non è adatta per pro-grammi di grandi dimensioniche effettuano molteplici scam-bi di dati, anche "accavallati"tra loro. Infatti, se gli scambi didati sono numerosi, questo �

Riquadro "Client"// per la classe Socketimport java.net.*;// per le classi di i/oimport java.io.*;

// programma che si collega alla porta 9999 del// proprio host, invia una stringa, attende una// stringa in risposta e la visualizza.public class Client{

// il punto di ingresso del programmapublic static void main(String [] args){

// il blocco try-catch cattura eventuali erroritry{

// Inizializza il socket verso il server// e implicitamente apre la connessioneSocket s=new Socket("localhost",9999);// Predispone un PrintWriter per poter inviare// agevolmente stringhe sul socketPrintWriter pw=new PrintWriter(s.getOutputStream());// Costruisce un BufferedReader per leggere // a righe il flusso di testo ricevuto dal serverBufferedReader br=new BufferedReader

(new InputStreamReader(s.getInputStream()));// Manda al server una stringa da trasformarepw.println("Testo da trasformare");System.out.println("Richiesta inviata");// Forza l'emissione immediata del messaggiopw.flush();// Legge il messaggio di risposta del serverString a=br.readLine();// Visualizza il messaggio ricevutoSystem.out.println("Risultato dal server: "+a);// Chiude le connessioni col serverpw.close();br.close();

}catch(Exception e){

e.printStackTrace();}

}}

Page 40: Corso_java.pdf

Java 4a lezione

3 Un Web browser scritto in Java

elenco di messaggi risulta tal-mente lungo da diventare vera-mente difficile e scomodo daseguire. Inoltre, in presenza dipiù "colloqui paralleli" su temidiversi, è probabile che l'esamedei messaggi, per tentare di se-guire uno in particolare di que-sti "colloqui", risulti molto dif-ficile.

Per queste ragioni, per la de-scrizione dell'interazione fraclient e server si fa uso di nota-zioni alternative.

Una particolarmente espres-siva e chiara, e per questo mol-to usata, riferita al nostro caso,è mostrata in figura 6. In questarappresentazione il temposcorre dall'alto in basso.

La colonna di sinistra rap-presenta la storia di tutto ciòche accade nel client e tutti gliscambi di dati che attraversa-no i suoi confini in entrata o inuscita. Discorso analogo per lacolonna di destra, che narra glieventi riguardanti il server.

Le comunicazioni intercor-se tra i due processi sono rap-

presentate da frecce orientatenel senso del traffico di dati. Laloro leggera inclinazione versoil basso, nel senso del tempocrescente, sta a rappresentareil fatto che per questi scambi didati è richiesto un tempo che,in caso di congestione o mal-funzionamento della rete, po-trebbe anche non essere tra-scurabile: da qualche microse-condo (nel caso di connessioniloopback) a qualche millise-condo (nel caso di reti localicon buone prestazioni) a qual-che secondo (per grandi di-stanze e/o reti affollate).

Anche se nel nostro caso ciòè irrilevante, in programmi piùcomplessi potrebbe essere si-gnificativo, in quanto, in attesache la comunicazione si com-pleti e magari torni indietro unqualche risultato, un client po-trebbe svolgere qualche suaelaborazione interna per sfrut-tare il tempo d'attesa.

Per esempio, mentre un webbrowser attende che arrivino idati della pagina da visualizza-

re, si occupa di gestire l'anima-zione che indica attività di rete(il logo del browser in alto a de-stra), o aggiorna la progressbar in basso, o altro ancora.

Questo tipo di diagramma,da sempre usato come stru-mento di progetto nel mondodelle telecomunicazioni per da-re rappresentazione di ciò che

accade in un sistema compo-sto di più programmi distinti inesecuzione magari su nodi di-versi (nel qual caso si parla di"sistema distribuito"), è statoripreso, canonizzato e standar-dizzato in tempi relativamenterecenti con il nome di SequenceDiagram nell'ambito dell'Uni-fied Modeling Language (UML).

Come "summa" di questa le-zione e dell'intero ciclo viproponiamo un esempio

di applicazione in cui impie-gheremo, tutte insieme, moltedelle tecniche viste finora.

L'applicazione è una imple-mentazione di un sempliceweb browser dotato di funzio-ne history (e relativo pulsanteBack), pulsante home, selezio-ne diretta delle pagine.

L'intero programma, scritto

in Java, è lungo appena 139 li-nee: un risultato inimmaginabi-le fino a solo pochi anni fa,usando linguaggi e librerie piùprimitivi.

Un'ulteriore riconferma del-la particolare idoneità di Javaper la rapida realizzazione diapplicazioni in rete con inter-faccia grafica (Figura 7).

Vediamo ora il funzionamen-to delle parti più importantidel programma. Il listato com-

pleto, WebBrowser.java, si tro-va sul CD di PC Open.

Le dichiarazioni e il mainLa classe principale, Web-

Browser, è dichiarata come unasottoclasse di JFrame, alla qua-le aggiunge i propri metodi e at-tributi di utilizzo applicativo.Creando un'istanza di Web-Browser nascerà quindi impli-citamente una finestra top le-vel: non sarà necessario crear-la appositamente.

WebBrowser dichiara di im-plementare due interfacce chesono richieste per poter gestiregli eventi salienti del nostrobrowser: HyperLinkListener,che serve per poter trattare iclic del mouse sui link presentisulla pagina Web, e ActionLi-stener, necessaria per riceverei clic del mouse sui pulsantiBack e Home e l'evento di pres-sione del tasto Invio (Return)sul campo di immissione dellaURL.

public class WebBrowser extends JFrame

implements HyperlinkListener, ActionListener

Le variabili che dichiariamoriguardano i quattro widget im-piegati (due pulsanti, un cam-po di testo e l'area di visualiz-zazione HTML), la URL da usa-re come pagina iniziale (a cuitorneremo ogni volta che l'u-tente premerà il tasto Home) esoprattutto la struttura datiusata per memorizzare la hi-story. Si tratta della classeStack, contenuta nel packagejava.util. Si tratta di una leggeravariante della classe Vector(che è concettualmente similea un array dotato però della ca-pacità di crescere di dimensio-ni a run time, quando vengonoinseriti nuovi elementi).

La classe Stack rappresentauna pila ordinata di valori. Lapila è caricata e scaricata dal-l'alto: per questa ragione, l'ulti-mo entrato è il primo a uscire(Last In First Out, LIFO).

Per caricare un valore in ci-ma allo stack si usa il metodopush; per estrarlo si usa il me-todo pop. Questi due metodisono aggiunti dalla classe Stackai metodi già presenti nella su-perclasse, Vector.

La history di un browser sicomporta esattamente come

7

6

Page 41: Corso_java.pdf

Java 4a lezione

uno stack. Infatti, ogni volta cheseguiamo un link da una paginaweb, è come se scrivessimo suun foglietto l'indirizzo della pa-gina appena lasciata e lo depo-sitassimo su una pila di fogliet-ti che rappresentano le paginegià visitate. Quando premiamoil pulsante Back per tornare al-l'ultima pagina visitata, concet-tualmente quello che succedeè che viene pescato il fogliettoin cima alla pila, viene letto l'in-dirizzo ivi trascritto e vieneaperta la relativa pagina Web.Dopodichè, per semplificare ilconcetto, possiamo assumereche il foglietto venga scartato(in realtà nei browser reali esi-ste anche un pulsante Forward,quindi il foglietto viene conser-vato in un'altra struttura datiche rappresenta una sorta di"history al contrario" e vieneusata dal tasto Forward). Pre-mendo ancora Back viene ripe-tuto lo stesso procedimento, ecosì via fino a quando sullostack non rimane più alcun "fo-glietto". Non essendo possibilerisalire oltre la history, in que-sta situazione il pulsante Backva disattivato, come vedremopiù avanti.

JButton jbHome; // pulsanteritorno pagina inizialeJButton jbBack; // pulsante

ritorno pag. precedente (history)JTextField jtfURL; // campo per

immettere URL desiderataJEditorPane jepHTML; // area di

visualizzazione della pagina WebString home; // conterrà la URL

della pagina inizialeStack history=new Stack(); //

contiene la history

Il main dell'applicazione è ri-dotto all'osso. Tutto quello cheviene fatto è creare una istanzadella classe WebBrowser. Il la-voro viene fatto tutto dal co-struttore di quella classe.

Ricordiamo che, essendoWebBrowser sottoclasse diJFrame, automaticamente na-scerà anche la finestra princi-pale dell'applicazione.

Come parametro al costrut-tore occorre fornire la paginainiziale desiderata: scegliamoun sito che i nostri lettori pro-babilmente conoscono moltobene!..

public static void main(String[] args) {

new WebBrowser("http://www.pcopen.it/");}

WebBrowser: il costruttoreVediamo solo i punti salienti

del costruttore; tralasceremoovviamente gli aspetti che nonpresentano novità rispetto aquanto visto finora.

super("Web Browser");La prima istruzione del co-

struttore è la chiamata al co-struttore della superclasse(JFrame), al quale viene passa-to il parametro da esso richie-sto (la stringa da visualizzarenella barra del titolo della fine-stra). La chiamata al costrutto-re della superclasse, se richie-sta, può essere fatta solo da uncostruttore di classe derivata edeve essere sempre la primis-sima istruzione di questo.

this.home = home; Il parametro home ha lo

stesso nome di un attributodella classe WebBrowser. Perassegnare il valore del parame-tro home all'attributo home,però, non possiamo scrivereun'istruzione del tipo"home=home": sarebbe ambi-gua. Per specificare che a sini-stra del simbolo "=" intendia-mo riferirci all'attributo homedi questa classe, e in particola-re di questa istanza, usiamo ilqualificatore this, che fornisceun riferimento a "questo" og-getto (siamo in un costruttorequindi si tratta appunto del-l'oggetto che stiamo inizializ-zando)

addWindowListener(new WindowAdapter()

{public void windowClosing (WindowEvent e){System.exit(0);}});

Questa istruzione impostaun Window Listener che ge-stirà l'evento di "clic sul qua-dratino di chiusura" facendoterminare l'applicazione.

jbBack.setEnabled(false); //inizialmente disabilitato

Durante la costruzione del-l'interfaccia grafica è necessa-rio impostare lo stato inizialedel pulsante Back a "disattiva-to": non esiste infatti alcuna hi-story all'avvio del browser. Siverrà invece a creare in segui-to, quando l'utente inizierà aseguire i link della pagina. Allo-ra un'apposita istruzione prov-vederà a riattivare (o a ri-disat-tivare) il pulsante, secondo ne-cessità.

jepHTML = new JEditorPane(home);jepHTML.setEditable(false); jepHTML.addHyperlinkListener(this);JScrollPane scrollPane = �

Listato WebBrowser.java,import javax.swing.*; // per widget Swingimport javax.swing.event.*; // per eventi Swingimport java.awt.*; // per BorderLayoutimport java.awt.event.*; // per classi gestione eventiimport java.net.*; // per la classe URLimport java.io.*; // per classi I/Oimport java.util.*; // per classe Stack

public class WebBrowser extends JFrame implements HyperlinkListener, ActionListener {JButton jbHome; // pulsante ritorno pagina inizialeJButton jbBack; // pulsante ritorno pag. precedente (history)JTextField jtfURL; // campo per immettere URL desiderataJEditorPane jepHTML; // area di visualizzazione della pagina WebString home; // conterra' la URL della pagina inizialeStack history=new Stack(); // contiene la history

public static void main(String[] args) {new WebBrowser("http://www.pcopen.it/");

}public WebBrowser(String home){super("Web Browser"); // inizializza JFrame e imposta titolothis.home = home; // imposta pagina iniziale// gestione chiusura finestra (termina applicazione)addWindowListener(new WindowAdapter(){public void windowClosing(WindowEvent e){System.exit(0);}});JPanel topPanel = new JPanel(); // area in altojbBack = new JButton("Back"); // crea pulsante historyjbBack.setEnabled(false); // inizialmente disabilitatojbBack.addActionListener(this); // gestiremo noi i suoi eventijtfURL = new JTextField(50); // crea campo per immiss. URLjtfURL.setText(home); // inizializza campo con URL home pagejtfURL.addActionListener(this); // gestiremo noi i suoi eventijbHome = new JButton("Home"); // crea pulsante HomejbHome.addActionListener(this); // gestiremo noi i suoi eventitopPanel.add(jbBack); // inserisce pulsante HistorytopPanel.add(jtfURL); // inserisce campo per immiss. URLtopPanel.add(jbHome); // inserisce pulsante Home// inserisce in alto il pannello con pulsanti e campo URLgetContentPane().add(topPanel, BorderLayout.NORTH);

try{

// crea HTML display inizializzato su pagina homejepHTML = new JEditorPane(home);

jepHTML.setEditable(false); // impedisce editing su pagina// gestiremo noi l'evento di mouseclick su un hyperlinkjepHTML.addHyperlinkListener(this); // Installa scrollbars per gestire pagine grandiJScrollPane scrollPane = new JScrollPane(jepHTML);// Inserisce HTML display in area principale finestragetContentPane().add(scrollPane, BorderLayout.CENTER);

} catch(Exception e)

{// segnala inizializzazione HTML display fallita// (unica causa possibile: impossibile aprire URL)

JOptionPane.showMessageDialog(this, "Impossibile aprire la pagina iniziale!","Errore", JOptionPane.ERROR_MESSAGE);

}// Calcola dimensione disponibile schermo

Dimension schermo = getToolkit().getScreenSize();// imposta posizione iniziale nell'angolo alto-sinistra

segue a pagina successiva

Page 42: Corso_java.pdf

Java 4a lezione

new JScrollPane(jepHTML);Queste quattro istruzioni so-

no la parte più importante delcodice di inizializzazione delnostro browser. La prima creaun visualizzatore multifunzio-nale adatto anche per HTML(JEditorPane), preimpostatoper mostrare la pagina inizialela cui URL è contenuta nella va-riabile home: sarà il JEditorPa-ne a risolvere l'indirizzo, a con-nettersi al server, a scaricaretesto e immagini, a disporre glielementi della pagina e a vi-sualizzarla sul video. Tutto inuna singola riga di codice, gra-zie alla notevole potenza dellelibrerie GUI/Internet di Java.

La seconda riga stabilisceche non sia possibile all'utentemodificare il testo contenutonel JEditorPane (questo per-ché tale classe, come se nonbastasse quanto già citato, for-nisce anche un limitato sup-porto all'editing WYSIWIG deidocumenti visualizzati!): in unbrowser questa funzione nonoccorre e rischia solo di gene-rare confusione nell'utente. Di-sattiviamola.

La terza riga registra "que-sta" (this) istanza di WebBrow-ser come gestore di eventi ine-renti l'attivazione di hyper-links. poiché WebBrowser di-chiara di implementare l'inter-faccia HyperlinkListener, è am-missibile la sua "candidatura"a gestore di tali eventi. Al veri-ficarsi di uno di essi, il sistemachiamerà il metodo hyper-linkUpdate, che vedremo frapoco.

Da ultima, la quarta istru-zione imposta delle scrollbarche, apparendo automatica-mente solo se necessario, con-sentiranno di muoversi su do-cumenti più grandi dello spa-zio disponibile a video.

JOptionPane.showMessageDialog(this, "Impossibile aprire la pagina

iniziale!","Errore", JOptionPane.ERROR_

MESSAGE);

Nel caso in cui la URL forni-ta come pagina iniziale non ri-sulti sintatticamente valida o ilsito o la rete abbiano proble-mi, l'inizializzazione si arreste-rebbe. L'applicativo avvisa

quindi l'utente con un messag-gio mediante la comoda fun-zione showMessageDialog dellaclasse JOptionPane, mostratasopra. I parametri sono, nel-l'ordine, il Frame a cui deve ri-ferirsi il dialog, il messaggio davisualizzare, il titolo del dialoge il tipo di messaggio (que-st'ultimo determina l'icona damostrare a sinistra del dialog).Codice del tutto simile a que-sto viene usato in altri puntidel programma per errori si-mili ma verificatisi durante lanavigazione anzichè in fase diinizializzazione; in ogni caso imessaggi che appaiono sonosimili a quello di figura 8.

Dimension schermo = getToolkit().getScreenSize();

setBounds(0,0,schermo.width-50,schermo.height-200);

setVisible(true);

Queste istruzioni servonoper impostare dimensioni eposizione iniziali della finestradi WebBrowser, in modo taleche occupi uno spazio propor-zionato allo spazio massimodisponibile sul display. La ter-za istruzione provvede poi afarla apparire a video.

Il metodo actionPerformedCome già detto, questo me-

todo è la sede in cui vengonogestiti gli eventi relativi ai duepulsanti Back e Home e al cam-po per la URL.

Se l'evento è stato generatodalla pressione di Return nelcampo URL, per prima cosaviene salvata sulla history (conpush) la pagina attuale e poiviene salvato nella variabile urll'indirizzo della prossima pagi-na, preso dal campo di testo:

if (event.getSource() == jtfURL) {history.push(jepHTML.getPage().

toString());url = jtfURL.getText(); }

Se invece è stato premutoBack, non occorre salvare nullasulla history, anzi l'indirizzodella prossima pagina deve es-sere prelevato dalla cima dellostack della history, quindi usan-do il metodo pop:

�// e dimensione adattata allo schermosetBounds(0,0,schermo.width-50,schermo.height-200);

// visualizza finestra principale browsersetVisible(true);

}

// Metodo di gestione eventipublic void actionPerformed(ActionEvent event) {String url;if (event.getSource() == jtfURL) // evento=immessa URL{// ricorda pagina attuale nella history prima di cambiare

history.push(jepHTML.getPage().toString());url = jtfURL.getText(); // questa sara' la nuova pagina} else if(event.getSource() == jbBack) // evento=click su Back{// "pesca" dalla history la URL per la nuova pagina

url=(String)history.pop(); } else // evento=click su Home{// ricorda pagina attuale nella history prima di cambiarehistory.push(jepHTML.getPage().toString());url = home; // la nuova pagina sara' quella iniziale}

// imposta nuova paginatry{jepHTML.setPage(new URL(url)); // Imposta nuova URLjtfURL.setText(url); // Aggiorna URL visualizzata in alto

} catch(Exception e) {

// Errore in apertura URL: segnalazioneJOptionPane.showMessageDialog(this, "Impossibile aprire "+url+": "+e, "Errore", JOptionPane.ERROR_MESSAGE);

}// Aggiorna lo stato di abilitazione del pulsante Back// in base a presenza e dimensione historyjbBack.setEnabled(history.size()>=1);

}

// Metodo per la gestione di evento click su hyperlinkpublic void hyperlinkUpdate(HyperlinkEvent event) {if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {try {

// ricorda pagina attuale nella history prima di cambiarehistory.push(jepHTML.getPage().toString());// passa alla nuova pagina

jepHTML.setPage(event.getURL());// aggiorna URL visualizzata in alto

jtfURL.setText(event.getURL().toExternalForm());} catch(Exception e) {

// Errore in apertura URL: segnalazioneJOptionPane.showMessageDialog(this, "Impossibile aprire "+event.getURL()+": "+e, "Errore", JOptionPane.ERROR_MESSAGE);

}}// Aggiorna lo stato di abilitazione del pulsante Back// in base a presenza e dimensione historyjbBack.setEnabled(history.size()>=1);

}}

8

Page 43: Corso_java.pdf

Java 4a lezione

else if(event.getSource() == jbBack)// evento=click su Back{url=(String)history.pop(); }

Se infine è stato premutoHome, le azioni sono simili aquelle relative alla immissionedi un nuovo indirizzo nella fi-nestrella URL: unica differen-za, l'indirizzo per la prossimapagina viene preso dalla varia-bile interna home, che abbia-mo inizializzato nel costrutto-re.

else // evento=click su Home{history.push(jepHTML.getPage().

toString());url = home; }

Una volta stabilito (nella va-riabile url) quale sarà la pros-sima pagina possiamo passarea impostarla sul JEditorPane.

poiché questa operazioneimplica un tentativo di con-nessione al server che potreb-be anche non riuscire, è ne-cessario prepararsi a trattarele relative eccezioni. Per que-sto motivo il codice è racchiu-so in un blocco try.

Si noti anche l'uso dellaclasse URL: il metodo setPagedi JEditorPane non accetta unindirizzo espresso come strin-ga (la nostra variabile url), ma

pretende che tale indirizzo siarappresentato come un ogget-to di tipo URL. Fortunatamen-te la classe URL dispone di uncomodo costruttore che inve-ce accetta un indirizzo scrittocome una stringa, per cui... cela caviamo a buon mercato,creando al volo un oggetto diclasse URL e passandolo a set-Page:

try{jepHTML.setPage(new URL(url)); jtfURL.setText(url); }

Dopo aver cambiato paginadobbiamo chiederci se nellahistory ci siano o no pagineverso cui tornare, per stabilirese il pulsante Back debba es-sere abilitato o meno.

Facciamo questo con unasingola istruzione:

jbBack.setEnabled(history.size()>=1);

Il metodo setEnabled richie-de un parametro di tipo boo-lean. Java permette di passare,come valore boolean, il risul-tato di una espressione logicacome quella qui mostrata.

Il concetto è che se la hi-story contiene almeno una pa-gina, allora la condizione è ve-ra, quindi abilitiamo il pulsan-te Back, altrimenti (history

vuota) lo disabilitiamo. Avremmo potuto ottenere

lo stesso effetto precalcolandouna variabile intermedia di ti-po boolean, ma questa nota-zione è più compatta ed evitadi dichiarare e usare variabiliinutili.

Il metodo hyperlinkUpdateQuesto metodo può trattare

vari possibili tipi di eventi ine-renti gli hyperlink, ma a noi in-teressa solo il caso di "clic sullink", ossia attivazione del link.Occorre quindi un test che ciassicuri di trovarci in questocaso:

if (event.getEventType() ==

HyperlinkEvent.EventType.ACTIVATED)

Le operazioni che seguonosono molto simili a quelle vi-ste nel metodo actionPerfor-med: l'unica differenza è la fon-te da cui viene tratta la URL dausare; non più dal campo jt-fURL o dalla variabile home enemmeno dalla history, ma di-rettamente dall'evento, che"porta con sè" questa infor-mazione.

Possiamo estrarla usando ilmetodo getURL():jepHTML.setPage(event.getURL());

Il risultato finale dei nostrisforzi è visibile in Figura 9.

4 Conclusioni

9

In questo primo ciclo diquattro puntate abbiamopresentato i temi fonda-

mentali della programmazio-ne in generale (dal concettodi algoritmo ai costrutti sin-tattici, dalla scelta dei nomiper gli identificatori al tratta-mento degli errori, dai tipi didato alla ricorsione) per poipassare ad argomenti più spe-cifici come la programmazio-ne a oggetti, quella a eventi, larealizzazione di una GUI, tec-niche e modelli di I/O su filese stream e infine la program-mazione di rete.

Naturalmente, dato il rap-porto tra la vastità e il numerodi argomenti e lo spazio dispo-nibile, non è stato possibilepresentare una trattazionecompleta e approfondita, ma cisiamo attenuti alla presenta-

zione dei concetti cardine e de-gli esempi concreti utili a chia-rirli.

Tuttavia, probabilmente,alcune conclusioni su Java ri-sultano ormai chiare.

Come il lettore avrà notatodagli esempi Java che hannoaccompagnato i vari argo-menti, questo linguaggio (esoprattutto le sue librerie) of-fre potenti mezzi per pro-grammare con relativa faci-lità applicazioni che in passa-to avrebbero richiesto unosforzo veramente notevole.

La "trasversalità" di alcunitemi, come la grafica, il trat-tamento delle eccezioni, l'I/Ocon stream, che sono presen-ti in applicazioni di ogni ge-nere, in Java è ben recepitadalle classi di libreria, per ilmodo "integrato" in cui sono

progettate: per riferirsi a unesempio visto in questa pun-tata, basti pensare al JEditor-Pane, che affronta e "banaliz-za" tutti i e quattro i temi ci-tati offrendone al program-matore una sintesi tanto riccaquanto facile da usare.

Per di più le applicazionirealizzate hanno dimensionimolto ridotte, possono giraresenza modifiche su moltissi-mi tipi diversi di macchine,godono di una certa prote-zione contro le minacce pro-venienti dalla rete, possonoessere dotate di meccanismidi trattamento degli errori,sono "automaticamente" pre-disposte per essere interna-zionalizzate e molto altro an-cora; e il tutto non ha richie-sto il pagamento di alcuna li-cenza.

Sicuramente quindi si trat-ta di un ausilio prezioso cheha sicuramente contribuito inmodo enorme ad accelerarelo sviluppo di applicazionipotenti e user-friendly, ad ab-bassare tempi e costi di svi-luppo e a rivitalizzare, sem-pre che ce ne fosse stato bi-sogno, il mondo dello svilup-po software.

Ma l’argomento ovviamen-te non si conclude qui. Pros-simamente su PC Open conti-nueremo a trattare ed ap-profondire, magari con temimonografici saldati alle basipresentate in questo corso,l'argomento della program-mazione in generale e dellaprogrammazione medianteJava in particolare.

Aspettiamo ora i vostri ri-scontri. �