380 60 21MB
Italian Pages [302] Year 2013
PAOLO CAMAGNI RICCARdO NIkOLAssy
CORsO dI JAVA Dalla programmazione ad oggetti alle applicazioni grafiche
HOEPLI
Corso di Java
Paolo camagni
riccardo nicolassY
Corso di Java Dalla programmazione ad oggetti alle applicazioni grafiche
editore ulrico hoePli milano
UN TESTO PIÙ RICCO E SEMPRE AGGIORNATO Nel sito www.hoepliscuola.it sono disponibili: • materiali didattici integrativi; • eventuali aggiornamenti dei contenuti del testo.
Copyright © Ulrico Hoepli Editore S.p.A. 2013 Via Hoepli 5, 20121 Milano (Italy) tel. +39 02 864871 – fax +39 02 8052886 e-mail [email protected]
www.hoepli.it
Tutti i diritti sono riservati a norma di legge e a norma delle convenzioni internazionali
Indice
Indice
Unità di apprendimento 1 La sintassi Java e l’ambiente di sviluppo
L4 Le operazioni sui dati
L1 L’ambiente di programmazione I programmi in Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . L’ambiente di programmazione . . . . . . . . . . . . . . . . Programmare in Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Installazione di JSESDK per Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Il settaggio delle variabili d’ambiente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Installare l’ambiente di sviluppo BlueJ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Utilizzare l’ambiente di sviluppo BlueJ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Il debugging con BlueJ. . . . . . . . . . . . . . . . . . . . . . . . . . . . Verifichiamo le conoscenze . . . . . . . . . . . . . . . . . . . . . . .
2 3 5 6 8 11 15 20 26
L2 La struttura del codice Le classi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Un programma Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . La classe System . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . I commenti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verifichiamo le conoscenze . . . . . . . . . . . . . . . . . . . . . . .
27 28 29 32 35
L3 Le variabili e i tipi primitivi Gli identificatori . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Le variabili . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Le costanti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . La tipizzazione dei dati . . . . . . . . . . . . . . . . . . . . . . . . . . Verifichiamo le conoscenze . . . . . . . . . . . . . . . . . . . . . . . Verifichiamo le competenze . . . . . . . . . . . . . . . . . . . . . .
37 38 40 40 44 46
Gli operatori aritmetici. . . . . . . . . . . . . . . . . . . . . . . . . . . Le conversioni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Gli operatori di confronto e logici. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . L’operatore ternario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Gli operatori di incremento e decremento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Le operazioni sui bit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Le priorità tra gli operatori . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verifichiamo le conoscenze . . . . . . . . . . . . . . . . . . . . . . . Verifichiamo le competenze . . . . . . . . . . . . . . . . . . . . . .
47 47 50 50 50 51 51 52 54
L5 Le stringhe Le stringhe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Le sottostringhe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Altre funzioni di confronto. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verifichiamo le conoscenze . . . . . . . . . . . . . . . . . . . . . . . Verifichiamo le competenze . . . . . . . . . . . . . . . . . . . . . .
55 58 58 62 64
L6 Le strutture di controllo I blocchi di istruzioni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . La struttura di sequenza . . . . . . . . . . . . . . . . . . . . . . . . . Il costrutto di selezione . . . . . . . . . . . . . . . . . . . . . . . . . . La selezione nidificata . . . . . . . . . . . . . . . . . . . . . . . . . . . . Gli operatori logici . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . L’iterazione con il ciclo while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . L’iterazione con il ciclo do . . . . . . . . . . . . . . . . . . . . . . Verifichiamo le conoscenze . . . . . . . . . . . . . . . . . . . . . . .
65 65 66 68 68 72 74 76
Indice
L7 Le strutture di controllo derivate La selezione multipla – Istruzione Switch. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . I cicli a conteggio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Le istruzioni break e continue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verifichiamo le conoscenze . . . . . . . . . . . . . . . . . . . . . . .
L6 Gli array 77 79 82 84
L7 Interfacce e casting tra oggetti
Unità di apprendimento 2 La OOP di Java
Le conversioni in Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165 Metodi e classi astratte . . . . . . . . . . . . . . . . . . . . . . . . . . . 169 Le interfacce . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170 Verifichiamo le conoscenze . . . . . . . . . . . . . . . . . . . . . . . 172 Verifichiamo le competenze . . . . . . . . . . . . . . . . . . . . . . 174
L1 La programmazione object oriented La programmazione a oggetti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . L’oggetto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . La classe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Il paradigma della OOP . . . . . . . . . . . . . . . . . . . . . . . . . . Gerarchia delle classi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verifichiamo le conoscenze . . . . . . . . . . . . . . . . . . . . . . . Verifichiamo le competenze . . . . . . . . . . . . . . . . . . . . . .
86 87 88 89 91 92 93
L2 I package e le classi I package . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94 Le classi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96 Gli attributi di una classe . . . . . . . . . . . . . . . . . . . . . . . . 97 I metodi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100 L’overloading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103 Verifichiamo le conoscenze . . . . . . . . . . . . . . . . . . . . . . . 105 Verifichiamo le competenze . . . . . . . . . . . . . . . . . . . . . . 107
L3 Il ciclo di vita degli oggetti Java e la memoria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109 Il ciclo di vita di un oggetto . . . . . . . . . . . . . . . . . . . . . 110 Verifichiamo le conoscenze . . . . . . . . . . . . . . . . . . . . . . . 114 Verifichiamo le competenze . . . . . . . . . . . . . . . . . . . . . . 115
L4 Le variabili reference e l’istanza delle classi Variabili e reference. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117 Le istanze di una classe . . . . . . . . . . . . . . . . . . . . . . . . . . 120 Verifichiamo le conoscenze . . . . . . . . . . . . . . . . . . . . . . . 126 Verifichiamo le competenze . . . . . . . . . . . . . . . . . . . . . . 127
L5 Derivazione ed ereditarietà Ereditarietà . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132 Derivazione di una classe derivata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135 L’override dei metodi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138 La classe Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140 Verifichiamo le conoscenze . . . . . . . . . . . . . . . . . . . . . . . 146 Verifichiamo le competenze . . . . . . . . . . . . . . . . . . . . . . 147 VI
I vettori in Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149 L’ordinamento a bolle . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154 La ricerca sequenziale in un array . . . . . . . . . . . . 157 La ricerca dicotomica in un array . . . . . . . . . . . . . 158 Le matrici . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159 Verifichiamo le conoscenze . . . . . . . . . . . . . . . . . . . . . . . 162 Verifichiamo le competenze . . . . . . . . . . . . . . . . . . . . . . 163
Unità di apprendimento 3 Realizzare applicazioni GUI L1 La classe grafica AWT Struttura di un interfaccia grafica . . . . . . . . . . . . . 176 Disegnare con la classe Canvas . . . . . . . . . . . . . . . . 179 La classe Graphics2D . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184 Immagini JPG e GIF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189 Verifichiamo le conoscenze . . . . . . . . . . . . . . . . . . . . . . . 191 Verifichiamo le competenze . . . . . . . . . . . . . . . . . . . . . . 191
L2 La gestione degli eventi Azioni ed eventi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193 Gestire eventi sulla finestra . . . . . . . . . . . . . . . . . . . . . 198 La classe ActionListener . . . . . . . . . . . . . . . . . . . . . . . . . 202 Gli eventi del mouse: la classe MouseListener . . . . . . . . . . . . . . . . . . . . . . . . . . 206 Gli ascoltatori della tastiera: KeyListener . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208 Gli oggetti Item e i relativi eventi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210 Verifichiamo le competenze . . . . . . . . . . . . . . . . . . . . . . 214
L3 L’interfaccia utente con NetBeans NetBeans come IDE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215 Creare un progetto con NetBeans . . . . . . . . . . . . . 216 Creare un interfaccia grafica con NetBeans . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219 Associare il codice agli elementi grafici . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222 L’oggetto TextArea . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227 L’oggetto List . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229 Gli oggetti ComboBox, RadioButton e CheckBox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232 Verifichiamo le competenze . . . . . . . . . . . . . . . . . . . . . . 239
Indice
Unità di apprendimento 4 Realizzare applicazioni Android L1 L’interfaccia IDE Eclipse Scaricare e installare Eclipse . . . . . . . . . . . . . . . . . . . 244 Creare un progetto con Eclipse . . . . . . . . . . . . . . . . 247 Il debugging con Eclipse . . . . . . . . . . . . . . . . . . . . . . . . . 251 Verifichiamo le conoscenze . . . . . . . . . . . . . . . . . . . . . . . 256
L2 Installare e configurare Eclipse per Android Il sistema operativo Android . . . . . . . . . . . . . . . . . . . . 258 Eclipse e Android . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259
Verifichiamo le competenze . . . . . . . . . . . . . . . . . . . . . . 267
L3 Creare un progetto Java per Android Creare un nuovo progetto . . . . . . . . . . . . . . . . . . . . . . . 268 La struttura del progetto . . . . . . . . . . . . . . . . . . . . . . . . . 272 La struttura di una applicazione Android . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273 Il ciclo di vita di una Activity . . . . . . . . . . . . . . . . . . . 274 I widget . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277 Usare l’id dei controlli . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279 Verifichiamo le competenze . . . . . . . . . . . . . . . . . . . . . . 288
VII
Presentazione
Presentazione Il volume è stato impostato tenendo conto dell’esperienza degli autori maturata in diversi anni di insegnamento nei quali hanno adottato Java come linguaggio di programmazione orientato agli oggetti assai versatile e utile per comprendere in modo chiaro e approfondito la programmazione Object Oriented. Il volume si pone come obiettivo la presentazione dei concetti relativi alla programmazione a oggetti con Java in maniera nuova, limitando al massimo le introduzioni noiose e nozionistiche. Gli esempi sono parte integrante del testo, l’allievo è stimolato alla comprensione mediante l’interazione con gli stessi attraverso le sezioni denominate “Prova adesso!”. Si sentiva la mancanza di un volume che potesse mettere in luce tutti gli aspetti peculiari del linguaggio, passando dalla programmazione attraverso l’ambiente di apprendimento BlueJ, per passare poi all’uso dell’ambiente GUI NetBeans, fino alla progettazione di semplici progetti per dispositivi mobili attraverso Eclipse per Android. La filosofia del volume si può esprimere in una breve citazione: “incominciare dove gli altri finiscono”. Il testo è organizzato in quattro unità di apprendimento. Ogni unità di apprendimento è organizzata in singole lezioni, dove vengono illustrati alcuni concetti corredati da segmenti di codice completamente commentati e descritti. La prima unità di apprendimento affronta in modo organico e articolato la sintassi di Java e l’ambiente di sviluppo JDK. La seconda unità di apprendimento illustra i concetti relativi alla programmazione ad oggetti, i package, le classi, le istanze, la derivazione, l’ereditarietà, il casting tra oggetti. La terza unità di apprendimento affronta la gestione degli ascoltatori e la definizione delle interfacce utente, facendo uso delle classi AWT e JSwing; vengono analizzati i singoli componenti mediante un approccio graduale, fornendo esempi di utilizzo dei AVA ASSI JE T metodi e degli eventi ad essi associati, prima N I S LA MBIENT lezio E L’A ILUPPO ne 5 in modalità “manuale”, scrivendo il codice in DI SV ENDIMENTO R modo diretto, quindi attraverso l’ambiente di P P Deriv DI A Azion UNITÀ e eD e reDitA sviluppo GUI NetBeans. L’ultima unità di aprietà prendimento illustra i concetti relativi alla realizzazione di applicazioni Android, iniziando dalla interfaccia IDE del pacchetto Eclipse, per poi passare a installare e configurare Eclipse per Android, creando infine alcuni semplici progetti Java per Android. UdA 2
1
e mbiente L 1 L’a ogrammazion di pr ra struttu L 2 La codice del i variabil L 3 Le imitivi pr e i tipi
i dati ioni su operaz L 4 Le he ing inghe str str Le Le 5 llo L L5 rollo ntro di cont re di co utture struttu 6 Le str L rollo L 6 Le di cont ntrollo re tu ut re di co str tu ut Le str 7 L L 7 Le rivatee de derivat
OBIE
nti IDE li ambie a ruolo deg erie Jav scere il re le libr • Ricono utilizza cere e • Conos la JVM eJ ori (JDK) e ti con Blu operat get gli pro e e ssi oni • Gestir e le cla le istruzi noscer per rico a Jav • Saper sintassi cere la one • Conos one e iterazi di selezi
P di Jav
in qU
estA le
nare og clone() todi getti an e equa ls( che in classi de ) della classe Objec rivate
TÀ er BlueJ debugg itor e il offerti are l’ed versioni • Utilizz i e le con are i tip a • Applic gio Jav uenza, uag seq dal ling oni per a le istruzi in Jav are lizz one • Uti e iterazi selezione
t
■ Ered ita
rietà
In questa lezion espand ere le car e passiamo a esa ditariet ˆ e der atteristiche di minare com e ivazione un ogg etto attr ereditare ed ▶. averso esemp ◀ ereio 12 Classe L’esem Quadril pio mo stra com In que atero sto ese e ◀ special mp nerica izzare ▶ in grado io la classe una Qu di gestire classe. adr un ipoteti ilatero è una classe co qua gedrilate ro, con un
Info
o al voM allegat i utili CD-RO sorgent 01 del ti e i file ella AU ti i proget Nella cart o. o presen diment lume son unità di appren sta per que
132
Indice degli obiettivi che si intendono raggiungere e delle attività che si sarà in grado di svolgere
a
zione • a defin impAre ire una remo ... • a impe classe de dir • a cono e la derivaz rivata ridefine ione di ndone scere i classi e i meto metodi • a clo di me
ATTIVI
TTIVI
La OO
Nella pagina iniziale di ogni unità di apprendimento è presente un indice delle lezioni trattate
All'inizio di ogni lezione sono indicati in modo sintetico i contenuti
◀ Eredita ne L’er rietà e derivaz editarietà iodi der ivare nuo consente partire ve clas da si a te. Una quelle già defi traverso classe derivat nia atchiama l’ereditarietà ta sottoc viene tiene i metod lasse e manie ti delle classi da gli attribuQuand cui der o da una una classe ere iva. parla di sola superclass dita ceversa eredità singola e si , multipla. si parla di ere ; vil’ereditari Java non pre dità ved età mu ditariet ltipla. L’er e à eche con è un mecca nism sente di riutilizz estende o are to in una il codice già re e scrit◀ Specialclasse. ▶ izza re La lizzazio spe ne Java rap di una clas ciase lità di crepresenta la pos in una clas are dei sottoti sibise già esis pi di tente. ▶
Le parole chiave vengono poste in evidenza e spiegate allo studente
Presentazione
UdA 2
Le finalità e i contenuti dei diversi argomenti affrontati sono descritti da obiettivi generali e attività indicate nell’intestazione di ciascuna unità di apprendimento, mentre per ciascuna lezione vi sono le indicazioni: “In questa lezione impareremo”.
La OOP di Java
è la defini◀ Classe La classe ra che l’ogzione. Nella fare una distin zione della struttu i di attribuè altro è necessario termin ◀ classe ▶ non Prima di tutto getto avrà, in a oggetti una , mentre programmazione quale l’oggetto viene creato ti e metodi. ▶ tà a dal una propria identi che lo schem elemento con un è tto invece l’ogge loro ti identici nella oria di ogget univoca. le caratterirre una categ e ne descrive sario per produ isce il prototipo di istanze in quanto ne schema neces ca classe ne costitu La classe è uno come una fabbri mo dire cha una struttura, possia ginare una classe mo anche imma di produzione: stiche. Possia a tecnic la e a possiede lo schem
■ La classe
classe
istanze
ro infinito Oggetto creati un nume mmatore possono essere 2 che il progra Da una classe e è sull’oggetto uti oppure di oggetti, inoltr il contenuto degli attrib Oggetto potrà modificarne i. Oggetto lo scopo di 1 eseguirne i metod una struttura statica, ha i) 3 ha attributi e metod La classe, che (in termini di fornidi forma ha lo scopo definire quale i o, se vogliamo, re diversi il quale defini avranno gli oggett Classe o generico entro re uno stamp prolo) (model all’interno del oggetti. essere molteplici definita no posso quella i : forma Gli oggett o è però avranno la stessa cesso, ma tutti . Ciascun oggett ca area di dato dalla classe di una specifi dallo schema proprio vase stante, dotato una entità a rizzazione del ta alla memo memoria dedica ▶ uti. lore degli attrib i dati che individuano degli attributi potrebbe oggetto sieme dei valori un ascensore Lo stato di un sentato dall’in oggetto quale e da parte oggetto è rappre Per esempio, lo stato di un ale prenotazion Lo stato di un re. ) e una eventu i possono opera (numero di piano su cui i metod e della cabina attual one posizi includere la
Per permettere allo studente di approfondire i concetti appresi è stato allegato al volume un CD-ROM che contiene tutti gli esempi presentati nel libro. In questo modo l’alunno può verificare direttamente quanto esposto nel testo e saggiare le proprie ipotesi di modifica. Le varia
e e l’ista
bili referenc
nza delle
classi
Lezione
4
una visipossiede cipale e ue altra classe prin cipale in qualunq della trica è la aGeome classe prin creare una istanza se Form nza della rtato la clas creare una ista è invece possibile del trica o sopra ripo cartella possibile aGeome a stessa , Nell’esempi in tal modo è nell Form se zato .java che lic, a clas memoriz o un file bilità pub all’interno dell , purché e associat into vien Solo dist se a clas classe. un file ngolo. i classe in o a ciascun classe Tria salvare ogn cipale. In tal mod le .class. o anche fi prin Possiam la classe ispondente nte corr il tene file con to, genera classi a compila nza delle una volt • Usare l’ista
88
o! Prova adess
visualizgrado di ombo() in rcizio 3 stampaR GETTO Ese eGeoun metodo se Form iungere APRI IL PRO e la clas n. 2. o da agg ziare e usar i di altezza ice in mod o di istan ifica il cod rombo di asterisch e in grad x. 1 Mod o un ometrich rcizio3_solu Ese zare a vide se UsaFormeGe etto nel prog una clas lla presente 2 Crea e. que e con metrich soluzion ta la tua 3 Confron iniziase Persona della clas rcizio 4 tre oggetti GETTO Ese di istanziare x. APRI IL PRO in grado iera. rcizio4_solu Persona ut da tast etto Ese classe Usa una nel prog i verso l’inp 1 Aggiungne gli attributi attracon quella presente e zion lizzando solu ta la tua 2 Confron
Le osservazioni aiutano lo studente a comprendere e ad approfondire
Zoom su...
ne alcuni per utilizzar sta classe .Random LI ro. Que se java.util i. Per RI CASUA tipo inte etto di clas re casuale di ri boolean Data RE NUME valo . erare valo mo un ogg genera un he di gen om in base al tipo istanziere tInt() che consentono anc esempio re rand In questo li per esempio nex odi che era un valo met gen qua rsi che odi dive odo met disposizione disponibile un met è mette a itivo : prim se Random ogni tipo della clas un’istanza
GENERA
;
random
Random
om() = new Rand
: o ottenere
Possiam
; booleano oolean() //valore = random.nextB b boolean intero ; //valore extInt() random.n int k =
123
Lo studente può mettere in pratica in itinere quanto sta apprendendo nel corso della lezione
In questa sezione viene approfondito un argomento di particolare importanza
Alla pagina web http://www.hoepliscuola.it sono disponibili le risorse online, tra cui unità didattiche integrative, numerosi esercizi aggiuntivi per il recupero e il rinforzo, nonché schede di valutazione di fine unità.
UdA 2
La OOP
di Java
nze o le conosce Verifichiam
Verifichiam o le comp etenze
c e Cane deri d) Bassotto = (Animale) fido; 1 Se la classmente corrette? a e) Animale sintattica a = fido; tta: a) Animale = fido; classe astra b1 ardo a una b) Cane c = fido; oggetti corretta rigu generare seguenti • c) Bassotto può le i d) tra e sottoclass mazione quale affer e) può aver concreti 2 Indica di metodi re dotata di astratti a) può esse re dotata di meto ette? corr te b) deve essee metodi astratti sintatticamen zioni sono c) può aver quali istru c) Y y = x; x = new X(), new Y(); va da Y e d) X x1 = 3 Se X deri x; ? a) X x1 = Y(); te corrette 10 ]; = new int[ sintatticamen b) x = new ]); e) long[] a enti sono segu le (new int[ 10 10 ]); zioni tra f) int[] f = g[ 4 Quali istru = (new Strin long[ 10 ] f null; int[] g) o= [])new a) Object null; int[] a = (int h) = s g ]; 10 b) Strin o = new int[ 10 ]); c) Object = (String[])(new int[ a d) String[] ortano: facce supp ola 5 Le inter ditarietà sing a) solo l’ere ietà multipla b) l’ereditardiretta ? i astratte e sono vere c) l’istanza no di istanziare class i e interfacc ardo class d) consento mazioni rigu affer enti segu ili a 6 Quali delle intercambiab ntare una interfacci facce sono eme interfaccia a) le inter e astratta può impl are da una concreta e class b) una class e astratta può deriv tta are da una classe astra c) una class e astratta può deriv are da una interfaccia d) una class e astratta può deriv are da una interfaccia concreta e) una class e concreta può deriv una reta ementare classe conc f) una class e concreta può impl tta are da una classe astra g) una class e concreta può deriv are da una h) una class e concreta può deriv una interfaccia da facce i) una class faccia può derivare da più inter e astratta j) una inter faccia può derivare da una class e derivata inter are una deriv k) faccia può da una class e concreta l) una inter faccia può derivare da una class interfaccia m) una inter faccia può derivare are da una interfaccia n) una inter e derivata può deriv una a concreta ementare o) una class e derivata può impl una interfaccireta ementare classe conc p) una class e derivata può impl tta are da una class deriv una può classe astra q) e derivata are da una r) una class e derivata può deriv s) una class
L’interfac cia uten
te con Net Beans
g Proble mi
zioni sono quali istru
Cane(), la do = new lta multip Cane, e fi deriva da rcizi a sce ale, Bassotto fido; g Ese va da Anim = (Animale)
1 Crea un progetto NetBeans che risolva un sistema lineare.
L’aspetto del programm a è riportato
Per la verifica delle conoscenze e delle competenze è presente un'ampia sezione di esercizi Per inserire le due pare sione del ntesi graff carattere e puoi collo attraverso care nel Fram l’attributo e due etich font. ette (Label), quindi mod 2 Crea un ificare la dime progetto NetBeans nche mostri un Frame come indic ato di segu ito:
Facendo Click su Agg e l’importo iung parziale calco i l’utente imm ette i dati sotto alle lato dal caselle di testo il prog prodotto del prezzo inseriti (il prezzo vendite. del prodotto per ramma deve , la calcolare il la quantità). Facendo prezzo med click sul pulsa quantità venduta io, la quan nte relativo tità media posto e il valore totale delle
172 239
X
di seguito:
Lezione
3
LA SINTASSI JAVA E L’AMBIENTE DI SVILUPPO
1
UNITÀ DI APPRENDIMENTO L 1 L’ambiente di programmazione L 2 La struttura del codice L 3 Le variabili e i tipi primitivi
L 4 Le operazioni sui dati L5 5 Le Le stringhe stringhe L L L6 6 Le Le strutture strutture di di controllo controllo L L7 7 Le Le strutture strutture di di controllo controllo derivate derivate
OBIETTIVI
ATTIVITÀ
• Riconoscere il ruolo degli ambienti IDE
• Utilizzare l’editor e il debugger BlueJ
• Conoscere e utilizzare le librerie Java (JDK) e la JVM
• Applicare i tipi e le conversioni offerti dal linguaggio Java
• Gestire progetti con BlueJ
• Utilizzare le istruzioni per sequenza, selezione e iterazione in Java
• Saper riconoscere le classi e gli operatori • Conoscere la sintassi Java per le istruzioni di selezione e iterazione
Info Nella cartella AU01 del CD-ROM allegato al volume sono presenti i progetti e i file sorgenti utili per questa unità di apprendimento.
UdA 1
La sintassi Java e l’ambiente di sviluppo
lezione
1
L’Ambiente di progrAmmAzione in qUestA Lezione impAreremo...
• a installare e utilizzare le librerie di Java (JDK) e la JVM • a utilizzare gli strumenti di debugging di BlueJ • a utilizzare i breakpoints e le variabili locali
■ I programmi in Java Prima di poter scrivere un programma mediante il linguaggio ◀ Java ▶ vogliamo introdurre il concetto di ambiente di programmazione di questo linguaggio. Innanzi tutto è necessario chiarire il concetto fondamentale di classe. ◀ Java Un programma Java si compone di una o più classi. Ogni classe risiede in un file che possiede lo stesso nome della classe con estensione .java, e inoltre deve esistere almeno una classe con il nome del programma e con un metodo speciale chiamato main. ▶
Programma Classe 1 Classe 2
Classe principale con metodo main
Classe 3 Programma
Tuttavia per i primi esempi utilizzeremo una sola classe che fungerà anche da programma includendo al suo interno il metodo main. 2
L’ambiente di programmazione
Lezione 1
■ L’ambiente di programmazione Per eseguire una qualunque applicazione dobbiamo svolgere tre attività: 1 editing: mediante questa fase rendiamo il programma accessibile al computer; 2 compilazione: mediante questa fase traduciamo il programma in un formato eseguibile dal computer; 3 esecuzione: mediante questa fase facciamo eseguire il programma al computer.
Editing Per rendere accessibile al calcolatore una classe o un programma dobbiamo memorizzare la definizione di ciascuna classe all’interno di un file di testo con estensione .java. La definizione della classe prende il nome di codice sorgente e può essere scritto mediante un programma chiamato Editor. La figura seguente mostra l’editing di una semplice applicazione in Java:
Ciascuna classe Java è contenuta all’interno di un singolo file di testo, il cui nome riflette quello della classe. Non è possibile memorizzare più classi pubbliche all’interno di uno stesso file di testo; in questo caso il compilatore segnala un errore di sintassi.
La compilazione e l’esecuzione in Java Un compilatore è un programma che in grado di tradurre programmi scritti in un linguaggio di programmazione nel ◀ linguaggio macchina ▶ del computer. ◀ Linguaggio macchina È un linguaggio di programmazione molto più elementare e primitivo di Java, ed è specifico di un computer. ▶
La fase di compilazione traduce da un linguaggio di alto livello a un linguaggio macchina: ovvero dal codice sorgente scritto in linguaggio di alto livello al codice eseguibile scritto in linguaggio macchina. Tuttavia la compilazione e l’esecuzione sono dipendenti dall’ambiente hardware e software e un compilatore in generale è in grado di tradurre uno specifico linguaggio di programmazione (come per esempio il linguaggio C++) in uno specifico linguaggio macchina relativo a uno specifico processore (per esempio Intel MMX) e inoltre relativamente a uno specifico sistema operativo (per esempio Windows 8). In tal modo il codice eseguibile generato da un compilatore potrà essere eseguito soltanto dai computer corredati di uno specifico ambiente hardware/software.
3
UdA 1
La sintassi Java e l’ambiente di sviluppo
Java utilizza un approccio molto particolare per la compilazione dei programmi, infatti possiamo compilare il codice sorgente scritto in Java ottenendo un codice misto chiamato bytecode Java, una sorta di linguaggio assembly di un calcolatore virtuale. Inoltre il programma nella forma di bytecode Java potrà essere eseguito da un interprete chiamato ◀ JVM ▶ (Java Virtual Machine). ◀ JVM È un’applicazione che sa eseguire il bytecode Java e inoltre rende il computer una macchina virtuale in grado di eseguire programmi in bytecode Java. ▶
Abbiamo un diverso compilatore Java e una diversa macchina virtuale JVM per ciascun ambiente hardware/software. Il bytecode Java prodotto in uno specifico ambiente hardware/software potrà essere infatti eseguito in qualsiasi altro ambiente hardware/software, purché dotato di una macchina virtuale Java apposita. In sintesi le fasi che consentono di compilare ed eseguire un programma scritto in Java sono così riassunte: Editing
Scrittura codice sorgente
CompilazionE Bytecode Java
… getstaƟc #6 …
Computer dotato di JVM
intErprEtazionE
Esecuzione programma
Una caratteristica Java è proprio quella di essere un codice sorgente scrivibile su una piattaforma qualsiasi ed eseguibile su altrettante diverse piattaforme secondo il motto “Write once, run everywhere”. 4
L’ambiente di programmazione
Lezione 1
Come possiamo notare nella figura seguente, una volta compilato il sorgente e generato il bytecode esso può essere eseguito su piattaforme tra loro diverse sia come caratteristiche hardware che software: … getstaƟc #6 …
Bytecode Java
Computer e dispositivi dotati di JVM
Tablet Android
PC Windows
Smartphone Android
PC Linux Apple MAC OS
Risultato esecuzione
■ Programmare in Java Per poter programmare in Java dobbiamo procurarci: ◗ un compilatore; ◗ un ambiente di sviluppo. Il primo si chiama JSESDK (Java Standard Edition Software Development Kit – spesso abbreviata in JDK) e consente di compilare i programmi realizzati in Java. Lo si può scaricare gratuitamente dal sito della Oracle (www.oracle.com) www.oracle.com/ technetwork/java/javase/downloads Il secondo programma di cui abbiamo bisogno è l’ambiente di sviluppo che funziona appoggiandosi al JDK scaricato prima. Esistono numerosi ambienti di sviluppo di tipo ◀ IDE ▶ tra cui citiamo i più noti: ◗ BlueJ: per uso didattico, freeware; ◗ Eclipse: per uso professionale, freeware; ◗ netBeans: fornito insieme alle librerie di JDK, freeware; ◗ JBuilder: fornito dalla Borland, a pagamento. ◀ IDE (Integrated Development Environment). Indica un software che consente di sviluppare programmi mediante un ambiente amichevole che aiuta il programmatore consentendo a volte l’immissione del codice in modo più agevole. ▶
L’ambiente di sviluppo può essere semplice o integrato. L’ambiente integrato offre tutte le funzionalità di sviluppo all’interno di una unica applicazione, con il vantaggio di fornire passaggi più agevoli alle varie fasi; in alternativa possiamo usare strumenti singoli per eseguire le varie fasi (editing, compilazione ecc.). 5
UdA 1
La sintassi Java e l’ambiente di sviluppo
JSESDK Java Standard Edition Software Development Kit è un ambiente di sviluppo per la programmazione in Java realizzato dalla Sun Microsystems per diverse piattaforme. Fornisce un certo numero di funzionalità sotto forma di comandi da eseguire in una shell dei comandi. Inoltre comprende i seguenti strumenti di programmazione: ◗ un compilatore Java – javac; ◗ una macchina virtuale Java – java; ◗ alcune librerie API (Application Programming Interface) di Java; ◗ un visualizzatore di Applet – appletviewer; ◗ un debugger – jdb; ◗ e un generatore automatico di documentazione – javadoc. Le attività che dobbiamo svolgere mediante JDK sono le seguenti: ◗ editing (mediante l’uso di un editor oppure direttamente nell’ambiente di sviluppo prescelto); ◗ compilazione (mediante l’uso del compilatore Java presente nel JSESDK ◀ Comando javac Se questo comando viene eseguito senza parametri, si ottiene una scher– ◀ comando javac ▶ da linea di comata che ne riassume l’uso: mando oppure mediante un comando presente nell’ambiente di sviluppo); ◗ esecuzione (mediante l’uso di una macchina virtuale Java presente nel JSESDK – comando java da linea di comando oppure mediante un comando presente nell’ambiente di sviluppo).
■ Installazione di JSESDK per Windows Per prima cosa dobbiamo scaricare l’ultima versione della SDK per Java dal sito della Oracle (www.oracle.com) www.oracle.com/technetwork/java/javase/downloads. La procedura che segue illustra come scaricare, installare e mandare in esecuzione la SDK.
6
1
Una volta aperto il sito all’indirizzo indicato sopra, dobbiamo focalizzare la nostra attenzione sulla sezione che ci interessa, cioè quella relativa al download. In questa sezione dobbiamo selezionare il pulsante che indica la piattaforma Java (Java Platform (JDK), così infatti si chiama l’intero kit di sviluppo per Java: ▶
2
A questo punto, dopo aver selezionato Accept License Agreement per indicare di accettare i termini della licenza d’uso, dobbiamo selezionare la versione di JDK che intendiamo scaricare, in accordo con il sistema in uso. In questo caso decidiamo di scaricare la versione a 64 bit per Windows: ▶
L’ambiente di programmazione
3
Il file che otteniamo è di questo tipo (dipenderà ovviamente dalla versione, quella della figura a fianco è valida al momento della scrittura del testo ma varierà nel tempo con versioni più aggiornate): ▶
4
Dopo aver fatto doppio click sul file appare la seguente finestra di installazione nella quale si deve selezionare Next per iniziare la fase di installazione: ▶
5
Adesso dobbiamo selezionare i componenti da installare, tuttavia è consigliabile installare tutti quelli proposti. Mediante il pulsante Change si può modificare il percorso di installazione. Facendo click su Next proseguiamo con l’installazione: ▶
6
A questo punto viene eseguita l’installazione di tutti i componenti necessari mediante una barra di progressione: ▶
7
Una volta completata la procedura viene chiesto di fare click su Continue per uscire dall’installazione: ▶
Lezione 1
Adesso la JVM è in esecuzione e tutti i componenti per la compilazione sono stati installati.
7
UdA 1
La sintassi Java e l’ambiente di sviluppo
■ Il settaggio delle variabili d’ambiente Affinchè la JDK possa funzionare è necessario settare le variabili d’ambiente del sistema operativo. In questo caso avendo deciso di effettuare una installazione per Windows a 64 bit procediamo come segue.
8
1
Facciamo click con il tasto destro del mouse sull’icona Computer e selezioniamo la voce Proprietà: ▶
2
A questo punto attiviamo la voce Impostazioni di sistema avanzate: ▶
3
Appare una finestra nella quale dobbiamo selezionare la scheda Avanzate e quindi fare click sul pulsante Variabili d’ambiente: ▶
L’ambiente di programmazione
4
A questo punto dobbiamo selezionare la variabile d’ambiente chiamata Path che contiene i percorsi che il sistema è in grado di riconoscere da solo. Il percorso della JDK è importante per poter utilizzare il compilatore e la macchina virtuale per l’esecuzione delle nostre applicazioni. Per fare questo dobbiamo selezionare la riga della variabile d’ambiente Path e aggiungere alla fine della riga chiamata valore variabile il percorso della JDK installata sul computer. Nel nostro caso esso è: C:\Programm Files\Java\jdk1.7.0._05\bin ▼
5
A questo punto possiamo provare il nostro primo programma. Per fare questo editiamo il codice seguente all’interno del file ScriviSulVideo.java, usando un editor qualunque: ▼
Lezione 1
9
UdA 1
La sintassi Java e l’ambiente di sviluppo
6
Salviamo il file in una cartella qualunque, in questo caso di nome prova direttamente nella root del disco fisso C:\ ▶
ScriviSulVideo.java 7
Adesso per verificare l’avvenuta installazione della JDK dobbiamo provare il compilatore usando l’ambiente a linea di comando. Per fare questo attiviamo l’ambiente prompt dei comandi attraverso il comando cmd.exe: ▶
8
Ora ci posizioniamo nella cartella che contiene il file. La cartella prova è stata creata proprio sotto alla root del disco fisso per agevolare gli spostamenti nell’ambiente a linea di comando. Digitiamo il comando cd \prova seguito dal tasto Invio per posizionarci all’interno della nostra directory che contiene il programma da compilare: ▼
9
Adesso possiamo compilare il codice sorgente rappresentato dal file ScriviSulVideo.java attraverso il comando javac seguito dal nome del file da compilare: ▼
Il risultato della compilazione viene memorizzato in un file avente il medesimo nome di quello della classe contenuta nel file java, ma con estensione .class. Si trova quindi nella stessa directory di lavoro utilizzata nel comando precedente, un file chiamato ScriviSulVideo.class che contiene il bytecode della classe compilata. Il bytecode non è direttamente eseguibile dalla macchina, ma viene interpretato dall’ambiente di esecuzione, il ◀ Java Runtime Environment ▶ (JRE). ◀ Java Runtime Environment Si tratta del software che consente l’esecuzione di programmi compilati per la piattaforma Java. All’interno di questo componente troviamo la Virtual Machine per la piattaforma Java che si occupa di eseguire il bytecode Java risultato della compilazione, le librerie di base che contengono le funzionalità di base della piattaforma, come per esempio la classe String e i file di supporto come per esempio i messaggi localizzati nelle diverse lingue supportate da Java, le icone e altro. ▶
10
L’ambiente di programmazione
Lezione 1
10 Se non ci sono errori il sistema non segnala nulla. Per verificare la creazione del bytecode pos-
siamo digitare il comando dir che mostra tutti i file presenti nella directory. Come si può notare appare anche un nuovo file chiamato ScriviSulVideo.class che rappresenta il codice bytecode: ▼
11 Adesso possiamo eseguire il bytecode attra-
verso una fase di interpretazione dello stesso. Per fare questo digitiamo il comando java seguito dal nome del file, in questo caso senza l’estensione: ▶ Come possiamo notare il file viene eseguito e appare sullo schermo il messaggio contenuto nel codice. Nell’esempio a fianco si nota che il file viene compilato nella cartella prova, quindi il controllo passa a un’altra directory che non contiene più il file .class. In questo caso siamo obbligati a specificare il percorso che contiene la classe da eseguire mediante l’opzione –cp (classpath) che indica il percorso dove trovare i file delle classi. Se la directory dove sono contenute le classi è diversa da quella attuale, va specificato un percorso completo assoluto o relativo. ▶ Non siamo obbligati a indicare l’estensione del file (.class). Come abbiamo visto per eseguire un programma Java non è possibile digitarne semplicemente il nome da linea di comando; per eseguire bytecode Java è infatti necessario lanciare l’ambiente di runtime, specificando il nome della classe da cui partire per l’esecuzione (e in cui deve essere definito il metodo main()).
■ Installare l’ambiente di sviluppo BlueJ BlueJ è un ambiente di sviluppo gratuito, a carattere didattico, ed è stato progettato proprio per imparare a programmare con Java. Per poter funzionare dobbiamo prima di tutto aver installato la JDK, come abbiamo visto sopra, in quanto BlueJ è un ambiente che mette a disposizione alcuni strumenti grafici ma non contiene il compilatore. Lo scopo principale di BlueJ è quello di fornire al programmatore un’interfaccia semplice per la programmazione.
Il programma è disponibile per vari sistemi operativi: ◗ Windows ◗ MAC OS ◗ Linux Nel nostro caso installeremo la versione di BlueJ per Windows a 64 bit.
11
UdA 1
La sintassi Java e l’ambiente di sviluppo
La procedura che segue illustra i vari passaggi necessari all’installazione di BlueJ in ambiente Windows.
Ricordati sempre di installare la JDK prima di iniziare l’installazione di BlueJ!
1
Collegati al sito www.bluej.org e cerca la sezione download: ▶
2
Una volta entrato nella sezione download devi selezionare la versione di BlueJ adatta al tuo sistema operativo. In questo caso scaricheremo la versione per Windows: ▼
3
Dopo aver fatto click sul collegamento ipertestuale prescelto viene scaricato il file. ▶ Il nome del file di installazione è bluej-xxx.msi, dove xxx rappresenta il numero della versione del programma. In questo caso la versione è la 3.07, tuttavia le versioni cambiano molto spesso, quindi scarica sempre la versione più aggiornata.
4
12
Adesso fai doppio click sul file per iniziare l’installazione, apparirà la seguente finestra in cui selezionare Next: ▶
L’ambiente di programmazione
5
La seguente finestra indica che è stata trovata una versione della JDK. In questa videata potrebbe verificarsi un errore qualora la JDK non venisse localizzata. Fai click su Next per proseguire. ▶
6
La finestra che segue chiede se l’installazione deve essere effettuata per l’utente attivo in quel momento oppure per tutti gli utenti. In questo caso decidiamo di installare il programma per tutti gli utenti selezionando Install for all users of this machine, quindi fai click su Next per proseguire come di consueto: ▶
Lezione 1
In alcuni sistemi operativi meno recenti è necessario effettuare una ricerca per localizzare la directory della JDK. 7
La finestra che segue ci mostra semplicemente se associare un collegamento a BlueJ anche sul desktop e se associare i file con estensione .bluej e .bjar a questo programma. Il consiglio è di lasciare le caselle di testo spuntate come indicato e quindi premere Next: ▶
13
La sintassi Java e l’ambiente di sviluppo
8
In questa finestra viene chiesto in quale directory installare il programma. Anche in questo caso il consiglio è quello di lasciare inalterata la cartella indicata. Fai click sul pulsante Next per proseguire: ▶
9
Facendo click sul pulsante Install inizierà l’installazione che copierà i file sul disco fisso: ▼
\
UdA 1
10 Fai click infine sul pulsante Finish per terminare
l’operazione. ▶
11 Sul desktop apparirà l’icona seguente che consen-
te di aprire il programma BlueJ: ▼
14
L’ambiente di programmazione
Lezione 1
■ Utilizzare l’ambiente di sviluppo BlueJ Ora analizzeremo come scrivere e compilare un programma in Java mediante BlueJ, anche se i concetti base di programmazione verranno introdotti in seguito. La seguente procedura illustra come creare un nuovo ◀ progetto BlueJ ▶. ◀ Progetto BlueJ I progetti BlueJ sono molto simili ai package standard di Java, e non sono altro che semplici cartelle contenenti i file inclusi nel progetto. Pertanto a ogni progetto viene associata una directory. ▶
Prima di tutto è necessario affermare che per scrivere un programma in Java mediante BlueJ dobbiamo necessariamente includerlo all’interno di un progetto BlueJ, anche se si tratta solo di una classe. 1
Fai doppio click sull’icona di BlueJ per aprire l’ambiente di sviluppo: appare la finestra seguente: ▶
2
Per creare un nuovo progetto fai click sul menu Project, quindi sulla voce New Project...: ▶
3
A questo punto viene richiesto di salvare il progetto. In realtà dobbiamo solo decidere il nome e il percorso della directory che rappresenta il nostro progetto BlueJ. In questo caso si chiamerà prova e sarà posizionata nella root del disco fisso C: ▼
15
UdA 1
La sintassi Java e l’ambiente di sviluppo
4
A questo punto appare un foglio stilizzato che rappresenta il progetto, esso contiene solo informazioni che ne documentano il contenuto, è il programmatore che deve compilarlo. ▶
Se vuoi aggiungere informazioni riguardo al progetto fai doppio click su di esso e scrivi per esempio il titolo, lo scopo, la versione, come eseguirlo, gli autori, la sintesi del codice, i dati in ingresso e in uscita ecc.
5
16
Passiamo adesso all’editing, cioè alla scrittura del codice Java del programma. Per creare un programma dobbiamo necessariamente creare una nuova classe. Per creare una classe fai click con il tasto destro all’interno del riquadro principale della finestra di BlueJ e seleziona la voce New Class... dal menu contestuale che appare: ▼
L’ambiente di programmazione
6
Lezione 1
La finestra a fianco chiede il nome della classe da creare. In questo caso la chiamaremo CiaoMondo: ▶ Puoi quindi scegliere tra quattro tipi di classi: classe standard (Class), astratta (Abstract), interfaccia (Interface) oppure Applet. Questa scelta determina quale struttura (skeleton) verrà inizialmente creata per la tua classe. Puoi cambiare il tipo di classe successivamente, modificando il codice sorgente.
7
Dopo aver fatto click su OK appare una nuova classe, rappresentata da un’icona nel diagramma. Se non è una classe standard, il tipo (interface, abstract o applet) è indicato nell’icona della classe. ▶
8
A questo punto passiamo alla scrittura del codice contenuto nella classe. Per fare questo fai click con il tasto destro del mouse e seleziona Open Editor: ▶
9
Come puoi notare si apre la finestra dell’editor che reca il listato di un programma di esempio: ▶
17
UdA 1
La sintassi Java e l’ambiente di sviluppo
10 Adesso devi cancellare questo codice di esempio per scrivere al suo posto il nostro programma
di esempio che mostra un messaggio in output: ▼
Osserva che quando il cursore si trova subito a destra di una parentesi graffa chiusa BlueJ evidenzia in grigio qual è la parentesi graffa aperta corrispondente. Questa funzione è molto utile perché uno degli errori di stesura più frequenti riguarda le parentesi graffe.
11 Dopo aver scritto il codice apri il menu Class
e seleziona la voce Save per salvare il codice sorgente: ▶
12 Adesso fai click su Close posto in alto a de-
stra, in tal modo ritornerai al menù principale. Abbiamo così terminato la stesura del programma e siamo pronti per compilarlo, per fare questo fai click sul pulsante Compile: ▶
13 Se non ci sono errori BlueJ mostrerà nella bar-
ra di stato il messaggio Compiling... Done: ▶
18
L’ambiente di programmazione
Lezione 1
Quando il compilatore trova degli errori all’interno del codice, si apre automaticamente la finestra dell’editor evidenziando sia la riga in cui si è verificato l’errore che il relativo messaggio di spiegazione. ▼
Se clicchiamo sul punto interrogativo posto a lato del messaggio, ci viene mostrata la spiegazione del messaggio d’errore prodotto dal compilatore. ▶
14 Quando un file viene compilato viene generato
un file con estensione .class che ne rappresenta il bytecode. Posizionati nella cartella prova che contiene il progetto per verificare se il file è stato effettivamente creato: ▶
15 Adesso possiamo testare il nostro programma.
Posizionati col puntatore del mouse sopra alla classe dove si trova il metodo main, quindi sopra alla classe CiaoMondo, cliccando col tasto destro apparirà un menu nel quale devi selezionare la voce void main(String[] args): ▶
19
UdA 1
La sintassi Java e l’ambiente di sviluppo
16 Appare la finestra seguente nella quale potresti inseri-
re dei parametri da passare al metodo main. In questo caso basterà fare click su OK per proseguire: ▶ 17 Come puoi notare si apre la finestra di output del
programma nella quale appare il testo visualizzato: ▼
La finestra di output di BlueJ purtroppo non cancella il contenuto a ogni esecuzione. Per cancellare lo schermo dobbiamo agire sul menu Options, quindi selezionare la voce Clear:
■ Il debugging con BlueJ Il programmatore, durante l’attività di scrittura di un programma deve tener conto dei possibili errori che si producono. Tali errori possono essere raggruppati in tre categorie: ◗ errori di compilazione causati dall’utilizzo di parole che non appartengono al linguaggio oppure dalla costruzione non corretta di istruzioni del codice; Per evitare di commettere errori, il ◗ errori in fase di esecuzione, chiamati anche errori programma deve essere progettadi run time, segnalati durante l’esecuzione del proto tenendo conto di tutti i possibili gramma; valori che l’utente potrà immettere ◗ errori logici che generano risultati diversi da quelli durante l’esecuzione. attesi. L’ambiente BlueJ mette a disposizione uno strumento che consente di individuare i diversi tipi di errore e di apportare al codice le opportune correzioni, chiamato ◀ debugger ▶. L’attività di individuazione e correzione degli errori del codice sorgente viene comunemente chiamata fase di debugging. Le principali funzionalità del debugger presente in questo ambiente di sviluppo sono: ◗ impostare i breakpoints; ◗ eseguire il codice istruzione dopo istruzione (step by step); ◗ ispezionare il contenuto delle variabili. 20
◀ Debugger Questo termine ha una origine molto lontana nel tempo, venivano infatti chiamati così i lavoratori che avevano il compito di ripulire le valvole dei primi computer dai nidi di alcuni tipi di coccinelle (dall’inglese bug che significa appunto coccinella). Più recentemente il termine ha preso via via un significato diverso, attualmente indica un software che ripulisce il programma dagli errori. ▶
L’ambiente di programmazione
Lezione 1
Impostare i breakpoints Per fissare i ◀ punti di interruzione ▶ si deve procedere facendo click nella colonna alla sinistra del codice sulla riga interessata. ◀ Punti di interruzione I punti di interruzione (in inglese breakpoints) servono per far eseguire il programma fino a un punto prefissato utile per verificare quale valore assumono alcune variabili in quel particolare istante. Il programma si interrompe prima della riga che contiene il punto di arresto. ▶
Per provare le operazioni di debug utilizziamo un progetto già creato; lo scopo è quello di verificare il funzionamento delle funzioni di debug. Prima di tutto devi compilare le due classi presenti: Demo e Car. A questo punto apri l’editor sulla classe Demo, cerca il metodo loop e imposta il breakpoint in qualsiasi punto del ciclo for. Apparirà un simbolo di stop accanto alla riga in cui è presente il breakpoint, in questo caso la riga sum = sum + i;:
Quando la riga del codice, che ha il breakpoint impostato, viene raggiunta, l’esecuzione del programma si interrompe. Per provare quanto indicato esegui la procedura che segue. 1
Apri il progetto Esempio1.
2
Crea un oggetto di classe Demo facendo click con il tasto destro del mouse sulla classe e selezionando la voce new Demo(). Quindi conferma con OK. A questo punto viene creato un oggetto di classe Demo chiamato demo_1: ▼
\ \ 21
UdA 1
La sintassi Java e l’ambiente di sviluppo
3
Adesso chiama il metodo loop passando ad esso un parametro, per esempio 10. Per fare questo fai click con il tasto destro sopra all’oggetto demo_1 e seleziona la voce int loop(int count): ▶
4
Adesso inserisci il parametro (in questo caso 10): ▶
5
In questo modo il programma viene eseguito e terminerà esattamente dove indicato nel breakpoint. Non appena il breakpoint viene raggiunto, si apre la finestra dell’editor che visualizza la riga corrente del codice insieme alla finestra di debugger: ▼
L’esecuzione si ferma prima che la riga che contiene il breakpoint venga eseguita.
22
L’ambiente di programmazione
6
Lezione 1
Adesso che l’esecuzione è interrotta momentaneamente possiamo usare il tasto Step per eseguire una istruzione per volta dal punto di arresto in poi. Ogni volta che clicchiamo sul pulsante Step viene eseguita una singola riga del codice e l’esecuzione interrotta. Il contenuto delle variabili in uso nel programma vengono mostrati nella finestra del debugger chiamata Local variables. Continua a fare click sul pulsante Step e nota il contenuto delle variabili fino al termine del metodo loop. A quel punto appare una finestra che mostra il valore restituito dal metodo: ▶ Il pulsante Continue nella finestra di debugger serve per riavviare l’esecuzione e far eseguire normalmente il programma.
L’esecuzione passo passo Per eseguire il programma passo passo (step by step) dobbiamo utilizzare i pulsanti Step e Step Into presenti nella finestra di debugger.
Step e Step Into si comportano allo stesso modo se la riga di codice non contiene una chiamata a un metodo.
Quando l’esecuzione del programma raggiunge il breakpoint ed è pertanto in fase di Stop, possiamo eseguire una istruzione per volta per verificare il corretto svolgimento del programma in esecuzione. Per fare questo dobbiamo ripetere più volte il click sul pulsante Step. L’evidenziatore di riga del codice sorgente in corso di esecuzione si sposterà via via secondo l’ordine prefissato dal programma. A ogni click su Step ogni singola riga del codice viene eseguita e l’esecuzione viene fermata di nuovo. I valori che le variabili assumono variano in tempo reale. Verifica per esempio il valore della variabile sum.
L’ispezione del contenuto delle variabili Quando utilizziamo il debugger risulta assai importante, per la verifica del funzionamento del codice, verificare il contenuto delle variabili durante l’esecuzione del codice. La finestra di debugger suddivide le variabili in due categorie: ◗ variabili locali; ◗ ◀ variabili di istanza ▶. ◀ Variabili di istanza Le variabili d’istanza vengono anche chiamate attributi o proprietà dell’oggetto corrente. ▶
23
UdA 1
La sintassi Java e l’ambiente di sviluppo
Le variabili locali sono le variabili utilizzate all’interno del metodo corrente e ne viene sempre visualizzato il contenuto aggiornato. È possibile anche selezionare i metodi nella sequenza delle chiamate per esaminare le variabili di altri oggetti e metodi ancora attivi. La seguente procedura illustra come.
24
1
Apri il progetto Esempio1.
2
Aggiungi un breakpoint all’interno del metodo carTest() ▶
3
Chiama il metodo carTest() dall’oggetto demo_1: ▶
4
Quando il breakpoint viene raggiunto, appare la finestra di debugger che ci informa sulla situazione delle variabili. ▶
L’ambiente di programmazione
5
Facendo click su Step viene eseguita la riga successiva che richiama il metodo seats() della classe Car.
6
Facendo invece click su Step Into viene indicato anche il metodo Car. seats(). Questo accade perché il metodo Car. seats è stato chiamato da Demo.carTest. Se selezioni Demo.carTest dalla lista chiamata Call sequence puoi ispezionare il codice sorgente e i valori delle variabili correnti di questo metodo. ▶
7
Procedendo alla riga successiva, che contiene l’istruzione new Car(…), puoi osservare che il valore della variabile locale myCar è visualizzata come .
Lezione 1
Tutti i valori di tipo oggetto, a eccezione delle stringhe (tipo String) vengono visualizzati in questo modo. Per ispezionare il contenuto di questa variabile fai doppio click su di essa, si aprirà una finestra di ispezione dell’oggetto uguale a quella descritta in precedenza.
Prova adesso!
• Uso del Debugger • Creazione breakpoints • Ispezione contenuto variabili • Esecuzione passo passo
APRI IL PROGETTO Esempio1 1 Riprova ancora con un altro metodo. Imposta un breakpoint nella classe Demo, nel metodo carTest(), quindi chiama il metodo. 2 Crea un oggetto di classe Car con 2 posti davanti e 3 posti dietro. 3 Aggiungi un secondo breakpoint al metodo seats() della classe Car. 4 Chiama il metodo seats() e verifica il contenuto delle variabili locali e attributi.
25
UdA 1
La sintassi Java e l’ambiente di sviluppo
Verifichiamo le conoscenze g Esercizi a scelta multipla 1 Metti in ordine logico le fasi di sviluppo di un programma in Java scritte a sinistra con il relativo software posto a destra: a) esecuzione Editor .................................... b) stesura del programma JDK .................................... c) compilazione in bytecode JRE .................................... d) interpretazione 2 Le classi in Java sono contenute: a) ciascuna classe in un metodo b) ciascuna classe in un singolo file c) ciascuna classe pubblica in un singolo file d) raggruppate in gruppi logici e memorizzate in pochi file sorgenti 3 Il compilatore a linea di comando viene eseguito con il comando: a) >javac Nomeclasse c) >java Nomeclasse b) >javac Nomeclasse.java d) >java Nomeclasse.java 4 Il compilatore produce file con estensione: a) .java b) .clas
c) .class d) .exe
5 Quale comando da linea tra i seguenti consente di eseguire un file bytecode? a) java –cp . Nomeclasse d) java Nomeclasse b) jre Nomeclasse.class e) java –cp . Nomeclasse.class c) jre Nomeclasse.java
g Esercizi di completamento 1 Indica almeno tre ambienti di sviluppo per Java: a) ........................................................................................................... b) ........................................................................................................... c) ........................................................................................................... 2 Il compilatore Java traduce il ........................................................................................................... in ............................................................................................................ 3 Il risultato della compilazione in Java è il ............................................................................................................ 4 La sigla JRE indica ............................................................................................................ 5 Il JRE è composto da due elementi principali: la ........................................................................................................................ che si occupa dell’esecuzione del codice mentre la ....................................................................................................................... contiene le funzionalità di base.
26
La struttura del codice
lezione
Lezione 2
2
LA strUttUrA deL codice
in qUestA Lezione impAreremo...
• a scrivere un primo programma e a compilarlo ed eseguirlo • a comprendere il ruolo del metodo main • a utilizzare le funzioni di input output • a creare la documentazione con javadoc
Prima di poter scrivere un programma mediante il linguaggio Java vogliamo introdurre il concetto di ◀ programma ▶: si tratta di una astrazione del problema che si vuol fare risolvere al computer; in qualsiasi tipologia di programma, dal più semplice al più complesso, l’elaborazione lavora sui dati di ingresso, denominati input, e li trasforma nei dati di uscita, chiamati output. Java è un linguaggio di programmazione ◀ object oriented ▶ in cui l’astrazione del problema avvie◀ Programma È un insieme di ne tramite l’isolamento e la definizione dei singoistruzioni che è necessario eseguili concetti che è necessario considerare al fine di re al fine di risolvere uno specifico raggiungere lo scopo prefissato. problema. ▶ ◀ Object oriented La programmaIn un programma che deve gestire per esempio un zione orientata agli oggetti, definita magazzino di merci secondo la programmazione anche OOP (Object Oriented Proa oggetti i concetti coinvolti potrebbero essere: le gramming), è un tipo di programmerci, gli scaffali e i codici a barre. Le azioni conmazione che permette di definire crete che deve svolgere il programma per risolvere oggetti software in grado di interail problema possiedono una importanza relativa in gire gli uni con gli altri attraverso lo quanto il peso maggiore è dato dalla rappresentascambio di messaggi. ▶ zione dei concetti coinvolti nel problema.
■ Le classi Nei linguaggi di programmazione orientati agli oggetti, un programma è costituito da un insieme di oggetti che cooperano tra di loro per raggiungere lo scopo per cui è stato scritto il programma. Gli ◀ oggetti ▶ rappresentano i concetti coinvolti nel problema.
◀ Oggetti Un oggetto è una entità dotata di specifici attributi e che è in grado di cooperare con altri oggetti svolgendo specifiche azioni. ▶
27
UdA 1
La sintassi Java e l’ambiente di sviluppo
Un oggetto può rappresentare qualsiasi cosa. Per esempio, l’oggetto Cane può avere come attributi il peso, il colore del pelo, l’età; le sue azioni possono includere il correre, il dormire e il mangiare un osso. L’oggetto Smartphone ha attributi come la risoluzione dello schermo, il peso e il colore, mentre le azioni includono l’invio di email, l’installazione di Apps, la composizione di un numero, l’accensione e lo spegnimento. Una classe è invece lo schema dal quale è possibile creare singoli oggetti. Possiamo paragonare una classe al progetto di una automobile. Per esempio, il progetto della Fiat Bravo, completo di tutti i dettagli che definiscono gli attributi e le azioni che questa macchina può svolgere, viene utilizzato dalla fabbrica per produrre i singoli esemplari. Paragonando queste definizioni alla programmazione, il progetto è la classe, mentre le singole autovetture sono i diversi oggetti presenti nel programma. Si pensi a un’azienda di noleggio macchine Fiat: per poter operare necessita di diversi esemplari di Fiat Bravo, che vengono forniti ai clienti per viaggiare. Allo stesso modo, un programma può aver bisogno di molteplici oggetti di una stessa classe al fine di raggiungere il proprio scopo.
■ Un programma Java Una volta appresa la struttura generale di un generico programma orientato agli oggetti, è necessario approfondire la struttura di un programma specificatamente scritto nel linguaggio Java. L’unità minima di azione che è possibile definire in Java è detta espressione. Una istruzione è invece ogni espressione che termina con un punto e virgola. Un esempio basilare di istruzione Java è la seguente dichiarazione di variabile:
Variabili e tipi verranno approfonditi più avanti, in una lezione dedicata. Un altro esempio di istruzione tipica è quella che consente di eseguire una assegnazione:
L’istruzione assegna alla variabile x il valore 20. Le istruzioni vengono raggruppate in unità esecutive chiamate metodi. Per esempio, un metodo il cui scopo è quello di riprodurre un motivo musicale memorizzato come file mp3 potrebbe eseguire le seguenti operazioni:
Nel codice presentato, le istruzioni leggiCanzone(), decodiwca() e riproduci() si occupano, rispettivamente, di leggere il file mp3, decomprimerne il contenuto e di riprodurlo nell’altoparlante del computer. A loro volta, i metodi sono contenuti all’interno delle classi, per esempio, il metodo play() potrebbe essere contenuto nella classe LettoreMp3: ▶
28
La struttura del codice
Lezione 2
Zoom su... iL metodo che AzionA LA cLAsse Ciascun programma necessita di un punto di inizio: è necessario indicare la prima istruzione da eseguire. A questo proposito esiste un metodo con un nome particolare, chiamato main() che rappresenta il punto di partenza dell’esecuzione di un programma scritto in Java. Ogni programma Java deve possedere almeno un metodo main per poter fare incominciare l’esecuzione del programma stesso. Il codice che segue mostra il codice del metodo main di una classe ipotetica che esegue una stampa di una stringa a video:
Come possiamo notare questo metodo deve essere preceduto da tre parole chiave, assolutamente obbligatorie chiamate public static void che verranno discusse ampiamente più avanti. Inoltre il codice del metodo che viene eseguito deve essere racchiuso all’interno delle parentesi graffe.
■ La classe System La classe System, presente nel ◀ package ▶ java.lang è il punto di accesso a diverse funzioni strettamente legate alle funzionalità di base di Java, come la Virtual Machine, i meccanismi di sicurezza e le proprietà di sistema. Oltre a queste, la classe System dispone di tre campi, che forniscono l’accesso all’I/O di sistema: ◗ in che consente di accedere all’input; ◗ out che permette l’accesso all’output; ◗ err che rappresenta un canale dedicato agli errori. ◀ Package I package di Java non sono altri che un insieme di classi impacchettate in un unico file di estensione .jar. Sono delle vere e proprie librerie alle quali possiamo dal codice Java. Possono essere create dal programmatore, anche se esamineremo i package già definiti in Java. Il nucleo del linguaggio Java contiene solo le parole chiave per la costruzione delle classi, i commenti, i normali costrutti if, switch, while, do-while, for, etichette, break, continue e return, tutto il resto è implementato nei package del linguaggio, comprese le istruzioni di Input e di Output. ▶
Per usare un package in una classe, prima della definizione della classe dobbiamo inserire l’istruzione import, per esempio volendo usare il package java.awt dobbiamo inserire all’inizio del codice la seguente istruzione, che indica l’intenzione di usare le classi presenti nel package: import java.awt.*;
Il package java.lang è il più importante di Java e racchiude le classi fondamentali del linguaggio, infatti anche se non scriviamo l’istruzione import per questo package, fa lo stesso in quanto esso verrà importato automaticamente.
29
UdA 1
La sintassi Java e l’ambiente di sviluppo
Attraverso l’asterisco (*) indichiamo al compilatore l’intenzione di utilizzare tutte le classi; qualora intendessimo usarne solo alcune dovremmo specificarlo per esteso. Per esempio l’istruzione: import java.awt.Frame;
limita l’accesso alla sola classe Frame del package awt. Il primo di questi campi è di tipo InputStream, mentre gli altri sono di tipo PrintWriter; queste classi appartengono al package java.io e dispongono di funzionalità di lettura e scrittura: in e out consentono di accedere all’input/output di sistema, mentre err viene utilizzato per la gestione degli errori: quando un programma ha la necessità di segnalare un evento critico, lo può inviare a err; quando la comunicazione è un normale output, viene inviata ad out.
La visualizzazione dei dati Il linguaggio Java, come tra l’altro tutti i linguaggi di programmazione, consente di comunicare con l’utente tramite due modalità: ◗ input; ◗ output. L’input (dati in ingresso) consente al programma di ricevere dati e informazioni dall’esterno allo scopo di poterli elaborare, mentre la fase di output (dati in uscita), consiste nella comunicazione all’utente dei dati e delle informazioni così elaborati. Tutti i programmi si basano sul seguente schema: Dati in ingresso (Input)
Istruzioni (Elaborazione)
Dati in uscita (Output)
Tradotto in parole, significa che il risultato (output) è ottenuto mediante le operazioni elementari descritte nell’algoritmo ed è il prodotto della elaborazione (trasformazione) dei dati in ingresso (input). In un computer i tipici dispositivi di input sono la tastiera e il mouse, mentre il monitor è il tipico dispositivo di output. Queste funzionalità sono disponibili in Java attraverso le classi presenti nella libreria di Java. La presentazione dei risultati sul video viene definita standard output, a cui si accede tramite l’oggetto ◀ System.out ▶. Il codice sotto riportato mostra come visualizzare il contenuto di una variabile intera e di una a doppia precisione. L’output è riportato a fianco del codice.
◀ System La classe System raccoglie alcune funzionalità di sistema; uno dei suoi attributi è out, un oggetto che contiene diversi metodi per produrre un output, studiati per operare con i tipi di dati presenti nel linguaggio Java. ▶
30
La struttura del codice
Lezione 2
In questi esempi è stato utilizzato il metodo println() che stampa il parametro passato andando successivamente a capo; per scrivere sempre sulla stessa riga è possibile utilizzare print(). Il codice seguente produce in output la scritta “Prezzo: 100 Euro”:
Il programma produce in output quanto segue
La lettura dei dati Le gestione dell’input avviene mediante la classe InputStream fornita da System. Tale classe tuttavia dispone solo delle funzionalità basilari di gestione dell’input; il metodo read() in essa definito, infatti, consente di leggere la tastiera solo un carattere per volta. Se vogliamo sviluppare un programma dobbiamo utilizzare funzioni più evolute, come per esempio la lettura di una intera riga di testo, quella che l’utente digita e termina con la pressione del tasto INVIO. Il metodo readLine() della classe BufferedReader, legge buffers (sequenze di caratteri) che restituisce ◀ Lettore di stream LÕutilitˆ di un sotto forma di stringhe. La classe BufferedReader, a lettore • quella di trasformare la sequenza di byte letti tramite qualsua volta, deve essere inizializzata fornendo un reader siasi oggetto di tipo InputStream (◀ lettore di stream ▶) di input. Tale lettore è rapprein una sequenza di caratteri. ▶ sentato da un’istanza della classe InputStreamReader. Per leggere una sequenza di byte da un oggetto di tipo InputStream dobbiamo usare un oggetto System.in, di classe InputStream. Un lettore bufferizzato può essere creato istanziando un oggetto della classe BufferedStreamReader nel seguente modo: InputStreamReader leggi= new InputStreamReader(System.in);
A questo punto faremo uso del lettore bufferizzato che abbiamo chiamato leggi per istanziare un oggetto di tipo BufferedReader. Quest’ultimo mette a disposizione del programmatore il metodo readLine() che consente la lettura di intere stringhe. Per istanziare un oggetto di classe BufferedReader dobbiamo procedere nel seguente modo: BuҬeredReader input = new BuҬeredReader( leggi);
Una volta che l’oggetto di lettura è stato costruito correttamente, possiamo utilizzarlo per leggere una linea di testo, operazione che viene eseguita dal metodo readLine(): input.readLine();
L’istruzione di inizializzazione dell’oggetto di tastiera (input) può essere ridotta alla seguente forma compatta:
31
UdA 1
La sintassi Java e l’ambiente di sviluppo
esempio
2
Stampa di un numero
Il programma acquisisce da tastiera un nome e un numero che rappresenta il saldo di un conto corrente e lo visualizza in output. ▶
È importante sottolineare come sia necessario aggiungere all’inizio del codice la riga: import java.io.*;
per poter utilizzare le istruzioni di input e output viste nella lezione.
Nel codice è presente il costrutto try/catch: viene utilizzato per gestire eventuali errori che si possono verificare in fase di lettura da un flusso di informazioni. In questo specifico caso è difficile che si verifichino problemi di lettura dalla tastiera: se questa smettesse di funzionare, probabilmente non si vedrebbero più sul video i caratteri digitati. Si pensi però se la lettura delle informazioni avvenisse da un dischetto removibile, evenienza che, dal punto di vista di Java, è equivalente come classi e metodi coinvolti: l’espulsione anticipata del dischetto farebbe mancare le informazioni che si stanno leggendo; in tal caso viene individuato da Java un errore di tipo IOException. La gestione degli errori sarà oggetto di una trattazione più approfondita più avanti nel testo.
In questo programma sono state usate due variabili, una per il nome da visualizzare e l’altra per memorizzare l’importo. Il concetto di variabile verrà tuttavia trattato nella prossima lezione.
■ I commenti Tutti i linguaggi di programmazione mettono a disposizione uno spazio per commentare il codice sorgente. Lo scopo è quello di documentare, descrivere o annotare informazioni importanti per lo sviluppatore. Tali messaggi prendono il nome di ◀ commenti ▶. ◀ Commenti Un commento fa parte del codice di un programma, ma non viene considerato al momento dell’esecuzione. In questo modo il programmatore può descrivere lo scopo di porzioni particolari di codice, in modo che sia più semplice ricordare perché un certo programma è stato scritto in quel modo, soprattutto quando è passato un certo tempo dopo la stesura del programma ed è difficile ricostruire le sue funzionalità. ▶
32
La struttura del codice
Lezione 2
A volte possiamo usare i commenti anche per non far eseguire alcune istruzioni senza doverle necessariamente cancellare dal codice.
Il linguaggio Java dispone di tre modalità per inserire i commenti: ◗ doppio slash (//) a inizio commento; ◗ slash asterisco (/*) all’inizio e viceversa (\*) alla fine del commento; ◗ slash e doppio asterisco (/**) all’inizio e viceversa (**/) alla fine del commento. Il primo tipo permette di considerare come commento tutto ciò che è scritto dopo di essa, fino al termine della riga. Quando invece il commento è composto da diverse righe, è più pratico utilizzare il secondo tipo in grado di identificare quanto contenuto tra delimitatore di inizio (/*) e delimitatore di fine (*/) commento. L’esempio che segue mostra i due tipi di commento indicati:
Il terzo tipo di commento si chiama commento Javadoc e consente di documentare le classi con un browser Web. All’interno dei commenti Javadoc è possibile inserire alcuni marcatori particolari, che permettono di specificare alcune informazioni utili, quali la versione e l’autore della classe, e che avranno un posto specifico nella documentazione generata. Per esempio, per includere autore e versione nella classe Automobile possiamo scrivere: ▶
A questo punto, dopo aver compilato la classe passiamo alla generazione dei codici di documentazione aprendo l’ambiente JDK. Per fare questo apriamo l’ambiente Prompt dei comandi e posizioniamoci nella cartella del progetto (quella che contiene la classe da documentare):
33
UdA 1
La sintassi Java e l’ambiente di sviluppo
Il comando necessario per generare i file della documentazione è javadoc seguito dal nome del file che contiene la classe sorgente, quindi in questo caso Automobile.java: ▶
Vengono generati moltissimi file che concorrono alla creazione della documentazione all’interno della cartella del progetto:
Aprendo il file index.html otteniamo la finestra di consultazione della nostra classe Automobile:
34
La struttura del codice
Lezione 2
Verifichiamo le conoscenze g Esercizi a scelta multipla 1 Una istruzione Java è una espressione che: a) corrisponde a una qualunque riga del programma, anche vuota b) effettua un ciclo c) contiene almeno una variabile d) termina con il punto e virgola 2 Ogni programma Java deve possedere almeno un metodo main? a) no, non è vero b) no, per poter far finire il programma c) no, per poter far iniziare il programma d) no, per poter salvare le variabili 3 Quale campo della classe System, tra i seguenti, consente di stampare a video una stringa? a) in b) out c) err d) print 4 I package sono: a) un insieme di classi salvate in un file .jre b) un insieme di classi salvate in un file .jar c) un insieme di variabili salvate in un file .jar d) un insieme di metodi salvati in un file .jar 5 Quale istruzione tra le seguenti istanzia un oggetto utile per la lettura da tastiera? a) BufferedReader input = new BufferedReader(System.in); b) BufferedReader input = new BufferedReader(System.out); c) InputStreamReader input = new InputStreamReader(System.in); d) Reader input = new Reader(System.in); 6 Quale tra i seguenti frammenti di codice rappresenta correttamente il metodo main? a) public void main( String[] args ); b) public static void main( String[] argv, int argc ); c) public final void main(); d) public static void main( String[] args ); 7 Un commento su singola riga è specificato dall’identificatore: a) /* b) /** c) // d) */
e) //**
8 Un blocco di commenti su più righe è delimitato da: a) /* ... */ b) /** ... **/ c) // ... // d) */ ... /* 9 Quali tra i seguenti sono commenti Javadoc standard? (tre risposte) a) /* @version 1.0 */ b) /** Commento @version 1.0 */ c) /* Commento @ejb.bean */ d) /** Commento */ e) /** Commento @ejb.bean */ 10 Un programma è normalmente formato da: a) figure b) istruzioni c) comandi
d) lettere e numeri
35
UdA 1
La sintassi Java e l’ambiente di sviluppo
g Test vero/falso 1 2 3 4 5 6 7 8 9 10 11 12
Il package fondamentale di Java è java.lang. Tutte le istruzioni sono espressioni. Tutte le espressioni sono istruzioni. Nell’istruzione import l’asterisco (*) indica al compilatore l’intenzione di utilizzare tutte le classi. Un oggetto è composto da più classi. Un metodo dewnisce le proprietà di un oggetto. I metodi contengono il codice. Il metodo print() e println() sono esattamente identici. Un programma è formato da un insieme di attributi. Un oggetto è dotato di attributi e metodi. Una classe dewnisce la struttura di un oggetto. Un oggetto è un tipo particolare di classe.
g Esercizi di completamento 1 Lo standard output viene utilizzato per produrre una stampa a ...........................................................................................................; a questo oggetto si accede tramite System.......................................................... 2 Un ........................................................................................................... fa parte del codice di un programma, ma non viene considerato al momento dell’esecuzione. 3 Il costrutto try/catch viene utilizzato per gestire eventuali ........................................................................................................... che si possono veriwcare in fase di ............................................................................................................ 4 Ogni programma Java deve possedere almeno un metodo ........................................................................................................... per poter fare incominciare l’esecuzione del programma stesso.
36
Le variabili e i tipi primitivi
lezione
Lezione 3
3
Le vAriAbiLi e i tipi primitivi
in qUestA Lezione impAreremo...
• a dichiarare e usare variabili e costanti • a conoscere i principali tipi primitivi
■ Gli identificatori Quando viene dichiarata una variabile, un metodo, un attributo o una classe, e in generale un qualsiasi elemento che costituisce il programma, il programmatore definisce un ◀ identiwcatore ▶. Java è un linguaggio che supporta i caratteri Unicode, lo standard internazionale che definisce l’insieme dei caratteri composti da due byte e che consentono di rappresentare alfabeti internazionali, contenenti caratteri specifici di alfabeti locali. Per esempio, in Italia utilizziamo le lettere accentate, che nel calcolatore vengono rappresentate da un unico carattere che comprende anche l’accento. Prima dell’avvento di Unicode, era necessario utilizzare il carattere apostrofo da far seguire alla lettera da accentare. Per esempio: Prima di Unicode
e’
Con Unicode
è
◀ Identificatore Si tratta del nome attribuito alle variabili, costanti, metodi, classi e package. ▶
Il ricorso allo standard di Unicode permette l’utilizzo di identificatori che contengono per esempio le lettere accentate. I caratteri che possiamo utilizzare per gli identificatori presenti in un programma Java includono: ◗ le lettere dell’alfabeto (A..Z, a..z); ◗ i numeri (da 0 a 9); ◗ il simbolo di sottolineatura (underscore, _); ◗ il simbolo del dollaro ($). Quest’ultimo elemento è supportato però solo per ragioni storiche, e non dovrebbe essere utilizzato nella normale scrittura di programmi Java. Gli identificatori devono iniziare per una lettera; non si possono utilizzare come identificatori le parole chiave, i valori booleani true e false e il valore letterale null.
37
UdA 1
La sintassi Java e l’ambiente di sviluppo
Le seguenti parole chiave non possono mai essere utilizzate come identificatori: abstract boolean break byte case catch char class const continue
default do double else extends wnal wnally yoat for goto
if implements import instanceof int interface long native new package
private protected public return short static strictfp super switch synchronized
this throw throws transient try void volatile while
Java è un linguaggio sensibile alle maiuscole (case sensitive): il compilatore considera come differente la stessa lettera se scritta in maiuscolo o minuscolo. Per esempio, l’identificatore: numeroProdotti è diverso dall’identificatore NumeroProdotti Le specifiche del linguaggio Java definiscono alcune regole sulla scelta dei nomi che facilitano la lettura del codice scritto da altri; è bene rispettare queste regole, in modo che il proprio codice sia elegante e armonico con il resto del software Java con cui si va a interfacciare. Le principali convenzioni sono: ◗ Nomi di classe. Composti da nomi dalle iniziali maiuscole (per esempio: Prodotto, NumeroTelefono, Cliente, Indirizzo); ◗ Nomi dei metodi. Composti da verbi con iniziali maiuscole, ma con il primo carattere in minuscolo (per esempio: calcolaTotale, eseguiTransazione, inviaOrdine); ◗ Nomi degli attributi. Composti da nomi o abbreviazioni con iniziali maiuscole, ma con il primo carattere in minuscolo (per esempio: NumeroProdotti, areaFigura, codiceOrdine); ◗ Nomi delle costanti. Composti da nomi o abbreviazioni, tutti in maiuscole, che utilizzino il carattere di sottolineatura per separare i termini (per esempio: PI_GRECO, MAX_PRODOTTI, IN_ESECUZIONE). Quelli che seguono sono dei possibili identificatori. totaleCosto
variabile che contiene il costo totale di un ordinativo
calcola()
metodo che esegue un determinato calcolo numerico
Rettangolo
classe che definisce il tipo di dato relativo all’omonima figura geometrica
■ Le variabili Una ◀ variabile ▶ consente di immagazzinare un dato ed è collocata all’interno della memoria RAM del computer. ◀ Variabile Con una variabile si individua una zona di memoria utilizzata per contenere uno o più dati che possono essere modificati durante l’esecuzione del programma. ▶
38
Le variabili e i tipi primitivi
Lezione 3
Le variabili contengono informazioni volatili, cioè necessarie per svolgere elaborazioni temporanee, paragonando la memoria del computer a una scacchiera, possiamo immaginare le variabili e le costanti come scatole che possono essere aggiunte a piacimento sulla scacchiera, posizionate da Java in coordinate precise. Il programmatore non ha la preoccupazione di stabilire la posizione di queste scatole, deve semplicemente assegnare un nome e indicare il tipo di informazioni che conterranno: per esempio numeri o parole. La differenza tra variabili e costanti è facilmente desumibile dal nome stesso: le variabili contengono informazioni che possono essere modificate ogni volta che si desidera, le costanti contengono dati che una volta assegnati, non possono essere modificati. Il contenuto della variabile può cambiare durante il funzionamento del programma, se questo contiene del codice che interviene su di essa. Per esempio, un metodo che deve calcolare la sommatoria di una serie di numeri potrebbe utilizzare una variabile per mantenere questa somma (somma). La variabile avrebbe inizialmente valore zero e il metodo potrebbe scorrere ciascun numero della serie, addizionandolo a somma. Una variabile possiede un nome identificativo e un tipo, che determina quali possibili valori possono essere memorizzati nella variabile; Java dispone di molte tipologie di dati diversi, che verranno affrontati nella prossima lezione. Inoltre, è possibile estendere il linguaggio creando nuovi tipi di dati: una classe è infatti anche automaticamente un tipo, e le variabili possono contenere riferimenti agli oggetti. Per poter utilizzare una variabile in un programma, è necessario prima di tutto dichiararla, in modo che il compilatore riservi la memoria necessaria a ospitare i dati. In Java, per dichiarare una variabile è necessario specificarne il tipo. Per esempio, per dichiarare una variabile di tipo intero dobbiamo scriverne il nome preceduto dal tipo:
Il compilatore riserva quindi la memoria necessaria a contenere un valore di tipo intero e gli assegna il nome i. Per impostare il valore di una variabile, è necessario utilizzare l’operatore di assegnamento. In Java questo è rappresentato dall’uguale (=). Per esempio, per assegnare il valore 15 alla variabile i, è possibile scrivere:
Così facendo, il compilatore immagazzina nell’area di memoria individuata dalla variabile i il valore 15.
L’inizializzazione delle variabili Prima di utilizzare una variabile locale (definita all’interno di un metodo) è necessario impostarne esplicitamente il valore, sia con una assegnazione, oppure tramite la sua ◀ inizializzazione ▶. Per esempio, per inizializzare la variabile i al valore 15, in modo contestuale alla sua dichiarazione possiamo scrivere:
◀ Inizializzazione Con questo termine si identifica l’assegnazione del valore iniziale della variabile direttamente in fase di dichiarazione. ▶
39
UdA 1
La sintassi Java e l’ambiente di sviluppo
Se una variabile viene utilizzata senza essere inizializzata, viene presentato un errore di compilazione. Per esempio:
produce il seguente errore di compilazione:
Le variabili definite a livello di classe, invece, sono automaticamente inizializzate a zero, o a valori equivalenti in funzione del tipo di dato utilizzato.
■ Le costanti Nella memoria possono esser contenute anche quelle informazioni che non vengono modificate, ma che sono necessarie per eseguire le elaborazioni; queste sono informazioni costanti. Quando è necessario utilizzare una area di memoria il cui contenuto non varia nel corso dell’esecuzione del programma, si deve dichiarare una costante. In Java le costanti si dichiarano come le variabili, ma preponendo il modificatore final al tipo della variabile. Per esempio, per dichiarare la costante pi greco scriviamo:
Ogni tentativo di cambiare il valore di una costante produce una segnalazione di errore.
■ La tipizzazione dei dati Il tipo di dato individua la natura dei dati che saranno memorizzati all’interno delle variabili. Per natura dei dati intendiamo la qualità, la quantità e i limiti dell’informazione che è possibile memorizzare. Ogni tipo di dato possiede uno specifico nome che ne identifica le caratteristiche. Tutti i linguaggi di programmazione, quindi anche Java, definiscono tipi standard di dati, detti anche tipi fondamentali, che definiscono sia la gamma dei valori memorizzabili, sia lo spazio occupato all’interno della memoria. Java, come abbiamo visto in precedenza, è un linguaggio indipendente dalla piattaforma, infatti un tipo di dato per esempio un tipo intero, occuperà la stessa quantità di memoria su un sistema Linux che su un sistema Windows. I linguaggi non indipendenti dalla piattaforma hanno lo svantaggio che i loro tipi di dati cambiano caratteristiche al variare del sistema sottostante.
40
Le variabili e i tipi primitivi
Lezione 3
I tipi di dati ◀ primitivi ▶ supportati dal linguaggio Java sono divisi in quattro tipi fondamentali: ◗ i valori interi; ◗ i valori in virgola mobile (numeri reali); ◗ i caratteri; ◗ i valori logici (booleani). ◀ Tipi primitivi I tipi di dati possono essere classificati secondo la struttura in tipi primitivi e tipi derivati. I tipi primitivi sono i tipi semplici che non possono essere decomposti, come per esempio numeri interi o booleani. I tipi derivati si ottengono dai tipi primitivi mediante opportuni operatori forniti dal linguaggio: essi includono i tipi strutturati (record) o gli array, le classi e così via. ▶
nome identificativo
tipo di dato
boolean
Valori logici (true/false)
char
Carattere
byte, short, int, long
Valori interi
yoat
Valori reali a precisione singola
double
Valori reali a doppia precisione
I valori interi e reali possono essere espressi con segno:
I numeri che presentano la virgola (identificata dal carattere punto .) vengono identificati automaticamente dal compilatore come di tipo double. Se vogliamo dichiararli di tipo yoat dobbiamo farli terminare dalla lettera “f” come indicato di seguito:
Se dichiariamo un numero intero, in mancanza di indicazione specifica, il compilatore li dichiara sempre come int anche se indichiamo espressamente il tipo long. Per dichiarare un tipo long dobbiamo aggiungere una lettera “L” maiuscola al termine della cifra:
I valori logici di tipo boolean possono invece assumere i valori true o false:
41
UdA 1
La sintassi Java e l’ambiente di sviluppo
Il tipo char consente di memorizzare un qualsiasi carattere Unicode. Il carattere è delimitato da apice singolo ('):
Il tipo di dato char può contenere alcuni caratteri che non possono essere specificati tramite la tastiera. Tali caratteri prendono il nome di sequenze di escape, possiamo definirle utilizzando il carattere back slash (\) seguito da un carattere come descritto nella tabella seguente: sequenza
significato
\n
Crea una nuova riga e va a capo
\r
Ritorno a capo
\f
Crea una nuova pagina
\’
Inserisce un apice singolo
\”
Inserisce un apice doppio
\\
Inserisce una barra (backslash)
\b
Cancella il carattere a sinistra (Backspace)
\t
Inserisce un carattere di tabulazione
A ciascun tipo di dato è associata un’area di memoria specifica, che individua i valori massimi e minimi che possono essere contenuti. Lo spazio di memoria associato a ciascun tipo di dato è riassunto nella tabella seguente: da... a...
nome boolean
dimensione Valori logici (true/false)
char
Unicode 0...
Unicode 216–1
2 byte
byte
–128...
+127
1 byte
short
15
–2 ...
15
+2 –1
2 byte
int
–231...
+231–1
4 byte
63
63
8 byte
long yoat double
–2 ...
+2 –1
–3,428 • 1038...
–1,401 • 10-45
1,401 • 10-45...
3,428 • 1038
–1,798 • 10138...
–4,941 • 10-324
4,941 • 10
1,798 • 10138
-324
...
4 byte 8 byte
I valori reali seguono lo standard IEEE754 che consente non solo di rappresentare numeri positivi e negativi, ma anche valori particolari, come: ◗ zeri positivi e negativi (+0, -0); ◗ gli infiniti; ◗ il valore speciale NaN (Not-a-Number), utilizzato per rappresentare valori non validi, come per esempio una divisione per zero. Per quanto riguarda gli infiniti e NaN, è necessario utilizzare valori speciali, specificati come costanti nelle classi java.lang.Float e java.lang.Double. Rispettivamente, questi sono: ◗ POSITIVE_INFINITY e NEGATIVE_INFINITY; ◗ NaN.
42
Le variabili e i tipi primitivi
Lezione 3
Per esempio, il seguente esempio assegna alla variabile inwnito il valore di infinito positivo:
Le classi Float e Double supportano anche le costanti MAX_VALUE e MIN_VALUE che specificano il valore massimo memorizzabile, rispettivamente, dal tipo di dato yoat e double. In Java esiste un particolare tipo, che sostanzialmente viene utilizzato per indicare “nessun tipo” ed è impiegato in particolare nella dichiarazione dei metodi. In poche parole il tipo void è un tipo non ancora assegnato. Si utilizza nei metodi che non restituiscono alcun valore, paragonabili alle procedure: in questo caso si indica al compilatore che il valore restituito è appunto void, in quanto il linguaggio Java non ammette che il tipo non venga esplicitamente indicato.
esempio
3
Somma di due numeri
Il programma acquisisce due numeri da tastiera e ne comunica la loro somma. L’esempio serve per mettere in evidenza i concetti visti nei paragrafi precedenti, realizzando un semplice programma che somma due numeri digitati dall’utente; le due righe di testo lette come input vengono convertite in int utilizzando la classe Integer. Il codice è mostrato a fianco: ▶
La sua esecuzione è la seguente: ▼
La riga di codice System.out.println(“Somma = “ + (numero1+numero2) );
contiene due volte il segno “+”: il primo serve per concatenare la stringa “Somma = “ con il risultato dell’espressione successiva racchiusa tra parentesi, il secondo indica l’operazione aritmetica da eseguire.
43
UdA 1
La sintassi Java e l’ambiente di sviluppo
Verifichiamo le conoscenze g Esercizi a scelta multipla 1 Il nome di un identificatore può contenere i seguenti caratteri: (tre risposte) a) lettere (A..Z) e (a..z) b) numeri (0..9) c) la barra /
d) la barra \ e) la sottolineatura _ f) il dollaro $
g) il meno –
2 Gli identificatori servono per rappresentare: (3 risposte) a) variabili b) parole chiave
c) metodi d) classi
e) iterazioni
3 Indica quali nomi tra i seguenti non possono essere usati come identificatori: (2 risposte) a) costoTotale b) null
c) apri() d) true
e) PI_GRECO
4 Quale affermazione sulle variabili, tra le seguenti, è corretta? a) b) c) d)
sono parti di codice non costante vengono individuate da una espressione contengono informazioni possono essere di sola lettura
5 Quale tra le seguenti righe di codice è corretta? (3 risposte) a) I = 5; b) I < 5; c) Int i = 5;
d) int k = 10; e) float n = 0.5f; f) float n = 0.5L;
g) float n = 0.5;
6 Quali affermazioni che riguardano le costanti sono corrette? (2 risposte) a) b) c) d)
possiedono un valore sono variabili in sola lettura sono variabili in sola scrittura sono parti di codice
7 Quale dichiarazione tra le seguenti è errata? a) b) c) d)
string s; int i; float f; Long n;
8 Il tipo di dato individua, per una variabile: (2 risposte) a) valore massimo b) valore minimo
c) valori discreti possibili d) qualità del dato
e) un nome
9 I tipi di dato in Java: a) sono indipendenti dal sistema operativo, ma variano in funzione del tipo di processore utilizzato b) variano in funzione del sistema operativo, ma sono indipendenti dal processore utilizzato c) variano in funzione della piattaforma e i limiti relativi devono essere determinati in fase di esecuzione del programma da opportune routine di inizializzazione d) sono indipendenti dal sistema operativo e dal processore utilizzato
44
Le variabili e i tipi primitivi
Lezione 3
10 Indica quali tra i seguenti tipi di dato rappresentano numeri interi: (2 risposte) a) int c) short e) boolean b) double d) float 11 Quale tra le seguenti dichiarazioni e inizializzazioni è corretta? (2 risposte) a) float f = 1.1; c) int a = 5; e) long j = 10000; b) double d = 1.2; d) Long j = 5000; 12 Quale tra i seguenti valori è valido per dati di tipo booleano? a) Undefined c) True b) True d) False 13 Quale tra le seguenti istruzioni dichiara e inizializza correttamente una variabile di tipo carattere? (2 risposte) a) Char c = “A”; c) char c = ’è’; e) char c = “ab”; b) char c = ‘a’; d) Char c = ‘ab’; f) Char c = ‘\n’;
g Esercizi di completamento 1 Un identiwcatore è un .......................................................................... che identiwca elementi in un programma. Java utilizza lo standard .......................................................................... per la rappresentazione dei caratteri. 2 È .......................................................................... utilizzare il simbolo del dollaro nei propri identiwcatori. 3 Gli identiwcatori sono sensibili alle ........................................................................... 4 Un carattere può memorizzare un qualsiasi carattere della codiwca ........................................................................... 5 Le .......................................................................... sono un modo per rendere più leggibile il codice e facilitare la lettura del proprio programma da parte di altri. Le principali convenzioni prevedono che le classi siano composte da nomi che iniziano per lettera ..........................................................................; i metodi da .......................................................................... che iniziano con lettera .................................................................................., mentre gli attributi sono .................................................................................. che iniziano per lettera ................................................................................... 6 Una variabile individua una ........................................................................................................................... 7 Una variabile possiede un .................................................................................. e un ................................................................................... 8 L’operazione che rende utilizzabile una variabile è la ................................................................................... 9 Per impostare il valore di una variabile si utilizza il carattere ................................................................................... 10 L’utilizzo di variabili non inizializzate produce un ................................................................................ in ................................................................................. 11 Il .................................................................................. di una costante non può essere cambiato e ogni tentativo di fare ciò produce un .................................................................................. di ................................................................................... 12 Il sufwsso ‘f’ identiwca costanti letterali di tipo ................................................................................... 13 Il sufwsso ‘L’ identiwca costanti letterali di tipo ................................................................................... 14 Un tipo non assegnato è identiwcato dalla parola chiave ...................................................................................
45
UdA 1
La sintassi Java e l’ambiente di sviluppo
Verifichiamo le competenze g Problemi 1 Crea una classe Java che consenta di leggere 3 numeri e stamparne la somma. 2 Crea una classe Java che consenta di leggere 3 numeri e stamparne il prodotto. 3 Crea una classe Java che consenta di leggere 2 numeri e stamparne la differenza. 4 Crea una classe Java che consenta di leggere 2 numeri (a e b). Dopo aver effettuato la divisione a/b stampane il quoziente e il resto. 5 Crea una classe Java che consenta di leggere 2 numeri (a e b). Dopo aver effettuato la divisione a/b stampane il risultato. 6 Crea una classe Java che consenta di leggere e stampare il contenuto di una variabile per tutti i tipi di variabili che hai imparato a dichiarare in questa lezione. 7 Crea una classe Java che consenta di leggere 2 numeri (n ed m) quindi stampi la radice n-esima del numero m utilizzando l’elevamento a potenza con Math.pow(base,esponente).
46
Le operazioni sui dati
lezione
Lezione 4
4
Le operAzioni sUi dAti
in qUestA Lezione impAreremo...
• a utilizzare i principali operatori aritmetici e logici • a convertire i dati nei vari tipi
■ Gli operatori aritmetici Gli operatori matematici consentono di eseguire le principali operazioni, quali per esempio la somma, la sottrazione, la divisione, la moltiplicazione e l’elevamento a potenza. Altre operazioni matematiche, quali per esempio la radice quadrata, necessitano di funzioni matematiche, che verranno illustrate più avanti. La tabella che segue mostra i principali operatori matematici di Java, con a fianco la loro descrizione, ai quali vanno aggiunti gli ◀ operatori speciali di divisione ▶: ◀ Operatori speciali di divisione Gli operatori speciali di divisione consentono di calcolare il quoziente di una divisione con il casting sulla divisione anteponendo (int) all’operazione stessa, mentre il resto lo otteniamo con l’operatore di percentuale %. ▶ operatore
descrizione
+
Somma i due operandi
–
Sottrae il secondo operando dal primo
*
Moltiplica i due operandi
/
Divide il primo operando per il secondo, il risultato è decimale
%
Ottiene il resto della divisione tra il primo operando e il secondo operando
■ Le conversioni Ciascun operatore deve operare con tipi di dati omogenei. Quando vogliamo operare con variabili di tipo similare, per esempio per sommare una variabile di tipo int a una di tipo short, avviene automaticamente una conversione: il valore short viene promosso a int, in modo da eseguire l’operazione su dati omogenei. Questo concetto è chiamato promozione numerica e viene eseguito da Java in modo automatico tra tipi omogenei, quindi interi con interi oppure decimali con decimali.
47
UdA 1
La sintassi Java e l’ambiente di sviluppo
Il meccanismo di promozione numerica interviene anche quando si tenta di assegnare una variabile a un’altra. Per esempio, nel codice seguente un valore intero viene memorizzato in un long; in questo caso avviene una conversione automatica:
◀ Il casting è consentito tra due tipi primitivi oppure tra due oggetti legati da un legame di ereditarietà. Sono quindi vietati i casting tra: ◗ Tra un tipo primitivo e un oggetto ◗ Tra due oggetti senza legame di ereditarietà Il casting può essere implicito o esplicito. Nel primo caso la conversione di tipo viene eseguita automaticamente in quanto si tratta di una operazione sicura. Nel secondo caso è il programmatore che, con cognizione di causa, forza la conversione in quanto potrebbe risultare pericolosa in termini di perdita di dati. ▶
Il percorso inverso non è però automatico. Per esempio, il seguente codice produce un errore di compilazione:
In questo caso il compilatore ci avverte che può esserci una perdita di precisione: visto che il tipo di dato long può contenere valori più grandi di quelli memorizzabili in un int, Java chiede che il programmatore specifichi esplicitamente che l’operazione scritta nel programma sia effettivamente quello che intendeva realizzare (e non, per esempio, un banale errore di programmazione). L’operazione che definisce una conversione esplicita di dati tra tipi compatibili prende il nome di ◀ casting ▶. Il casting viene specificato nel codice utilizzando le parentesi tonde, che racchiudono il tipo di dato a cui deve essere convertita l’espressione che segue. Per esempio:
la variabile l viene convertita in un valore intero. Si noti che il casting è possibile solo tra tipi di dati compatibili: non è possibile per esempio eseguire la conversione tra una stringa e un numero, oppure un carattere in un booleano. In generale, numeri reali e interi, di qualsiasi dimensione, sono convertibili tra di loro, ma non con altri tipi.
Le conversioni tra numeri e stringhe Può capitare, però che sia necessario ottenere il valore di un numero contenuto in una stringa, in modo da poterlo assegnare a una variabile di tipo numerico, al fine di produrre una elaborazione matematica. Per esempio: String numero = “123.45”;
Per ottenere un valore double a partire da una stringa è necessario utilizzare la classe Double presente nel package java.lang e richiamare il metodo parseDouble(): double numeroDouble = Double.parseDouble( numero );
Il metodo ritorna il valore decodificato, ma se la stringa contiene valori non validi (per esempio contiene delle lettere), viene generato un errore, sollevando una eccezione di tipo NumberFormatException. La gestione delle eccezioni sarà oggetto di discussione più avanti nel testo. Per ciascun dato primitivo esiste la corrispettiva classe che ne rappresenta il tipo:
48
Boolean
Byte
Double
Long
Short
Void
Float
Integer
Le operazioni sui dati
Lezione 4
Quasi tutte queste classi contengono un metodo specifico di conversione a partire da una stringa: Byte
parseByte()
Double
parseDouble()
Float
parseFloat()
Integer
parseInteger()
Long
parseLong()
Short
parseShort()
Ovviamente ciascuna conversione viene eseguita tenendo in considerazione la dimensione del tipo di dato. Per esempio, un valore intero molto lungo potrebbe generare un errore se si tenta di convertirlo con Byte.parseByte(), ma la conversione potrebbe venir completata con successo con Long. parseLong(). esempio
4
Ti indovino il numero
Il programma deve indovinare il numero pensato dall’utente. Dopo aver chiesto all’utente di pensare un numero, comunica all’utente le operazioni che deve effettuare, al termine indovina il risultato finale, cioè il numero che sta pensando il giocatore. La sequenza di istruzioni per l’utente che il programma deve mostrare sono: ◗ pensa un numero; ◗ raddoppialo; È semplice verificare, mediante il programma, ◗ aggiungi a tale numero 12; come non esistano trucchi: il risultato infatti è ◗ calcola la metà del risultato; sempre uguale a 6, indipendentemente dal va◗ sottrai tale valore dal totale calcolato; lore pensato. ◗ Il risultato è: 6! Il programma per prima cosa legge il numero pensato dal giocatore nella variabile numPensato, quindi prima mostra all’utente le operazioni che deve eseguire e poi le calcola. Il risultato dei calcoli viene effettuato nella variabile numCalcolato, che per correttezza viene visualizzata alla fine. Il codice è quello a fianco: ▶
49
UdA 1
La sintassi Java e l’ambiente di sviluppo
■ Gli operatori di confronto e logici Un’altra importante categoria di operatori è quella che consente di confrontare valori diversi, per capire, per esempio, se un determinato valore è maggiore, o uguale a un altro. Gli operatori di confronto ritornano un valore booleano, indicano cioè se l’espressione specificata è vera o falsa. Per esempio: 15 > 12 vale true. Gli operatori di confronto sono riassunti nella tabella seguente: operatore
significato
==
Uguaglianza
!=
Diversità
Maggiore di
=
Maggiore o uguale a
&&
And
||
Or
Per esempio:
■ L’operatore ternario L’◀ operatore ternario ▶ si aspetta tre espressioni e ne valuta la prima, se è vera, ritorna la seconda espressione, se è falsa, ritorna la terza. L’operatore ternario possiede la sintassi seguente:
◀ Operatore ternario Consente di sostituire le istruzioni di tipo if then else qualora i due rami possiedano una sola istruzione. ▶
espressione1 ? espressione2 : espressione3; Nel codice di esempio indicato di seguito vogliamo confrontare due variabili a e b, se a>b, viene ritornata la stringa “a maggiore di b”, altrimenti viene ritornata la stringa “b maggiore di a”: String risultato = (a>b) : “a maggiore di b” : “ b maggiore di a “
■ Gli operatori di incremento e decremento Un’operazione che spesso viene utilizzata nei programmi per computer è l’incremento delle variabili di una unità, così come il decremento. Le operazioni di incremento o di decremento vengono usate molto spesso per effettuare dei conteggi e vengono applicate ai contatori. L’istruzione di incremento classica si esprime nel modo seguente: i = i +1;
Per abbreviare questa operazione di incremento unitario è possibile utilizzare due operatori specifici, chiamati operatori di pre o post incremento/decremento. Si tratta dei simboli del doppio più 50
Le operazioni sui dati
Lezione 4
(++) per l’incremento e del doppio meno per il decremento (– –). Questi operatori, rispettivamente, aumentano e diminuiscono una variabile di una unità. Per esempio, il codice sopra riportato può essere riscritto in questo modo: i++;
■ Le operazioni sui bit I tipi di dati interi vengono immagazzinati in memoria in formato binario; il inguaggio Java dispone di una serie di operatori che permettono di manipolare i singoli bit di una variabile intera; questi sono riassunti nella tabella seguente: operatore
significato
~
Negazione
&
And
|
Or
^
Or esclusivo
>
Scorrimento a destra
>>>
Scorrimento a destra senza segno
I primi quattro operatori sono banali, ed eseguono semplici operazioni logiche sugli operandi. Per esempio:
Gli operatori di scorrimento sono leggermente più complessi; si occupano in particolare di far scorrere a destra o sinistra la disposizione dei bit all’interno di una variabile intera. Il primo operando è il dato da elaborare, mentre il secondo è un valore intero che specifica la quantità di bit da spostare. Per esempio:
L’operazione a = 0.
84
2
LA OOP DI JAVA
UNITÀ DI APPRENDIMENTO L 1 La programmazione object oriented
L 5 derivazione ed ereditarietà
L 2 i package e le classi
L 6 gli array
L 3 il ciclo di vita degli oggetti
L 7 interfacce e casting tra oggetti
L 4 Le variabili reference e l’istanza delle classi
OBIETTIVI
ATTIVITÀ
• Risolvere un problema individuando gli oggetti e la loro interazione secondo la OOP
• Utilizzare gli oggetti e le classi nella programmazione
• Capire il ruolo dell’istanza delle classi • Conoscere il ciclo di vita degli oggetti • Dewnire una classe attraverso i suoi attributi e i suoi metodi
Info Nella cartella AU02 del CD-ROM allegato al volume sono presenti i progetti e i file sorgenti utili per questa unità di apprendimento.
• Utilizzare la derivazione e l’ereditarietà tra le classi • Applicare gli oggetti e l’ereditarietà alle situazioni operative • Applicare gli array alle diverse situazioni di programmazione
UdA 2
La OOP di Java
lezione
1
LA progrAmmAzione obJect oriented in qUestA Lezione impAreremo...
• a capire la differenza tra classe e oggetto • a definire una classe per rappresentare un elemento del sistema • a comprendere i concetti di incapsulamento e polimorfismo
■ La programmazione a oggetti Java è un linguaggio ◀ orientato agli oggetti ▶ ◀ Orientato agli oggetti Lo sono i (object oriented). linguaggi che consentono di creare La programmazione orientata agli oggetti (OOP), nuovi tipi, definendo quindi nuove vuole realizzare il concetto di astrazione. Alcuni classi, le quali potranno essere utilizlinguaggi hanno un’astrazione minore, come per zate per creare il programma. ▶ esempio il linguaggio assembly che è strettamente legato alla macchina. Java appartiene invece ai linguaggi con maggiore astrazione che forniscono concetti di programmazione a livello più alto, utilizzati dal programmatore per sviluppare le applicazioni. Mediante la programmazione a oggetti dobbiamo ragionare in termini di entità e concetti che rappresentano il problema, di conseguenza l’attenzione maggiore non deve più essere riposta nell’implementazione della soluzione, ma nella descrizione del problema. Nella programmazione orientata agli oggetti il programma non è altro che un insieme di oggetti che interagiscono reciprocamente scambiandosi messaggi, al fine di realizzare le funzionalità richieste. Gli oggetti definiscono le entità del sistema e i concetti presenti nel dominio del problema. Dati
Dati
Funzioni
Dati
Funzioni
Funzioni
Funzioni
Funzioni
Negli oggetti, dati e algoritmi sono contenuti all’interno della stessa entità.
86
La programmazione object oriented
Lezione 1
La caratteristica peculiare degli oggetti è che incorporano sia le informazioni che i comportamenti che devono assumere. La programmazione orientata agli oggetti si basa su alcuni concetti chiave, una sorta di glossario di termini che viene utilizzato quando si parla di oggetti, come polimorfismo, ereditarietà, incapsulamento, invio dei messaggi, attributi e metodi.
■ L’oggetto Secondo la definizione di ◀ Booch ▶ un oggetto è un elemento che possiede uno stato, un comportamento e una identità. ◀ Booch Grady Booch è un informatico statunitense noto soprattutto per aver contribuito alla definizione dello Unified Modeling Language (UML) insieme a James Rambaugh e Ivar Jacobson (i “tres amigos”) e per aver definito molti degli attuali principi della programmazione a oggetti. ▶
Lo stato è rappresentato dagli attributi o proprietà, mentre il comportamento dai metodi: i primi definiscono i dati che l’oggetto potrà contenere o elaborare mentre i secondi rappresentano le operazioni che l’oggetto sarà in grado di eseguire. La figura seguente mostra uno schema, molto semplificato, di come può essere definito l’oggetto Animale. A esso sono stati associati gli attributi zampe e posizione e i due metodi, muoviti() e mangia(). Animale zampe:int posizione:int muoviti() mangia() Come possiamo notare il comportamento dell’oggetto Animale sarà determinato dai metodi che hanno lo scopo di modificarne lo stato interno, per esempio il metodo muoviti() consentirà di modificare il contenuto dell’attributo posizione, in quanto a ogni movimento dell’animale ne cambia la posizione. Gli attributi indicano rispettivamente il numero di zampe possedute e la posizione, mentre i metodi consentono le due azioni di spostamento e nutrimento. In sintesi potremmo affermare che la programmazione a oggetti è una metodologia di programmazione in cui il problema da risolvere viene affrontato rappresentando la realtà come un insieme di elementi interagenti fra loro, chiamati oggetti, ciascuno in grado di eseguire una serie di azioni specifiche. Per indicare a un oggetto di eseguire un’azione dobbiamo per così dire inviargli un messaggio. A seconda del messaggio inviato, l’oggetto eseguirà (o invocherà) il metodo relativo. Nella riga di codice seguente inviamo un messaggio all’oggetto Animale indicandogli di correre: animale.corri();
Come avrete notato viene usato l’operatore punto (.) per separare il nome dell’oggetto dal metodo, in questo esempio viene inviato il messaggio “corri”, che ha il risultato di invocare il metodo omonimo. Quando il sistema incontra questa riga di codice, identifica il tipo della variabile Animale e rileva il nome del messaggio da inviare, scritto dopo il punto; da qui in poi, ricerca nell’elenco dei metodi del tipo Animale quello corrispondente al messaggio indicato e passa l’esecuzione del codice a quel metodo.
87
UdA 2
La OOP di Java
■ La classe Prima di tutto è necessario fare una distinzione. Nella programmazione a oggetti una ◀ classe ▶ non è altro che lo schema dal quale l’oggetto viene creato, mentre invece l’oggetto è un elemento con una propria identità univoca.
◀ Classe La classe è la definizione della struttura che l’oggetto avrà, in termini di attributi e metodi. ▶
La classe è uno schema necessario per produrre una categoria di oggetti identici nella loro struttura, possiamo dire cha una classe ne costituisce il prototipo e ne descrive le caratteristiche. Possiamo anche immaginare una classe come una fabbrica di istanze in quanto ne possiede lo schema e la tecnica di produzione:
classe
istanze
Da una classe possono essere creati un numero infinito di oggetti, inoltre è sull’oggetto che il programmatore potrà modificarne il contenuto degli attributi oppure eseguirne i metodi. La classe, che ha una struttura statica, ha lo scopo di definire quale forma (in termini di attributi e metodi) avranno gli oggetti o, se vogliamo, ha lo scopo di fornire uno stampo generico entro il quale definire diversi oggetti. Gli oggetti possono essere molteplici all’interno del processo, ma tutti avranno la stessa forma: quella definita dallo schema dato dalla classe. Ciascun oggetto è però una entità a se stante, dotato di una specifica area di memoria dedicata alla memorizzazione del proprio valore degli attributi. ▶
Oggetto 2 Oggetto 1
Oggetto 3 Classe (modello)
Lo stato di un oggetto Lo stato di un oggetto è rappresentato dall’insieme dei valori degli attributi che individuano i dati su cui i metodi possono operare. Per esempio, lo stato di un oggetto quale un ascensore potrebbe includere la posizione attuale della cabina (numero di piano) e una eventuale prenotazione da parte
88
La programmazione object oriented
Lezione 1
di utenti, con l’indicazione del piano da cui proviene. Ovviamente, è anche possibile interagire con i dati di altri oggetti, ma il canale privilegiato di accesso ai dati è con il proprio stato interno.
■ Il paradigma della OOP Come abbiamo visto all’inizio di questa lezione la programmazione a oggetti si basa su tre paradigmi fondamentali: ◗ incapsulamento; ◗ ereditarietà; ◗ polimorfismo.
Incapsulamento Secondo il paradigma della OOP, l’incapsulamento prevede che tutto quello che riguarda un oggetto deve essere necessariamente definito al suo interno: teoricamente l’utilizzatore dell’oggetto dovrebbe accedere agli attributi unicamente attraverso i metodi, evitando di accedervi direttamente. Per esempio nel caso di un oggetto Pixel dello schermo, che possiede l’attributo colore, per modificarne il colore dovremo utilizzare nel codice l’ipotetico metodo settaColore() evitando di scrivere direttamente il nuovo colore nell’attributo dell’oggetto. Teoricamente, secondo il paradigma dell’incapsulamento, ciascun attributo disponibile all’utilizzatore dovrebbe avere un corrispondente metodo che ne possa impostare o leggere il valore.
Con l’incapsulamento si ottiene l’information hiding, ovvero il processo di nascondere il più possibile i dettagli di implementazione al fine di ridurre la complessità di un oggetto. Il programmatore può quindi focalizzarsi sul nuovo oggetto senza preoccuparsi dei dettagli di implementazione, avendo dunque a che fare con classi più semplici. Un oggetto dotato di buon incapsulamento possiede un’alta information hiding, poiché espone all’esterno pochi elementi. Quando un altro oggetto, nel programma, dovrà fare uso dell’oggetto ben incapsulato, utilizzerà quei pochi elementi visibili dall’esterno: con pochi punti di contatto, è più semplice cambiare l’uno o l’altro oggetto, in quanto è necessario cambiare meno codice. Per esempio, un’autovettura dotata di cambio manuale dispone di una serie di comandi che il conducente deve utilizzare per pilotarla. Il pedale dell’acceleratore comanda il funzionamento del motore, mentre quello della frizione consente di staccare la forza motrice dalle ruote, consentendo il cambio di marcia. Ciò avviene utilizzando la leva del cambio, con cui il conducente seleziona il rapporto da applicare. I pedali, la leva del cambio e il volante sono tutti elementi che compongono l’interfaccia della macchina verso il pilota. Nel caso di cambio automatico, l’interfaccia si semplifica: spariscono il pedale della frizione e la leva del cambio; la macchina è quindi guidabile con un numero inferiore di comandi. Nel caso di cambio manuale possiamo dire che il conducente e l’autovettura hanno un alto accoppiamento, ovvero possiedono un significativo numero di elementi di connessione l’uno con l’altro. Nel caso del cambio automatico, l’accoppiamento è minore, in quanto gli elementi di interfaccia sono in numero inferiore. Gli oggetti ben incapsulati possiedono un’alta information hiding, e presentano un basso accoppiamento, rendendo più facile il riutilizzo del codice: oggetti estremamente correlati sono più complessi da modificare e non risultano facilmente utilizzabili in altre parti di applicazione.
89
UdA 2
La OOP di Java
Ereditarietà
◀ Sottoclassi Le sottoclassi o
Mediante l’ereditarietà possiamo definire una gerarclassi derivate, ereditano tutte le caratteristiche della superclasse. chia di oggetti che condividono proprietà e metodi. Gli attributi e i metodi definiti nelI discendenti ereditano tutto ciò che appartiene agli la superclasse risultano dunque antenati, con la possibilità di aggiungere ulteriori presenti anche nella sottoclasse, proprietà e metodi che ne caratterizzano il comporanche se il codice per definirli non tamento senza ripartire da zero ogni volta. L’erediè fisicamente presente (è infatti tarietà è quella proprietà che consente di estendere presente nella classe base). ▶ nuove classi da quelle esistenti. La classe di partenza è detta classe base o superclasse, mentre quella che eredita viene denominata sottoclasse o classe derivata. Per esempio, è possibile definire la classe base Animale, con attributi generici come per esempio la Forma o il Peso e metodi altrettanto generici come per esempio Nasce(), Comunica() ecc. dalla quale estendere nuove classi (◀ sottoclassi ▶) che ne ereditano le proprietà e i metodi iniziali: Nome della classe: Animale Proprietà: Forma Peso Comportamenti: Nasce Si muove Comunica
Possiamo affermare che nella programmazione orientata agli oggetti si programma per differenze, aggiungendo nelle sottoclassi gli elementi distintivi necessari a specializzarle rispetto alle superclassi. L’ereditarietà può essere di due diversi tipi: singola e multipla. Nel primo caso una classe può derivare da una sola superclasse, mentre nel secondo caso possiamo creare una classe derivandola da più superclassi.
Java supporta solo l’ereditarietà singola, mentre in altri linguaggi, come per esempio C++, è invece possibile creare una classe derivandola da più superclassi, in quanto è concettualmente abbastanza complessa, e può portare a problemi logici non indifferenti.
Polimorfismo Il concetto di ◀ polimorwsmo ▶ indica la possibilità di assegnare differenti significati o utilizzi a uno specifico elemento di un’applicazione, siano queste variabili, funzioni o oggetti. Nello specifico, consente a questi elementi di avere più di una forma. ◀ Polimorfismo Termine di origine greca che significa “molte forme” e indica la possibilità di elaborare oggetti in modo diverso lasciando inalterati il loro tipo di dato o la loro classe. Nello specifico, il polimorfismo è l’abilità di ridefinire nuovi metodi nelle classi derivate. ▶
90
La programmazione object oriented
Lezione 1
Prendiamo in esame una classe chiamata Poligono, come indicato nella figura seguente: Poligono area ()
Triangolo area ()
Quadrato area ()
Rettangolo area ()
Attraverso il polimorfismo possiamo definire più metodi con lo stesso nome (area()) per ciascuna sottoclasse, come la classe Triangolo, Quadrato o Rettangolo. Indipendentemente da quale sia la forma dello specifico oggetto, la chiamata al metodo area() ritornerà il valore corretto.
■ Gerarchia delle classi L’insieme delle classi di un’applicazione assume una ◀ Albero L’albero di ciascuna struttura gerarchica per ◀ albero ▶, dove ciascuna classe e sue derivate prende il classe possiede almeno un genitore: la classe base da nome di gerarchia delle classi. ▶ cui deriva. Nel caso di ereditarietà singola, il genitore è solo uno; nel caso di ereditarietà multipla, possono esservi diversi genitori. Nell’immagine abbiamo voluto mostrare un albero di classi, le linee indicano il rapporto gerarchico esistente, per esempio la classe MenuItem deriva dalla classe MenuComponent:
L’albero completo delle classi di Java è visibile nel sito http://falkhausen.de/en/diagram/diagram.html. 91
UdA 2
La OOP di Java
Verifichiamo le conoscenze g Esercizi a scelta multipla 1 Gli oggetti incorporano: a) informazioni
b) programmi
2 Secondo Booch, un oggetto possiede: a) stato c) comportamento b) informazioni d) identità
c) interfacce utente
d) comportamenti
e) universalità f) funzione specifica
3 Una classe definisce: a) livelli b) attributi
c) funzioni d) metodi
e) array
4 La classe è una struttura: a) dinamica b) statica
c) multipla d) indiretta
e) diretta f) singola
5 Una classe B{ void m2() } derivata da A{ void m2(); void m3(); } contiene i metodi: a) m3 c) m2 ed m3 e) m1, m2 ed m3 b) m2 d) m1 ed m2 6 Ciascun oggetto: a) condivide la memoria con altri oggetti
b) ha una sua area di memoria specifica
7 Lo stato: a) è definito dal numero di attributi di un oggetto b) è definito dal numero di metodi di un oggetto
c) è definito dal valore degli attributi di un oggetto
8 La classe di partenza è detta: a) classe base c) involuta b) classe inizio d) superbase
e) superclasse
9 L’ereditarietà può essere: a) diretta b) indiretta
c) prossima d) singola
e) multipla f) trasversale
g) astratta h) statica
g Esercizi di completamento 1 Un oggetto con una buona ................................................................................... nasconde all’esterno la propria implementazione. 2 Con la ................................................................................... si creano nuove classi utilizzando quelli esistenti. 3 Con il polimorwsmo è possibile ridewnire ................................................................................... nelle classi derivate. 4 L’insieme delle classi di una applicazione prende il nome di come un ....................................................................................
...................................................................................
e si rappresenta
5 Lo scopo della programmazione è quello di realizzare il concetto di astrazione ................................................................................... 6 La programmazione orientata agli oggetti considera più importante la ................................................................................... del problema, piuttosto che l’................................................................................... della soluzione.
92
La programmazione object oriented
Lezione 1
7 Un programma è un insieme di ................................................................................... che interagiscono tra di loro. 8 La classe è lo ................................................................................... da cui viene creato un oggetto. 9 L’invio di un messaggio a un oggetto comporta l’invocazione del ................................................................................... relativo. 10 In Java l’invio di un messaggio avviene tramite l’operatore .................................................................................... 11 Nella programmazione orientata agli oggetti è possibile derivare una nuova classe da una esistente: questa caratteristica è detta .................................................................................... 12 Java dispone dell’ereditarietà ......................................................................... perché concettualmente ........................................................................... 13 Polimorwsmo signiwca .............................................................................. e permette di ridewnire nuovi ............................................................................. nelle classi ....................................................................................
Verifichiamo le competenze g Problemi 1 Dewnisci gli attributi e i metodi della classe Video. 2 Dewnisci gli attributi e i metodi della classe MezzoLocomozione e la classe Treno derivata. 3 Dewnisci le classi MemoriaCentrale e Stampante e le interazioni possibili tra di loro. 4 Dewnisci gli attributi e i metodi della classe Lampada. 5 Dewnisci la gerarchia degli oggetti di tipo CapoVestiario. 6 Indica quali membri può ereditare la classe Televisore dalla classe Video. 7 Dewnisci gli attributi e i metodi della classe Libro. 8 Dewnisci gli attributi e i metodi della classe Aereo. 9 Dewnisci gli attributi e i metodi della classe Animale e la classe Gatto derivata. 10 Dewnisci gli attributi e i metodi della classe Treno. 11 Dewnisci gli attributi e i metodi della classe Rettangolo e della classe Quadrato derivata. 12 Dewnisci gli attributi e i metodi della classe Telefono. 13 Dewnisci gli attributi e i metodi della classe MezzoDiTrasporto e delle classi derivate Automobile, Treno e CarroFunebre. 14 Dewnisci gli attributi e i metodi della classe MotoGP. 15 Dewnisci gli attributi e i metodi della classe Lampada e della classe derivata Lampione. 16 Dewnisci gli attributi e i metodi della classe Abito da sposa. 17 Dewnisci gli attributi e i metodi della classe Libro. 18 Dewnisci gli attributi e i metodi della classe Gallina. 19 Dewnisci gli attributi e i metodi della classe Lavatrice.
93
UdA 2
La OOP di Java
lezione
2
i pAckAge e Le cLAssi
in qUestA Lezione impAreremo...
• a creare un package e definire una classe • a definire attributi e metodi • a passare parametri ai metodi e implementare l’overloading
■ I package I package rappresentano un meccanismo per organizzare classi Java all’interno di sottogruppi ordinati. Si tratta di uno strumento assai utile per organizzare le classi in modo logico e ordinato sotto a un unico nome, in tal modo possiamo associare un insieme di classi dell’applicazione in gruppi separati, per organizzare meglio il lavoro. Le classi principali della libreria di base di Java sono raccolti nel package java.lang. I package consentono anche di creare classi pubbliche con nomi uguali, per fare questo è sufficiente collocarle in package diversi. Il nome di un package deve essere univoco, non possono coesistere due package con lo stesso nome utilizzati contemporaneamente nel programma. Il nucleo di Java contiene solo le parole chiave per la costruzione delle classi, i commenti, i normali costrutti if, switch, while, do-while, for, etichette, break, continue e return, tutto il resto è implementato nei package del linguaggio, comprese le normali primitive di Input e di Output. Per collocare una classe all’interno di un package dobbiamo usare la parola chiave package seguita dal percorso che contiene la classe stessa. L’esempio che segue mostra come collocare una classe all’interno di un package. L’istruzione package non deve assolutamente essere preceduta da nessuna linea di codice, se una classe non viene definita come appartenente a un package, viene automaticamente assegnata a un package di default.
esempio
1
Creare un nuovo package
Innanzi tutto scriviamo il codice della classe che intendiamo collocare in un nuovo package, in questo caso il file lo chiamiamo Scrivi.java. 94
I package e le classi
Lezione 2
Nel codice possiamo notare che è stata aggiunta una riga a inizio codice che indica il percorso del package: package Java.ºles;
Il codice completo è il seguente: Esiste una corrispondenza biunivoca fra il nome del package e la posizione nel file system delle classi del package, per esempio un package di nome pippo richiede che tutte le sue classi si trovino in una cartella (directory) di nome pippo.
Dopo aver definito il nome di un package, deve essere creata su disco la struttura di cartelle che rappresenti la gerarchia definita dai nomi. In questo caso il percorso Java\wles è stato creato nella cartella C:\. Come possiamo notare le cartelle sono state scritte all’interno del codice utilizzando il punto al posto dello slash.
Adesso dobbiamo comunicare alla Java Virtual Machine dove è localizzato il package definito, utilizzando la variabile di ambiente CLASSPATH, inserendone il percorso completo fino alla cartella di primo livello nella gerarchia dei package. Nel nostro esempio il percorso è c:\Java\wles:
95
UdA 2
La OOP di Java
Adesso dobbiamo aggiungere il percorso c:\Java\ wles alla variabile d’ambiente PATH per indicare al compilatore Java dove è presente il file .java da compilare: ▶ A questo punto possiamo compilare il file mediante il comando da linea di comando, tenendo presente che per compilare una classe che fa parte di un package dobbiamo posizionarci nella cartella superiore del percorso del package (C:\) e da lì invocare il compilatore con il percorso completo della classe (javac Java\wles\scrivi.java): ▶ Adesso per eseguire la nostra classe (Scrivi.class) dobbiamo posizionarci nella cartella superiore del percorso del package (C:\) e da lì eseguire la classe indicando il percorso completo della classe usando il punto come separatore al posto dello slash (java Java.wles.Scrivi): ▶
• Utilizzare i package
Prova adesso! APRI LA CLASSE Scrivi.java dell’Esempio 1 1 Crea una nuova classe che importi il package Esempio1.
■ Le classi Una classe viene definita in Java attraverso la parola chiave class, seguita dal nome della classe che si intende dichiarare e dalle parentesi graffe che racchiudono il codice della classe stessa. Per esempio, per dichiarare la classe Animale è necessario scrivere il codice seguente: ◀ Visibilità di default La visibilità di default si ha quando non vengono utilizzati modificatori di visibilità, cioè quando il nome della classe non è preceduta da alcuna parola chiave. Una classe con visibilità di default è visibile solo all’interno del package in cui è dichiarata. ▶
Gli attributi e i metodi presenti in una classe devono essere scritti all’interno delle parentesi graffe, in base al concetto di incapsulamento il linguaggio Java dispone di una serie di parole chiave che consentono di aumentarne o ridurne la visibilità di classi, metodi e attributi dal resto del programma. Per esempio, una classe potrebbe essere visibile nel solo package dove è stata dichiarata, oppure a tutte le sottoclassi. Quando si dichiara una classe come nell’esempio precedente, questa ha ◀ visibilità di default ▶. 96
I package e le classi
Lezione 2
Una classe pubblica è definita attraverso il modificatore public e possiede la caratteristica di essere visibile da tutto il resto del codice. Per esempio, per dichiarare pubblica la classe Animale, scriviamo: In tal modo la classe Animale sarà accessibile da ogni punto del programma.
■ Gli attributi di una classe La parola chiave class serve per iniziare la dichiarazione di una classe ed è seguita dal nome della classe stessa. Tra le parentesi graffe possiamo inserire tutto ciò che la classe conterrà, cioè i suoi ◀ attributi ▶ e metodi. Possiamo creare anche classi che contengano solo attributi oppure soltanto metodi. La sintassi con cui creare classi e metodi all’interno di una classe è la seguente: class {
{
◀ Attributi Gli attributi vengono anche chiamati campi e possono essere rappresentati da un dato primitivo, oppure da un oggetto. ▶
L’esempio che segue mostra una classe chiamata Automobile, all’interno della quale sono stati dichiarati alcuni attributi e un metodo:
Gli attributi devono essere dichiarati all’interno del blocco di una classe, ma esternamente rispetto ai metodi. Vengono anche chiamati variabili di istanza in quanto ogni oggetto, che è una istanza di una classe, possiede le proprie variabili nelle quali memorizza i valori dei propri attributi. Gli attributi devono essere dichiarati esattamente come le variabili, indicando la visibilità, il tipo e infine il nome identificativo. In questo caso alla classe Animale, viene dichiarato un attributo di nome zampe e di tipo intero: Per quanto riguarda sintassi e inizializzazione degli attributi dobbiamo rifarci a quanto enunciato nell’unità di apprendimento precedente riguardo alla variabili.
97
UdA 2
La OOP di Java
Visibilità degli attributi Per quanto riguarda la visibilità degli attributi possiamo stabilire un livello di visibilità che stabilisce quali sono le classi che possono accedere all’attributo per leggerne o modificarne il valore. I livelli di visibilità, in ordine che va dal meno al più restrittivo, sono i seguenti: ◗ ◗ ◗ ◗
public protected package private
◗ public indica che l’attributo è accessibile a qualsiasi altra classe e a qualunque punto della classe. Se, per esempio, si desidera che la classe Animale esponga all’esterno il numero delle zampe, possiamo scrivere:
◗ protected indica che l’attributo è accessibile alle classi dello stesso package e alle sottoclassi, anche di altri package, della classe che contiene l’attributo. In altre parole, un attributo protetto definito nella classe Animale è visibile dalle sottoclassi di Animale e dalle classi contenute nel package in cui è definita Animale. Per esempio:
◗ package è il livello di visibilità di default, indica che è visibile solo alle classi che appartengono allo stesso package. Questo significa che l’attributo è accessibile da qualsiasi classe contenuta nello stesso package dove è definita la classe che contiene l’attributo. Per esempio, l’attributo zampe seguente è accessibile a tutte le classi presenti nel package it.hoepli:
◗ private indica che l’attributo non è accessibile alle altre classi, ma solo alla classe in cui è stato dichiarato. In pratica questo livello nasconde l’attributo all’interno dell’oggetto secondo i principi dell’incapsulamento, e più precisamente dell’information hiding.
98
I package e le classi
Lezione 2
Possiamo così riassumere i diversi livelli di visibilità:
public
esempio
2
protected
package
private
Progettare gli attributi di una classe conto corrente
In questo esempio vogliamo progettare una classe per la memorizzazione dei dati essenziali del conto corrente. Un conto corrente possiede un numero di conto (di tipo intero), un intestatario (di tipo stringa) e un saldo (di tipo double). Il codice è il seguente:
Zoom su... iniziALizzAzione di Un AttribUto Ciascun attributo di una classe, quando da questa viene creato un oggetto, viene inizializzato con un valore nullo: i numeri interi e reali vengono azzerati, i booleani impostati a falso e tutti i riferimenti a oggetti vengono posti a null. Per esempio, un oggetto di tipo Animale appena creato:
possiede tutti gli attributi impostati a questi valori: Attributo
valore
zampe
0
specie
null
staCorrendo
false
Per inizializzare un attributo a un determinato valore possiamo comportarci esattamente come per le variabili, specificando il valore direttamente nella dichiarazione, utilizzando l’operatore di assegnazione (=). Per esempio, se si desidera che la classe Animale abbia come numero di zampe il valore di default di 4, possiamo scrivere:
99
UdA 2
La OOP di Java
■ I metodi Un ◀ metodo ▶ può ricevere dalla propria interfaccia alcuni dati contenuti in variabili dette parametri producendo una variazione dello stato dell’oggetto e può restituire anche un valore di ritorno. ◀ Metodo È quella parte della classe che definisce i comportamenti di un oggetto, ovvero le azioni che esso può eseguire. Un metodo è una porzione di codice che descrive un’operazione eseguibile da un oggetto. ▶
Un metodo produce una variazione dello stato dell’oggetto, eseguendo una elaborazione che utilizza come input sia i parametri che gli vengono passati, sia gli attributi stessi dell’oggetto. Un metodo è caratterizzato dai seguenti quattro elementi: ◗ un livello di visibilità; ◗ il tipo di valore di ritorno; ◗ un nome e un elenco di parametri; ◗ un blocco di istruzioni chiamato spesso implementazione del metodo. La dichiarazione avviene all’interno del blocco della classe che lo contiene secondo la seguente sintassi: () {
}
Il livello di visibilità di un metodo indica quali sono le classi che possono richiamare il metodo analogamente a quanto visto per gli attributi. L’insieme del tipo e del nome di un metodo rappresenta quella che viene chiamata la firma del metodo. È l’unica parte del metodo che è obbligatorio indicare. In una stessa classe non possono esistere più metodi con la stessa firma, tuttavia in virtù dell’overloading, possiamo dichiarare nella stessa classe metodi con lo stesso identificatore ma firma diversa. Questo può avvenire variando il tipo e il numero di parametri del metodo come vedremo più avanti. Il tipo restituito può essere uno tra i tipi primitivi oppure un tipo oggetto, se il metodo non restituisce alcun valore può essere usato il tipo che indica nessun valore, cioè la parole chiave void. Per restituire un valore utilizziamo la parole chiave return seguita dal valore da restituire, che provoca anche la conclusione dell’esecuzione del metodo. Per esempio, il metodo cambiaVelocità() della classe Automobile ha come parametro un numero intero (accelero) che rappresenta l’accelerazione impartita alla vettura, valore sommato algebricamente all’attributo velocita e che viene restituito dal metodo:
Il metodo cambiaVelocita() restituisce un valore di tipo intero che rappresenta la velocità dell’automobile.
100
I package e le classi
Lezione 2
L’istruzione return può essere scritta senza farne seguire alcun valore di ritorno. Il metodo seguente, che non restituisce alcun valore deve essere dichiarato di tipo void:
Il passaggio dei parametri I parametri sono utilizzati per comunicare informazioni tra il metodo da chiamare e il codice che esegue la chiamata. Innanzi tutto dobbiamo chiarire che per far eseguire un metodo (◀ invocare un metodo ▶) si deve istanziare l’oggetto della classe nella quale è stato dichiarato il metodo stesso. Una volta che l’oggetto è stato istanziato la chiamata avviene nel seguente modo: .(parametri);
◀ Invocare un metodo Nei linguaggi object oriented, riguardo ai metodi, si usa il termine invocare al posto di chiamare. Tuttavia il significato è lo stesso, l’invocazione del metodo prevede che il codice di cui esso è formato venga eseguito. ▶
Il termine istanziare indica la realizzazione concreta di una classe, cioè un oggetto. Un’istanza è un particolare oggetto di una determinata classe. Ogni istanza è separata dalle altre, ma condivide le sue caratteristiche generali con gli altri oggetti della stessa classe. I metodi definiti in una classe sono disponibili automaticamente in tutti gli oggetti di quella classe e possono essere invocati inviando un messaggio all’oggetto attraverso l’operatore punto (.). Per esempio, dopo aver istanziato l’oggetto wat di classe Automobile, per chiamarne il metodo cambiaVelocita(), dobbiamo semplicemente scriverne il nome dell’oggetto seguito dal punto e quindi dal metodo da invocare: in questo caso viene passato il parametro rappresentato dalla variabile tipo intero speed, che vale 5:
In questo esempio speed è un parametro attuale, cioè una variabile passata durante una invocazione di un metodo, mentre i parametri che appaiono nella dichiarazione del metodo sono detti parametri formali. Il metodo definito all’interno della classe presenta, invece, il parametro chiamato accelero come possiamo notare dalla figura seguente:
Il dato contenuto nella variabile speed viene passato quindi alla variabile accelero utilizzabile all’interno del metodo. 101
UdA 2
La OOP di Java
Ciascun parametro di un metodo è di un tipo definito in fase di programmazione e può essere: ◗ un tipo di dato primitivo; ◗ un oggetto. I parametri di tipo primitivo vengono passati sempre per valore. Questo significa che, al momento della chiamata, il valore del parametro attuale viene copiato nel parametro formale. Il risultato è che il parametro formale e il parametro attuale occupano due diverse aree di memoria, pertanto la modifica del contenuto del parametro attuale non si riflette sul parametro formale. I parametri di tipo oggetto vengono, invece, passati per riferimento. In questo caso non viene eseguita una copia dell’oggetto, ma il parametro attuale punterà alla stessa area di memoria dell’oggetto puntato dal parametro formale. esempio
3
Chiamata di un metodo
In questo esempio si definisce una classe che contiene un metodo che riceve un oggetto come parametro. Il metodo mangia(), definito nella classe PezzoScacchi, modifica lo stato dell’oggetto passato come parametro, impostandone un indicatore che rivela che un particolare pezzo è stato mangiato. Questo avviene assegnando all’attributo mangiato il valore true che rappresenta, per ciascun oggetto, se questo particolare pezzo è stato già mangiato, e quindi eliminato dal gioco, o se può essere ancora in gioco. Quando questo metodo viene chiamato, il parametro formale chi punta allo stesso oggetto puntato da pedone. La modifica allo stato dell’oggetto si riflette dunque sia nel codice all’interno del metodo sia nel codice chiamante.
Questo esempio è solo a carattere esplicativo, infatti non può ancora funzionare in quanto mancano molte parti del codice che devono essere ancora sviluppate.
Le variabili locali All’interno di un metodo è spesso necessario implementare algoritmi non banali, all’interno dei quali si devono eseguire elaborazioni intermedie e creare dati temporanei. 102
I package e le classi
Lezione 2
Per questo scopo è possibile utilizzare le ◀ variabili locali ▶. Per esempio, il seguente metodo esegue la media di tre numeri interi, calcolando prima la somma di questi numeri e poi dividendo per tre. Il risultato viene quindi restituito al chiamante attraverso return: ◀ Variabili locali Le variabili locali sono variabili definite all’interno del metodo e utilizzabili solo all’interno dello stesso. ▶
In questo esempio sono state definite due variabili locali: somma e media. A ogni chiamata del metodo, il valore delle variabili viene cancellato.
■ L’overloading Java consente di dichiarare metodi con lo stesso nome ma con parametri diversi, questa caratteristica è chiamata ◀ overload ▶. A volte infatti capita che un metodo sia utile anche a fronte di parametri di tipo diverso o in diverso numero. Il codice dell’esempio che segue mostra come dotare la classe Matematica vista nel codice di esempio del paragrafo precedente di altri metodi media() in grado di elaborare anche parametri numerici in virgola mobile piuttosto che interi. ◀ Overload Il termine è inglese e significa sovraccarico ed è una caratteristica di Java che rappresenta la possibilità di utilizzare un identificatore per riferirsi a diversi elementi nella stessa area di visibilità, applicato ai metodi prende il nome di overload dei metodi. ▶
esempio
4
Metodi e overload
Vogliamo realizzare una classe che disponga di tre metodi per creare un nome di file completo di estensione. Occorre definire tre metodi: il primo metodo riceve solo il percorso del file, il secondo anche un nome per il file e il terzo anche l’estensione. Il nome del file deve essere creato concatenando questi tre elementi. Per esempio, nel caso di percorso seguente: \utenti\io, con nome test e suffisso arc, il nome completo sarà: \utenti\io\ test.arc. Il primo metodo da realizzare è quello che riceve tutti e 3 i parametri: String creaNome(String percorso, String nome, String estensione)
103
UdA 2
La OOP di Java
All’interno di questo viene inserito il codice necessario a eseguire l’elaborazione richiesta; negli altri metodi non si farà altro che richiamare il metodo base passando, al posto di alcuni parametri, delle costanti.
esempio
5
Metodo che calcola se l’anno è bisestile
Il metodo bisestile della classe Anno verifica se l’anno che è stato passato come parametro di tipo intero è bisestile o meno. Per fare questo il metodo restituisce il valore boolean relativo. Per verificare se un anno è bisestile deve essere divisibile per 400 o divisibile per 4 ma non divisibile per 100. Per esempio, l’anno 1996 è un anno bisestile in quanto divisibile per 4 ma non divisibile per 100.
Prova adesso!
• Applicare i metodi • Utilizzare i parametri
CREA IL PROGETTO Esercizio 1 1 Scrivi un metodo giorniMese che riceva i parametri mese e anno e restituisca il numero dei giorni del mese, verificando, per febbraio, se l’anno è bisestile. 2 Verifica la tua soluzione con quella contenuta nel progetto Esercizio1_solux.
104
I package e le classi
Lezione 2
Verifichiamo le conoscenze g Esercizi a scelta multipla 1 Indica quali tra i seguenti nomi di package sono validi per essere utilizzati da un nostro programma. a) java.lang b) javax.swing c) it.hoepli d) it.mariobianchi 2 Un package rappresenta: a) un archivio di classi b) un archivio di metodi c) un raggruppamento logico di classi
d) un raggruppamento logico di metodi e) un raggruppamento logico di attributi
3 Indica dove sono contenuti i package principali di Java: a) java.lang b) java.util.jar c) java-lang
d) it.hoepli
4 Se non si specifica alcun package, il codice del programma: a) rientra nel package di default b) non è accessibile c) è accessibile ma non può essere utilizzato d) è accessibile in fase di compilazione ma non di esecuzione 5 Il nome di una classe: a) non può cominciare per maiuscola b) deve iniziare con una lettera minuscola
c) deve iniziare con una lettera maiuscola d) deve essere completamente maiuscolo
6 Il nome di un metodo: a) non può cominciare per maiuscola b) deve iniziare con una lettera minuscola
c) deve iniziare con una lettera maiuscola d) deve essere completamente maiuscolo
7 Una classe senza modificatore di visibilità: a) è visibile da tutto il programma b) è visibile solo alle sottoclassi
c) è visibile solo dalle classi dello stesso package d) è dichiarata automaticamente di visibilità public
8 Indica cosa rappresenta un attributo: a) un metodo b) un campo
c) un membro
d) una variabile locale
9 Indica quali dichiarazioni del metodo main sono corrette: a) public static void Main(String args[]){...} d) static public main (String argomenti[]) [...] b) public static void main(String argomenti[]) {...} e) public static void main(String argomenti) {...} c) public static void main(String Argomenti[]) {...} 10 Indica quali tra le seguenti dichiarazioni di classe sono corrette: a) public class {...} d) class Auto {...} b) public Class Auto {...} e) Class auto {...} c) public class Auto {...} 11 Un attributo di tipo numerico quando dichiarato ha per default valore: a) null c) indefinito b) 0 d) produce un errore di compilazione 12 Un attributo di tipo String quando dichiarato ha per default valore: a) stringa vuota c) indefinito e) produce un errore di compilazione b) null d) 0
105
UdA 2
La OOP di Java
13 Quale affermazione, riguardo i metodi, tra le seguenti è corretta? a) può eseguire elaborazioni b) produce sicuramente una variazione dello stato dell’oggetto c) possiede sempre parametri d) può produrre una variazione nello stato dell’oggetto 14 Quale affermazione, riguardo i metodi, tra le seguenti è corretta? a) non può restituire un valore di ritorno c) può restituire un valore di ritorno b) restituisce sempre un valore di ritorno d) restituisce un valore di ritorno in caso di errori 15 La firma di un metodo è formata da: a) il tipo del valore di ritorno e il nome del metodo b) il tipo del valore di ritorno, il nome del metodo e l’elenco dei parametri c) il tipo del valore di ritorno e l’elenco dei parametri d) il nome del metodo e l’elenco dei parametri 16 Un parametro di tipo primitivo: a) viene passato per riferimento b) viene passato per valore c) viene passato in modo formale
d) viene passato in modo attuale e) viene passato solo se il metodo è overloading
17 Un parametro di tipo oggetto passato a un metodo: a) può essere letto all’interno del metodo b) non può essere modificato c) ogni variazione del parametro si riflette sul chiamante d) è congelato se il metodo è overloading 18 Le variabili locali: a) sono attributi con visibilità private b) sono dichiarate all’interno di un metodo
c) possono essere viste da altri oggetti d) possono essere solo di tipo primitivo
19 Indica cosa viene visualizzato nelle righe 4 e 13 del programma:
a) b) c) d)
106
20 e 0 0 e 20 0e0 20 e 20
I package e le classi
Lezione 2
g Test vero/falso 1 2 3 4 5
Il package fondamentale di Java è java.lang. È possibile dichiarare un metodo al di fuori del blocco di codice che dewnisce una classe. Il blocco di codice che dewnisce un metodo è delimitato da due parentesi tonde. Il blocco di codice che dewnisce un metodo è delimitato da due parentesi quadre. La seguente dichiarazione del metodo main() è corretta.
public static main(String argomenti[]) {...}
6 Un package è wsicamente una cartella che contiene classi, le quali hanno dichiarato esplicitamente di far parte del package stesso nei rispettivi wle sorgente.
g Esercizi di completamento 1 Per dewnire una classe si utilizza la parola chiave ......................................................................... 2 Una classe può essere dichiarata ........................................................................ e quindi essere vista da ....................................................................... 3 Una classe senza modiwcatore di visibilità si dice che ha visibilità ......................................................................... 4 Un attributo senza modiwcatore di visibilità si dice che ha visibilità ........................................................................ e può essere visto dalle classi presenti nello stesso ......................................................................... 5 Un parametro di tipo oggetto è passato per ......................................................................... 6 Un parametro di tipo primitivo passato a un metodo comporta che il parametro ........................................................................ sia una ........................................................................ del valore iniziale. 7 Un attributo può essere dewnito ................................................................ ed essere visto così da tutte le classi dell’applicazione. 8 Per inizializzare un attributo si utilizza l’operatore ......................................................................... 9 Un metodo è richiamabile su un oggetto tramite l’operatore ......................................................................... 10 Un attributo può essere dewnito ........................................................................ ed essere visto così da tutte le classi del package e dalle sottoclassi, anche se non presenti nel package della classe base. 11 Per impedire la visibilità di un attributo a qualsiasi classe al di fuori della classe in cui è dichiarato lo si indica come ......................................................................... 12 Un attributo booleano per default ha valore .........................................................................
Verifichiamo le competenze g Problemi 1 Crea la classe Massimi che contiene i metodi max() e min(), che calcolano rispettivamente il valore massimo e minimo di due numeri passati come parametri per i tipi di dati int, long, yoat e double. 2 Realizza il codice della classe Animale presentato nella lezione.
107
UdA 2
La OOP di Java
3 Dewnisci la classe Prodotto nel package it.hoepli.magazzino.model dotata degli attributi codice (int), descrizione (String), prezzo (yoat), giacenza (int) in modo che nessun attributo sia visibile dall’esterno. 4 Dewnisci la classe Cliente nel package it.hoepli.anagrafe con i metodi fattura(Fattura) e Fattura[] getElencoFatture() pubblici. 5 Dewnisci la classe Fattura nel package it.hoepli.anagrafe con gli attributi pubblici totale (yoat), numeroProdotti (int) e il metodo pubblico aggiungi(Prodotto). 6 Crea la classe Penna con gli attributi colore = “Nero” e inchiostroRimanente=9. 7 Dichiara una classe Modem che disponga dei metodi per comporre il numero, oppure il numero con prewsso, utilizzando l’overloading. 8 Dewnisci la classe Figura in modo che sia visibile da tutto il programma. 9 Dewnisci la classe Quadrato in modo che sia presente nel package it.hoepli.math e che sia possibile crearne sottoclassi, ma solo da parte di classi presenti nel package it.hoepli.math. Dichiara un metodo che abbia parametri formali ma mai attuali. 10 Identiwca e progetta le classi necessarie per dewnire la rappresentazione informatica di un libretto dei voti. 11 Identiwca le classi necessarie per la rappresentazione di scontrini emessi dal registratore di cassa di un bar. 12 Scrivi il metodo giorniMese che calcoli e stampi la data (giorno, mese, anno) del giorno successivo a una data ricevuta in input. 13 Scrivi il metodo punti che calcoli e restituisca la distanza tra due punti passati come parametri. 14 Scrivi il metodo fattoriale che calcoli e restituisca il fattoriale di un numero passato come parametro. 15 Scrivi il metodo punti che calcoli e restituisca la somma dei primi n numeri dispari, con n passato come parametro.
108
Il ciclo di vita degli oggetti
lezione
Lezione 3
3
iL cicLo di vitA degLi oggetti
in qUestA Lezione impAreremo...
• a comprendere il significato della garbage collection • a definire il ciclo di vita degli oggetti • a utilizzare l’auto referenza this
■ Java e la memoria Un programma Java è un insieme di oggetti che cooperano reciprocamente al fine di fornire la funzionalità richiesta. Quando da una classe, che rappresenta uno schema, viene creato un oggetto, la JVM (Java Virtual Machine) deve ◀ allocare la memoria ▶ necessaria a contenere gli at◀ Allocare la memoria Si tratta di un tributi di quell’oggetto: da questo momento processo mediante il quale viene asseinizia il ciclo di vita dell’oggetto, che verrà gnato uno spazio di memoria ben preciso a un programma affinché esso venga colmantenuto in memoria tutto il tempo neceslocato nella memoria stessa. ▶ sario, per poi essere rilasciato e la memoria occupata restituita al sistema operativo per ◀ Ciclo di vita Il ciclo di vita di un oggetto parte dal momento della sua creazione essere riutilizzata. (cioè quando si usa la parola chiave new) Il ◀ ciclo di vita ▶ di un oggetto è interamene finisce quando l’oggetto è eliminato te controllato dalla JVM. dal garbage collector. In java l’allocazioIl linguaggio Java gestisce la memoria in mone degli oggetti nella memoria avviene in do automatico attraverso un metodo chiamauno spazio riservato che prende il nome to garbage collector, termine inglese che letdi heap. ▶ teralmente significa raccoglitore di spazzatura, dove la spazzatura sono gli oggetti ormai inutili al programma. Il garbage collector si occupa di capire quando un oggetto non è più utilizzato e di rimuoverlo dalla memoria. I dettagli sul funzionamento di questo componente non incidono sul modo di sviluppare del programmatore, il quale segue comunque questa logica: 1 quando un oggetto è necessario, lo crea; 2 esegue le operazioni attraverso l’oggetto creato; 3 quando non serve più, può semplicemente smettere di considerarlo nel programma.
109
UdA 2
La OOP di Java
■ Il ciclo di vita di un oggetto Il ciclo di vita di un oggetto Java comincia con la sua creazione, che avviene tramite l’operatore new. Durante l’esecuzione di una istruzione che contiene l’onew peratore new, Java alloca lo spazio necessario a contenere l’oggetto in memoria e richiama il ◀ metodo costruttore ▶ appropriato, che si occupa di inizializfinalize() Esistente zare l’oggetto come previsto dal programmatore. ▶ Quando la JVM decide di eliminare l’oggetto, perché identifica che questo è divenuto inaccessibile da qualsiasi parte del programma, prima di eliminarlo ◀ Metodo costruttore Sono esegue il metodo wnalize(), se presente, nell’oggetto. metodi particolari richiamati auTale metodo può essere implementato dal programtomaticamente durante la fase di matore proprio per eseguire quelle operazioni di pucreazione dell’oggetto, possiamo lizia proprie della fine della vita di un oggetto. Per facilmente identificarli tra i metoesempio, può chiudere eventuali file aperti oppure di di una classe in quanto possieinterrompere connessioni di rete. dono lo stesso nome della classe che li contiene e inoltre non restiI costruttori sono metodi che si occupano di iniziatuiscono alcun valore. ▶ lizzare l’oggetto, per esempio, aprendo i file necessari alla classe o stabilendo le necessarie connessioni di rete; possono modificare il valore degli attributi, chiamando eventualmente altri metodi. Mediante il metodo costruttore in sostanza facciamo eseguire tutte le operazioni necessarie affinché la classe si ponga nello stato iniziale, solitamente inizializzando gli attributi ai valori passati al costruttore stesso. esempio
6
Una classe e il suo metodo costruttore
La classe Automobile è definita come una classe che non eredita da nessuna altra classe. Gli attributi sono associati a quattro variabili di istanza, ovvero quattro variabili che ogni oggetto appartenente alla classe Automobile possiederà come copia privata. La classe Automobile possiede due metodi: il metodo costruttore Automobile() che inizializza gli attributi nel momento in cui avverrà la creazione dell’oggetto mediante l’istanza, e un metodo MessaInMoto() che definisce in modo sommario l’accensione del motore dell’automobile. ▶ Una classe può avere un numero qualsiasi di costruttori, a patto che la firma del metodo (il tipo e il numero dei parametri) sia diverso per ogni costruttore.
110
Il ciclo di vita degli oggetti
Lezione 3
L’oggetto corrente this Possiamo assegnare ai parametri di un metodo lo stesso nome di un attributo di classe. L’uso dello stesso nome consente di non dover inventare nomi diversi di variabile, agevolando la lettura del codice. Tuttavia in questo modo la variabile locale nasconderà, al codice presente nel metodo, l’attributo. In altre parole per accedere all’attributo dovremo necessariamente anteporre la parola chiave this. La parola this costituisce un riferimento all’oggetto corrente, in tal modo l’oggetto è in grado di capire che ci stiamo riferendo a lui stesso. Viene anche chiamato operatore di auto referenza esplicita. Nel prossimo esempio viene mostrato come referenziare un attributo o una variabile locale aventi lo stesso nome. esempio
7
Uso di auto referenza con this
Prendiamo il seguente esempio nel quale al metodo impostaValore() viene passato un parametro che possiede lo stesso nome di un attributo. Il metodo impostaValore() riceve un parametro numero e quindi tenta di assegnare tale numero all’interno dell’attributo numero. Il riferimento alla variabile numero è ambiguo, è necessario chiarire il modo con cui un oggetto possa referenziare se stesso. L’operatore di auto referenza (this) indica che si tratta di un oggetto attivo, cioè l’istanza della classe in esecuzione durante la chiamata al metodo corrente.
Esiste anche un caso di auto referenza implicita quando il riferimento non è ambiguo, per mezzo del quale è possibile accede a dati membro o metodi di una classe senza necessariamente utilizzare esplicitamente l’operatore this. Il meccanismo su cui si basa l’auto referenza implicita è legato alla ◀ visibilità di una variabile ▶. Inizialmente, Java ricerca la dichiarazione della variabile all’interno del blocco di istruzioni corrente, in cui cioè facciamo riferimento alla variabile stessa: 1 se la variabile non è un parametro appartenente al blocco, risale tra i vari livelli del codice fino ad arrivare alla lista dei parametri del metodo corrente; 2 se neanche la lista dei parametri del metodo soddisfa la ricerca, legge il blocco di dichiarazione dell’oggetto corrente utilizzando implicitamente la variabile reference this; 3 se la variabile non è neanche un dato membro ◀ Visibilità di una variabile La visibidell’oggetto, un codice di errore è generato al lità di una variabile è limitata al blocmomento della produzione del Bytecode dal co e ai sottoblocchi di codice in cui è compilatore. stata effettuata la sua dichiarazione. Anche se l’uso implicito di variabili facilita la scrittuIl compilatore cercherà una variabile ra di codice, riducendo la quantità di caratteri da dinon qualificata risalendo a ritroso tra gitare, abusare di questa tecnica può essere causa di i diversi livelli dei blocchi di codice. ▶ ambiguità all’interno della definizione della classe.
111
UdA 2
La OOP di Java
Prova adesso!
• Applicare i metodi • Utilizzare il metodo costruttore
APRI IL PROGETTO Esercizio 2 1 Scrivi un costruttore della classe Libri in grado di inizializzarne tutti gli attributi in base ai valori passati come parametri. 2 Definisci un secondo metodo costruttore senza parametri in overload. 3 Aggiungi due metodi in grado di assegnare l’autore e il titolo agli attributi relativi. 4 Verifica la tua soluzione con quella contenuta nel progetto Esercizio2_solux.
esempio
8
Classe conto corrente
Vogliamo definire una classe per la gestione di un conto corrente chiamata appunto ContoCorrente. Ciascun conto corrente possiede un numero di conto (di tipo intero), un intestatario e un saldo. Vogliamo inoltre realizzare due costruttori, uno che presenti i parametri per tutti gli attributi e un secondo senza il saldo, intendendolo azzerato. Inoltre vogliamo realizzare i metodi get() per leggere il contenuto degli attributi secondo l’information hiding:
112
Il ciclo di vita degli oggetti
esempio
9
Lezione 3
Classe prodotto
Vogliamo definire una classe per la gestione degli articoli chiamata Prodotto. Ciascun prodotto possiede un codice identificativo (di tipo intero), una descrizione (di tipo stringa), un prezzo (espresso in Euro, quindi di tipo reale), lo sconto applicato (in percentuale, quindi di tipo reale). Vogliamo realizzare i seguenti costruttori: ◗ un costruttore con tutte le variabili d’istanza; ◗ un costruttore senza il codice (assegnare codice -1); ◗ un costruttore senza prezzo e senza sconto (assegnare prezzo convenzionale 0 7 e sconto 0); ◗ un costruttore con soltanto la descrizione del prodotto. Aggiungiamo infine i metodi get() per l’accesso agli attributi.
113
UdA 2
La OOP di Java
Verifichiamo le conoscenze g Esercizi a scelta multipla 1 Indica da quale componente viene gestita la memoria in Java: a) Garbage corrector c) Garbage inijector e) Memory collector b) Runtime d) Garbage collector f) Virtual Machine 2 Metti in ordine le seguenti operazioni necessarie all’utilizzo di un oggetto: a) ............... abbandono b) ............... creazione c) ............... interazione 3 Nel codice seguente la variabile ordineCorrente: public void ordina(Prodotto[] prodotti) { Ordine ordineCorrente = new Ordine(); ordineCorrente.add(prodotti); ordineCorrente.invia(); }
a) b) c) d)
ha validità all’interno della classe ha validità all’interno del package ha validità all’interno del metodo ha validità all’interno di tutta la classe dopo la prima chiamata al metodo
5 Indica cosa prevede la gestione della memoria da parte della JVM: a) l’allocazione e il rilascio c) l’apertura e la chiusura b) il blocco e lo sblocco d) può essere astratta o concreta 6 Il costruttore: a) è un tipo particolare di attributo b) è un tipo particolare di metodo c) viene chiamato in fase di eliminazione dell’oggetto d) viene chiamato dal metodo delete() e) viene chiamato in fase di creazione dell’oggetto f) viene chiamato in fase di creazione del programma g) ne può apparire uno per classe h) ne possono apparire diversi per classe i) non può apparire in classi derivate j) non può apparire in classi astratte k) non può apparire in interfacce 7 Nel codice che segue l’operatore di auto referenza esplicita: public class Nome { String nome; String cognome; public int nomi(String nome, String cognome) { nome=this.nome; cognome=this.cognome; }
a) b) c) d)
114
assegna all’attributo il contenuto del parametro. assegna al parametro il contenuto dell’attributo della classe. genera un errore. assegna al parametro il valore contenuto in se stesso.
Il ciclo di vita degli oggetti
Lezione 3
g Test vero falso 1 Una variabile d’istanza deve essere per forza inizializzata dal programmatore. 2 Una variabile locale condivide il ciclo di vita con l’oggetto in cui è dewnita. 3 Un parametro ha un ciclo di vita coincidente con il metodo in cui è dichiarato: nasce quando il metodo viene invocato, muore quando termina il metodo. 4 Un costruttore è un metodo che non restituisce mai niente, infatti ha come tipo di ritorno void.
g Esercizi di completamento 1 In Java la gestione della memoria è controllata dal ....................................................................................... 2 Nei linguaggi con gestione della memoria diversa da Java si possono veriwcare ....................................................................................., quando la memoria utilizzata non viene opportunamente restituita al sistema. 3 La creazione di un oggetto avviene tramite l’operatore ....................................................................................... 4 Al rilascio di un oggetto viene chiamato il metodo ....................................................................................... 5 L’operatore di auto referenza è ....................................................................................... 6 L’operatore this viene anche chiamato operatore di .......................................................................................
Verifichiamo le competenze g Problemi 1 Crea una classe Negozio con due costruttori che inizializzino lo stato dell’oggetto, dotato dei seguenti parametri: _ String proprietario _ String nomeNegozio 2 Crea una classe ContoCorrente che contiene tre costruttori che inizializzino lo stato dell’oggetto, dotato dei seguenti parametri: _ String nome _ String codiceConto _ double saldo 3 Crea una classe Prodotto con quattro costruttori che inizializzino lo stato dell’oggetto, dotato dei seguenti parametri: _ int codice _ String descrizione _ double prezzo _ double sconto
115
UdA 2
La OOP di Java
4 Crea la classe Prodotto utilizzando meno codice possibile con tre costruttori che inizializzino lo stato dell’oggetto, dotati dei seguenti parametri: _ int codice _ String descrizione; _ int codice, String descrizione 5 Crea la classe Fattura utilizzando meno codice possibile con tre costruttori che inizializzino lo stato dell’oggetto, dotati dei seguenti parametri: _ Cliente cliente; _ Cliente cliente, int numeroProdotti; _ int numeroProdotti; 6 Aggiungi un costruttore di default al codice dell’esercizio precedente. 7 Crea la classe Cliente che stampi un messaggio nel wnalizzatore. 8 Crea la classe Fornitore che allochi una stringa nel costruttore e la rilasci nel wnalizzatore. 9 Crea la classe Prodotto che allochi una stringa nel costruttore e la rilasci nel wnalizzatore. 10 Crea la classe Negozio che allochi una stringa nel costruttore e la rilasci nel wnalizzatore.
116
Le variabili reference e l’istanza delle classi
lezione
Lezione 4
4
Le vAriAbiLi reference e L’istAnzA deLLe cLAssi in qUestA Lezione impAreremo...
• a conoscere il significato e l’uso delle variabili reference • a determinare lo scope delle variabili • a istanziare classi in oggetti
■ Variabili e reference La principale differenza tra una classe e un tipo primitivo è che le variabili primitive vengono allocate in memoria al momento della loro dichiarazione mentre gli oggetti no. Se infatti dichiariamo una variabile come di seguito: »oat numero;
il compilatore crea un puntatore a una variabile chiamata numero allocando 32 bit necessari alla memorizzazione del numero stesso, inizializzandone il valore a 0: Numero 00000000 00000000 00000000 00000000
Quando le classi vengono istanziate in oggetti la JVM crea una variabile che contiene il puntatore all’oggetto in memoria, ma non alloca risorse per caricare la classe. Di fatto l’oggetto non viene fisicamente creato. Identificatore ???
117
UdA 2
La OOP di Java
Le variabili di questo tipo vengono chiamate ◀ variabili reference ▶. Una variabile reference contiene due informazioni rilevanti: l’indirizzo in memoria e l’intervallo di puntamento definito dalla relativa classe. ◀ Variabili reference Una variabile riferimento (reference) è in grado di puntare soltanto all’oggetto per il quale è stata creata. Per esempio una variabile reference di tipo Animale non può contenere puntatori oggetti di tipo Libro. ▶
Zoom su... Lo scope di UnA vAriAbiLe In Java le variabili possono essere dichiarate in ◀ Scope Lo scope di una variabile un punto qualunque del programma, in ragioJava è il blocco all’interno del quane di questo dobbiamo porre attenzione allo le essa è visibile e che ne determina ◀ scope ▶ che esse possiedono. anche il ciclo di vita individuando I blocchi di istruzioni ci forniscono il meccaniquando la variabile debba essere smo necessario a determinare i confini dello allocata o rimossa dalla memoria. ▶ scope di una variabile: di fatto, una variabile è visibile solo all’interno del blocco di istruzioni che contiene la sua dichiarazione. Per esempio le variabili dichiarate all’interno del blocco di dichiarazione di una classe sono dette attributi, lo scope degli attributi è determinato dall’intero blocco di dichiarazione della classe come indicato nella figura che segue:
Per quanto concerne i metodi dobbiamo sottolineare che le variabili dichiarate all’interno del blocco del metodo sono chiamate variabili locali. Anche in questo caso lo scope di una variabile locale è determinato dal blocco di codice all’interno del quale è dichiarata. Nell’esempio che segue si nota come la variabile numero sia visibile all’interno del blocco del metodo, mentre la variabile k solo nel sottoblocco in cui è dichiarata:
118
Le variabili reference e l’istanza delle classi
Lezione 4
Il codice di esempio seguente genera un errore in fase di compilazione in quanto la variabile numero viene usata all’esterno del suo blocco di dichiarazione: ▶
Come abbiamo visto dunque una variabile viene distrutta dalla JVM quando usciamo dal suo blocco di scope, pertanto le variabili locali non potranno mai conservare un dato quando verranno richiamate più volte. Il codice che segue mostra che la variabile numero contiene sempre il medesimo valore a ogni chiamata del blocco all’interno del ciclo for: ▶
L’output del programma mostra che a ogni ciclo la variabile viene incrementata di 3, ma al successivo ciclo ritorna al valore di inizializzazione precedente, cioè 5: ▶
119
UdA 2
La OOP di Java
■ Le istanze di una classe L’operatore new carica dinamicamente in memoria un oggetto, riservando la memoria necessaria al nuovo oggetto e restituendone il riferimento, che può quindi essere memorizzato in una variabile riferimento (reference) del tipo appropriato. La sintassi dell’operatore new è la seguente: new NomeClasse();
Le parentesi sono necessarie e hanno un significato particolare che sveleremo presto mentre NomeClasse è il nome di una definizione di classe appartenente alle API di Java oppure definita dal programmatore. Per esempio per creare un oggetto di classe Animale faremo uso dell’istruzione: Animale cane = new Animale();
Il codice indicato sopra dichiara una variabile istanza di Animale di classe Animale, quindi crea l’oggetto cane utilizzando la definizione di classe e memorizza il puntatore alla memoria riservata nella variabile reference: ▶ Un risultato analogo può essere ottenuto anche nel modo seguente:
Animale cane
=
new Animale();
cane Animale
Variabile reference
Memoria
Gli esempi che seguono mostrano come istanziare due classi viste in precedenza. esempio 10
Istanziare la classe Automobile
In questo esempio vogliamo istanziare la classe Automobile in un oggetto per usarne il metodo messaInMoto(). Prima di tutto dobbiamo compilare il file sorgente Automobile.java, per ottenere il file contenente la classe (Automobile.class): ▶ A questo punto creiamo una nuova classe contenente il metodo main, chiamata UsaAutomobile, il cui codice contiene l’istanza della classe mediante costruttore al quale vengono passati quattro parametri e l’uso dell’oggetto wat500 e del relativo metodo messaInMoto():
120
Le variabili reference e l’istanza delle classi
Lezione 4
L’esecuzione provoca il seguente output:
la variabile wat500 del nostro esempio conterrà quindi il riferimento o puntatore allo spazio di memoria in cui saranno presenti i valori degli attributi. Il valore del riferimento a un oggetto non può essere modificato esplicitamente all’interno del codice di un programma in quanto Java ne maschera il contenuto.
Prova adesso!
• Utilizzare l’istanza delle classi • Utilizzare i metodi e gli attributi
APRI IL PROGETTO Esempio 10 1 Modifica il codice sorgente aggiungendo il metodo spegni() in grado di spegnere il motore dell’auto e visualizzarne lo stato. 2 Modifica il codice sorgente modificando gli attributi di classi: aggiungi l’attributo serbatoio e capienzaMassima. 3 Aggiungi quindi un metodo faiIlPieno() in grado di effettuare il pieno dei litri passati come parametro, visualizzando quanti litri ci sono ancora nel serbatoio e se abbiamo superato il limite di capienza massima.
esempio 11
Istanziare la classe ContoCorrente
In questo esempio vogliamo utilizzare la classe ContoCorrente creata nell’esempio 8. Per fare questo dobbiamo creare l’istanza della classe ContoCorrente mediante il consueto operatore new. Come possiamo notare il secondo oggetto viene creato utilizzando un metodo costruttore con due soli parametri. Vengono quindi istanziati due oggetti, sui quali vengono invocati i metodi getNumeroConto(), getIntestatario(), getSaldo():
121
UdA 2
La OOP di Java
L’output è il seguente:
• Utilizzare classi e metodi
Prova adesso! APRI IL PROGETTO Esempio 8 1 Aggiungi un metodo prelevaConto() che riceve come parametro un importo da detrarre dal conto corrente. 2 Aggiungi un metodo versaConto() che riceve come parametro un importo da aggiungere al conto corrente.
APRI IL PROGETTO Esempio 11 3 Aggiungi un versamento di 100 Euro dal conto di Elena e 200 dal conto di Paolo invocando i due metodi relativi.
Classi e file Possiamo dichiarare più classi nello stesso file, tuttavia solo una classe può contenere il metodo main. Tale classe viene chiamata classe principale che deve essere sempre di tipo public. Il codice di esempio che segue mostra un file che contiene due classi, una sola delle due è di tipo public e contiene il metodo main:
Il termine public indica che la classe è visibile a tutte le altre classi: i livelli di visibilità di classe sono identici nel significato a quelli studiati per attributi e metodi. Se non facciamo precedere il nome della classe da alcuna parola chiave che ne indica il livello di visibilità viene automaticamente dichiarata di tipo package.
122
Le variabili reference e l’istanza delle classi
Lezione 4
Nell’esempio sopra riportato la classe FormaGeometrica è la classe principale e possiede una visibilità public, in tal modo è possibile creare una istanza della classe principale in qualunque altra classe. Solo all’interno della classe FormaGeometrica è invece possibile creare una istanza della classe Triangolo. Possiamo anche salvare ogni classe in un file distinto, purché memorizzato nella stessa cartella del file contenente la classe principale. In tal modo a ciascuna classe viene associato un file .java che, una volta compilato, genera il corrispondente file .class.
• Usare l’istanza delle classi
Prova adesso! APRI IL PROGETTO Esercizio 3 1 Modifica il codice in modo da aggiungere un metodo stampaRombo() in grado di visualizzare a video un rombo di asterischi di altezza n. 2. 2 Crea una classe UsaFormeGeometriche in grado di istanziare e usare la classe FormeGeometriche. 3 Confronta la tua soluzione con quella presente nel progetto Esercizio3_solux.
APRI IL PROGETTO Esercizio 4 1 Aggiungi una classe UsaPersona in grado di istanziare tre oggetti della classe Persona inizializzandone gli attributi attraverso l’input da tastiera. 2 Confronta la tua soluzione con quella presente nel progetto Esercizio4_solux.
Zoom su... generAre nUmeri cAsUALi In questo esempio istanzieremo un oggetto di classe java.util.Random per utilizzarne alcuni metodi quali per esempio nextInt() che genera un valore casuale di tipo intero. Questa classe mette a disposizione diversi metodi che consentono anche di generare valori booleani. Per ogni tipo primitivo è disponibile un metodo che genera un valore random in base al tipo. Data un’istanza della classe Random: Random random = new Random();
Possiamo ottenere: //valore booleano boolean b = random.nextBoolean(); //valore intero int k = random.nextInt();
123
UdA 2
La OOP di Java
//valore intero compreso tra 0 e n (n positivo), in questo caso 10 int n = 10; int k = random.nextInt(n); //un valore »oat compreso sempre tra 0.0 e 1.0 »oat f = random.nextFloat(); //un valore long long l = random.nextLong(); //un valore double di una distribuzione Gaussiana double d = random.nextGaussian(); // Riempie l’array di byte generati in maniera casuale. byte [] b = new byte[10]; random.nextByte(b);
In questo esempio usiamo la classe Random per simulare il lancio di un dado, visualizzando un numero casuale compreso tra 1 e il numero di facce possedute dall’oggetto nell’attributo numeroFacce. Nel costruttore avviene l’istanza dell’oggetto casuale di classe Random. Esistono due costruttori, uno che riceve il numero di facce come parametro e un altro, senza parametri, che le definisce per default a 6. Il metodo setFacce() consente di modificare le facce secondo la tecnica dell’information hiding, essendo l’attributo numeroFacce di tipo private. Infine il metodo lancia() restituisce un numero compreso tra 1 e il valore espresso dal campo numeroFacce. Il codice della classe Dado è il seguente:
124
Le variabili reference e l’istanza delle classi
Lezione 4
Per istanziare alcuni oggetti di classe Dado creiamo una classe UsaDado il cui codice è abbastanza semplice:
L’esecuzione del metodo main() della classe UsaDado fornisce i seguenti risultati:
125
UdA 2
La OOP di Java
Verifichiamo le conoscenze g Esercizi a scelta multipla 1 Che differenza c’è tra una variabile di tipo primitivo e un oggetto? a) Le variabili primitive vengono allocate in memoria al momento della dichiarazione mentre gli oggetti no. b) Gli oggetti vengono allocati in memoria al momento della dichiarazione mentre le variabili primitive no. c) Le variabili primitive e gli oggetti vengono entrambi allocati in memoria nello stesso modo. d) Gli oggetti vengono allocati in memoria dopo averne inizializzato il valore mentre le variabili primitive no. 2 Quando una classe viene istanziata la JVM crea una variabile che contiene il puntatore all’oggetto: a) e alloca le risorse per caricare la classe in memoria b) e non alloca ancora le risorse per caricare la classe in memoria c) e alloca il solo puntatore in memoria, l’oggetto viene allocato in memoria di massa d) e alloca il puntatore in memoria di massa 3 Una variabile reference: a) può puntare qualunque oggetto b) può puntare solo l’oggetto per il quale è stata creata c) può puntare l’array che contiene il puntatore agli oggetti creati in precedenza d) può puntare solo variabili primitive 4 Una variabile reference contiene: a) l’indirizzo di memoria b) l’intervallo di puntamento c) l’indirizzo di memoria, l’intervallo di puntamento e il tipo di classe d) l’indirizzo di memoria e l’intervallo di puntamento 5 Lo scope di una variabile in Java è: a) il metodo che contiene la variabile b) il blocco all’interno del quale è visibile c) il blocco all’interno del quale viene usata d) il blocco all’interno del quale viene dichiarata 6 Una variabile locale: a) è una variabile dichiarata all’interno di una classe b) è una variabile dichiarata all’interno di un blocco c) è una variabile dichiarata all’interno di un ciclo d) è una variabile dichiarata all’interno di un metodo 4 Indica quale tra i seguenti frammenti dichiara una reference di Telefono. a) mioTelefono = new Telefono(); b) Telefono mioTelefono; c) new Telefono(); d) Telefono mioTelefono = new Telefono(); 8 Indica quale tra i seguenti frammenti assegna un oggetto di classe Telefono. a) mioTelefono = new Telefono(); b) Telefono mioTelefono; c) new Telefono(); d) Telefono mioTelefono = new Telefono();
126
Le variabili reference e l’istanza delle classi
Lezione 4
9 Indica quale tra i seguenti frammenti istanzia un oggetto di classe Telefono. a) mioTelefono = new Telefono(); b) Telefono mioTelefono; c) new Telefono(); d) Telefono mioTelefono = new Telefono(); 10 Quale affermazione, riguardo a più classi incluse nello stesso file, tra le seguenti è corretta? a) La classe che contiene il main deve essere solo una i di tipo protected. b) Solo una classe può essere public, ma il metodo main può essere inserito in un’altra classe. c) Tutte le classi possono essere public e possono esserci più metodi main. d) Solo una classe può contenere il metodo main ed essere di tipo public.
Verifichiamo le competenze g Problemi 1 La seguente classe dewnisce il concetto di numero intero come oggetto. In essa vengono dichiarati una variabile e un metodo che stamperà la variabile stessa. public class NumeroIntero { public int numeroIntero; public void stampaNumero() { System.out.println(numeroIntero); } }
Crea una classe che istanzi almeno 2 oggetti della classe NumeroIntero e cambi il valore delle relative variabili veriwcando le assegnazioni, utilizzando il metodo stampaNumero(). Aggiungi un costruttore alla classe NumeroIntero che inizializzi l’attributo. 2 Dewnisci e istanzia una classe NumeroReale per rappresentare numeri reali, di tipo double. Per fare questo utilizza un unico wle che contiene la classe dewnita e la classe UsaNumeroReale per istanziare la classe iniziale. 3 Dewnisci e istanzia una classe NumeroRazionale per rappresentare numeri razionali come coppia di numeri interi (di tipo int). Per fare questo utilizza un unico wle che contiene la classe dewnita e la classe UsaNumeroRazionale per istanziare la classe iniziale. 4 Dewnisci e istanzia una classe NumeroComplesso per rappresentare numeri complessi come coppia di numeri reali (di tipo double). Per fare questo utilizza un unico wle che contiene la classe dewnita e la classe UsaNumeroComplesso per istanziare la classe iniziale. 5 Dewnisci una classe Libro per rappresentare oggetti libro con il nome dell’autore, il titolo e il numero di pagine e con opportuni metodi d’istanza tra cui un metodo del tipo String toString() per la sua descrizione. Crea una classe UsaLibro per inserire e visualizzare i dati di alcuni libri. 6 Dewnisci una classe LineaBus per rappresentare oggetti linea di autobus urbano con il numero identiwcativo, il nome dei due capolinea e con opportuni metodi d’istanza tra cui un metodo del tipo String toString() per la descrizione del suo percorso. Crea una classe UsaLineaBus per inserire e visualizzare i dati di alcune linee.
127
UdA 2
La OOP di Java
7 Dewnisci una classe Auto per rappresentare oggetti automobile con il nome della marca, il nome del modello, la targa e l’anno di immatricolazione e con opportuni metodi d’istanza tra cui un metodo del tipo String toString() per la sua descrizione. Crea una classe UsaAuto per inserire e visualizzare i dati di alcune auto. 8 Dewnisci una classe Studente per rappresentare oggetti studente con il cognome, il nome, il codice wscale, il numero di matricola e con opportuni metodi d’istanza tra cui un metodo del tipo String toString() per la sua descrizione. Crea una classe UsaStudente per inserire e visualizzare i dati di alcuni studenti. 9 Dewnisci una classe Punto che contenga gli attributi (x,y) di tipo intero, un costruttore senza argomenti che crea un punto di coordinate (0,0), un costruttore con due argomenti di tipo intero che inizializza le variabili di istanza con i valori passati per argomento, due metodi pubblici: getX che restituisce l’ascissa del punto e getY che restituisce l’ordinata. Inoltre deve contenere un metodo pubblico trasla() con due parametri di tipo int, che trasla il punto aggiungendo alle coordinate i valori passati per argomento e un metodo pubblico distanza() di tipo double e con un solo parametro di tipo Punto, che restituisce la distanza tra l’oggetto su cui viene invocato e il punto passato per argomento, calcolata usando il Teorema di Pitagora. Aggiungi inwne un metodo toString senza argomenti, che restituisca le coordinate del punto sotto forma di stringa. Crea una classe UsaPunto per inserire e gestire i dati di almeno due punti. 10 Dewnisci la classe Item i cui oggetti rappresentano degli articoli in vendita. Ogni articolo comprende un campo per il nome, uno per la descrizione, uno per il nome del reparto, uno per il costo unitario e uno per indicare se l’articolo è in offerta o no. Crea una classe UsaItem per inserire i dati di alcuni articoli e visualizzarne il totale degli articoli e gli articoli in offerta. 11 Dewnisci la classe Segmento i cui oggetti rappresentano dei segmenti orizzontali di lunghezza intera sul piano cartesiano. Dewnisci inoltre i seguenti costruttori: _ un costruttore con due parametri, left di tipo Punto e lunghezza di tipo intero, che crei un segmento con vertice sinistro di coordinate left e lunghezza; _ un costruttore con un parametro intero che crei un segmento con vertice destro (0,0) e lunghezza uguale al parametro se maggiore di zero, altrimenti di lunghezza unitaria; _ un costruttore con due parametri formali di tipo Punto (left e right), che rappresentano i vertici sinistro e destro del segmento se allineati, altrimenti con vertice sinistro left e lunghezza unitaria; Dewnisci i seguenti metodi: _ getLeft(), che restituisce il vertice sinistro del segmento; _ getRight(), che restituisce il vertice destro del segmento; _ getLength(), che restituisce la lunghezza del segmento; _ toString() che restituisce una stringa che è una rappresentazione testuale del segmento, per esempio “Segmento[(xleft, yleft),length]”, dove xleft è l’ascissa del vertice sinistro, yleft è l’ordinata del vertice sinistro e length è la lunghezza. _ inclusoIn() di tipo boolean con un singolo parametro di tipo Segmento, che restituisce true se e solo il segmento su cui il metodo viene invocato è contenuto nel segmento passato come parametro. Crea una classe UsaSegmento per creare alcuni segmenti e veriwcarne il funzionamento. 12 Dewnisci la classe Lampadina i cui oggetti rappresentano delle lampadine elettriche. Una lampadina può essere accesa, spenta o rotta, e mette a disposizione due sole operazioni: toString() che restituisce una stringa che indica lo stato corrente della lampadina e click() che ne cambia lo stato da accesa a spenta o da spenta a accesa o la rompe. Una lampadina si rompe dopo un certo numero di click deciso dal fabbricante. Dewnisci gli attributi, i metodi e i costruttori che ritenuti opportuni. Crea la classe UsaLampadina per istanziare e usare la classe Lampadina che ammetta un numero massimo di click deciso dall’utente e poi, in maniera iterativa, offra all’utente la possibilità di invocare una delle due funzionalità (visualizzando l’esito dell’operazione) o di terminare l’esecuzione.
128
Le variabili reference e l’istanza delle classi
Lezione 4
13 Dewnisci la classe Interruttore i cui oggetti rappresentano degli interruttori. Ogni interruttore è collegato a una lampadina (oggetto della classe Lampadina) e ne regola l’accensione e spegnimento. La classe deve contenere tutti gli attributi, i costruttori e i metodi ritenuti opportuni. Crea la classe UsaInterruttore per istanziare e usare due interruttori entrambi collegati alla stessa lampadina (uno stesso oggetto della classe Lampadina) e poi, in maniera iterativa, offra all’utente la possibilità di agire su uno dei due interruttori, visualizzando l’esito dell’operazione, o di terminarne l’esecuzione. 14 Dewnisci la classe Buffer: un buffer può essere vuoto oppure contenere un messaggio di tipo stringa, un buffer mette a disposizione tre operazioni principali: vuoto () che permette di controllare se il buffer è vuoto; inserisci(s) che consente l’aggiunta di una stringa s in un buffer, tuttavia solo se il buffer è vuoto; leggi() che consente l’estrazione della stringa contenuta nel buffer, solo se il buffer non è vuoto. Crea una classe UsaBuffer per istanziare un oggetto di classe Buffer e in maniera iterativa offre all’utente la possibilità di invocare una delle tre funzionalità sul buffer, visualizzando l’esito dell’operazione, o di terminarne l’esecuzione. 15 Dewnisci una classe PrimaClasse contenente due metodi costruttori: uno senza argomenti, l’altro con un argomento di tipo String, due metodi di classe: _ stampa() che ha un argomento di tipo String e non restituisce alcun risultato; _ leggi() che non ha argomenti e restituisce un oggetto della classe String. Dewnisci inoltre un attributo stato di tipo String. Crea una classe UsaPrimaClasse che istanzi due oggetti di classe PrimaClasse. 16 Dewnisci una classe ContoCorrente che implementi un conto corrente bancario. Il saldo del conto corrente deve essere memorizzato in un attributo intero, i metodi che deve possedere sono i seguenti: _ deposita() che aggiorna il saldo in base a un valore intero passato come argomento; _ preleva() che aggiorna il saldo in base a un valore intero passato come argomento e restituisca un valore booleano corrispondente alla corretta esecuzione dell’operazione; _ leggisaldo() che restituisce il saldo del conto corrente. Crea una classe UsaContoCorrente per istanziare alcuni oggetti di classe Conto Corrente e utilizzi i metodi per simulare alcuni prelevamenti e versamenti. 17 Dewnisci una classe Persona i cui oggetti mantengano le seguenti informazioni su una persona: _ Nome; _ Cognome; _ Titolo (es. “Sig.”, o “Ing.”); _ Professione. La classe Persona deve fornire i seguenti metodi: _ Costruttore che riceva nome, cognome, titolo e professione e inizializzi le informazioni dell’oggetto; _ Costruttore che riceva solo nome e cognome e inizializzi le informazioni dell’oggetto assumendo come titolo la stringa nulla “” e come professione “disoccupato”; _ ottieniInfoPersona() che restituisca una stringa con tutte le informazioni sulla persona (es.: “ing. Marco Bianchi, professione analista programmatore”); _ impostaTitolo() che riceva una stringa titolo e cambi l’informazione relativa al titolo della persona memorizzata nell’oggetto; _ impostaProfessione() che riceva una stringa professione e cambi l’informazione relativa alla professione della persona memorizzata nell’oggetto.
129
UdA 2
La OOP di Java
Crea una classe UsaPersona che: _ Costruisca un oggetto persona1 della classe Persona per Roberto Marelli, di professione insegnante; _ Costruisca un oggetto persona2 della classe Persona per l’Ing. Marco Redaelli, di professione disoccupato; _ Stampi su schermo la stringa ottenuta invocando il metodo ottieniInfoPersona su persona1. Dovrebbe comparire la scritta: “Roberto Marelli, professione insegnante”; _ Stampi su schermo la stringa ottenuta invocando il metodo ottieniInfoPersona su persona2. Dovrebbe comparire la scritta: “Ing. Marco Redaelli, professione disoccupato”; _ Modifichi il titolo di persona1 a “Sig.” e la professione a “macchinista” invocando i metodi impostaTitolo e impostaProfessione; _ Stampi su schermo la stringa ottenuta invocando il metodo ottieniInfoPersona su persona1. 18 Dewnisci le seguenti classi, attributi, metodi costruttori e istanze. a) Libro Creare la classe Libro e quindi due sue istanze, assegnando i valori per gli attributi e dando una rappresentazione sia sintetica sia completa. b) Aereo Creare la classe Aereo e quindi due sue istanze, assegnando i valori per gli attributi e dando una rappresentazione sia sintetica che completa. c) Televisione Creare la classe Televisione dandone la rappresentazione sia sintetica sia completa anche di una sua istanza. d) Treno Creare la classe Treno dandone la rappresentazione sia sintetica che completa anche di una sua istanza. e) Rettangolo Creare una classe che rappresenti un Rettangolo e successivamente creare due sue istanze, assegnando valori per la base e l’altezza. f) Telefono Creare la classe Telefono dandone la rappresentazione sia sintetica sia completa anche di una sua istanza. g) Mezzo di trasporto Creare una classe che rappresenti un MezzoDiTrasporto e successivamente creare tre sue istanze: un’Automobile, un Treno e un carroFunebre. h) MotoGP Descrivere due classi per il motomondiale, rispettivamente la classe dei piloti e quella delle motociclette, e istanziare un oggetto per classe. i) Lampada e lampione Creare una classe che rappresenti una Lampada e successivamente creare due sue istanze. Successivamente modellare la classe Lampione e confrontarla con la classe Lampada per individuare eventuali analogie. j) Abito da sposa Creare una classe che rappresenti un AbitoDaSposa e successivamente creare due sue istanze: per una ragazza brasiliana e per una donna di mezza età svedese. Successivamente modellare la classe più astratta CapoVestiaro e istanziare tre oggetti: un Maglione, un paio di Calze, e una tutaDaSubacqueo. Confrontare le due classi ed evidenziare metodi e attributi comuni. k) Gallina Creare una classe che rappresenti una Gallina e successivamente creare due sue istanze: provare quindi a istanziare con la stessa classe un Tacchino. Quali inconvenienti si riscontrano? Come deve essere modificata la classe Gallina in una seconda classe DaCortile che possa contemplare entrambi gli animali? E volendo aggiungere anche i Conigli, che cosa deve essere ulteriormente aggiunto?
130
Le variabili reference e l’istanza delle classi
Lezione 4
19 Dewnisci una classe FormeGeometriche con i seguenti metodi: _ stringaAsterischi(int n) restituisce una stringa di ‘n’ asterischi. Per esempio, stringaAsterischi(5) restituisce “*****”; _ stampaRettangolo(int n, int m) stampa un rettangolo di dimensioni n x m. Per esempio, stampaRettangolo(4,6) stampa: ****** ****** ****** ****** (Suggerimento: per realizzare questo metodo sfruttate il metodo stringaAsterischi()) _ stampaQuadrato(int n) stampa un quadrato (di asterischi) di lato n; _ stampaRombo(int n) stampa un rombo di altezza n. Per esempio, stampaRombo(7) stampa: * ** **** ****** **** ** * Crea una classe UsaFormeGeometriche che stampi un rettangolo, un quadrato e un rombo.
131
UdA 2
La OOP di Java
lezione
5
derivAzione ed ereditArietà
in qUestA Lezione impAreremo...
• a definire una classe derivata ridefinendone i metodi • a impedire la derivazione di classi e metodi • a conoscere i metodi clone() e equals() della classe Object • a clonare oggetti anche in classi derivate
■ Ereditarietˆ In questa lezione passiamo a esaminare come ereditare ed espandere le caratteristiche di un oggetto attraverso ◀ ereditarietà e derivazione ▶. esempio 12
Classe Quadrilatero
L’esempio mostra come ◀ specializzare ▶ una classe. In questo esempio la classe Quadrilatero è una classe generica in grado di gestire un ipotetico quadrilatero, con un
132
◀ Ereditarietà e derivazione L’ereditarietà consente di derivare nuove classi a partire da quelle già definite. Una classe derivata attraverso l’ereditarietà viene chiamata sottoclasse e mantiene i metodi e gli attributi delle classi da cui deriva. Quando una classe eredita da una sola superclasse si parla di eredità singola; viceversa, si parla di eredità multipla. Java non prevede l’ereditarietà multipla. L’ereditarietà è un meccanismo che consente di estendere e riutilizzare il codice già scritto in una classe. ▶ ◀ Specializzare La specializzazione di una classe in Java rappresenta la possibilità di creare dei sottotipi di una classe già esistente. ▶
Derivazione ed ereditarietà
Lezione 5
metodo costruttore che riceve i quattro lati come parametri interi in ingresso. La classe inoltre possiede un metodo perimetro() che calcola il perimetro della forma geometrica. Poiché esistono alcuni tipi di quadrilatero particolari, come per esempio il rettangolo, possiamo pensare alla classe Rettangolo come a una derivazione della classe Quadrilatero. Per fare questo è necessario derivare la classe figlia (Rettangolo) dalla classe padre (Quadrilatero). Questo tipo di derivazione consiste in una specializzazione della classe Quadrilatero, in quanto il rettangolo è un quadrilatero che gode di particolari proprietà (gli angoli sono tutti di 90° e i lati opposti sono uguali).
Quando in Java si applica il meccanismo di ereditarietà si dice che una classe deriva da un’altra, per essere più precisi diciamo che una classe deriva da un’altra classe quando viene costruita a partire da questa. La classe derivata possiede tutti gli attributi e i metodi della classe di partenza, e quindi è necessario sviluppare solo il codice che estende o sostituisce il codice ereditato. Per indicare al compilatore che una classe deriva da una classe padre è necessario usufruire della parola chiave extends nella riga di dichiarazione della classe seguito dal nome della classe dalla quale ereditare attributi e metodi. In Java è possibile derivare una sola classe, ereditandone i metodi e gli attributi. Questo concetto è chiamato ereditarietà singola. Inoltre la classe da cui si deriva viene detta classe base.
esempio 13
Derivare la classe Quadrilatero
In questo caso vogliamo derivare la classe Rettangolo a partire dalla classe Quadrilatero. L’istruzione che consente di effettuare la derivazione è la seguente:
Nel nostro esempio, la classe Quadrilatero è la classe base della classe Rettangolo. La nuova classe Rettangolo può ora utilizzare tutti i metodi della sua classe base in modo diretto, come se fossero propri. In tal modo per esempio possiamo istanziare una classe Rettangolo che utilizzerà il metodo perimetro() derivato dalla classe Quadrilatero:
Ma torniamo al nostro esempio, poiché un rettangolo non è altro che un quadrilatero con i lati opposti uguali, possiamo definire il costruttore della nuova classe in modo che abbia solamente due parametri anziché i quattro richiesti dal costruttore della classe Quadrilatero. La definizione della classe Rettangolo risulta la seguente:
133
UdA 2
La OOP di Java
A questo punto, è necessario invocare il costruttore della ◀ Super La parola chiave classe base nel corpo del costruttore della classe Rettansuper serve per fare riferimengolo, usando la parola chiave ◀ super ▶. to alla classe base (chiamata Notiamo che al costruttore della classe base vengono pasanche superclasse) da una classati 4 parametri formati da due parametri ripetuti due se derivata, in questo caso rivolte. Questo perché la classe Rettangolo non è altro che chiamandone il costruttore. ▶ una specializzazione della classe Quadrilatero, in pratica un rettangolo possiede le misure dei lati a due a due identiche. Se, al contrario, avessimo definito degli elementi privati o pubblici della classe Rettangolo (che quindi non fanno parte della classe Quadrilatero), avremmo dovuto inizializzarli esplicitamente all’interno del costruttore della classe Rettangolo.
esempio 14
Rettangolo derivato da Quadrilatero con modifica attributi
In questo esempio vogliamo modificare la classe Rettangolo aggiungendo l’attributo colore, che riporti il codice del colore del rettangolo come indicato nella seguente tabella: 0
Nero
1
Blu
2
Giallo
3
Rosso
4
Verde
5
Bianco
Nel codice che segue è stato aggiunto un secondo costruttore secondo la tecnica dell’overriding. Tale costruttore che prende in ingresso anche il colore del rettangolo, mantenendo tuttavia il costruttore con i soli due parametri formati dalla lunghezza dei lati. Tale metodo imposta un colore di default pari a zero. Vediamo quindi il codice completo della classe:
134
Derivazione ed ereditarietà
Lezione 5
Il costruttore con tre parametri invoca il costruttore della classe base utilizzando la parola chiave super, e poi imposta l’attributo di classe colore con il valore del parametro, inizializzando il colore dell’oggetto in fase di creazione. Il secondo costruttore riceve solo la dimensione dei due lati, imposta come colore il valore 0, invocando il primo costruttore della classe Rettangolo l’operatore di auto referenza this. Possiamo inoltre verificare la derivazione dalla classe base alla classe derivata attraverso la finestra offerta dall’ambiente di lavoro BlueJ che mostra una freccia come collegamento tra le due classi:
L’invocazione di un costruttore della classe base o della classe attuale da parte di un altro costruttore è la prima operazione che può comparire nel codice del costruttore stesso. Non è possibile, per esempio, nella classe Rettangolo, invertire l’ordine delle due seguenti righe: super(a,b,a,b); colore = c;
perché si produrrebbe un errore in fase di compilazione. La prima operazione del costruttore, se è richiesto dalla logica del programma, è la chiamata a un altro costruttore. Inoltre i due costruttori della classe Rettangolo modificano ora un attributo proprio della classe Rettangolo che non esiste nella classe Quadrilatero. La valorizzazione dell’attributo avviene nel corpo del costruttore.
■ Derivazione di una classe derivata Java consente di creare strutture di codice via via più complesse mediante le classi derivate, creando nuovi tipi di dati a partire da quelli esistenti e creando così una gerarchia di entità. Capita così che una classe derivata rappresenti una classe base per una successiva classe derivata. Per illustrare meglio questo concetto, possiamo ora definire la nuova classe Quadrato che consiste nella derivazione della classe Rettangolo che, a sua volta, è una derivazione della classe Quadrilatero. La gerarchia, a questo punto, può essere rappresentata dal seguente grafico: Quadrilatero Rettangolo Quadrato
135
UdA 2
La OOP di Java
esempio 15
Derivazione di derivazione: creazione di una classe Quadrato
Anche in questa classe creiamo due metodi costruttori analogamente a quanto visto per la classe Rettangolo. Il corpo della classe può essere semplicemente definito in questo modo:
I due metodi costruttori effettuano semplicemente la chiamata al costruttore della classe base (Rettangolo), inoltre la classe Quadrato erediterà tutti i metodi della classe Rettangolo da cui viene derivata e anche della classe Quadrilatero. Aggiungiamo una nuova classe UsaQuadrilatero per istanziare e usare gli oggetti di queste classi. Tale classe contiene un metodo main per testare la creazione degli oggetti delle varie classi viste finora:
Dal codice possiamo notare come il metodo perimetro() possa essere richiamato da uno qualsiasi degli oggetti nella gerarchia e come il metodo getColore() possa essere richiamato anche sugli oggetti Quadrato. Il risultato è il seguente: Possiamo notare che la freccia segmentata rappresenta l’istanza della classa riferita, mentre la freccia intera indica il rapporto di derivazione. Infatti la classe UsaQuadrilateri contiene l’istanza di un oggetto di classe Quadrato, che a sua volta eredita dalla classe Rettangolo, la quale a sua volta eredita dalla classe Quadrilatero.
136
Derivazione ed ereditarietà
Lezione 5
L’esecuzione del programma UsaQuadrilateri (quando una classe contiene il metodo main può essere chiamata programma) deve essere effettuata eseguendone il metodo main, come indicato nella finestra seguente:
L’esecuzione provoca il seguente risultato:
Prova adesso!
• Usare la specializzazione delle classi • Usare l’override dei metodi
APRI IL PROGETTO Esercizio 5 1 Crea una classe ContoCorrenteConSpese che estende la classe ContoCorrente aggiungendovi l’attributo spese. Un conto con spese possiede un attributo commissione che a ogni operazione di deposito o prelievo, deve essere addebitata sul conto. 2 Si crei una classe UsaContoCorrenteConSpese con costruttore che crei un conto corrente con commissione di 2 Euro e versamento iniziale di 500 Euro, quindi visualizzare il saldo e effettuare un prelevamento di 100 Euro, un versamento di 300 Euro e una nuova visualizzazione del saldo. 3 Confronta la tua soluzione con quella contenuta nel progetto Esercizio5_solux.
137
UdA 2
La OOP di Java
■ L’override dei metodi Quando si definisce una sottoclasse, potrebbe essere necessario modificare il comportamento di uno o più metodi. Nel caso del metodo perimetro(), il funzionamento è sempre il medesimo, in quanto il calcolo del perimetro per un quadrilatero, rettangolo o quadrato prevede l’utilizzo della medesima formula, e cioè la somma della lunghezza dei lati (anche se per quadrilateri regolari è possibile esprimere la formula in modo diverso). In altri casi la sequenza delle operazioni da compiere è diversa: si pensi per esempio all’area. Nel caso di un generico quadrilatero, per calcolare l’area necessitano informazioni aggiuntive rispetto alla misura dei lati come, per esempio, l’ampiezza degli angoli o la misura delle diagonali e, inoltre, le formule risultano diverse e più complesse rispetto a quelle utilizzate per l’area del rettangolo o del quadrato. Infatti, nel caso di rettangoli e quadrati il calcolo dell’area è affare molto più semplice: basta moltiplicare, nel primo caso, il lato lungo per quello corto, oppure, nel secondo caso, il lato per se stesso. Il codice che segue mostra un ipotetico metodo area() che calcola l’area di un quadrilatero, scritto all’interno della classe Quadrilatero:
Per implementare una versione diversa di tale metodo, necessario al calcolo dell’area per la diversa forma geometrica, è necessario riscrivere il metodo area() secondo la tecnica chiamata ◀ override. ▶ Per esempio, la classe Rettangolo potrebbe fornire la propria implementazione del metodo area(), moltiplicando tra loro i primi due lati (si noti che, per come vengono passati al costruttore della classe base, sicuramente latoA e latoB conterranno uno il lato lungo e l’altro il lato corto del rettangolo):
Infine per eseguire correttamente l’override dobbiamo rispettare le seguenti norme di programmazione: ◗ quando decidiamo di riscrivere un metodo in una sottoclasse, dobbiamo utilizzare lo stesso nome e tipo restituito, altrimenti eseguiremo un overload al posto di un ◀ override ▶; ◗ il metodo ridefinito nella sottoclasse non può avere un livello di accessibilità inferiore rispetto al metodo originale della superclasse, non possiamo per esempio dichiarare in override un metodo privato se nella superclasse era protetto; ◗ il tipo di ritorno del metodo deve coincidere con quello del metodo che stiamo ridefinendo. ◀ Override È l’implementazione di un metodo già esistente nella classe base al fine di fornire un comportamento specializzato alle esigenze della classe derivata. ▶
138
Derivazione ed ereditarietà
esempio 16
Lezione 5
La classe Trapezio
In questo esempio viene implementata una classe Trapezio che possiede una propria formula specifica per il calcolo dell’area. Nella classe seguente il costruttore si aspetta base superiore, base inferiore, gli altri due lati e l’altezza; come si nota, è necessario aggiungere un attributo, chiamato altezza, per memorizzare questa nuova informazione, non presente nella classe Quadrilatero. Nel metodo area() viene utilizzata la nota formula che somma la base superiore con quella inferiore, moltiplica per l’altezza e divide per due:
A questo punto, chiamando il metodo area() su un generico Quadrilatero si invocherà la formula di Bretschneider; chiamando invece sulle sottoclassi Rettangolo e Trapezio, si utilizzeranno le formule specifiche per queste due figure. Nelle classi derivate, infatti, il metodo area() è stato completamente sostituito da una nuova implementazione.
Zoom su... impedire LA derivAzione o LA ridefinizione Nella progettazione delle classi che compongono un programma possiamo impedire la derivazione di una determinata classe tramite il livello di visibilità wnal. È utile impedire la derivazione di una classe quando per esempio non sarebbe sensata dal punto di vista logico, oppure quando l’implementazione stessa della classe non potrebbe supportare una classe derivata. La parola chiave wnal identifica in Java un elemento non modificabile: viene infatti usato per dichiarare una costante, una classe che non può essere ereditata, oppure un metodo che non può essere ridefinito. Per esempio, per fare in modo che la classe Quadrilatero degli esempi precedenti non possa essere derivata possiamo agire come di seguito indicato: public ºnal class Quadrilatero { . . . }
Il tentativo di compilare una classe che erediti da Quadrilatero produrrà, in seguito a questa modifica, un errore di compilazione. Per rendere non ridefinibile un metodo possiamo, analogamente alle classi, anteporre wnal prima del nome del metodo: public class Quadrilatero {
139
UdA 2
La OOP di Java
//attributi privati private int latoA, latoB, latoC, latoD; //metodo costruttore public Quadrilatero(int a, int b, int c, int d) { … } //Metodo ºnal non rideºnibile public ºnal int perimetro() { … } }
In questo caso la classe è derivabile ma tuttavia il metodo perimetro() in essa contenuto non è ridefinibile.
■ La classe Object La gerarchia delle classi di Java parte dalla classe Object che ne rappresenta la radice dell’albero, come mostrato nella figura. Boolean Object Component
Container
Panel
System
Per questo motivo, direttamente o indirettamente, tutti gli oggetti presenti in un programma Java derivano dalla classe comune ◀ Object ▶. ◀ La classe Object può essere definita la superclasse di tutte le classi. Ciò significa che, quando codificheremo una classe qualsiasi, erediteremo tutti i metodi di Object ▶.
La classe Object contiene alcuni metodi che possono essere utilizzati ridefinendoli in override. I metodi principali della classe Object sono: ◗ Object clone(); restituisce una copia di questo oggetto; ◗ boolean equals(Object); indica se due oggetti sono equivalenti; 140
Derivazione ed ereditarietà
Lezione 5
◗ void wnalize(); chiamato quando l’oggetto viene liberato dalla memoria; ◗ int hashCode(); restituisce un codice univoco identificativo dell’oggetto; ◗ String toString(); restituisce una descrizione testuale del contenuto dell’oggetto. Il metodo toString() ha il compito di restituire una stringa descrittrice dell’oggetto, tuttavia nella classe Object, che astrae il concetto di oggetto generico, questo metodo non ha alcun significato perché una istanza di Object non può avere variabili d’istanza che lo caratterizzano. È quindi stato deciso di implementare il metodo toString() in modo tale che restituisca una stringa contenente informazioni sul reference del tipo: NomeClasse@indirizzoInEsadecimale
Se proviamo a stampare a video una reference di un oggetto con l’istruzione seguente: System.out.println(nomeDiUnReference);
Otterremo: intervalloDiPuntamento@indirizzoInEsadecimale
Il metodo equals() consente di confrontare due reference: sul primo viene chiamato il metodo e il secondo viene passato come parametro. Tale metodo restituisce true quando i due reference puntano al medesimo oggetto, cioè quando possiedono lo stesso indirizzo di puntamento. Possiamo effettuare il confronto tra due reference a un oggetto anche attraverso l’operatore doppio uguale (==). In effetti il metodo equals() nella classe Object è definito come segue: public boolean equals(Object obj) { return (this == obj); }
La classe Object è troppo generica per poter avere un’implementazione più accurata, per cui in molte sottoclassi di Object, come String, il metodo equals() è stato riscritto in modo tale da restituire true anche nel caso di confronto tra due reference che puntano a oggetti diversi, ma con gli stessi contenuti. Se vogliamo confrontare due oggetti creati da una nostra classe, quindi, dovremmo effettuare l’override del metodo equals(), in modo tale che restituisca true se le variabili d’istanza dei due oggetti coincidono. La classe Object definisce un metodo clone(), con livello di visibilità protected, che restituisce una copia dell’oggetto corrente. La clonazione di un oggetto produce un oggetto della stessa classe dell’originale con le variabili d’istanza contenenti gli stessi valori. Essendo dichiarato protected, per essere reso disponibile per l’invocazione nelle nostre classi, bisognerà sottoporlo a override cambiandone il modificatore a public, come nel seguente frammento di codice: public Object clone() { return super.clone(); }
141
UdA 2
La OOP di Java
esempio 17
I metodi dell’oggetto Object
In questo esempio useremo il metodo clone() della classe Object per effettuare la copia di un oggetto. L’esempio è formato da quattro classi, una classe principale chiamata Punto, il cui schema è indicato dalla figura che segue:
Partiamo dalla classe Punto il cui codice è il seguente:
All’interno di questa classe, che definisce un punto nel piano cartesiano, cioè formato da due attributi che ne identificano le coordinate (x,y) viene usata la parola chiave implements Cloneable. Per poter clonare un oggetto occorre specificare che l’oggetto implementi l’◀ interfaccia ▶ Cloneable, altrimenti si verificherebbe un errore in fase di run-time. L’interfaccia Cloneable non contiene nessun metodo, nemmeno lo stesso metodo clone(). Serve solo come etichetta per indicare che è possibile clonare gli oggetti della classe che implementa l’interfaccia.
142
◀ Interfaccia Una interfaccia è paragonabile a una classe con l’unica differenza che è formata da metodi solo dichiarati e non implementati, infatti tutti i metodi di un’interfaccia sono implicitamente abstract. ▶
Le interfacce e le implementazioni verranno discusse più avanti.
Derivazione ed ereditarietà
Lezione 5
Possiamo notare che all’interno della classe è stato ridefinito il metodo clone(). Nel codice del metodo appare prima di tutto una istanza della classe Punto, passando come parametri al costruttore gli stessi attributi: in questo modo l’oggetto creato conterrà gli stessi valori dell’oggetto di partenza. Il metodo clone() viene ridefinito in quanto esso è di visibilità protected: quindi può essere usato da ogni sottoclasse di Object ma non può essere invocato sugli oggetti della classe a meno che non venga ridefinito come public. Il metodo restituisce il valore ritornato dal metodo clone invocato dalla classe superiore, attraverso l’operatore di referenza superiore (super). L’invocazione di tale metodo avviene all’interno del blocco try ... catch; siamo costretti a usare tale blocco altrimenti si verificherebbe un errore in fase di compilazione. In sintesi per poter clonare o copiare gli oggetti di una classe dobbiamo: 1 ridefinire il metodo clone() come public; 2 dichiarare che la classe implementa l’interfaccia Cloneable; 3 gestire l’eccezione CloneNotSupportedException. A questo punto vediamo il codice della classe UsaPunto che istanzia un oggetto di classe Punto e successivamente lo clona. L’istruzione che effettua la copia dell’oggetto è la seguente: q=(Punto) p.clone();
Si tratta di una istruzione che richiama il metodo clone() dell’oggetto p e lo converte (casting tra oggetti, che verrà discusso ampiamente più avanti) in tipo Punto. In realtà siamo costretti a convertire l’oggetto restituito dal metodo clone() in quanto possiede tipo Object.
A quel punto il programma visualizza il contenuto degli attributi degli oggetti per mettere in luce come essi siano due elementi distinti anche se dello stesso tipo.
143
UdA 2
La OOP di Java
Clonare un oggetto di una classe derivata Adesso vediamo come realizzare il clone di un oggetto di una classe derivata. Per fare questo definiamo una classe Punto3D che eredita dalla classe Punto:
Come possiamo notare anche in questa classe dobbiamo necessariamente ridefinire il metodo clone(). In questo caso basta chiamare il metodo clone della classe superiore per restituirne il valore restituito: return super.clone();
La classe che segue mostra come eseguire quanto indicato, creiamo una classe UsaPunto3D per definire nel metodo main la copia di un oggetto di classe Punto3D:
144
Nella classe derivata non viene indicato implements cloneable, in quanto presente nella classe superiore.
Derivazione ed ereditarietà
Lezione 5
Anche in questo caso notiamo come effettuare la copia di un oggetto di classe Punto3D, dobbiamo convertire l’oggetto restituito dall’invocazione del metodo clone() dall’oggetto p in un tipo Punto3D: q=(Punto3D) p.clone();
Prova adesso!
• Clonare oggetti • Clonare oggetti in classi derivate
APRI IL PROGETTO Esempio17 1 Modifica il codice della classe Punto in modo che gli attributi della classe siano solo privati. 2 Modifica il codice della classe Punto3D in modo che gli attributi della classe siano solo privati. 3 Modifica il codice delle classi UsaPunto e UsaPunto3D in modo da visualizzare il reference degli oggetti. 4 Confronta la tua soluzione con quella contenuta nel progetto Esempio17_solux.
145
UdA 2
La OOP di Java
Verifichiamo le conoscenze g Esercizi a scelta multipla 1 Per fare riferimento, dalla sottoclasse, alla classe da cui si è derivato si utilizza la parola chiave: a) base b) super c) original d) extended 2 Per fare riferimento alla classe attuale si utilizza la parola chiave: a) current b) actual c) class
d) this
3 Per impedire la derivazione di una classe o la ridefinizione di un metodo è possibile utilizzare la parola chiave: a) const b) unvar c) final d) fixed 4 Un sistema tipico per evitare la proliferazione di nomi all’interno di una classe, per esempio in merito ai costruttori, è quello di: a) spostare le funzionalità nella gerarchia di classi b) creare una classe di supporto c) invocare un costruttore da un altro d) spostare le funzionalità negli attributi 5 La clase base di tutti gli oggetti è: a) Base b) Super
c) Object
d) Normal
6 Indica quali, tra i seguenti, sono metodi disponibili in ogni classe: a) clone() c) equals(Object) e) finalize() b) open() d) format() f) hashCode()
g) toRecord() h) toString()
7 Il metodo clone() consente di copiare: a) una classe b) un metodo
d) un attributo
c) un oggetto
8 Per poter effettuare una clonazione di oggetto derivato è necessario: a) utilizzare extends cloneable nella classe da derivare b) utilizzare extends cloneable nella classe superiore c) utilizzare implements cloneable nella classe da derivare d) utilizzare implements cloneable nella classe superiore
g Esercizi di completamento 1 L’...................................................................................... è la creazione di una classe derivata. 2 In Java la derivazione si dichiara con la parola chiave ....................................................................................... 3 L’ereditarietà in Java è solo ....................................................................................... 4 La classe da cui si deriva è detta classe ...................................................................................... o ....................................................................................... 5 Una classe derivata può essere classe ...................................................................................... per una seconda classe derivata. 6 La ridewnizione di un metodo è detta ...................................................................................... e permette di specializzare il comportamento di un metodo nella classe derivata.
146
Derivazione ed ereditarietà
Lezione 5
7 Il metodo ...................................................................................... esegue una copia speculare dell’oggetto, ma il suo funzionamento è ..................................................................... dall’override presente nella speciwca sottoclasse; il metodo ..................................................................... indica se due oggetti sono equivalenti; il metodo ...................................................................................... fornisce invece una rappresentazione testuale del contenuto dell’oggetto. 8 Si dice che una classe ...................................................................................... da un’altra classe quando possiede tutti gli attributi e i metodi della classe di partenza. 9 La classe da cui si deriva un’altra classe viene detta ....................................................................................... 10 Una classe derivata può essere una classe ...................................................................................... per una seconda classe derivata. 11 Si dice che una classe ha una ereditarietà ...................................................................................... quando deriva da una sola classe, mentre si dice che una classe ha una ereditarietà ...................................................................................... quando deriva da più classi contemporaneamente. 12 Ogni classe fondamentale nella lista di ereditarietà deve essere preceduta da una delle tre parole chiave: public, private o protected. Se si omette la parola chiave, il compilatore prenderà il valore di default di ......................................................................................
Verifichiamo le competenze g Problemi 1 Dewnisci la classe ordineArticolo attraverso gli attributi: DescrizioneArticolo, QuantitàOrdinata, PrezzoVendita, DataOrdine, IndirizzoCliente. Dewnisci il metodo per istanziarla e il metodo per ottenere l’importo dell’ordine. Deriva da ordineArticolo la classe spedizioneArticolo, aggiungendo l’attributo DataConsegna. Deriva da quest’ultima la classe fatturaArticolo con l’aggiunta dell’attributo AliquotaIva e il metodo TotaleFattura. 2 Dewnisci la classe movimentoContabile attraverso gli attributi: causale, tipo (che può avere i due valori Dare o Avere), importo. Dewnisci l’intestazione dei metodi archiviaMovimento e registraMovimento senza implementarli. Deriva la classe fatturaPagata dalle classi fatturaArticolo dewnita nell’esercizio precedente e movimentoContabile. 3 Dewnisci una classe Triangolo attraverso le misure dei suoi tre lati: a, b, c. Dewnisci il suo costruttore e il metodo per calcolare il perimetro. Deriva da Triangolo la classe triangoloIsoscele e dewnirne il costruttore. Deriva da triangoloIsoscele la classe triangoloEquilatero e dewnirne il costruttore 4 Dewnsci la classe Mucca come specializzazione di una catena di tre antenati. 5 Dewnisci la gerarchia di classi per dewnire cantanti, musicisti e compositori e i rispettivi metodi e attributi. 6 Dewnisci la gerarchia di classi per dewnire nemici, mostri, giocatore e colpi per un videogioco di tipo spaceinvader, con i rispettivi metodi e attributi. 7 Dewnisci la gerarchia di classi per dewnire una classe Pianoforte a partire da una superclasse StrumentiMusicali. 8 Dewnisci la gerarchia di classi per dewnire i pezzi del gioco degli scacchi e i rispettivi metodi e attributi.
147
UdA 2
La OOP di Java
9 Dewnisci la gerarchia di classi per dewnire un telefono cellulare, metodi e attributi, a partire dalla classe MezziComunicazione. 10 Dewnisci un insieme di classi per la rappresentazione delle principali wgure geometriche (Poligono, Ottagono, Rettangolo, Quadrato, TriangoloIsoscele). Di ognuna deve essere possibile calcolare l’area e il perimetro e confrontare questi valori con quelli di un’altra wgura geometrica. 11 Dewnisci una gerarchia di classi che modelli i dispositivi elettronici discreti (bipoli, tripoli, resistori, condensatori, diodi ecc). 12 Crea una classe Quadrilatero in grado di calcolare area e perimetri di un qualunque quadrilatero. Crea una classe UsaQuadrilatero che istanzi un oggetto di tale classe e lo cloni. 13 Dewnisci e istanzia una classe NumeroReale per rappresentare numeri reali, di tipo double. Per fare questo utilizza un unico wle che contiene la classe dewnita e la classe UsaNumeroReale per istanziare la classe iniziale e clonarla in 3 oggetti. 14 Dewnisci e istanzia una classe NumeroComplesso per rappresentare numeri complessi come coppia di numeri reali (di tipo double). Per fare questo utilizza un unico wle che contiene la classe dewnita e la classe UsaNumeroComplesso per istanziare la classe iniziale e clonarla in 2 oggetti. 15 Dewnisci una classe LineaBus per rappresentare oggetti linea di autobus urbano con il numero identiwcativo, il nome dei due capolinea e con opportuni metodi d’istanza tra cui un metodo del tipo String toString() per la descrizione del suo percorso. Crea una classe UsaLineaBus per clonarla in 3 oggetti. 16 Dewnisci una classe Auto per rappresentare oggetti automobile con il nome della marca, il nome del modello, la targa e l’anno di immatricolazione e con opportuni metodi d’istanza tra cui un metodo del tipo String toString() per la sua descrizione. Crea una classe UsaAuto per clonarla in 5 oggetti. 17 Dewnisci una classe Studente per rappresentare oggetti studente con il cognome, il nome, il codice wscale, il numero di matricola e con opportuni metodi d’istanza tra cui un metodo del tipo String toString() per la sua descrizione. Crea una classe UsaStudente per clonarla in 10 oggetti di tipo Studente. 18 Dewnisci la classe Segmento i cui oggetti rappresentano dei segmenti orizzontali di lunghezza intera sul piano cartesiano. Dewnisci inoltre i seguenti costruttori: _ un costruttore con due parametri, left di tipo Punto e lunghezza di tipo intero, che crei un segmento con vertice sinistro di coordinate left e lunghezza; _ un costruttore con un parametro intero che crei un segmento con vertice destro (0,0) e lunghezza uguale al parametro se maggiore di zero, altrimenti di lunghezza unitaria; _ un costruttore con due parametri formali di tipo Punto (left e right), che rappresentano i vertici sinistro e destro del segmento se allineati, altrimenti con vertice sinistro left e lunghezza unitaria; Dewnisci i seguenti metodi: _ getLeft(), che restituisce il vertice sinistro del segmento; _ getRight(), che restituisce il vertice destro del segmento; _ getLength(), che restituisce la lunghezza del segmento; _ toString() che restituisce una stringa che è una rappresentazione testuale del segmento, per esempio “Segmento[(xleft, yleft),length]”, dove xleft è l’ascissa del vertice sinistro, yleft è l’ordinata del vertice sinistro e length è la lunghezza. _ inclusoIn() di tipo boolean con un singolo parametro di tipo Segmento, che restituisce true se e solo il segmento su cui il metodo viene invocato è contenuto nel segmento passato come parametro. Crea una classe UsaSegmento per clonare almeno 2 oggetti e veriwcarne il funzionamento.
148
Gli array
lezione
Lezione 6
6
gLi ArrAy
in qUestA Lezione impAreremo...
• a definire un array a una dimensione • a effettuare ordinamenti e ricerche su array • a definire un array a due dimensioni
■ I vettori in Java In Java un ◀ vettore ▶ permette di utilizzare un elenco di elementi ed è l’istanza della classe array. Per utilizzare un array in Java dobbiamo passare attraverso tre fasi, come per gli oggetti: dichiarazione, creazione e inizializzazione.
◀ Vettore È un insieme ordinato di oggetti omogenei, ovvero appartenenti a un unico tipo. È possibile accedere ai vari oggetti del vettore utilizzando il loro numero di indice. ▶
La dichiarazione di array Di seguito presentiamo due dichiarazioni di array. Nella prima dichiariamo un array di char (tipo primitivo), nella seconda dichiariamo un array di istanze di Button (classe appartenente al package java.awt): //dichiarazione array di tipo primitivo char alfabeto []; //oppure char [] alfabeto; //dichiarazione array di oggetti Button pulsante []; //oppure Button [] pulsante;
Quindi per dichiarare un array dobbiamo inserire una coppia di parentesi quadre prima oppure dopo l’identificatore.
149
UdA 2
La OOP di Java
La creazione di array Un array è una classe speciale in Java e in quanto tale possiede una istanza leggermente diversa da quelle di tutti gli oggetti fin qui studiati. La sintassi è la seguente: pulsante = new Button[5]; alfabeto = new char[21];
Per specificare la dimensione dell’array dobbiamo scriverne il valore nella parentesi quadra: questa operazione è obbligatoria. Un array di 5 elementi possiede elementi che vanno dall’indice posizionale 0 all’indice posizionale 4, in quanto il primo elemento dell’array possiede sempre posizione zero e non 1. A questo punto gli elementi dei due array dichiarati vengono inizializzati a un valore null. Dichiarazione e creazione possono essere utilizzate nella stessa istruzione. Nel caso precedente avremmo potuto scrivere: //dichiarazione vettore di tipo primitivo di 21 elementi char alfabeto[] = new char[21]; //dichiarazione vettore di oggetti di 5 elementi Button pulsante[] = new Button[5];
L’inizializzazione di array Per inizializzare un array dobbiamo inizializzarne ogni singolo elemento di cui esso è formato, come nel codice di esempio che segue: alfabeto [0] = ‘a’; alfabeto [1] = ‘b’; alfabeto [2] = ‘c’; ........... alfabeto [20] = ‘z’; pulsante [0] = new Button(); pulsante [1] = new Button(); ........... pulsante [4] = new Button();
Come possiamo notare l’indice posizionale degli array comincia sempre da zero. Possiamo affermare che inizializzare un array in questo modo è assai scomodo e poco leggibile, immaginiamo per esempio di dover inizializzare un vettore di 10000 elementi! Possiamo allora eseguire tutti e tre i passi principali per creare un array tramite una particolare sintassi che di seguito presentiamo: char alfabeto [] = {‘a’, ‘b’, ‘c’, …, ‘z’}; Button pulsante [] = {new Button(), ... , new Button()};
Si noti la differenza tra un array dichiarato di tipo primitivo e di tipo oggetto. Il primo contiene direttamente i suoi elementi, mentre il secondo contiene solo reference al posto degli elementi stessi. Esiste inoltre un attributo della classe array, chiamato length che restituisce la dimensione effettiva dell’array stesso e che possiamo utilizzare per velocizzarne l’inizializzazione.
150
Gli array
Lezione 6
Per esempio per dichiarare, creare e inizializzare un array di tipo intero di nome numeri[ ] possiamo scrivere il seguente codice:
Per accedere all’i-esimo elemento di un vettore è necessario indicare tra parentesi quadra l’indice dell’elemento che vogliamo identificare. esempio 18
Accesso a un array di numeri
Dopo aver inizializzato un vettore con 4 elementi secondo lo schema seguente: vettore Dati
37
53
11
28
Indice
0
1
2
3
determiniamo il valore della somma del primo e dell’ultimo elemento. Oltre ai dati di input e di output occorre, quando si utilizzano strutture dati, definire in fase di analisi anche quale struttura si intende utilizzare. In questo primo esempio, la struttura da utilizzare è indicata direttamente dal testo del problema e consiste, semplicemente, in un vettore composto da quattro elementi di tipo intero.
In molti casi, nasce l’esigenza di accedere a tutti gli elementi di un vettore per poter eseguire una elaborazione su ciascuno di essi. Nel caso in esame, torna molto utile ricorrere alla struttura della iterazione enumerativa: attraverso di essa si utilizza un indice che assume tutti i valori che vanno da 0 alla dimensione –1 del vettore e che serve per riferirsi a ciascun elemento.
Inserimento e lettura con ciclo L’inserimento o caricamento e la visualizzazione dei dati di un vettore possono essere effettuati attraverso un ciclo for. Il codice che segue mostra come riempire un array con valori provenienti da tastiera, in seguito avviene la stampa sempre mediante un ciclo for:
151
UdA 2
La OOP di Java
Come avrete notato appare la parola chiave throws Exception accanto al nome del metodo main. Come abbiamo visto nell’unità di apprendimento precedente siamo obbligati a indicare ciò ogni volta che utilizziamo la lettura da tastiera. Un errore piuttosto frequente quando si lavora con i vettori consiste nel superamento dei limiti del vettore, ma Java fornisce un meccanismo che segnala il problema e impedisce di accedere oltre la dimensione prefissata. In questo caso stiamo tentando di accedere all’elemento 10 del vettore dichiarato in precedenza con dimensione 5. Il runtime environment di Java segnala che si è tentato un accesso a un elemento non esistente dell’array, indicando la linea di codice incriminata e l’indice richiesto. L’esecuzione del codice dunque si interrompe, segnalando una eccezione. Questo è il modo di gestire gli errori utilizzato dal linguaggio Java, che verrà affrontato più avanti. Per ora, basti sapere che il linguaggio consente l’intercettazione delle eccezioni e la loro gestione.
L’output del programma è il seguente:
152
Gli array
esempio 19
Lezione 6
Trova giorni tra due date
In questo esempio vi sono due classi, una denominata Giorni con tre attributi che definiscono rispettivamente giorno, mese e anno di una determinata data, e una seconda chiamata UsaGiorni necessaria per verificare il funzionamento della classe Giorni: ▶
La classe Giorni possiede tre metodi: il primo è semplicemente il costruttore, il secondo (daInizioAnno) restituisce un dato intero che rappresenta i giorni trascorsi dall’inizio dell’anno e la data memorizzata nella classe, il terzo (bisestile) restituisce true se l’anno passato come parametro è bisestile oppure false in caso contrario:
Come possiamo notare il metodo daInizioAnno utilizza il vettore mesi[ ] che contiene la durata dei mesi dell’anno. Attraverso un ciclo for viene accumulato nella variabile parziale il totale dei giorni fino al mese precedente la data in questione. Alla variabile parziale viene quindi sommato il numero dei giorni della data. Il calcolo successivo prevede l’aggiunta di un controllo se il giorno in questione è il 29 febbraio. In questo caso viene accettata la data solo l’anno è bisestile, in caso contrario il me153
UdA 2
La OOP di Java
todo daInizioAnno restituisce il valore –1 che identifica un errore. Infine viene sommato un giorno in più se l’anno è bisestile e la data appartiene a un mese superiore a febbraio. La classe UsaGiorni ha lo scopo di verificare quanto enunciato. In sostanza è il programma che istanzia un oggetto di classe Giorno chiamato data al quale viene passata come parametro la data 25 marzo 2012:
• Usare gli array
Prova adesso! APRI IL PROGETTO Esempio 19 1 Modifica il codice della classe Giorni in modo tale che il metodo daInizioAnno restituisca -1 anche quando la data è errata, per esempio quando il mese è inferiore a zero o maggiore di 12, oppure il giorno è riferito a un mese errato (per esempio 31/11). 2 Modifica il codice della classe UsaGiorni in modo tale che stampi errore se la data inserita è errata.
■ L’ordinamento a bolle L’algoritmo più famoso, anche perché è tra i più utilizzati per la sua semplicità, si chiama ordinamento a bolle, dall’inglese bubble-sort, così chiamato perché nella sua esecuzione assomiglia al movimento delle bolle di diversa dimensione immerse in un contenitore pieno d’acqua che si spostano verso l’alto o il basso a seconda del loro peso. Analogo “destino” è riservato ai numeri del vettore: l’algoritmo confronta a due a due gli elementi adiacenti e li scambia tra loro se il primo elemento è maggiore (“più pesante”) del secondo, spostando verso l’alto (prime posizioni) i più piccoli (i più “leggeri”) e verso il basso (ultime posizioni) i più grandi (i più “pesanti”). Vediamo graficamente una simulazione, disponendo il vettore verticalmente. Partiamo dalla prima posizione e confrontiamo due alla volta gli elementi consecutivi finché si verifica che il primo è più grande del secondo (posizioni 2 e 3 contenenti 35 e18): si esegue lo scambio dei due numeri.
154
Successivamente si prosegue il confronto e un nuovo scambio è necessario tra il 35 e l’8 nelle posizioni 3 e 4.
Di seguito è necessario operare gli scambi su altre coppie: (35, 14), (40, 5) e (40, 37).
Gli array
Lezione 6
Avviciniamo le colonne in modo da seguire “visivamente” il movimento dei “numeri bolle”. È possibile vedere come i numeri pesanti (35 e 40) si spostano velocemente verso il fondo: il numero 40, che è il più grande tra tutti i numeri presenti nel vettore, viene posizionato nella sua destinazione finale (ultima cella del vettore). Anche i numeri “leggeri” si spostano, ma più lentamente: infatti il 18, l’8, il 14, il 5 e il 37 sono “saliti verso l’alto”, ma di una sola posizione!
Effettuiamo ora un secondo passaggio completo riscorrendo il vettore dalla prima posizione: Di nuovo i numeri pesanti (20 e 35) si spostano verso il fondo: il numero 20 ferma la sua discesa quando incontra un numero maggiore di lui, appunto il 35, che inizia la discesa fino alla sua posizione definitiva. I numeri “leggeri” si spostano sempre lentamente verso l’alto: infatti l’8, il 18, il 14 e il 5 continuano la loro ascesa, sempre di una posizione alla volta.
Nuova iterazione (la terza) e nuovi spostamenti: In questa iterazione: – il 18 si muove dalla sua posizione per raggiungere il numero 20; – il numero 18 ferma la sua discesa e la comincia il numero 20, che si posiziona prima del 35; – i numeri “leggeri” si spostano lentamente verso l’alto: infatti l’8, il 14 e il 5 continuano la loro ascesa, sempre di una posizione alla volta.
Successive iterazioni e nuovi spostamenti: In questa iterazione si muovono solo due numeri: il 18, che dalla sua posizione si sposta e raggiunge la cella n. 4, e il 5, che continua a salire di una posizione alla volta.
Anche in questa iterazione si muovono solamente due numeri: il 14, che si muove di una posizione, e il 5, che continua a salire di una posizione alla volta.
Con un solo ultimo scambio si esaurisce la successiva iterazione: ora il vettore è completamente ordinato.
Possiamo formulare alcune osservazioni: ◗ a ogni iterazione i numeri “leggeri” che non sono in posizione ordinata si spostano verso l’alto di una sola posizione: quindi, nell’ipotesi in cui il più piccolo si trovasse nell’ultima cella di un vettore di n posizioni, è necessario effettuare n iterazioni; ◗ a ogni iterazione solamente il numero di “peso” maggiore viene spostato fino alla sua definitiva posizione: infatti ogni numero pesante in discesa “si ferma” quando incontra un numero più pesante di lui, che inizia esso stesso a muoversi verso il basso. 155
UdA 2
La OOP di Java
Per realizzare il nostro algoritmo occorrono due cicli: ◗ un ciclo interno che si occupa degli scambi tra tutte le coppie (n – 1) degli elementi in modo da portare il più pesante nella sua posizione definitiva; ◗ un ciclo esterno che ripete questo procedimento per ogni elemento nel vettore. L’esempio che segue illustra la codifica dell’algoritmo. esempio 20
L’algoritmo di bubble-sort rientra in una famiglia di algoritmi chiamati “algoritmi di ordinamento per scambio”, in quanto il procedimento consiste in un insieme di operazioni di scambio di posto tra elementi presenti in un vettore a seconda dell’esito del confronto tra le coppie di elementi stessi.
Ordinamento bubble-sort
Il codice rappresenta una classe che riempie un array di numeri casuali e ne effettua l’ordinamento secondo l’algoritmo indicato sopra: ▶
L’esecuzione produce il seguente risultato: ▶
• Usare gli array
Prova adesso! APRI IL PROGETTO Esempio 20 1 Modifica il codice in modo da far caricare il vettore con numeri da tastiera e non generandoli random dal programma.
156
Gli array
Lezione 6
■ La ricerca sequenziale in un array Ricercare un elemento in un vettore significa individuare la posizione che questo assume all’interno del vettore. La ricerca avviene scandendo sequenzialmente tutti gli elementi dell’array fino all’ultimo elemento dell’array. Possiamo considerare le tre situazioni possibili: ◗ l’elemento ricercato non è presente nel vettore; ◗ l’elemento è presente una sola volta; ◗ l’elemento è presente più volte nel vettore. La strategia risolutiva può essere così sintetizzata: Strategia risolutiva ◗ Si legge il numero di cui si vuole effettuare la ricerca. ◗ Si inizia il confronto con la variabile memorizzata nella prima posizione del vettore e si prosegue fino a: – trovare il numero ricercato, oppure – esaurire il vettore senza aver trovato il numero.
Dovendo ricercare solo la prima ◀ occorrenza ▶ ◀ Occorrenza Ripetizione di un termidel numero, non è necessario scorrere il vettore ne durante una sessione di ricerca. ▶ fino all’ultimo elemento dell’array, il ciclo terminerà quando è stato trovato un elemento uguale a quello richiesto. Questa osservazione ci suggerisce di utilizzare una variabile sentinella e un ciclo a condizione finale (o iniziale) invece di un ciclo a conteggio, che ci obbligherebbe in ogni situazione a eventuali elaborazioni inutili. esempio 21
Ricerca sequenziale
In questo esempio viene dichiarato un vettore di 100 elementi caricato con numeri interi casuali. Dopo aver letto da tastiera il valore numerico da ricercare, inserito nella variabile numeroDaCercare, avviene il ciclo do while di ricerca. Tale ciclo termina quando l’elemento è stato trovato (trovato=true) oppure quando il contatore i ha raggiunto l’ultimo elemento. Quando l’elemento viene trovato ne viene salvata la posizione nella variabile posizioneTrovato che viene successivamente stampata a video:
157
UdA 2
La OOP di Java
■ La ricerca dicotomica in un array Riconsideriamo il vettore con i dati ordinati: la particolare disposizione dei dati ci suggerisce un algoritmo che sfrutti proprio questa particolarità per effettuare la ricerca. Confrontiamo il numero da trovare con l’elemento centrale del vettore di n elementi: in tal modo possiamo ridurre la ricerca a un sottovettore di dimensione n/2, in quanto ci troveremo in una delle seguenti situazioni: 1 il numero è minore dell’elemento centrale vettore [n/2]; 2 il numero è maggiore dell’elemento centrale vettore [n/2]; 3 il numero è uguale all’elemento centrale vettore [n/2] (caso particolare). Poiché i numeri nel vettore sono ordinati, nel primo caso il numero dovrà essere ricercato nella prima metà del vettore e nel secondo caso nella seconda metà. Trascuriamo il caso particolare (che è il caso migliore), in quanto l’algoritmo deve essere scritto considerando sempre il caso peggiore. La stessa operazione viene reiterata sul sottovettore considerato, effettuando il confronto con il nuovo elemento centrale e via di seguito; in tal modo, con ogni confronto si continua a dimezzare il vettore finché esso si riduce a dimensione unitaria. Questo algoritmo rientra in una particolare famiglia di algoritmi che sfruttano la strategia del divide et impera e riducono la complessità dell’algoritmo effettuando la suddivisione progressiva della dimensione del problema in due parti, cioè applicando la riduzione dicotomica (dal greco dikha, “in due”, e temnein, “tagliare”): ogni iterazione infatti dimezza il numero di elementi che devono essere confrontati, mentre con la ricerca sequenziale si diminuiva solamente di un elemento a confronto! Partendo dalla tabella seguente, effettuiamo un semplice calcolo. problema
dimensione naturale n
passo 1
n/2
passo 2
n/4
passo 3
n/8
passo 4
n/16
… passo X
n/(2x)
Quindi, il numero di passi x da effettuare in un problema di dimensione naturale n è: x = log2 n Questo algoritmo rientra nella classe di complessità logaritmica e spesso viene indicato con il nome di algoritmo di ricerca logaritmica (o dicotomica). Descriviamo dunque l’algoritmo. Indichiamo con inizio e ºne, rispettivamente, la prima e l’ultima posizione del vettore, indicate in modalità parametrica per poter utilizzare il ciclo iterativo a partire dal primo confronto; con medio indichiamo la variabile che conterrà il valore intero della posizione centrale ottenuto dalla divisione tra numeri interi. Quindi: ◗ individuiamo la posizione dell’elemento centrale medio = (inizio + ºne)/2; ◗ effettuiamo il confronto (numero = vettore[medio]) con i tre casi: 1 se è vero termina la ricerca, ponendo per esempio (trovato TRUE); 2 se (numero < vettore[medio]) riduciamo il vettore alla prima metà spostando l’estremo superiore proprio a metà del vettore, cioè sostituendo ºne con medio; 3 se (numero > vettore[medio]) riduciamo il vettore alla seconda metà spostando l’estremo inferiore proprio a metà del vettore, cioè sostituendo inizio con medio; ◗ ripetiamo questa operazione finché i due estremi inizio e ºne, convergendo, diventano uguali, con l’eventuale fallimento della ricerca se il numero non è presente nel vettore. 158
Gli array
esempio 22
Lezione 6
Ricerca binaria
In questo esempio vogliamo applicare le conoscenze relative alla ricerca binaria. Rispetto alla ricerca sequenziale vista in precedenza, sono state introdotte due variabili per la memorizzazione degli estremi del vettore man mano che viene dimezzato (inizio e wne). Dopo aver dimezzato la posizione di ricerca, viene confrontato l’elemento del vettore identificato dalla posizione contenuta in posizioneControllata.
■ Le matrici Nei paragrafi precedenti abbiamo visto come lavorare con array a una dimensione, chiamati anche vettori. Possiamo però definire anche vettori a due o più dimensioni. Solitamente i vettori a due dimensioni sono chiamati ◀ matrici ▶. ◀ Matrici Una matrice è un insieme di dati dello stesso tipo organizzati in una griglia dove ciascun elemento che compone la matrice è individuato dal numero di riga e dal numero di colonna in cui l’elemento è posizionato. ▶
Per definire una matrice dobbiamo specificare due indici che ne indicano la dimensione di riga e colonna. Il codice seguente: int matrice[][] = new int[3][4];
159
UdA 2
La OOP di Java
dichiara una matrice con elementi di tipo int formata da 3 righe e MATRICE 4 colonne. Si intende che la struttura di nome matrice è composta 0 7 37 24 3 da 3 righe (da 0 a 2) e da quattro colonne (da 0 a 3), come mostraRighe 1 45 12 18 81 to nella figura a lato: ▶ 2 11 53 37 28 Per identificare il singolo elemento della matrice dobbiamo speci0 1 2 3 ficarne il numero di riga e il numero di colonna. Con riferimento alla matrice precedente possiamo affermare che 18 è contenuto Colonne nella cella individuata da matrice[1][2]. Quando vogliamo accedere a un elemento Analogamente ai vettori, possiamo inizializdella matrice dobbiamo racchiuderne le cozare la matrice al momento della sua dichiaordinate tra parentesi quadre. Dunque, per razione come di seguito: assegnare il valore 21 al secondo elemento della terza riga della matrice indicata nel int[][] matrice = { frammento di codice precedente, dobbiamo {1, 2, 3, 4}, scrivere: {5, 6, 7, 8}, {9, 10, 11, 12}, matrice [2][1] = 21; {13, 14, 15, 16}, {17, 18, 19, 20}}; ricordando che, anche per le matrici, la numerazione per ogni dimensione comincia da 0. Il caricamento di una matrice avviene in modo analogo ai vettori, il codice che segue mostra come caricare e visualizzare a video una matrice di 5 righe e 4 colonne. La soluzione del problema prevede l’utilizzo di due iterazioni enumerative, una interna all’altra. La più interna serve per scrivere gli elementi di una riga e utilizza un indice che va da 0 al numero di colonne; l’iterazione più esterna contiene la routine per la scrittura di una riga e il comando per andare a capo riga e utilizza un indice che va da 0 al numero delle righe. ▼
Produce in output: ▼
Tutti i valori nelle colonne sono allineati a sinistra e occupano 4 caratteri (due per il numero, uno per lo spazio, uno per il separatore |).
160
Gli array
esempio 23
Lezione 6
Il crivello di Eratostene
Il crivello o “setaccio” di Eratostene consente di selezionare i numeri primi da un elenco di numeri procedendo per eliminazione. Il metodo costruttore Eratostene( ) riempie un array chiamato numeri[ ] di numeri progressivi, in questo caso da 1 a 1000. Il vettore parallelo primi[ ] viene riempito di valori boolean true. L’algoritmo procede in questo modo: ◗ 1 è primo ◗ 2 è primo, tolgo i suoi multipli ◗ 3 è primo, tolgo i suoi multipli ◗ ... e così via fino a trovare il mio massimo. Il metodo calcolaPrimi( ) effettua un ciclo nel quale prima di tutto passa il primo numero al metodo elMul( ) affinché vengano eliminati tutti i suoi multipli, partendo dal 2. Quindi richiama il metodo prossimo( ) che restituisce il prossimo numero primo da controllare fino alla fine del vettore. Il metodo scriviPrimi( ) effettua un ciclo per tutti e due i vettori mostrando solo i numeri il cui corrispondente valore booleano posto nel vettore primi è true. ▶
L’esecuzione è mostrata di seguito: ▼
161
UdA 2
La OOP di Java
Verifichiamo le conoscenze g Esercizi a scelta multipla 1 Indica le istruzioni corrette che dichiarano un array di numeri interi: a) int vettore; b) int[] vettore; c) int vettore[int]; d) int[] vettore[int]; e) int vettore[]; 2 Indica le 3 operazioni preliminari, nell’ordine corretto, che consentono di dichiarare un array e iniziare a usarlo: a) .................... istanza b) .................... inizializzazione c) .................... dichiarazione d) .................... creazione 3 Per accedere al primo elemento di un vettore possiamo scrivere: a) vettore[1]; b) vettore[0]; c) vettore(1);
d) vettore(0);
4 Indica quale tra le seguenti è una istruzione valida di dichiarazione di più vettori: a) int v1[], v2; b) int[] v1[], v2[]; c) int[] v1, v2, v3; d) int v1[], v2[]; 5 Quale codice tra i seguenti crea un array di dieci elementi interi: a) new int * 10; b) int[10]; c) new int [10];
d) new int[] 10;
6 Quale codice tra i seguenti inizializza in fase di creazione un array di tre interi: a) int[] v = [1, 2, 3]; b) int v = {1, 2, 3}; c) int[] v = {1, 2, 3};
d) int v = new int[1, 2, 3];
7 Quale tra i seguenti frammenti di codice inizializza in fase di creazione una matrice: a) int[][] m = [1, 2, 3][4, 5, 6][7, 8, 9]; b) int[][] m = {{1, 2,3}, {4, 5, 6}, {7, 8, 9}}; c) int[][] m = new int[1, 2, 3][4, 5, 6][7, 8, 9]; d) int[][] m = new int{{1,2,3},{4,5,6},{7,8,9}}; 8 Quali di questi frammenti di codice sono errati? a) char vett[7] = “Ricorda”; b) int dimensione = 10; char vett[dimensione]; c) int vett[4] = {1, 2, 3, 4}; vett[4] = 0; d) int vett[] = {1, 2, 3, 4}; vett[4] = 0; e) int vett[4] = {1, 2, 3}; int vett[3] = 2; f) int vett[][] = {{1, 2}, {1}}; int[3][1] = 0; g) int vett[] = new int[4]; h) int vett[5] = new int[5]; i) int vett[] = new long[4];
162
Gli array
Lezione 6
g Test vero falso 1 2 3 4 5 6
Un array è un oggetto e quindi può essere dichiarato, istanziato e inizializzato. Un array bidimensionale è un array i cui elementi sono altri array. Il metodo length restituisce il numero degli elementi di un array. Un array non è ridimensionabile. Un array è eterogeneo di default. Un array di interi può contenere come elementi byte, ovvero le seguenti righe di codice non producono errori in compilazione: int arr [] = new int[2]; byte a = 1, b=2; arr [0] = a;arr [1] = b;
7 Un array di interi può contenere come elementi char, ovvero le seguenti righe di codice non producono errori in compilazione: char a = ‘a’, b = ‘b’; int arr [] = {a,b};
8 Un array di stringhe può contenere come elementi char, ovvero le seguenti righe di codice non producono errori in compilazione: String arr [] = {‘a’, ‘b’};
Verifichiamo le competenze g Problemi 1 Crea una classe che carichi un vettore di numeri casuali e calcoli il totale dei valori contenuti. 2 Crea una classe che carichi un vettore di numeri interi letti da tastiera con dimensione data in input e mostri a video la media e lo scarto quadratico medio dei valori contenuti nel vettore. 3 Dopo aver caricato in memoria un vettore di interi con dimensione data in input, accoda un nuovo elemento, aumentando la dimensione del vettore. 4 Crea una classe che carichi un vettore di interi con dimensione non superiore a 100, elimina dal vettore un elemento la cui posizione è speciwcata in input. 5 Crea una classe che carichi un vettore di interi con dimensione non superiore a 100, acquisisci in input i dati per modiwcare un elemento del vettore. 6 Crea una classe che carichi una matrice di interi di 4 righe e 4 colonne, calcola il totale riga per riga e colonna per colonna.
163
UdA 2
La OOP di Java
7 Crea una classe che carichi una matrice quadrata di 4 righe e di 4 colonne, composta da valori interi, calcola la somma degli elementi che appartengono alla diagonale principale della matrice. 8 Crea una classe che carichi un array di numeri casuali. Dato un numero (inserito da tastiera) veriwca l’esistenza di tale numero nell’array ed elimina tutte le occorrenze compattando l’array. 9 Crea una classe che carichi un array contenente un elenco di nominativi e il suo array parallelo il codice wscale. Crea una routine in grado di cercare il codice wscale (tramite ricerca binaria) e visualizzarne il nome. 10 Crea una classe che carichi un array di numeri casuali, quindi ricerca il valore massimo e minimo e stampane la posizione, eliminandoli dall’array (compattando la struttura). 11 Crea una classe che carichi due array di interi, trova gli elementi con lo stesso valore e stampane la posizione. 12 Crea una classe che carichi una matrice di numeri casuali interi, trova il valore massimo e quello minimo per ogni riga. 13 Crea una classe che carichi una matrice di numeri casuali, calcola la sommatoria degli elementi di ciascuna riga e di ciascuna colonna e immetti i risultati in due vettori separati. 14 Crea una classe che carichi due array paralleli riempiti di numeri casuali interi, crea un terzo array nel quale inserire, ordinatamente, gli elementi dei vettori iniziali. 15 Crea una classe che carichi due array paralleli, nel primo vi sono un elenco di nominativi e nel secondo delle date di nascita espresse come (anno mese giorno). Ordina i due array e ricerca i nominativi nati fra due date.
164
Interfacce e casting tra oggetti
lezione
Lezione 7
7
interfAcce e cAsting trA oggetti in qUestA Lezione impAreremo...
• a convertire il tipo di un oggetto • a conoscere e utilizzare metodi e classi astratte • a definire e implementare semplici interfacce
■ Le conversioni in Java Il linguaggio Java offre numerose combinazioni di conversioni possibili, le cui principali sono: ◗ allargamento delle primitive (primitive widening): è il processo di conversione di una variabile più piccola in una più capiente; per esempio, un int può essere convertito in un long senza perdita di dati, perché quest’ultima è più capiente; ◗ restringimento delle primitive (primitive narrowing): è il processo opposto, quando una variabile viene convertita in una meno capiente, come nella trasformazione di un long in un int; in questo caso è necessario esplicitare il cast, perché può verificarsi una perdita di informazione; ◗ allargamento dei riferimenti (reference widening); è l’esecuzione di una conversione da una classe derivata a una delle possibili superclassi; ◗ restringimento dei riferimenti (reference narrowing); è l’operazione che permette di convertire un riferimento da una classe base a una classe più specifica; ◗ conversioni di stringhe: qualsiasi tipo di dato primitivo, incluso null, può essere convertito in una stringa, semplicemente utilizzando l’operatore di concatenamento più (+). Per esempio: int a = 5; String s = “” + a;
Le conversioni si applicano principalmente in due contesti: quando avviene un assegnamento e anche quando le espressioni vengono passate come parametri di metodo.
165
UdA 2
La OOP di Java
Upcasting Quando si crea una sottoclasse di una classe base, si sta creando un nuovo tipo di dato. Se per esempio la classe A deriva da B possiamo affermare che A è di tipo B; allo stesso modo, se Cane deriva da Animale, si dice che Cane è di tipo Animale. Se una variabile è di tipo Animale, può contenere un riferimento anche a oggetti di tipo Cane: public class Animale { … } public class Cane extends Animale { … } public static void main( String[] args ) { Animale a = new Cane(); }
Il fatto di poter ospitare un riferimento a un og◀ Upcasting L’upcasting è l’esecuzione getto il cui tipo è derivato dal tipo della variabidi una conversione da una classe derile è una caratteristica costruita nel linguaggio vata a una delle possibili superclassi. ▶ Java, ed è chiamata ◀ upcasting ▶. L’operazione di upcasting è sempre sicura in quanto passiamo da un dato più specifico a uno più generale. Una sottoclasse è infatti l’insieme degli elementi della classe base più eventuali aggiunte; la sottoclasse può contenere dunque anche più metodi della classe base, ma contiene come minimo tutti i metodi della superclasse. L’unica cosa che può succedere all’interfaccia di un oggetto sottoposto a upcasting è di perdere dei metodi, mai di guadagnarne. Per questo motivo, l’operazione è supportata direttamente dal compilatore senza richiedere una sintassi specifica né generare errori. L’upcasting può essere esplicito, ma è assolutamente opzionale, non obbligatorio. Nel codice che segue notiamo come venga effettuato un upcasting mediante casting esplicito, in cui avviene la conversione dell’oggetto Cane in un riferimento di tipo Animale: public static void main( String[] args ) { Animale a = (Animale) new Cane(); }
Downcasting L’operazione inversa dell’upcasting è il ◀ down◀ Downcasting Il downcasting è l’ocasting ▶, che deve essere esplicitato dal properazione che permette di convertire grammatore e che consente di passare da un un riferimento da una classe base a tipo A a una sua sottoclasse B. una classe più specifica. ▶ Per esempio, un oggetto Cane contenuto in una variabile di tipo Animale (operazione consentita per via dell’upcasting automatico), può essere riconvertito nel tipo Cane, ma questa operazione deve essere esplicitata tramite un cast, indicando cioè il tipo a cui convertire, indicato tra parentesi tonde: Animale ºdo = new Cane(); Cane jack = (Cane) ºdo;
Se si omette il downcast (chiamato, in generale e per brevità, anch’esso, cast), il compilatore solleva un errore. Specificare il tipo tra parentesi è il modo che il programmatore ha a disposizione
166
Interfacce e casting tra oggetti
Lezione 7
per dichiarare al compilatore: “so che i due tipi di dato non sono perfettamente compatibili, ma so anche che l’oggetto che voglio convertire è effettivamente del tipo di dato a cui voglio convertire”. Quando affronta un’operazione di downcast, Java verifica che effettivamente l’oggetto da convertire sia del tipo richiesto, e in caso contrario solleva un’eccezione di tipo Class CastException. Per esempio, la porzione di codice seguente crea un oggetto stringa e prova a eseguire un casting, che però fallisce, perché l’oggetto o contiene un oggetto che non è di tipo Cane (né di classe Cane né di una delle sue superclassi): Object o = new String(“Prova”); Cane c = (Cane) o;
I prefissi up e down utilizzati nelle parole upcasting e downcasting derivano storicamente dalla rappresentazione grafica dei diagrammi di classe, che visualizzano gerarchicamente un insieme di oggetti. In questi diagrammi si parte infatti solitamente dalla classe base e si scrivono, in colonna verso il basso, le varie classi derivate, in sequenza. Per questo motivo, upcasting si riferisce al fatto di risalire nella gerarchia, mentre downcasting allude alla discesa nella stessa.
Zoom su... conversioni di ArrAy e nULL Le conversioni disponibili nel linguaggio Java supportano anche due tipi particolari di oggetti: null e Array. L’upcasting è supportato per il tipo null e per qualsiasi array della classe Object. È dunque possibile convertire null in un oggetto di tipo Object: Object o = null;
Di conseguenza, questo meccanismo consente a Java di assegnare il valore null a qualsiasi oggetto, visto che tutti derivano da Object; inoltre, è possibile assegnare a una variabile di tipo Object un qualsiasi tipo di array. In questa porzione di codice, per esempio, viene creato un array di interi di 10 elementi, che viene poi assegnato alla variabile oggetto di tipo Object: int[] c = new int[10]; Object oggetto = c;
Il downcasting degli array funziona in modo simile: è infatti possibile convertire qualsiasi riferimento di tipo Object in un array. Nell’esempio seguente, viene creato un array di interi lungo 10 elementi associato a una variabile di tipo Object eseguendo in tal modo un upcasting; nella riga di codice successiva, la variabile di tipo Object subisce il downcasting al tipo array int[]. L’operazione si esegue senza errori perché l’oggetto è in effetti un array di interi: Object oggetto = new int[10]; int[] c = (int []) oggetto;
Il casting funziona anche tra array di tipo diverso, a patto che l’operazione richiesta sia disponibile per le primitive che formano l’array.
167
UdA 2
La OOP di Java
Per esempio, è possibile convertire un array di interi in un array di interi lunghi utilizzando l’upcasting in questo modo: long[] al = new int[10];
Il dowcasting avviene in modo simile al dowcasting delle primitive, ma ovviamente il tipo indicato tra le parentesi tonde riporta le doppie parentesi quadre che identificano gli array: int[] a1=(int[]) new long[10];
Un meccanismo non diverso da quanto visto per gli array delle primitive si applica agli array di oggetti: l’upcasting e il downcasting di questa tipologia di array è possibile se è possibile la stessa operazione di cast a livello dei semplici oggetti. Si considerino, per esempio, le due classi Animale e Cane, dove Cane è una sottoclasse di Animale: public class Animale { … } public class Cane extends Animale { … }
Essendo Animale e Cane parte della medesima gerarchia è possibile eseguire delle conversioni. Per esempio, se si crea un array di oggetti Cane, è possibile eseguire un upcasting a un array di tipo Animale: Animale[] laika = new Cane[10];
Lo stesso array può essere riconvertito in un array di oggetti Cane, utilizzando il downcasting esplicito: Cane[] ºdo = (Cane []) laika;
L’operatore instanceof Per conoscere il tipo dell’oggetto contenuto in una variabile durante l’esecuzione del programma, possiamo utilizzare l’operatore instanceof, che restituisce la classe da cui è stato istanziato. Per esempio, per sapere se wdo è di tipo Cane possiamo scrivere: Cane ºdo = new Cane(); if (ºdo instanceof Cane ) { System.out.println(“Fido è eҬettivamente un oggetto di classe Cane”); }
Si noti che instanceof non indica solo se un oggetto è di un tipo specifico, ma anche se questo è di quel tipo in senso lato. Se per esempio Cane deriva da Animale e si esegue il codice seguente: Cane ºdo = new Cane(); if (ºdo instanceof Animale ) {
168
Interfacce e casting tra oggetti
Lezione 7
System.out.println(“Fido è anche un oggetto di classe Animale”); }
la condizione dell’istruzione if risulta verificata, perché Cane è anche di tipo Animale, in quanto Cane estende Animale.
■ Metodi e classi astratte Un metodo astratto non implementa un proprio blocco di codice e quindi il suo comportamento, sostanzialmente un metodo astratto non definisce parentesi graffe, ma termina con un punto e virgola. Un esempio di metodo astratto potrebbe essere il seguente: public abstract void leggiNumeri();
Ovviamente, questo metodo non potrà essere richiamato da altri metodi in quanto, la sua esistenza è legata alla opportunità di poterlo riscrivere (override) all’interno di una sottoclasse. Inoltre, un metodo astratto può essere definito solo all’interno di una ◀ classe astratta ▶. ◀ Classe astratta Una classe astratta è una Una classe dichiarata astratta non può esseclasse che contiene anche un solo metodo astratto. Affinché una classe possa contere istanziata, dobbiamo essere consapevoli nere un metodo astratto deve anch’essa che da una classe di tipo abstract non saessere dichiarata di tipo astratto. ▶ ranno istanziabili oggetti. Consideriamo la seguente classe astratta: public abstract class Giorni { . . . public abstract void estraiMese(); . . . }
La classe Giorno ha un senso solo non abbiamo intenzione di istanziarla direttamente, possiamo pensare a una classe generica. Il modiwcatore abstract, per quanto riguarda le classi, potrebbe essere considerato l’opposto del modificatore final. Infatti, una classe final non può essere estesa, mentre una classe dichiarata abstract deve essere estesa. Non è ovviamente possibile utilizzare congiuntamente i modificatori abstract e wnal, per chiari motivi logici. Il vantaggio offerto dalle classi astratte è quello che costringe le sue sottoclassi a implementarne un comportamento. A livello di progettazione le classi astratte costituiscono uno strumento fondamentale. Spesso è utile creare classi astratte tramite la generalizzazione in modo da sfruttare il polimorfismo. Prendiamo in esame una classe Veicolo, dalla quale possono essere estese altre classi quali per esempio Moto, Automobile, Nave ecc. Le sottoclassi devono implementare i metodi ereditati quali per esempio accelera() oppure decelera(): public class Veicolo { public void accelera() {
169
UdA 2
La OOP di Java
. . . } public void decelera() { . . . } }
Come accelera o decelera il veicolo? La risposta è indefinita, dipende dal contesto. Dichiariamone i metodi di tipo astratto: public abstract class Veicolo { public abstract void accelera(); public abstract void decelera(); }
In tal modo una classe potrà estendere questa classe riscrivendone i metodi in override. I modificatori abstract e static non possono essere applicati contemporaneamente alla stessa classe o metodo.
■ Le interfacce Un’◀ interfaccia ▶ è un’evoluzione del concetto ◀ Interfaccia Una interfaccia possiedi classe astratta. de tutti i metodi dichiarati public e Una interfaccia può essere implementata da una abstract e tutte le variabili dichiaraclasse che ne eredita tutti i metodi e fornisce lote public, static e wnal. Per utilizzare ro un blocco di codice. Per farlo si pospone alla una interfaccia dobbiamo necessadichiarazione della classe la parola chiave impleriamente estenderla, non potendo ments, seguita dall’identificatore dell’interfaccia essere istanziata. ▶ che si desidera implementare. La differenza tra l’implementare un’interfaccia ed estenderla consiste essenzialmente nel fatto che, mentre possiamo estendere una sola classe alla volta, possiamo invece implementare un numero indefinito di interfacce, simulando di fatto l’ereditarietà multipla, ma senza i suoi effetti collaterali negativi. Il codice che segue mostra come dichiarare una interfaccia:
Possiamo evitare di riscrivere ogni volta il modificatore abstract davanti ai metodi in quanto è sottinteso. Anche le interfacce possiedono le stesse estensioni dei file contenenti le classi, quindi .java per i file sorgenti e .class per i file compilati.
170
Interfacce e casting tra oggetti
Lezione 7
Un’interfaccia, essendo un’evoluzione di una classe astratta, non può essere istanziata. Potremmo utilizzare l’interfaccia dell’esempio precedente affinché venga implementata nella classe SalutiEbaci che ne scrive il metodo saluta():
Possiamo notare come l’ambiente di sviluppo BlueJ mostra l’interfaccia e la classe che la implementa:
In sintesi: ◗ Una classe (astratta o concreta) può implementare più interfacce ma estendere solo una classe (astratta o concreta). ◗ Una classe (astratta o concreta) può essere usata per “fattorizzare” codice comune alle sue sottoclassi, una interfaccia non può contenere codice. ◗ Una classe astratta può contenere chiamate di metodi astratti prescindendo dalla loro implementazione, una classe concreta non può usare metodi astratti, una interfaccia non può contenere codice.
171
UdA 2
La OOP di Java
Verifichiamo le conoscenze g Esercizi a scelta multipla 1 Se la classe Cane deriva da Animale, Bassotto deriva da Cane, e fido = new Cane(), quali istruzioni sono sintatticamente corrette? a) Animale a = fido; d) Bassotto c = (Animale) fido; b) Cane b1 = fido; e) Animale a = (Animale) fido; c) Bassotto c = fido; 2 Indica quale affermazione tra le seguenti è corretta riguardo a una classe astratta: a) può essere dotata di metodi concreti d) può generare oggetti b) deve essere dotata di metodi astratti e) può avere sottoclassi c) può avere metodi astratti 3 Se X deriva da Y e x = new X(), quali istruzioni sono sintatticamente corrette? a) X x1 = x; c) Y y = x; b) x = new Y(); d) X x1 = new Y(); 4 Quali istruzioni tra le seguenti sono sintatticamente corrette? a) Object o = null; e) long[] a = new int[ 10 ]; b) String s = null; f) int[] f = (new int[ 10 ]); c) Object o = new int[ 10 ]; g) int[] f = (new String[ 10 ]); d) String[] a = (String[])(new int[ 10 ]); h) int[] a = (int [])new long[ 10 ] 5 Le interfacce supportano: a) solo l’ereditarietà singola b) l’ereditarietà multipla c) l’istanza diretta d) consentono di istanziare classi astratte 6 Quali delle seguenti affermazioni riguardo classi e interfacce sono vere? a) le interfacce sono intercambiabili b) una classe astratta può implementare una interfaccia c) una classe astratta può derivare da una interfaccia d) una classe astratta può derivare da una classe concreta e) una classe astratta può derivare da una classe astratta f) una classe concreta può derivare da una interfaccia g) una classe concreta può implementare una interfaccia concreta h) una classe concreta può derivare da una classe concreta i) una classe concreta può derivare da una classe astratta j) una interfaccia può derivare da una interfaccia k) una interfaccia può derivare da più interfacce l) una interfaccia può derivare da una classe astratta m) una interfaccia può derivare da una classe derivata n) una interfaccia può derivare da una classe concreta o) una classe derivata può derivare da una interfaccia p) una classe derivata può implementare una interfaccia q) una classe derivata può implementare una interfaccia concreta r) una classe derivata può derivare da una classe concreta s) una classe derivata può derivare da una classe astratta
172
Interfacce e casting tra oggetti
Lezione 7
7 Se Gatto deriva da Animale, Mammone deriva da Gatto, e micio = new Gatto(), quali espressioni restituiscono true? a) micio instanceof Gatto c) micio instanceof Mammone b) micio instanceof Animale d) micio[] instanceof Animale 8 Quali istruzioni sono sintatticamente errate? a) abstract class A {} b) abstract final class A {} c) class A { abstract void a(); }
d) abstract class A { void a() {} } e) abstract class A { abstract void a(); }
9 Una classe che implementa una interfaccia: a) può essere concreta b) deve essere concreta c) può essere astratta d) deve essere astratta e) deve essere derivata f) una classe non può implementare una interfaccia, ma derivarla 10 Una interfaccia: a) contiene metodi privati b) contiene metodi concreti c) contiene metodi concreti privati
d) contiene metodi pubblici e) contiene metodi pubblici astratti f) contiene una implementazione
11 Una interfaccia: a) può contenere attributi variabili b) può contenere elementi non statici c) può contenere campi non statici
d) supporta solo attributi final int e) supporta solo attributi static final f) può contenere costanti
g Esercizi di completamento 1 Un metodo ...................................................................................... è privo di implementazione. 2 Una classe astratta viene dichiarata con il modiwcatore ....................................................................................... 3 Per conoscere il tipo dell’...................................................................................... contenuto in una variabile durante l’esecuzione del programma, si usa l’operatore ....................................................................................... 4 L’interfaccia si dichiara con la parola chiave ....................................................................................... 5 Per implementare una interfaccia si usa l’operatore ....................................................................................... 6 Il ...................................................................................... è il processo di ...................................................................................... delle variabili primitive, da una variabile più piccola in una più capiente. 7 Il ...................................................................................... è il processo di ...................................................................................... delle variabili. 8 Si dice ...................................................................................... l’esecuzione di una conversione da una classe derivata a una delle possibili superclassi. 9 Si dice ...................................................................................... l’operazione che permette di convertire un riferimento da una classe base a una classe più speciwca.
173
UdA 2
La OOP di Java
Verifichiamo le competenze g Problemi 1 Crea una interfaccia di nome PiGreco contenente il campo PIGRECO che vale 3.14, scrivendo meno codice possibile. 2 Crea la classe astratta Soggetto da cui derivano le classi concrete Fornitore e Cliente. 3 Crea il metodo String tipo() astratto nella classe Soggetto e le relative implementazioni in Fornitore e Cliente; l’implementazione deve restituire F nel caso di fornitore e C nel caso di cliente. 4 Modiwca Fornitore e Cliente per non usare stringhe letterali nel metodo tipo() ma utilizzare la costante TIPO dewnita nelle due classi. 5 Modiwca l’esercizio precedente per sostituire la costante TIPO con la variabile tipo a livello di classe. 6 Modiwca l’esercizio precedente per inizializzare il valore della variabile tipo nell’inizializzatore della classe. 7 Dewnisci l’interfaccia Descrivibile con metodo String descrivi(). 8 Modiwca Fornitore e Cliente per implementare descrivi(); l’implementazione del metodo deve restituire la descrizione del soggetto, contenuta nell’attributo String descrizione presente in Soggetto. 9 Aggiungi all’interfaccia Descrivibile il metodo descrivi(int) che si aspetta come parametro TUTTO o PARZIALE, due costanti dewnite sempre in Descrivibile. Nell’implementazione di descrivi(int) deve essere restituita la descrizione + tipo nel caso di TUTTO o solo descrizione in caso di PARZIALE. 10 Crea una classe Java che crei la classe Soggetto dotata dell’attributo descrizione di tipo String e metodo getDescrizione() che la restituisce e classe Cliente che deriva da Soggetto. 11 Crea una classe Main dotata del metodo stampa(Soggetto) che stampa la descrizione del soggetto. 12 Modiwca la classe Main e aggiungi il metodo stampa(Cliente) che stampa la descrizione del cliente. 13 Modiwca la classe Main e aggiungi il metodo tipo(Soggetto) che stampa “S” se il parametro attuale è un soggetto oppure “C” se è un cliente. 14 Crea la classe Fornitore che deriva da Soggetto e modiwca la classe Main per includere il metodo stampa(Fornitore).
174
3
REALIZZARE APPLICAZIONI GUI
OBIETTIVI
ATTIVITÀ
• Risolvere un problema utilizzando elementi GUI
• Realizzare applicazioni con interfacce grawche
• Comprendere il ruolo degli elementi grawci
• Realizzare disegni geometrici con colori e forme
• Capire la struttura della classe AWT e JSwing
• Individuare le diverse tipologie di eventi
• Dewnire un nuovo progetto con NetBeans
• Gestire gli eventi sulle wnestre e sui componenti
UNITÀ DI APPRENDIMENTO L 1 La classe grafica AWt L 2 La gestione degli eventi L 3 L’interfaccia utente con netbeans
• Comprendere il ruolo degli ascoltatori di eventi • Associare ascoltatori a oggetti
• Creare e registrare gli ascoltatori
• Installare e utilizzare NetBeans • Creare applicazioni GUI • Modiwcare gli eventi e le proprietà • Associare eventi a oggetti GUI
Info Nella cartella AU03 del CD-ROM allegato al volume sono presenti i progetti e i file sorgenti utili per questa unità di apprendimento.
UdA 3
Realizzare applicazioni GUI
lezione
1
LA cLAsse grAficA AWt
in qUestA Lezione impAreremo...
• a progettare l’interfaccia grafica attraverso le finestre • a realizzare disegni geometrici con colori e forme
■ Struttura di una interfaccia grafica Per sviluppare un programma contenente una interfaccia utente di tipo grafico abbiamo bisogno di un ambiente di alto livello chiamato toolkit, che ha il compito di semplificare l’utilizzo di oggetti ed ◀ eventi ▶.
◀ Eventi Gli eventi sono messaggi inviati dal sistema operativo al programma per notificare che qualcosa è successo, o che qualche condizione è cambiata a seguito di un’azione effettuata dall’utente. ▶
L’interfaccia utente è lo strumento attraverso il quale un’applicazione comunica con l’utente.
Il toolkit è una API (Application Programming Interface) che mette a disposizione gli oggetti di tipo finestra (icone, pulsanti, menu), chiamati anche widget, che ci consentono di personalizzare l’interfaccia utente. Un widget è caratterizzato dai seguenti elementi: ◗ la finestra su cui è posizionato e che lo contiene; ◗ lo stato; ◗ un insieme di metodi che possono modificare la finestra e lo stato; ◗ un insieme di eventi a esso associati, che possono essere intercettati direttamente; ◗ un sistema di collegamento tra eventi e widget, che permette al programmatore la gestione personalizzata delle risposte all’evento. I widget sono organizzati in classi: creare un widget significa istanziare una classe in un oggetto, mentre invece per personalizzarne i comportamenti dobbiamo riscriverne i metodi. Possiamo classificare i widget in due categorie fondamentali: ◗ widget contenitori che servono per raggruppare altri widget all’interno di un contesto grafico; ◗ widget componenti che servono per presentare o ricevere informazioni dall’utente. 176
La classe grafica AWT
Lezione 1
In Java, finestre ed elementi di una interfaccia grafica, vengono solitamente chiamati semplicemente componenti. Java mette a disposizione due librerie di classi necessarie per la gestione delle interfacce grafiche: ◗ Java AWT (Java Abstract Widget Toolkit); ◗ Java Swing. La libreria AWT contiene un numero limitato di componenti di interfaccia poco sofisticati (per esempio, i pulsanti possono avere etichette solo testuali), che prendono nome di componenti heavyweight. La libreria Java Swing invece mette a disposizione una maggiore varietà di componenti molto più sofisticati, con funzioni avanzate per la grafica, che prendono il nome di componenti lightweight. Le principali caratteristiche di un’interfaccia grafica sono la presenza di una o più finestre e quella di vari elementi o dispositivi di interfaccia. Affronteremo ora il progetto di un’interfaccia grafica, seguendo il procedimento illustrato di seguito: 1 importazione dei package necessari; 2 definizione di una finestra top level; 3 definizione del contenuto, cioè dei dispositivi di interfaccia; 4 gestione degli eventi nei componenti. Partiamo, innanzitutto, dalla realizzazione della finestra top level selezionando la libreria Swing, che è la più completa e sofisticata: essa permette di costruire una finestra a nostro piacimento, partendo dall’area di schermo occupata e aggiungendo bordi e altri elementi in base alle effettive necessità: noi, per ora, utilizzeremo finestre predefinite, cioè oggetti Window e JWindow. esempio
1
Creazione finestra con classe JWindow
Nell’esempio che segue vogliamo istanziare un oggetto di classe JWindow per creare una finestra. La classe JWindow è contenuta nel package javax, che contiene tutte le classi della libreria Java Swing, o più semplicemente JSwing: ▶ Dell’oggetto window1 andiamo a modificarne la dimensione mediante il metodo setSize() che riceve come parametri i pixel complessivi che determinano la larghezza e l’altezza della finestra, e il metodo setVisible() necessario per rendere visibile la finestra. Tuttavia l’esecuzione della classe Finestra mostra una finestra rappresentata da un semplice riquadro grigio nella posizione in alto a sinistra (coordinate 0,0) dello schermo: ▶ Per far terminare il programma siamo costretti a chiudere BlueJ, oppure rimuovere l’applicazione java.exe dal gestore di attività, attivabile con Ctrl+Alt+Canc.
177
UdA 3
Realizzare applicazioni GUI
Per chiudere la finestra creata nell’esempio precedente avremo bisogno di alcuni pulsanti. L’esempio seguente crea una finestra più completa, che sarà il nostro contenitore principale, utilizzando la classe Frame della libreria Java AWT. esempio
2
Creazione finestra con classe Frame
In questo esempio istanziamo un oggetto di classe Frame: ▶
Mediante la classe Frame l’oggetto finestra diventa una finestra di uso più amichevole, infatti appaiono i noti pulsanti usati nelle finestre di Windows: ▶ Anche in questo caso tuttavia per chiudere la finestra siamo costretti a passare al gestore delle attività di Windows in cui terminare l’esecuzione del file javaw.exe.
In questo secondo esempio abbiamo creato il bordo e i pulsanti, ma come abbiamo visto, anche se facciamo click sul pulsante di chiusura non otteniamo alcun risultato. Questo accade perché non è sufficiente posizionare i pulsanti, dobbiamo associare a ogni componente di una finestra il corrispondente evento e la relativa azione da effettuare. L’esempio che segue mostra come utilizzare una terza tipologia di finestra, di classe JFrame. esempio
3
Creazione finestra con classe JFrame
In questo esempio proviamo a utilizzare una finestra della libreria Swing, creandola mediante l’istanza di un oggetto della classe JFrame. ▶
L’esecuzione mostra una finestra che può essere chiusa. ▶ Proviamo a utilizzare i tre pulsanti della finestra, noteremo che sono tutti abilitati. Si tratta di una classe che implementa automaticamente la gestione degli eventi sugli oggetti della finestra.
178
La classe grafica AWT
Lezione 1
La chiusura della finestra non chiude proprio tutto: lascia aperta l’applicazione javaw.exe. Per chiudere anche l’applicazione dobbiamo aggiungere nel main del programma l’istruzione seguente: frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Di seguito è riportata la classificazione dei principali dispositivi di interfaccia presenti in una GUI. Quelli che trovano posto in una finestra possono essere suddivisi in tre categorie, in base al loro comportamento. ◗ Aree dedicate a disegni e scopi speciali: aree grafiche, per la visualizzazione o l’editing dei testi e così via. ◗ Elementi inerti: servono solo per l’aspetto estetico della finestra; appartengono a questa categoria le etichette, le scritte, le immagini jpg o bmp e così via. ◗ Elementi attivi: sono sensibili agli input e servono per la comunicazione. Sono elementi di questo tipo i menu, i pulsanti, i campi di input e così via. Tutti i componenti sono definiti mediante classi derivate dalle classi base Component oppure MenuComponent della libreria AWT e JComponent di Java Swing. Vedremo come posizionare questi elementi nella finestra, eventualmente usando contenitori intermedi: infatti, gli oggetti creati a partire dalle classi AWT possono essere inseriti direttamente in Frame (che è un contenitore), mentre gli oggetti Swing possono essere aggiunti nel pannello di contenuto associato alla finestra JFrame. Per prima cosa ci occuperemo delle aree grafiche che permettono di realizzare disegni o componenti personalizzati. Java mette a disposizione una classe specifica per questo tipo di elementi, la classe Canvas, e un metodo particolare per la loro gestione, il metodo paint().
■ Disegnare con la classe Canvas Possiamo associare ai componenti della classe AWT del codice per personalizzarli: tale procedimento è spesso l’unico modo per inserire immagini in una interfaccia costruita ricorrendo a oggetti appartenenti alle classi della libreria AWT. Il componente in tal modo disegna se stesso utilizzando un oggetto della classe Graphics, che rappresenta il contesto grafico corrente e fornisce funzioni per tracciare linee, poligoni, cerchi, mappare immagini e così via. Ogni componente Java contiene numerosi metodi che consentono di ridisegnare i componenti stessi, ma i più importanti sono: ◗ void repaint(), che accoda una richiesta di ridisegno del componente; ◗ void paint(Graphics g), che ridisegna il componente. Il metodo paint() non viene mai invocato direttamente nel codice scritto da noi ma viene invocato in modo automatico ogni volta che l’oggetto deve essere ridisegnato, per esempio quando una finestra viene ridotta a icona, ridimensionata ecc. Il programmatore ha invece a disposizione il metodo repaint() per richiedere al sistema di ridisegnare un componente secondo le proprie esigenze: in particolare, nelle animazioni il metodo repaint() chiama automaticamente il metodo paint() e il metodo update() che vedremo più avanti. Ogni componente inserito in un disegno occupa dello spazio e viene collocato nel contenitore attraverso un sistema di coordinate bidimensionale. Possiamo tuttavia collocare il componente secondo un sistema di coordinate relative, calcolate in pixel, con origine posta nell’angolo in alto a sinistra
179
UdA 3
Realizzare applicazioni GUI
della finestra: l’estremo in basso a destra sarà il punto di coordinate relative (d.width-1,d.height-1), dove d è un oggetto appartenente alla classe Dimension definito con la seguente istruzione: Dimension d = nome_oggetto.getSize();
Lo schema di questa classe è riportato nella figura seguente. classe Dimension Attributi
Metodi costruttori
Metodi modiwcatori
int width, height
Dimension () Dimension get/Size () Dimension (Dimension d) void setSize (int width, int height) Dimension (int width, int height) boolean equals (Object obj) int hashCode () string tostring ()
Il metodo paint(Graphics g) ha come argomento un oggetto appartenente alla classe Graphics, che viene passato automaticamente dal sistema e contiene informazioni sullo stato, come il colore e i font utilizzati. Per intervenire sul colore della finestra è possibile ricorrere ai seguenti metodi: ◗ Color getColor(), che restituisce il colore corrente; ◗ setColor(Color c), che assegna il colore. Per intervenire sul font usato per scrivere nella finestra, si possono utilizzare i seguenti metodi: ◗ Font getFont(), che restituisce il font corrente; ◗ setFont(Font), che assegna il font. Ma passiamo all’azione e disegniamo una finestra. Per prima cosa è necessario definire un’area dotata di un contesto grafico: nella libreria AWT un oggetto di questo genere appartiene alla classe Canvas. Esistono sostanzialmente quattro metodi adatti a perseguire i nostri scopi: ◗ drawXXX(), che disegna il contorno di un’area; ◗ wllXXX(), che riempie l’interno di un’area; ◗ drawString(), che scrive un testo nell’area; ◗ drawImage(), che disegna un’immagine. Iniziamo con la creazione di un semplice componente che contiene due scritte in due font di colore diverso: il codice consiste nel richiamare il metodo paint() in oggetto appartenente alla classe AreaTesto, implementata ereditando dalla classe Canvas. import java.awt.*; class AreaTesto extends Canvas { public void paint(Graphics g) { g.setFont(new Font(“Times New Roman”, Font.BOLD, 16)); g.setColor(Color.red); g.drawString(“prima scritta”, 10, 20); g.setFont(new Font(“Arial”, Font.BOLD, 20)); g.setColor(Color.blue); g.drawString(“seconda scritta”, 10, 60); } }
180
La classe grafica AWT
Lezione 1
Il codice modifica gli attributi dell’oggetto grafico g, nel quale vengono inserite scritte mediante il metodo drawString(). Riprendiamo la finestra creata con la classe Frame e aggiungiamo a essa un componente della classe AreaTesto appena definita: // creazione del componente (nuovo oggetto) AreaTesto at = new AreaTesto(); f.add(at);
esempio
4
Creazione finestra con classe Canvas
In questo esempio utilizziamo una classe AreaTesto che riscrive il metodo paint della classe Canvas per inserire alcune scritte con font diversi. Come possiamo notare la nostra classe estende la classe Canvas. Il metodo paint vuole il parametro g di classe Graphics che deve essere usato per invocarne i metodi (setColor, drawString, setFont ecc.):
La classe che istanzia AreaTesto è Esempio4 che prima di tutto importa il package AWT per poterne usare la classe Frame. Come possiamo notare prima di tutto viene istanziata la classe Frame nell’oggetto f, quindi la classe AreaTesto nell’oggetto at. Per poter visualizzare l’oggetto at è necessario collocarlo nel frame f mediante il metodo f.add():
L’esecuzione mostra la finestra seguente: ▶
181
UdA 3
Realizzare applicazioni GUI
Prova adesso! APRI IL PROGETTO Esempio4
• Usare la classe Canvas • Riscrivere il metodo paint • Usare la classe JFrame • Importare il package javax.swing
1 Modifica il codice in modo tale da rendere la finestra richiudibile. 2 Confronta la tua soluzione con quella presente nel progetto Esempio4_solux.
Zoom su... metodi principALi deLLA cLAsse grAphics Elenchiamo sinteticamente le principali primitive che la classe Graphics fornisce per disegnare. //ripulisce il rettangolo indicato, riempiendolo col colore di sfondo. void clearRect(int x, int y, int width,int height) //disegnano un rettangolo, rispettivamente vuoto e pieno. void drawRect(int x, int y, int width,int height) void wllRect(int x, int y, int width, int height) // disegnano un rettangolo con angoli smussati, rispettivamente vuoto e pieno. void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) void wllRoundRect(int x, int y,int width, int height, int arcWidth, int arcHeight) // disegna il segmento che congiunge i punti di coordinate (x1,y1) e (x2,y2). void drawLine(int x1, int y1, int x2, int y2) //disegnano un arco circolare o ellittico, rispettivamente vuoto e pieno. //Gli angoli devono essere indicati in gradi: numero positivo indica misura in senso antiorario //negativo in senso orario //Il centro dell’arco corrisponde al centro (x,y) del rettangolo di origine. void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) void wllArc(int x, int y, int width, int height, int startAngle, int arcAngle) //disegnano un’ellisse rispettivamente vuoto e pieno void drawOval(int x, int y, int width, int height) void wllOval(int x, int y, int width, int height) //disegnano un poligono, rispettivamente vuoto e pieno, con vertici xPoints e yPoints void drawPolygon(int[] xPoints, int[] yPoints, int nPoints) void wllPolygon(int[] xPoints, int[] yPoints, int nPoints) //disegna una linea spezzata poligonale con n vertici xPoints e yPoints. void drawPolyline(int[] xPoints, int[] xPoints, int nPoints) //disegna una stringa che inizia nelle coordinate (x,y) void drawString(AttributedCharacterIterator iterator, int x, int y)
182
La classe grafica AWT
Lezione 1
//disegna l’immagine image nella posizione (x,y). boolean drawImage(image, x, y, ImageObserver) //disegna l’immagine image nella posizione (x,y) //scalata della larghezza e dell’altezza indicate dai parametri width e height boolean drawImage(image, x, y, width, height, ImageObserver): //Per disegnare un’immagine che occupa tutto il componente g si deve scrivere il seguente Dimension d = getSize(); g.drawImage (image, 0,0, d.width,d.height, this);
Ecco un semplice esempio di codice contenente tutte le funzioni elencate. esempio
5
Disegno con Canvas
In questo esempio vogliamo disegnare alcuni cerchi e ovali. Per fare questo creiamo una classe chiamata AreaOvali in cui estendere la classe Canvas. Riscriviamo il metodo paint() utilizzando i metodi drawOval(), wllOval() per disegnare alcuni cerchi e ovali: ▶
Nella classe UsaAreaOvali istanziamo un oggetto di classe JFrame in cui collocare attraverso il metodo add un oggetto di classe AreaOvali: ▶
Il risultato è il seguente: ▶
183
UdA 3
Realizzare applicazioni GUI
■ La classe Graphics2D Abbiamo visto che l’argomento del metodo paint() è di classe Graphics; in realtà esso appartiene anche alla sottoclasse Graphics2D che contiene funzionalità grafiche più avanzate. Graphics2D infatti estende la classe Graphics e consente di effettuare una conversione esplicita: Graphics2D g2 = (Graphics2D)g;
In tal modo l’oggetto g2, equivalente a g, verrà considerato di classe Graphics2D. Da qui in poi utilizzeremo sempre oggetti di classe Graphics2D: basterà aggiungere l’istruzione indicata sopra prima di utilizzare l’oggetto g2. Lo schema della classe Graphics2D è riepilogato nella figura seguente: classe Graphics 2D Metodi modiwcatori
Color get/setBackground() GraphicsConºguration getDeviceConºguration() FontRenderContext getFontRenderContext() Paint get/setPaint() Object get/setRenderingHint(Key hintKey) RenderingHints getRenderingHint() Stroke get/setStroke() AҬine Transform get/setTransform() void setRenderingHints(Map hints) void setRenderingHints(Map hints) void clip(Shape s) void draw(Shape s) void drawGlyphVector(GlyphVector g, »oat x, »oat y) boolean drawImage(Image img, AҬineTransform xform, ImageObserver obs) void drawimage(BuҬerImage img, BuҬerImageOp op, int x, int y) void drawRenderableImage(RenderableImage img, AҬineTransform xform) void drawRenderedImage(RenderedImage img, AҬineTransform xform) void drawString(String s, »oat x, »oat y) void drawString(AttributedCharacterIterator iterator, »oat x, »oat y) void ºll(Shape s) boolean hit(Rectangle rect. Shape s, boolean onStroke) void rotate(double theta) void rotate(double theta, double x, double y) void scale(double sx, double sy) void shear(double shx, double shy) void transform(AҬineTransform Tx) void translate(double tx, double ty)
Un’istanza della classe java.awt.Graphics2D costituisce il cosiddetto ◀ contesto grawco ▶. Il contesto grafico costituisce il mezzo per disegnare tre tipi di oggetti: ◗ forme; ◗ testi; ◗ immagini. 184
◀ Contesto grafico Termina che deriva dalla lingua inglese (graphic context), rappresenta una superficie su cui è possibile disegnare, come l’area di un componente, una pagina di testo o un generico buffer. ▶
La classe grafica AWT
Lezione 1
Il termine contesto si riferisce al fatto che in oggetti di questo tipo sono contenute informazioni di tipo contestuale. Ci sono vari modi per acquisire un oggetto di classe Graphics2D: 1 partendo dalle librerie AWT o Swing, invocando il metodo paint() di un componente; 2 direttamente, da un buffer di immagine offscreen; 3 copiando un oggetto esistente. Il primo metodo • quello che abbiamo appena visto e consiste nel riscrivere il metodo paint(). Il secondo metodo sarˆ utilizzato per ridurre lo sfarfallio delle animazioni. Nella tabella che segue riportiamo, per completezza, le proprietˆ del contesto grafico. proprietà
descrizione
color
Colore o pattern in uso.
stroke
Determina il modo di diseganre le linee.
font
Determina il font dei testi.
transformation
Consente di effettuare trasformazioni geometriche (rotazioni e translazioni).
compositing rule
Determina la modalità di combinazione dei colori di una nuova operazione con quelli esistenti, attraverso la definizione degli attributi di opacità, trasparenza e così via.
clipping shape
Definisce un’area entro cui limitare le operazioni di rendering. Di default la clipping shape è l’intera area del componente, ma può essere modificata.
rendering hints
È un insieme di valori che specifica alcuni dettagli tecnici che influenzano l’estetica dell’oggetto grafico.
Di seguito procediamo alla descrizione delle proprietˆ pi• significative alle quali ricorreremo.
Transformation Le trasformazioni servono per passare dallo ◀ user space ▶ al device space.
Compositing rule
◀ User space Lo user space è il sistema di coordinate logico, in cui il programmatore definisce le primitive, mentre il device space è il sistema di coordinate fisico del componente su cui si deve disegnare, che è ancorato allo schermo ed espresso in pixel. ▶
La composizione del colore definisce la precedenza delle primitive quando si sovrappongono sulla stessa area della finestra. Lo stabilisce la modalitˆ di composizione del colore, le regole seguite sono essenzialmente queste: ◗ la primitiva disegnata per ultima copre la precedente (default); ◗ la primitiva disegnata per prima copre la successiva; ◗ di ciascuna delle due viene disegnata solo la parte esterna allÕaltra, mentre la parte che si sovrappone non viene disegnata.
Clipping shape Il verbo inglese to clip significa tagliare via, quindi impostare questa proprietˆ significa non disegnare quello che cade fuori dallÕarea contenuta in un perimetro di clipping (clipping path). Valgono le seguenti osservazioni: ◗ di norma, il perimetro di clipping • la finestra contenitore: gli elementi posizionati in coordinate esterne a quelle del device non vengono visualizzati; ◗ • possibile specificare diversi perimetri di clipping aggiuntivi: ci˜ che cade dentro la finestra ma al di fuori del perimetro di clipping non sarˆ visualizzato.
185
UdA 3
Realizzare applicazioni GUI
Metodi della classe Graphics2D I metodi della classe Graphics2D vengono impiegati per tracciare (draw) o riempire (fill) una figura geometrica, in base ai parametri numerici che la descrivono. Molti di questi metodi sono validi anche per la superclasse Graphics. Le istruzioni che seguono disegnano un rettangolo con effetto di rilievo 3D e angoli smussati, rispettivamente vuoto e pieno: void draw3DRect(x, y, width, height, arcWidth,arcHeight); void ºll3DRect(x, y, width, height, arcWidth,arcHeight);
La classe Shape Serve specificamente per rappresentare figure geometriche, da disegnare chiamando i metodi della classe Graphics2D che iniziano con draw e fill. La classe Shape fornisce, nelle sue sottoclassi, vari tipi di forme geometriche, che possono essere disegnate usando i metodi della classe Graphics2D. Le istruzioni che seguono creano una figura geometrica, rispettivamente vuota e piena: draw(Shape s); ºll(Shape s);
Di seguito sono elencate le sottoclassi della classe Shape: ◗ Arc2D, Ellispe2D, Rectangle2D, RoundRectangle2D: sono figure geometriche inscritte in un rettangolo; ◗ Line2D, QuadCurve2D, CubicCurve2D: segmenti di retta, di curva quadratica, di curva cubica; ◗ GeneralPath: linea spezzata formata da segmenti di diverso tipo; ◗ Area: figura 2D generale, sulla quale è possibile eseguire operazioni insiemistiche. L’esempio che segue costituisce un esempio di utilizzo delle sottoclassi della classe Shape. esempio
6
Disegno di una mezza luna sfruttando le sottoclassi di Shape
La classe MezzaLuna riscrive il metodo paint() per disegnarne due oggetti ellittici (di classe Ellipse2D). In questo esempio vengono disegnati due ellissi. Uno chiamato palla e un secondo chiamato rotto. Viene usata la sottoclasse di Canvas chiamata Area per collocarne mediante il metodo add(), prima l’oggetto palla, quindi a essa viene sottratto l’oggetto rotto attraverso il metodo subtract().
186
La classe grafica AWT
Lezione 1
La classe UsaMezzaLuna invece istanzia un oggetto di classe MezzaLuna, quindi lo colloca nel JFrame chiamato f:
L’esecuzione del programma è la seguente ▶:
Classe Color Il colore è rappresentato secondo la codifica RGB (Red, Green, Blue), utilizzando 24 bit (8 per ciascuno dei colori primari rosso, verde e blu): il nero è (0,0,0), il bianco (255,255,255). Di seguito è riportato un esempio di definizione di un colore casuale. r = (int) Math.»oor(Math.random()*256); g = (int) Math.»oor(Math.random()*256); b = (int) Math.»oor(Math.random()*256); g.setColor(new Color (r,g,b));
Esistono per questa classe alcune costanti predefinite, che rappresentano i colori più comuni: Color. black (nero), Color.white (bianco), Color.red (rosso), Color.green (verde), Color.blue (blu), Color. cyan (ciano), Color.darkGray (grigio scuro), Color.gray (grigio medio), Color.lightGray (grigio chiaro), Color.magenta (magenta), Color.orange (arancione), Color.pink (rosa), Color.yellow (giallo).
Classe BasicStroke Implementa lo spessore delle linee da tracciare. Nel codice che segue vengono disegnate cinque linee, di colore e spessore diverso. esempio
7
Linee parallele
In questo esempio tracciamo cinque linee parallele di spessore e colori diversi, utilizzando la classe BasicStroke. Come possiamo notare vengono dapprima generati i colori in modo casuale, viene dapprima generato un intero tra 0 e 255, quindi passato al metodo setColor(). Attraverso il metodo
187
UdA 3
Realizzare applicazioni GUI
setStroke(new basicStroke(1.0f*i)) viene generato uno spessore che aumenterà di 1 a ogni passo nel ciclo. Infine il metodo drawLine() traccia la linea dello spessore definito:
La classe UsaRighe colloca in un JFrame l’oggetto di classe Righe precedentemente ceato:
Infine l’esecuzione del programma mostra una finestra in cui appaiono alcune righe di spessore e colore diversi ▶:
188
La classe grafica AWT
Lezione 1
■ Immagini JPG e GIF Le immagini vengono gestite mediante la classe Image e i suoi metodi, riassunti schematicamente nella figura seguente. Anche questa classe non può essere direttamente istanziata dal programmatore, quindi non possiede metodi costruttori. classe Image Attributi
Metodi modiwcatori
Object UndeºnedProperty int SCALE_DEFAULT SCALE_PAST SCALE_SMOOTH SCALE_REPLICATE SCALE_AREA_AWERAGING
Graphics getGraphics() int getHeight (ImageObserver observer) Object getProperty (String name, ImageObserver observer) Image getScaledInstance (int width, int height, int hints) ImageProducer getSource() int getWidth (ImageObserver observer) void »ush()
La procedura per incorporare un immagine da seguire è illustrata di seguito: 1 Dichiarare un oggetto di tipo Image, con una istruzione simile alla seguente: Image img;
2 Richiamare il file che contiene l’immagine, mediante il metodo getImage(). È comodo utilizzare la classe Toolkit messa a disposizione da Java. La classe Toolkit è un’API di livello più alto rispetto alla libreria di base fornita dal WMS. Un ◀ ambiente a wnestre ▶ gestisce il desktop e mette a disposizione le classi della libreria Java AWT (Abstract Windowing Toolkit), che permettono di creare e gestire l’interfaccia utente.
◀ Ambiente a finestre Un ambiente a finestre è un software basato sul sistema operativo che si frappone tra le applicazioni e i dispositivi di I/O, al fine di facilitare le operazioni di interazione tra l’utente e le applicazioni. ▶
Le interfacce utente Java sono costruite mediante l’annidamento di componenti elementari all’interno di finestre esterne più complesse. La classe Toolkit ha il compito di semplificare e standardizzare l’inoltro di richieste e la gestione degli eventi da parte dell’applicazione, che quindi non dovrà interagire direttamente con il sistema operativo. Un’immagine può, per esempio, essere inserita molto facilmente in un disegno applicando il metodo drawImage() a un’istanza della classe Image, che al momento della creazione viene inizializzata con un’immagine predefinita dal metodo getDefaultToolkit() della classe Toolkit. Per esempio, nell’istruzione: Image palla = Toolkit.getDefaultToolkit().getImage(“tennis.jpg”);
associamo direttamente all’oggetto palla l’immagine contenuta nel file tennis.jpg. Successivamente, per disegnare l’immagine utilizziamo il metodo drawImage() della classe Graphics, che possiede diverse sintassi per permettere l’eventuale ridimensionamento dell’immagine. g.drawImage(palla, 100, 350, 100, 72, this);
L’ultimo argomento del metodo drawImage() è di tipo ImageObserver. Non entreremo nel dettaglio su questo elemento: è sufficiente in genere usare l’auto referenza (this). Di seguito è riportato un esempio in cui viene riprodotta una fotografia in due dimensioni diverse.
189
UdA 3
Realizzare applicazioni GUI
esempio
8
Inserimento di una immagine
Definiamo la classe Immagine1 e alcuni suoi attributi, tra cui l’immagine image; nel costruttore sarà poi definito il collegamento con il file tree.jpg della fotografia. In quest’esempio abbiamo incluso nella stessa classe anche il main in cui istanziare il JFrame della finestra:
Nel metodo paint() devono essere inserite alcune operazioni, oltre a richiamare due volte il metodo drawImage(). In seguito all’esecuzione del metodo main otteniamo il seguente risultato:
190
La classe grafica AWT
Lezione 1
Verifichiamo le conoscenze g Test vero/falso 1 2 3 4 5 6 7
L’interfaccia utente serve per aiutare l’utente all’utilizzo del computer. La wnestra top level corrisponde alla top window. I widget sono organizzati in classi. Il metodo draw(Shape s) traccia il contorno di una wgura geometrica. Il metodo wll(Shape s) riempie l’interno di una wgura geometrica. Il toolkit è una API che mette a disposizione gli oggetti di tipo wnestra. La classe Image fa parte della libreria Swing di Java.
g Domande di autoverifica 1 2 3 4 5 6 7 8
Spiega con parole tue cosa rappresenta una interfaccia utente GUI. Illustra il signiwcato di top window e top level. Spiega, aiutandoti con esempi, come sono organizzate le coordinate in pixel. Metti in luce cosa genera un evento. Illustra la differenza tra oggetti di tipo toolkit e oggetti di tipo widget. Fornisci alcuni esempi pratici che mettano in luce le differenze tra le librerie Java AWT e Swing. Spiega qual’è la differenza più rilevante tra la classe Frame e la classe JFrame. Fornisci una dewnizione di “graphic context”.
Verifichiamo le competenze g Problemi 1 2 3 4 5 6 7
Dewnisci una classe in grado di disegnare un uovo di Pasqua. Dewnisci una classe in grado di disegnare un millepiedi. Dewnisci una classe in grado di disegnare un treno a vapore. Dewnisci una classe in grado di disegnare un mela morsa (tipo Apple). Dewnisci una classe in grado di disegnare un orologio. Dewnisci una classe in grado di disegnare una scacchiera con le dimensioni passate come parametri. Dewnisci una classe in grado di disegnare una pila di dischi, trasferendone la quantità passata in un parametro. 8 Dewnisci una classe in grado di disegnare una Scala Reale con carte lette da wle JPG. 9 Dewnisci una classe in grado di disegnare una carta in varie dimensioni. 10 Dewnisci una classe in grado di disegnare un fumetto con la scritta “Ciao”.
191
UdA 3
Realizzare applicazioni GUI
11 Crea una classe in grado di disegnare un’automobile come indicato nella wgura a wanco:
12 Crea una classe in grado di disegnare i cinque cerchi delle olimpiadi, come indicato nella wgura a wanco:
13 Crea una classe in grado di disegnare le seguenti righe, come indicato nella wgura a wanco:
14 Crea una classe in grado di disegnare le seguenti forme, come indicato nella wgura a wanco:
192
La gestione degli eventi
lezione
Lezione 2
2
LA gestione degLi eventi
in qUestA Lezione impAreremo...
• a individuare le diverse tipologie di eventi • a creare e registrare gli ascoltatori • a gestire gli eventi sulle finestre e sui componenti
■ Azioni ed eventi Abbiamo detto che ogni componente dellÕinterfaccia utente genera azioni: ◗ le caselle di controllo, i pulsanti di opzione e i pulsanti di comando creano azioni quando sono selezionati dallÕutente ◗ i menu e le liste a scorrimento creano unÕazione (con argomento lÕelemento selezionato) quando si seleziona, rispettivamente, una voce del menu e della lista ◗ i campi testo creano unÕazione quando si preme sulla tastiera il tasto di invio ◗ la stessa finestra che contiene i componenti • soggetta a un insieme di azioni: quando si chiude, si riduce a icona, si apre e cos“ via. Ogni azione genera un evento e ogni evento deve essere gestito dal programmatore secondo la sequenza indicata di seguito: 1 lÕutente effettua unÕazione; 2 lÕazione genera un evento; 3 lÕevento deve essere gestito; 4 in risposta, deve essere generata unÕazione. Un evento • un avvenimento asincrono, cio• non sappiamo quando avviene: dobbiamo quindi preoccuparci di organizzare strumenti che ci segnalino quando lÕevento viene generato. Tali elementi si chiamano ◀ ascoltatori ▶ che in Java sono naturalmente degli oggetti. ◀ Ascoltatori Gli ascoltatori sono in pratica veri e propri oggetti. Ogni oggetto appartiene alla classe che ne implementa l’interfaccia di uno specifico evento e prende il nome di listener object. A ogni componente che è in grado di generare un evento viene associato un oggetto ascoltatore. ▶
193
UdA 3
Realizzare applicazioni GUI
Ogni oggetto ascoltatore appartiene a una classe ben definita, è quindi specializzato per ascoltare solo un particolare tipo di eventi. Per esempio, su una casella di testo la necessità è quella di ascoltare la digitazione dei caratteri ma anche quella di ascoltare quando viene digitato l’invio, nei due casi useremo due distinti ascoltatori: nel primo caso un KeyListener, nel secondo un ActionListener. Ciascuna classe di ascoltatori è costituita da un insieme di metodi astratti che dobbiamo riscrivere volta per volta in base alle nostre esigenze: quindi creeremo e istanzieremo un oggetto di tale classe, il quale soddisferà la generazione di un evento sullo specifico oggetto a cui lo abbiamo associato, ovvero gli darà una risposta. Tale oggetto prende il nome di gestore dell’evento (event handler). Il procedimento descritto viene chiamato ◀ modello a delegazione ▶ in quanto la risposta all’azione generata da un oggetto sorgente viene delegata a un oggetto ascoltatore, che lo ◀ Modello a delegazione Nel modello a delegazione gli eventi possono essere clasprocesserà in un luogo diverso da quello in sificati secondo due modalità: cui è stato richiamato. ◗ eventi per categoria (eventi sui contenitori ed eventi sui componenti); ◗ eventi per tipologia (eventi di basso livello, ovvero su una finestra, ed eventi semantici, ovvero associati a un oggetto AWT). ▶
Per esempio se premiamo un pulsante e vogliamo che appaia una scritta su una etichetta dobbiamo tenere presente che: ◗ il pulsante è la sorgente dell’evento; ◗ l’evento è la pressione del pulsante, che sarà un oggetto istanziato direttamente dalla JVM dalla classe ActionEvent; ◗ il gestore dell’evento sarà un oggetto istanziato da una classe a parte che implementa un’interfaccia ActionListener (in italiano “ascoltatore d’azioni”). Quest’ultima ridefinirà il metodo actionPerformed (ActionEvent e) con il quale sarà gestito l’evento. Infatti, la JVM invocherà automaticamente questo metodo su quest’oggetto quando l’utente premerà il pulsante. Ovviamente bisognerà anche effettuare un’operazione supplementare che registri il pulsante con il suo ascoltatore. È necessario infatti istruire la JVM su quale oggetto invocare il metodo di gestione dell’evento, come mostrato dal codice che segue: public class Ascolta { private Frame f; private Button b; public Ascolta() { f = new Frame(“ºnestra di ascolto”); b = new Button(“pulsante b”); } public void settaAscoltatori() { b.addActionListener(new ButtonHandler()); f.add(b,BorderLayout.CENTER); f.pack(); f.setVisible(true); } public static void main(String args[]) { Ascolta ascolta = new Ascolta(); ascolta.settaAscoltatori(); } }
L’istruzione: b.addActionListener(new ButtonHandler());
194
La gestione degli eventi
Lezione 2
registra il gestore di evento al suo pulsante, in tal modo la JVM conosce su quale oggetto di tipo Listener chiamare il metodo actionPerformed(), in questo caso l’oggetto b di classe Button. Il metodo addActionListener() si aspetta come parametro un oggetto di tipo ActionListener, ed essendo quest’ultima un’interfaccia, significa che addActionListener() si aspetta un oggetto istanziato da una classe che implementa tale interfaccia. La classe di cui stiamo parlando e che gestisce l’evento (il listener) è la seguente: import java.awt.event.*; public class ButtonHandler implements ActionListener { public void actionPerformed(ActionEvent e) { System.out.println(“È stato premuto il bottone”); System.out.println(“E la sua etichetta è: “+e.getActionCommand()); } }
in pratica, ogni volta che viene premuto il pulsante, viene stampata sulla riga di comando l’etichetta (“pulsante b”) del pulsante stesso, mediante il metodo getActionCommand(). In sintesi per associare un evento a un oggetto dobbiamo: ◗ creare la GUI; ◗ creare un listener; ◗ registrare il componente interessato con il rispettivo listener. Alla pressione del pulsante viene istanziato un oggetto di tipo ActionEvent, contenente informazioni riguardanti l’evento, che viene passato in input al metodo actionPerformed() dell’oggetto listener associato. In realtà nell’esempio appena visto c’è una enorme e vistosa semplificazione. La scritta, piuttosto che venire stampata sulla stessa GUI, viene stampata sulla riga di comando.
Gli ascoltatori di eventi Indipendentemente dalla classificazione, Java mette a disposizione un insieme di classi di ascoltatori; l’oggetto istanziato da ciascuna delle classi prende il nome di ascoltatore (Listener). Gli ascoltatori appartengono quindi alle rispettive classi Listener riportate nella tabella (le più frequentemente usate) dove il nome della classe è così formato: nome della categoria_evento_ascoltato+Listener
classe
metodi
note
oggetti “fire”
Basso livello
FocusListener
2 focusGained (FocusEvent e) focusLost (FocusEvent e)
Focus preso Focus perso
(Praticamente tutti)
MouseListener
5 mouseClicked (MouseEvent e) mouseEntered (MouseEvent e) mouseExited (MouseEvent e) mousePressed (MouseEvent e) mouseReleased (MouseEvent e)
Mouse cliccato Mouse entrato Mouse uscito Mouse premuto Mouse rilasciato
(Praticamente tutti)
MouseMotionListener
2 mouseDragged (MouseEvent e) mouseMoved (MouseEvent e)
Premuto e mosso Mosso
(Praticamente tutti)
195
UdA 3
Realizzare applicazioni GUI
classe
metodi
note
oggetti “fire”
Basso livello
KeyListener
3 keyPressed (KeyEvent e) keyReleased (KeyEvent e) keyTyped (KeyEvent e)
Premuto tasto (Praticamente tutti) Rilasciato tasto Stai premendo il tasto
WindowListener
3 windowActivated (WindowEvent e) windowClosed (WindowEvent e) windowClosing (WindowEvent e) windowDeactivated (WindowEvent e) windowDeiconiºed (WindowEvent e) windowIconiºed (WindowEvent e) windowOpened (WindowEvent e)
Finestra attivata Finestra chiusa Chiudi finestra Finestra disattivata Finestra deiconificata Riduci a icona Finestra aperta
Window Frame Dialog Filedialog
WindowFocusListener
2 windowGainedFocus (WindowEvent e) windowLostFocus (WindowEvent e)
Focus preso Focus perso
(Idem)
Semantici
ActionListener
1 actionPerformed (ActionEvent e)
Azione su oggetto
Button CheckboxMenuItem List MenuMenuItem PopupMenu TextField
ItemListener
1 itemStateChanged (ItemEvent e)
Click su oggetti
List Checkbox CheckboxMenuItem Choice
TextListener
1 textValueChanged (TextEvent e)
Modifica testo
TextField TextArea
Per categoria
ContainerListener
Obj aggiunto 2 ComponentAdded (ContainerEvent e) ComponentRemoved (ContainerEvent e) Obj tolto
ComponentListener
4 ComponentHidden (ComponentEvent e) ComponentResized (ComponentEvent e) ComponentMoved (ComponentEvent e) ComponentShown (ComponentEvent e)
Obj nascosto Obj ridimensionato Obj mosso Obj mostrato
(Praticamente tutti)
Per esempio supponiamo di voler associare un ascoltatore alla chiusura di una finestra. Procediamo così: A riscriviamo la classe (interfaccia) WindowListener che ascolta gli eventi sulla finestra, in particolare il metodo della classe che ascolta l’evento desiderato (WindowClose()); B creiamo una istanza della classe che rappresenta l’ascoltatore vero e proprio (istanza di un oggetto di classe WindowListener); C associamo l’ascoltatore all’oggetto che vogliamo “sorvegliare. Quando l’utente effettua l’azione, viene eseguito il metodo di risposta riscritto al punto A.
196
La gestione degli eventi
Lezione 2
Zoom su... gerArchiA deLLe cLAssi event A ogni evento che si verifica, Java crea un oggetto evento e lo passa automaticamente come parametro al metodo della classe ascoltatore. Esiste infatti una classe evento per quasi tutte le classi di ascoltatori, infatti, i metodi della classe WindowListener hanno tutti come parametro un oggetto della classe WindowEvent, in quanto l’evento su una finestra avrà attributi diversi da quelli dell’evento su un Listbox. Tuttavia gli eventi possiedono elementi in comune fra di loro, ereditati dalla classe AWTEvent che deriva dalla classe EventObject: Di seguito è riportato l’albero completo della gerarchia degli eventi. InputMethodEvent
EventObject
ItemEvent
WindowEvent
HierarchyEvent
ContainerEvent
ComponentEvent
PanelEvent
InvocationEvent
FocusEvent
TextEvent
Input
AWTEvent
ActionEvent
MouseEvent
KeyEvent
AdjustmentEvent Vediamo per esempio, nella figura seguente, il diagramma della classe EventObject. classe EventObject Metodi costruttori
Metodi modiwcatori
EventObject (Object source)
Object getSource () String toString ()
Il diagramma mostra un unico costruttore con un oggetto per parametro che è il sorgente, chiamato anche fuoco dell’evento, e due metodi: ◗ il metodo toString() già noto ◗ il metodo getSource() che restituisce proprio l’oggetto che ha generato il fuoco o per meglio dire, il suo reference. Possiamo quindi utilizzare tale metodo per individuare l’oggetto che ha sprigionato l’evento, inoltre, tale metodo è utilizzabile in tutte le classi degli eventi, dato che tutte hanno
197
UdA 3
Realizzare applicazioni GUI
EventObject come antenata. Per esempio, disponendo un oggetto evento come evento, la seguente istruzione: if (e.getSource()==pulsante1) { //azione conseguente alla pressione del pulsante1 System.out.println(“premuto pulsante1”); … }
confronta il reference dell’evento con quello contenuto nell’identificatore pulsante1 per individuare quindi se l’evento è stato generato proprio sull’oggetto pulsante1: il codice successivo nel ramo della selezione viene eseguito in caso affermativo ed è proprio l’azione di risposta all’evento. Analogo discorso vale per la classe AWTEvent, di cui riportiamo lo schema seguente: classe AWTEvent Metodi costruttori
Metodi modiwcatori
AWTEvent (Event event) AWTEvent (Object source, int id)
Int getId () # boolean isConsumed () void setSource (object new Source) string toString () string paramString () # void consume ()
tutti i metodi sono visibili e quindi invocabili su ogni oggetto evento nella gerarchia delle classi. Ognuna di queste classi si trova nella libreria awt.event che è necessario importare all’inizio di ogni classe con l’istruzione seguente: import java.awt.event.*;
■ Gestire eventi sulla finestra La classe che gestisce gli eventi su di una finestra è WindowListener che dispone di sette metodi che hanno come parametro un oggetto di classe WindowEvent: public public public public public public public
void void void void void void void
esempio
9
windowActivated(WindowEvent e){}; (“Finestra attivata”) windowClosed(WindowEvent e) {}; (“Finestra chiusa “) windowClosing(WindowEvent e) {}; (“Chiudi ºnestra “) windowDeactivated(WindowEvent e) {}; (“Finestra disattivata”) windowDeiconiºed(WindowEvent e) {}; (“Fin. deiconiºcata”) windowIconiºed(WindowEvent e) {}; (“Riduci a icona”) windowOpened(WindowEvent e) {} (“Finestra aperta”);
L’evento di chiusura di una finestra
In questo esempio ci proponiamo di intercettare la richiesta di chiusura dell’applicazione facendo clic sull’icona di chiusura [X] presente sulla finestra. Per prima cosa, implementiamo la classe
198
La gestione degli eventi
Lezione 2
astratta WindowListener in una nuova classe MioAscoltaWin e in essa riscriviamo i sette metodi riportati sopra:
MioAscoltaWin è la classe degli ascoltatori degli eventi sulla finestra: essa richiede sempre la riscrittura di tutti i metodi. In questo esempio abbiamo inserito alcune righe di codice in ogni metodo, che scrivono a video un messaggio di diagnostica che controlla l’avvenuta intercettazione dell’evento. Questa implementazione viene effettuata a scopo di prova: nella future realizzazioni o vengono inserite istruzioni “effettive”, da eseguirsi in risposta al corrispondente evento, oppure il metodo resta vuoto. La classe UsaMioAscoltaWin istanzia l’oggetto ascoltatore e lo registra sull’oggetto che intendiamo osservare:
199
UdA 3
Realizzare applicazioni GUI
Mandiamo in esecuzione ed effettuiamo alcune operazioni: nella finestra Terminal, che appare in quanto viene effettuata una istruzione di stampa a video (println), dopo avere effettuato alcune operazioni, avremo come eco una situazione del tipo illustrato nella figura seguente:
L’esempio ha mostrato un’applicazione con due classi; in questo modo, la classe MioAscoltaWin potrebbe essere utilizzata per tutte le applicazioni future senza necessità di riscrivere, bensì semplicemente inserendo un’istruzione nei nostri esercizi: la registrazione dell’oggetto ascoltatore sull’oggetto fuoco. Naturalmente, dobbiamo verificare se il codice dei diversi metodi soddisfa volta per volta le nostre necessità. Generalmente, nel caso di eventi sulla finestra, le esigenze sono limitate alla chiusura della finestra stessa e quindi potremo, per esempio, definire la classe ChiudiWin() universale nel modo seguente: class ChiudiWin implements WindowListener { public void windowActivated(WindowEvent e) {} public void windowClosed(WindowEvent e) {} public void windowClosing(WindowEvent e) { System.exit(0); } public void windowDeactivated(WindowEvent e) {} public void windowDeiconiºed(WindowEvent e) {} public void windowIconiºed(WindowEvent e) {} public void windowOpened(WindowEvent e) {} }
e inserire in tutte le classi che andremo a realizzare la sola istruzione: XXX.addWindowListener(new ChiudiWin ());
Possiamo anche inserire la classe degli ascoltatori come classe interna alla classe “generatrice” dell’evento (sarà la metodologia che useremo per comodità nelle prossime classi di ascoltatori) e contemporaneamente riduciamo il main alla sola creazione dell’oggetto mentre spostiamo il codice della definizione della finestra e di registrazione dell’ascoltatore in un metodo costruttore. La soluzione completa è nell’esempio seguente. 200
La gestione degli eventi
esempio 10
Lezione 2
Una classe ascoltatore nel metodo costruttore
In questo esempio la classe che contiene i metodi ascoltatori (MioAscoltaWin) viene scritta all’interno della classe che la utilizzerà (ascoltaWinInterna). In pratica nel main della classe AscoltaWinInterna istanziamo solo la classe stessa, mentre il codice che crea la finestra da ascoltare è presente nel metodo costruttore. ▶
In questo caso: ◗ il metodo main si riduce alla sola creazione di un’istanza della classe; ◗ il costruttore svolge tutte le funzioni, cioè definisce il frame, lo inizializza e, successivamente, crea l’oggetto osservatore e lo registra sul frame. Naturalmente, avremmo potuto utilizzare anche qui la notazione sintetica: window1.addWindowListener(new MioAscoltaWin ());
La classe WindowListener è utilizzabile sulle seguenti classi awt: 1 Dialog; 2 FileDialog; 3 Frame; 4 Window. Sugli oggetti di queste classi è possibile quindi registrare un osservatore della classe WindowListener: se si prova a registrare uno di tali osservatori per esempio su un oggetto della classe Button, si ottiene un errore in fase di compilazione in quanto Java effettua un controllo di coerenza logica. Possiamo migliorare il nostro programma creando una classe che eredita direttamente dalla classe Frame: in questo modo si possono richiamare direttamente nella nuova classe i metodi relativi alla finestra e semplificare la scrittura. Infatti, ogni oggetto istanziato eredita auto-
201
UdA 3
Realizzare applicazioni GUI
maticamente le caratteristiche di una finestra poiché la classe che viene progettata è l’estensione di una finestra (Frame). import java.awt.event.*; import java.awt.*; class Ascolta1 extends Frame { public Ascolta1 () { setTitle(ÒAscolta1 • un Frame!Ó); setLocation(100,100); setSize(200,200); addWindowListener(new ChiudiWin ()); setVisible(true); } public static void main (String [] s) { System.out.println(ÒAscolto gli eventi Asco1Ó); new Ascolta1(); } }
Registriamo gli ascoltatori su tale frame utilizzando la classe esterna ChiudiWin() definita in un file a parte; in seguito richiameremo sempre tale classe per semplificare la scrittura del codice.
■ La classe ActionListener Una classe di particolare importanza è la ActionListener perché è quella che ci permette di riconoscere la pressione sui pulsanti. Il diagramma della classe è riportato nella figura: ▶
classe ActionListener Metodi modiwcatori
void actionPerformed (ActionEvent e)
La classe dispone di un unico metodo che ha per parametro un oggetto della classe ActionEvent, come mostrato nella figura seguente: classe AWTEvent Attributi
Metodi costruttori
Metodi modiwcatori
int
ActionEvent (Object source, int id, String command) ActionEvent (Object source, int id, String command, int modiºers) ActionEvent (Object source, int id, String command, long when,int modiºers)
String getActionCommand () int getModiºers () long getWhen ()
SHIFT_MASK, CTRL_MASK, META_MASK, ALT_MASK, ACTION_FIRST, ACTION_LAST, ACTION_PERFORMED
String paramString ()
La classe ActionEvent ascolta gli eventi generati da un’azione e tale azione può essere effettuata su un oggetto delle seguenti classi di componenti: ◗ Button; ◗ CheckboxMenuItem; ◗ List; ◗ MenuMenuItem; ◗ PopupMenu; ◗ TextField.
202
La gestione degli eventi
Lezione 2
Quindi, possiamo registrare un oggetto ascoltatore ActionListener su ogni oggetto delle classi prima elencate e tale oggetto sarà in grado di riconoscere un’azione che viene generata, il che solitamente avviene al clic del mouse. Iniziamo con oggetti della classe Button: nella AWT la classe è molto semplice, ha due soli costruttori e offre poche possibilità, a differenza della classe Swing che vedremo in seguito. classe Button Metodi costruttori
Metodi modiwcatori
Button () Button (String label)
String get/setActionCommand () String get/setLabel () void add/removeActionListener (ActionListener|) ActionListener [] getActionListeners () void processActionEvent (ActionEvent e)
esempio 11
Inserimento di pulsanti
Realizziamo una semplice classe con tre pulsanti e le associamo gli ascoltatori: iniziamo con il definire i tre pulsanti e con la loro aggiunta sul nostro frame. Prima di tutto la classe Azione1 estende Frame, quindi ne eredita metodi e attributi. Possiamo notare che nella classe Azione1 vengono istanziati 3 oggetti che rappresenteranno gli attributi della classe. Il metodo costruttore esegue il metodo setLayout(null) che ha il compito di lasciare libera la dislocazione dei componenti all’interno del Frame. I pulsanti vengono aggiunti al Frame mediante il metodo add(pulsante”n”) associato all’operatore di auto referenza this. Il metodo setBounds(x,y,larghezza,altez za) posiziona i pulsanti nel Frame. A questo punto viene associata la classe che implementa ActionListener ai nostri tre pulsanti mediante il metodo addActionListener(). Come possiamo notare vengono associate tre diverse classi ai tre pulsanti. Infine vengono scritte le tre classi indicate in precedenza che implementano la classe ActionListener sovrascrivendone, in particolare, il metodo actionPerformed(ActionEvent e) che risponde all’evento di click sul pulsante, scrivendo a video una stringa indicante il nome del pulsante coinvolto. ▶
203
UdA 3
Realizzare applicazioni GUI
L’esecuzione mostra la seguente finestra: Per individuare il componente sul quale è avvenuto l’evento possiamo anche usare il metodo getSource() della classe EventObject, per esempio: if (e.getSource()==pulsante1) ... if (e.getSource()==pulsante3) ...
Questa è una valida alternativa, soprattutto quando utilizzeremo i pulsanti della classe Swing privi di etichetta (la classe Swing ci permette di inserire immagini bmp come etichette per i pulsanti).
• Utilizzare gli ascoltatori ActionListener • Usare il metodo getActionCommand()
Prova adesso! APRI IL PROGETTO Esempio11
1 Aggiungi altri 2 pulsanti alla finestra Frame. 2 Modifica il codice della classe Azione1 in modo da utilizzare una sola classe da associare agli ascoltatori dei 5 pulsanti, chiamandola AscoActionListener (ricorda di usare l’oggetto e di classe ActionEvent e il metodo getActionCommand). 3 Associa alla pressione sul pulsante5 l’uscita dalla finestra. 4 Confronta la tua soluzione con quella riportata nel progetto Esempio11_solux.
Gli eventi associati alla classe TextField Lo schema che rappresenta i metodi della classe TextField è il seguente: classe TextField Metodi costruttori
Metodi modiwcatori
TextField TextField TextField TextField
int get/setColumns () char get/setEcoChar () Dimension getMinimumSize (int columns) Dimension getPreferredSize (int columns)
() (String text) (int columns) (String text, int columns)
void add/removeActionListener (ActionListener|) ActionListener [] getActionListeners () # void processActionEvent (ActionEvent e) boolean EcoCharlsSet ()
204
La gestione degli eventi
esempio 12
Lezione 2
Inserimento di una casella di testo
Il pulsante2 è stato qui sostituito da una casella di testo di classe TextField: su di essa, l’azione viene sentita quando l’utente conferma con l’invio la scritta che ha digitato. Prima di tutto vediamo il codice della classe principale chiamata Azione3 in cui è stata implementata anche la classe WindowFocusListener, il cui schema è il seguente: classe WindowFocusListener Metodi modiwcatori
void windowGainedFocus (WindowEvent e) void windowLostFocus (WindowEvent e)
Nel metodo main avviene l’istanza della classe stessa, mentre il metodo actionPerformed() viene riscritto per intercettarne le azioni:
205
UdA 3
Realizzare applicazioni GUI
Il risultato è il seguente: ▶
■ Gli eventi del mouse: la classe MouseListener La classe degli ascoltatori del mouse dispone di cinque metodi, elencati nella figura a lato ▶, mentre nella successiva è riportato il diagramma della classe MouseEvent. ▼
classe MouseListener Metodi modiwcatori
void void void void void
mouseClicked (MouseEvent e) mouseEntered (MouseEvent e) mouseExited (MouseEvent e) mousePressed (MouseEvent e) mouseReleased (MouseEvent e)
classe MouseEvent Attributi
int
Metodi costruttori
MOUSE_FIRST, MouseEvent (Component source, MOUSE_LAST, int id, long when, MOUSE_CLICKED, int modiºers, int x, int MOUSE_PRESSED, y, int clickCount, booMOUSE_RELEASED, lean popupTrigger) MOUSE_MOVED, MouseEvent (Component source, MOUSE_ENTERED, int id, long when, MOUSE_EXITED, int modiºers, int x, int NOBUTTON, y, int clickCount, boBUTTON1, olean popupTrigger, int BUTTON2, button) BUTTON3, MOUSE_DRAGGED, MOUSE_WHEEL
Metodi modiwcatori
String getMouseModiºersText (int modiºers) int getButton () int getClickCount () point getPoint () int getX () int getY () boolean isPopupTrigger () String paramString () void translatePoint (int x, int y)
Il mouse viene ascoltato su tutti gli oggetti di classe awt, o per meglio dire su ogni oggetto è possibile associarne l’evento del mouse. Il seguente codice mostra come individuare il nome di un oggetto catturato dall’evento: Component oggetto =e.getComponent(); String nome =oggetto.getName();
Naturalmente, non è sempre necessario riconoscere tutti gli eventi del mouse su tutti i componenti della nostra interfaccia: noi lo realizziamo solo come esempio, riprendendo la classe Azione3 e completandola come nell’esempio seguente.
206
La gestione degli eventi
esempio 13
Lezione 2
Eventi del mouse
In questo esempio viene creata una classe Mouse1 che implementa le classi MouseListener e ActionListener. Prima di tutto istanzia due pulsanti e una casella di testo, quindi il metodo costruttore, dopo aver aggiunto i componenti nel Frame ne associa gli ascoltatori. In questo caso vengono associati gli ascoltatori per l’azione e per il mouse, mediante i metodi addActionListener(this) e addMouseListener(this). Alla finestra (Frame) viene associato l’ascoltatore di finestra (addWindowListener()) e del mouse (addMouseListener()). Il metodo main infine richiama il costruttore della classe: ▶
Successivamente riscriviamo i cinque metodi sfruttando alcune possibilità offerte dai metodi dell’oggetto MouseEvent, parametro di tutti i cinque metodi: per esempio per sentire le coordinate del mouse usiamo i metodi getx() e gety(): ▶
207
UdA 3
Realizzare applicazioni GUI
Naturalmente questi metodi vanno implementati solo quando servono ma, come i metodi sulla finestra, è obbligatorio sovrascriverli tutti, eventualmente lasciandoli vuoti. Non dobbiamo dimenticarci di implementare anche il metodo actionPerformed per i pulsanti, in questo caso facendo click su Esci chiudiamo la finestra, oppure visualizziamo il nome del pulsante premuto:
Nella figura seguente si vede una possibile esecuzione. ▶
■ Gli ascoltatori della tastiera: KeyListener La classe KeyListener permette di stare in ascolto in riferimento a eventi legati alla pressione dei tasti su quasi tutti gli oggetti. Il diagramma della classe, che possiede tre metodi, è il seguente: classe KeyListener Metodi modiwcatori
void keyPressed (KeyEvent e) void keyReleased (KeyEvent e) void keyTyped (KeyEvent e)
Nella figura seguente è riportato il diagramma della classe KeyEvent: classe KeyEvent Metodi costruttori
Metodi modiwcatori
KeyEvent
String getKeyModiºersText (int modiºers) String getKeyText (int keyCode)
KeyEvent
(Component source, int id, long when, int modiºers, int keyCode, char keyChar) (Component source, int id, long when, int modiºers, int keyCode, char keyChar, int keyLocation)
char get/setKeyChar () int get/setKeyCode () int getKeyLocation () boolean isActionKey () String paramString ()
208
La gestione degli eventi
esempio 14
Lezione 2
Riconoscere il tasto premuto
Di seguito riportiamo il codice che consente di riconoscere il carattere premuto.
Gli ascoltatori sono stati registrati sia sulla TextArea sia sul TextField e i tre metodi della classe KeyListener sono stati riscritti in modo da ottenere l’esecuzione visibile nella figura seguente, ovvero tipo dell’azione, carattere del tasto e corrispondente valore Unicode.
209
UdA 3
Realizzare applicazioni GUI
• Utilizzare gli ascoltatori sulla testiera
Prova adesso! APRI IL PROGETTO Esempio14
1 Modifica il codice del metodo keyPressed in modo tale che individui l’oggetto sul quale è avvenuto uno dei tre eventi. 2 Confronta la tua soluzione con quella del progetto Esempio14_solux.
■ Gli oggetti Item e i relativi eventi La classe ItemListener permette di riconoscere la selezione nei Listbox, Combobox, Checkbox, CheckboxMenuItem. Li tratteremo tutti insieme, in quanto la definizione degli ascoltatori resta analoga alle precedenti. La classe ha un unico metodo con parametro un oggetto ItemEvent come mostrato nelle figure seguenti. classe ItemListener Metodi modiwcatori
void itemStateChanged (ItemEvent e) classe MouseEvent Attributi
Metodi costruttori
Metodi modiwcatori
int
ItemEvent (ItemSelectable source, int id, Object item, int stateCharge)
Object getItem () ItemSelectable getItemSelectable () int getStateChange ()
ITEM_FIRST, ITEM_LAST, ITEM_STATE_ CHANGED, SELECTED, DESELECTED
String paramString ()
Gli elementi grafici tipici della GUI, di tipo item, come i pulsanti CheckBox, CheckBoxGroup, ListBox e ComboBox vengono anche chiamati componenti.
Vediamo la differenza tra i quattro elementi, si tratta di elementi tipici degli ambienti GUI:
210
La gestione degli eventi
Lezione 2
Vediamo un esempio per la creazione di oggetti di questo tipo. esempio 15
Finestra con componenti item
In questo esempio vogliamo sviluppare il codice necessario per mostrare in una finestra alcuni oggetti (componenti) di tipo item.
Quindi realizziamo il costruttore della classe e posizioniamo i componenti sul contenitore.
211
UdA 3
Realizzare applicazioni GUI
Fino a questo punto, abbiamo definito gli oggetti nel frame: ora il codice prosegue con la registrazione degli ascoltatori su tutti gli oggetti, definendo una sola classe di ascoltatori ▶. All’esecuzione del codice, vedremo come è possibile riconoscere su quale oggetto si è verificato l’evento e quale opzione è stata selezionata, in quanto Object wre = e.getItemSelectable(); è equivalente al metodo e.getSource();.
Prova adesso!
• Usare gli ascoltatori sui componenti item
APRI IL PROGETTO Esercizio1 1 Modifica il codice in modo tale da poter effettuare la copia e lo spostamento degli elementi tra le due liste. Per fare questo devi implementare direttamente la classe MouseListener. Per comodità, posiziona l’item selezionato nella posizione [0] di un vettore temporaneo, quindi aggiungilo e toglilo dai due listbox. Completa l’interfaccia abilitando i pulsanti creando una classe apposita GestorePulsante() avente un costruttore che riceve come parametri i due oggetti list. Quindi registra i due ascoltatori su entrambi gli oggetti. Riscriviamo l’unico metodo riconoscendo, per esempio, il pulsante che viene premuto (con un clic sulla sua etichetta) e quindi copia o sposta l’item selezionato. 2 Confronta la tua soluzione con quella riportata nel progetto Esercizio1_solux in cui è stato aggiunto anche l’uso del doppio click sull’elemento per effettuare lo spostamento.
Infine nella tabella che segue ricapitoliamo, per ogni classe di oggetti, i possibili ascoltatori che Java mette a disposizione per intercettare gli eventi: come già detto, è il programmatore, volta per volta e in base alle proprie esigenze, a decidere quale utilizzare e a sceglierne le modalità (interno, esterno e così via). 212
La gestione degli eventi
componenti
Lezione 2
eventi associati a questi componenti
Adjustable
AdjustmentEvent
Applet
ContainerEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent
Button
ActionrEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent
Canvas
FocusEvent, KeyEvent, MouseEvent, ComponentEvent
Checkbox
ItemEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent
CheckboxMenuItem
ActionEvent, ItemEvent
Choice
ItemEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent
Component
FocusEvent, KeyEvent, MouseEvent, ComponentEvent
Container
ContainerEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent
Dialog
ContainerEvent, WindowEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent
FileDialog
ContainerEvent, WindowEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent
Frame
ContainerEvent, WindowEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent
Label
FocusEvent, KeyEvent, MouseEvent, ComponentEvent
List
FocusEvent, KeyEvent, MouseEvent, ComponentEvent
Component
ActionEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent
Component
ActionEvent
MenuItem
ActionEvent
Panel
ContainerEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent
PopupMenu
ActionEvent
Scrollbar
AdjustmentEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent
ScrollPane
ContainerEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent
TextArea
TextEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent
TextComponent
TextEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent
TextField
ActionEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent
Window
ContainerEvent, WindowEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent
Nella prossima lezione vedremo come realizzare applicazioni in Java attraverso uno strumento che ci consente di evitare la programmazione diretta degli ascoltatori e delle problematiche a essi collegate: si tratta di un ambiente di programmazione IDE. Tuttavia le conoscenze e le competenze acquisite all’interno di questa unità non vanno dimenticate, anzi sono un valido requisito per comprendere appieno il funzionamento delle routine che operano sulle interfacce utente.
213
UdA 3
Realizzare applicazioni GUI
Verifichiamo le competenze g Problemi 1 Dewnisci gli attributi e i metodi della classe Auto in grado di disegnare il cruscotto di un’automobile in cui collocare: • 4 bottoni con testo: Freccia Sx, Freccia Dx, Freno Manuale, Avvia Motore; • un’etichetta con testo Luci con associate 3 caselle opzione con testo posizioni, anabbaglianti, abbaglianti, tutte non attivate; • un’etichetta carburante e accanto una casella di testo. 2 Inserisci due pulsanti e un campo di testo in una wnestra: un pulsante incrementa il numero presente nella casella di testo, l’altro pulsante lo decrementa. 3 Realizza un generatore casuale di pallini-rettangoli-ovali (colorati) al clic del mouse: se successivamente clicchi su una wgura verrà cancellata. 4 Inserisci in una casella di testo un numero intero, quindi tramite un pulsante disegna una circonferenza avente tale numero come raggio in una posizione random dello schermo. 5 Inserisci tre pulsanti in una wnestra, ciascuno con i nomi di tre squadre calcistiche (per esempio Juve, Inter, Milan). Alla pressione di uno dei tre pulsanti, colora la wnestra con i relativi colori sociali a strisce verticali. 6 Inserisci tre pulsanti, uno per ogni colore fondamentale R G B: facendo clic su ogni pulsante incrementa il contatore della variabile corrispondente e modiwca il colore di sfondo dello schermo. 7 Inserisci un’immagine in una wnestra e tramite quattro pulsanti spostala nella direzione dei quattro punti cardinali. 8 Suddividi una wnestra in 16 quadrati e modiwca il colore del quadrato su cui è posizionato il puntatore del mouse. 9 Realizza due wnestre con un pulsante e una casella di testo: cliccando sul pulsante riporta il contenuto della casella di testo nella casella dentro la seconda wnestra e viceversa. 10 Realizza una calcolatrice in grado di effettuare la conversione dei numeri da decimale alla base di numerazione selezionata dall’utente nella CheckBox sottostante. 11 Inserisci una lista (List) con un elenco di nomi in una wnestra, che vengono eliminati facendo click su di essi. Colloca nel Frame anche un pulsante in grado di riempire nuovamente la lista di elementi. 12 Inserisci due list box in una wnestra e agisci su due pulsanti: il primo sposta l’elemento selezionato dalla prima alla seconda lista, il secondo li trasferisce tutti. 13 Inserisci due caselle di testo e un pulsante in una wnestra: scritta una parola nella prima casella, il clic sul pulsante fa scrivere in cifre la lunghezza della parola appena scritta nella seconda casella di testo. 14 Realizza il gioco del tris. Per fare questo devi realizzare una wnestra con tre caselle combinate (ComboBox): nella prima seleziona un colore tra 4 opzioni, nella seconda una forma e nella terza un numero di elementi. Facendo clic su di un pulsante il programma deve disegnare in posizioni casuali tanti oggetti del tipo e del colore prescelto (usa tre caselle di testo per inserire le tre scelte selezionate dai menu). 15 Crea un programma in grado di trasferire il contenuto di una casella di testo in una lista (List) alla pressione su di un pulsante. Raggiunti i 10 elementi, visualizza una wnestra modale con la segnalazione di termine inserimento.
214
L’interfaccia utente con NetBeans
lezione
Lezione 3
3
L’interfAcciA Utente con netbeAns in qUestA Lezione impAreremo...
• a installare e utilizzare NetBeans • a creare applicazioni GUI • a modificare gli eventi e le proprietà • ad associare eventi a oggetti GUI
■ NetBeans come IDE NetBeans è un programma che consente di creare applicazioni in Java offrendo un approccio di tipo ◀ IDE ▶. Si tratta di un ambiente di sviluppo multi-linguaggio realizzato, in linguaggio Java dalla Sun Microsystem distribuito dall’anno 2000. Consente di realizzare interfacce grafiche tramite la tecnica del Drag and drop di componenti. ◀ IDE È l’acronimo di integrated development environment, che significa ambiente di sviluppo integrato. Si tratta in sintesi di un software che aiuta i programmatori nello sviluppo del codice mettendo a disposizione del programmatore un editor di codice sorgente, un compilatore o un interprete, un tool di building automatico, e un debugger. ▶
Come abbiamo visto attraverso l’ambiente di programmazione BlueJ, ciascun programma può essere formato da diverse classi che vengono incluse all’interno di un progetto. Anche in NetBeans esistono i progetti che tuttavia possono essere di diversa natura. L’ambiente IDE consente la produzione automatica di molte righe di codice per la gestione, per esempio, dei componenti grafici (pulsanti, caselle di testo ecc.) e dei relativi eventi da gestire, grazie all’uso di un ◀ wizard ▶. ◀ Wizard Il termine, che deriva dalla lingua inglese, significa mago o magia, e identifica appunto una semplice esecuzione di una procedura complessa, come per magia. Indica, in sostanza, un procedimento che ci consente di semplificare operazioni complesse attraverso una sequenza di passi successivi. ▶
215
UdA 3
Realizzare applicazioni GUI
NetBeans è particolarmente adatto per scrivere programmi usando il linguaggio Java e può essere scaricato gratuitamente dal sito http://www.netbeans.org/downloads/:
In questo abbiamo scaricato la versione base (Java SE), che consente la produzione di progetti che utilizzano la Java Standard Edition. Prima di installare l’ambiente di sviluppo NetBeans, come abbiamo già accennato per l’ambiente BlueJ, dobbiamo installare le librerie e i compilatori Java (JSE). Il file che otteniamo è il seguente, tuttavia le versioni vengono aggiornate con una certa frequenza: ▶ Dopo aver scaricato il software avviamo l’installazione, si tratta di una operazione assai semplice, pertanto la lasciamo al lettore. Durante l’installazione dobbiamo soltanto verificare che il percorso della JSE suggerito da NetBeans sia quello corretto.
■ Creare un progetto con NetBeans Dopo aver installato il programma possiamo iniziare con il nostro primo progetto. Per fare questo seguiamo i passi indicati nella figura. Possiamo creare direttamente una classe o un progetto. Nel primo caso, viene richiesto comunque di creare un progetto per la classe. Una volta scelto il nome del progetto e dove salvarlo passiamo alla creazione guidata della classe. La procedura che segue illustra come creare un nuovo progetto: 1
216
Apri il programma NetBeans: ▶
L’interfaccia utente con NetBeans
2
Per prima cosa facciamo click sul menu File e selezioniamo New Project...:
3
Nella finestra che appare dobbiamo selezionare il tipo di progetto che intendiamo creare, iniziamo dal progetto più elementare chiamato Java Application:
4
Dopo aver fatto click su Next appare la finestra successiva:
Lezione 3
217
UdA 3
Realizzare applicazioni GUI
In questa finestra dobbiamo assegnare il nome al progetto Project Name: e selezionare, tramite il pulsante Browse... in corrispondenza dell’etichetta Project Location:, la corrispondente cartella di destinazione dove verranno salvati tutti i file a esso associati. 5
Se lasciamo spuntata l’opzione Create Main Class il programma creerà automaticamente una classe con il nome indicato dal corrispettivo campo di testo, contenente il metodo main dell’applicazione. Lasciamo spuntato Create Main Class dando come nome alla classe SalveMondo, quindi facciamo click su Finish
Se non attiviamo la voce Create Main Class il programma crea un progetto vuoto. Per poterlo avviare dobbiamo creare manualmente la classe principale in un secondo momento, facendo click col tasto destro del mouse sul progetto, selezionando quindi New e poi Java Class... ▶: La classe appena creata sarà tuttavia priva del metodo main che andrà scritto manualmente. 6
A questo punto apparirà nell’editor di testo la classe SalveMondo contenente il main. Inseriamo nella procedura main la seguente riga di codice la quale non fa altro che visualizzare a video la frase “Salve Mondo!”: System.out.println(“Salve Mondo!”);
218
L’interfaccia utente con NetBeans
7
Adesso non ci resta che lanciare l’applicazione cliccando su Run Project dal menu Run, oppure cliccando sull’apposita icona come mostrato dalla seguente figura:
8
Come possiamo notare appare il risultato del programma nella finestra di Output, come mostrato nella figura: ▶
Lezione 3
Per eseguire il progetto possiamo anche premere il tasto F6. L’eseguibile di tale programma è utilizzabile anche da altri utenti che non possiedono NetBeans. Tuttavia per compilare e creare l’eseguibile in formato .jar del programma dobbiamo attivare il menu Run, quindi selezionare Build Project: ▶
La cartella del progetto, in questo caso chiamata prova, è una sottocartella di NetBeansProject. Tale cartella contiene, nella sottocartella dist, il file eseguibile in formato .jar: ▶
In sintesi per creare una applicazione dobbiamo: 1 Creare un progetto: quando creiamo un progetto IDE dobbiamo anche creare l’ambiente che conterrà il necessario per l’esecuzione della nostra applicazione. 2 Aggiungere del codice al file sorgente generato: il file sorgente contiene del codice, scritto in linguaggio Java. Un progetto IDE quando viene creato fornisce uno scheletro del codice sorgente automaticamente generato. Occorrerà modificare tale codice per creare la nostra applicazione. 3 Compilare il file sorgente in un file .class: L’IDE invoca il compilatore java (javac), il quale traduce il sorgente in bytecode che la JVM è in grado di eseguire. 4 Eseguire il programma: L’IDE invoca il programma di lancio (java), il quale utilizzerà la JVM per eseguire l’applicazione.
■ Creare un’interfaccia grafica con NetBeans Uno degli usi principali di NetBeans è quello che consente di creare un’interfaccia grafica di tipo GUI (Graphical User Interface) attraverso la tecnica Drag and drop, senza scrivere una riga di codice. Mediante il disegnatore di NetBeans possiamo trascinare un oggetto nella finestra (Frame) facendo generare al programma il codice necessario per la gestione dell’oggetto stesso. La procedura che segue mostra come realizzare una finestra inserendovi un pulsante.
219
UdA 3
Realizzare applicazioni GUI
1
Prima di tutto apri NetBeans e crea un nuovo progetto. In questo caso ripartiremo dal progetto precedente.
2
Adesso dobbiamo creare la finestra in cui collocare un pulsante e un’etichetta. Per fare questo dobbiamo fare click col tasto destro sul progetto appena creato, scegliendo la voce New, quindi JFrame Form...: ▶
3
Nella finestra che appare dobbiamo inserire nell’apposita casella Nome della Classe il nome da assegnare alla classe che contiene la finestra, in questo caso lasceremo il nome che appare normalmente (NewJFrame), quindi facciamo click su Finish: ▶
4
Appare la seguente finestra in cui vengono evidenziate le varie sezioni del programma:
Codice sorgente
Componenti Swing trascinabili
Area di disegno Proprietà oggetto selezionato
Area di navigazione
220
L’interfaccia utente con NetBeans
Lezione 3
Vediamo il significato delle sezioni indicate. ◗ Area di navigazione Sono presenti tutti gli oggetti che sono stati aggiunti nella nostra interfaccia grafica. Essi vengono organizzati ad albero dove un oggetto nodo può identificare un contenitore nel quale sono presenti altri oggetti. Questa finestra mette in luce la struttura dell’interfaccia grafica che stiamo realizzando. Inoltre semplifica la selezione di un determinato oggetto, soprattutto quando il progetto ne contiene molti. ◗ Area di disegno Si tratta di una finestra nella quale possiamo trascinare gli oggetti mediante una semplice operazione di drag and drop. Inoltre la finestra mostra il layout degli oggetti così come verranno visualizzati durante l’esecuzione del programma secondo la logica Wysiwyg (What You See Is What You Get). ◗ Componenti Swing trascinabili Questa finestra si chiama Palette e mostra l’elenco di tutti i componenti grafici (oggetti di classe JSwing) che possiamo utilizzare. Vengono mostrati suddivisi per categorie, per utilizzarli è sufficiente selezionare quello che ci interessa per poi trascinarlo nell’area disegno. ◗ Proprietà oggetto selezionato Consente di visualizzare e modificare tutte le proprietà, disponibili in fase di progettazione, di un determinato oggetto selezionato e di gestire gli eventi a esso associato. Come si può notare in quest’area sono presenti delle voci (Properties, Binding, Events, Code) che suddividono le caratteristiche dell’oggetto in esame. ◗ Codice sorgente Facendo click su questa sezione possiamo visualizzare il codice legato alla finestra. 5
Adesso passiamo all’inserimento nella finestra, mediante trascinamento, degli oggetti che ci interessano, in questo caso di un pulsante. ▶
6
Per fare questo dalla finestra Palette, nella categoria Swing Controls, selezioniamo e trasciniamo nell’area disegno un oggetto Button e un oggetto Label: ▶
Possiamo ridimensionare un oggetto, una volta selezionato, cliccando e trascinando uno dei ganci che lo circonda, come nella figura: ▶ 7
Clicca e trascina
Adesso proviamo a modificare alcune proprietà degli oggetti per migliorarne l’aspetto, per fare questo selezioniamo il pulsante, dall’area di navigazione o direttamente dall’area disegno, tenendo presente che per selezionare gli oggetti nell’area disegno, il pulsante Selection Mode deve essere selezionato: ▶ 221
UdA 3
Realizzare applicazioni GUI
8
Adesso proveremo a modificare una proprietà dell’oggetto pulsante, per fare questo dobbiamo innanzi tutto assicurarci che la scheda Properties sia attiva. Modifichiamo la proprietà text che consente di cambiare il testo che appare sul pulsante: Oggetto a cui appartengono le proprietà visualizzate
Scheda Properties attiva
Proprietà text da modificare
Tipo della proprietà
9
In questo caso abbiamo modificato la proprietà dell’oggetto jButton1. Per vedere il risultato e ottenere così una anteprima dell’output del programma facciamo click su Preview Design: ▶ Otteniamo la seguente finestra: Preview Design mostra solo l’aspetto grafico della finestra di classe JFrame e quindi ogni eventuale funzionalità dei componenti dell’interfaccia, implementate col codice, non sono eseguite, l’unica eccezione è legata alla funzionalità della finestra come per esempio la chiusura e il ridimensionamento.
Adesso abbiamo solo imparato come inserire alcuni oggetti e modificarne le proprietà. Nel paragrafo seguente vedremo come associare l’evento e il codice relativo.
■ Associare il codice agli elementi grafici In questo caso il codice che intendiamo associare modificherà il contenuto dell’etichetta jLabel1, alla pressione del pulsante jButton1 inseriremo un testo nell’etichetta. Per fare questo dobbiamo gestire l’evento actionPerformed del pulsante, inserendo un ascoltatore di eventi. Tuttavia NetBeans, lo farà automaticamente, come mostrato dalla procedura che segue:
222
L’interfaccia utente con NetBeans
1
Selezioniamo il pulsante jButton1, quindi nella sezione Properties attiviamo la scheda Events. ▶
2
Qui vengono elencati tutti gli eventi standard associabili al pulsante. L’evento che ci interessa è actionPerformed, dobbiamo quindi selezionare l’oggetto da associare all’evento semplicemente facendo click su di esso: ▶
3
A questo punto si aprirà l’editor di testo in cui NetBeans creerà automaticamente un metodo (jButton1ActionPerformed()) a cui associare l’evento actionPerformed, che verrà eseguita ogni qualvolta si farà click sul pulsante jButton1:
Lezione 3
La stessa operazione, cioè quella di associare l’evento al pulsante selezionato, la possiamo ottenere facendo doppio click sul pulsante. 4
Adesso non dobbiamo fare altro che scrivere il codice necessario alla modifica della proprietà text dell’oggetto jLabel1, attraverso l’invocazione del metodo setText():
223
UdA 3
Realizzare applicazioni GUI
NetBeans offre un valido strumento chiamato intellisense che consente di avere un elenco dei metodi e delle proprietà dell’oggetto che stiamo digitando. Basta infatti scrivere il nome dell’oggetto seguito dal punto e automaticamente apparirà un elenco di metodi e proprietà dai quali selezionare la voce interessata facendo click su di essa:
5
A questo punto per eseguire il file dobbiamo fare click con il tasto destro del mouse sul file del programma (NewJFrame.java), quindi selezionare la voce Run File: ▶
6
Otteniamo la finestra seguente nella quale se facciamo click sul pulsante Saluti otteniamo una scritta nell’etichetta posta accanto: ▶
7
Tuttavia all’avvio del programma l’etichetta mostra la scritta jLabel1, per cancellarla dobbiamo eliminare la scritta di default dell’etichetta, che può essere effettuata in due modi: ◗ attraverso la scheda Properties dell’oggetto in fase di progettazione; ◗ attraverso il costruttore della classa NewJFrame per modificarla in fase di run time a ogni esecuzione. In questo caso proviamo la seconda strada per comprendere come scrivere nel costruttore della classe. Scriviamo nel costruttore il seguente codice:
Che sostanzialmente disattiva la scritta, associando a essa un valore null.
224
L’interfaccia utente con NetBeans
Lezione 3
Possiamo vedere che nel costruttore appare la chiamata a un metodo chiamato initComponents(). Questo metodo viene generato automaticamente dal programma e contiene tutto il codice dell’interfaccia grafica che abbiamo costruito nell’area disegno; il codice non può essere modificato direttamente ma solo attraverso l’area delle proprietà dei singoli oggetti. In sintesi vediamo il codice generato da NetBeans nell’esempio precedente: Dichiarazione della classe come estensione di JFrame Costruttore: richiama il metodo InitComponet. Qui dobbiamo impostare le proprietà iniziali del Frame Metodo creato automaticamente da NetBeans. Aggiunge il listener per gestire gli eventi di chiusura della finestra. È meglio non scrivere nulla all’interno di questo metodo.
Metodo main opzionale, possiamo creare un programma driver al suo posto
Metodo run, eseguito all’esecuzione istanzia un frame e lo rende visibile
esempio 16
Una applicazione GUI: calcolo dell’area del rettangolo
In questo esempio utilizziamo due pulsanti di classe Button (Calcola e Cancella) e tre caselle di testo di classe TextField per leggere base e altezza e visualizzare il risultato. La classe TextField rappresenta una casella di testo ed è presente nella finestra laterale chiamata Palette dove sono presenti anche tutti gli altri elementi grafici. Per inserire due caselle di testo non dobbiamo fare altro che trascinarle all’interno del form:
Dopo aver aggiunto gli oggetti caselle di testo, dobbiamo inserire alla proprietà text il valore null per evitare che mostrino il valore di default, assai poco gradevole esteticamente. Per fare questo scriviamo le seguenti righe di codice nel metodo costruttore della classe NewJFrame: ▶
225
UdA 3
Realizzare applicazioni GUI
Aggiungiamo adesso anche tre etichette (Label) per associare una scritta accanto alle caselle di testo, dopo aver modificato la proprietà delle tre etichette, sostituendo per esempio a jLabel1 il testo “base”: ▶
Le caselle di testo devono essere dimensionate opportunamente per evitare che abbiano una dimensione troppo ridotta. Per fare questo dobbiamo agire sulla proprietà Horizontal Size, assegnando un valore pari a 100:
Una volta inseriti tutti gli oggetti associamo al primo pulsante (jButton1) il codice seguente:
Associamo al secondo pulsante (jButton2) il codice seguente:
Infine per fare in modo che la finestra, presente nella classe NewJFrame, sia mostrata avviando il pulsante Run Project, dobbiamo scrivere il codice seguente nel main della classe che utilizzerà la finestra:
226
L’interfaccia utente con NetBeans
Lezione 3
In tal modo avviene una istanza della classe NewJFrame. Adesso eseguiamo il nostro programma facendo click sul pulsante Run Project, l’esecuzione è mostrata di seguito:
Prova adesso! APRI IL PROGETTO Esercizio2
• Creare un progetto GUI con NetBeans • Aggiungere oggetti al frame • Utilizzare Button, Label e TextField
1 Modifica il progetto in modo tale che sia in grado di convertire anche le temperature da gradi Fahrenheit a gradi Celsius. 2 Confronta la tua soluzione con quella presente nel progetto Esercizio2_solux.
■ L’oggetto TextArea Si tratta di una lista di classe JTextArea che consente di contenere più righe di testo all’interno dello stesso elemento. È importante sottolineare che per aggiungere righe all’interno di una lista si usa il metodo append(). L’esempio seguente mostra come utilizzarlo per stampare a video un elenco di numeri. esempio 17
Numeri primi
Nell’esempio proposto vi è un metodo che riceve come argomento un numero n e restituisce un valore true o false a seconda che esso sia primo oppure no. Il metodo va inserito subito dopo Generate Code, come mostrato di seguito:
227
UdA 3
Realizzare applicazioni GUI
A questo punto inserisci due caselle di testo di tipo TextField e due etichette (Label) affiancate a esse. Modifica il testo (proprietà text) delle etichette in “Numeri >=” e “e