CAPITOLO 4 LINGUAGGIO JAVA: COSTRUTTI DI BASE
ALFABETO Java adotta la codifica standard Unicode della società Unicode, Inc. (ftp://ftp.unicode.org) definito in: The Unicode Standard, Worldwide Character Encoding, Version 2.0 I programmi Java possono essere scritti con altre codifiche (e.g. la codifica ASCII a 7 o 8 bit), ma devono essere convertiti alla codifica Unicode (a 16 bit) prima di essere utilizzati da parte di compilatori, interpreti, debugger, etc. N.B. - Nello standard Unicode, un segmento della codifica è riservato ai caratteri speciali
CODICE SORGENTE Consiste di una o più unità di compilazione Ogni unità può contenere (oltre ai commenti) –al più una istruzione package –una o più istruzioni import –una o più dichiarazioni di classe (class) –una o più dichiarazioni di interfaccia (interface) Per ciascuna unità di compilazione, al più una classe può essere dichiarata public Il codice sorgente viene compilato a bytecode (cfr. The Java Virtual Machine Specification), un insieme di istruzioni machine-independent
COMMENTI Presenti in tre diversi stili, non hanno alcuna influenza sulla semantica dei programmi –// Primo stile: io sono un commento su di una sola linea –/* Secondo stile: anch'io sono un commento monolinea */ –/* Secondo stile: io, invece, sono un commento più lungo: non ci sto in una sola linea e quindi ne occupo ben due */ –/** Terzo stile: io sono un commento di tipo speciale, che chiamano "commento di documentazione". */ Un commento del terzo tipo va collocato solo immediatamente prima di una dichiarazione (di classe, di interfaccia, di costruttore o di membro); viene incluso nella documentazione generabile automaticamente (ex sorgente)
TIPI SEMPLICI "Non tutti i dati sono oggetti" (impurità di Java) boolean- tipo enumerativo (due elementi) char- carattere di 16 bit (Unicode 1.1.5) byte- intero di 8 bit (complemento a 2) short- intero di 16 bit (complemento a 2) int- intero di 32 bit (complemento a 2) long- intero di 64 bit (complemento a 2) float- floating-point di 32 bit (IEEE 754) double- floating-point di 64 bit (IEEE 754) Tutti i tipi semplici (o di base) sono predefiniti
TIPI COMPOSTI Equivalenti alle variabili strutturate PASCAL, nella loro versione dinamica (operatore new) I dati composti sono mediati, i.e. manipolati per mezzo di puntatori non visibili all'utente (i dati semplici sono immediati, i.e. manipolati direttamente) Tre categorie di tipi composti –Matrici- versione dinamica degli array PASCAL –Classi- versione "attiva" dei record PASCAL –Interfacce- simili alle dichiarazioni forward in PASCAL Di questi, solo i tipi matrice sono predefiniti: le librerie di classi e di interfacce non lo sono
LETTERALI (1) Rappresentazione "esterna" di dati e oggetti Alcuni tipi semplici ammettono letterali –boolean- true, false –char- 'a', '\u03ff', '\037', '\x7f' - '\', '\n', '\t', '\b', '\r', '\f', '\\', '\'', '\"' –int- 127, 0177, 0x7F, 0X7F, 0x7f, 0X7f –long- 127L, 127l, –float- 3.14, 3.1E12, 3.1e-12,.1e12, 2e-12 –double- 3.14D, 3.14d, 3.1e-12D,.1e12d, 2e-12D Uno solo dei tipi composti ammette letterali –String- "", "Da\ncapo", "Io non sono Luigi Stringa" Per gli altri esiste il letterale generico null
LETTERALI (2) Letterali distinti possono rappresentare lo stesso valore –int- 127, 0177, 0x7F I numeri negativi, di qualunque tipo siano (salvo gli esponenti), non sono esprimibili mediante letterali Le routine di stampa sono in grado di stampare qualunque dato di tipo semplice Il formato di stampa è coerente con quello dei letterali (se esiste); nel caso di più formati possibili, ne viene scelto uno come standard
PAROLE CHIAVE (RISERVATE) Parole chiave: abstract, boolean, break, byte, case, catch, char, class, const (*), continue, default, do, double, else, extends, final, finally, float, for, goto (*), if, implements, import, instanceof, int, interface, long, native, new, package, private, protected, public, return, short, static, super, switch, synchronized, this, throw, transient (*), try, void, volatile, while (*) Inutilizzata nella specifica attuale di Java
IDENTIFICATORI Debbono iniziare con una lettera (v. sotto) o con uno dei due simboli '_' e '$', e possono contenere qualunque carattere non speciale della codifica Unicode; non hanno (in pratica) limiti di lunghezza Ai fini di Java, sono lettere –tutti i caratteri compresi tra 'A' e 'Z' e tra 'a' e 'z' –le non-cifre di codice Unicode superiore a 00C0 (hex) Esempi: identifier, user_name, User_name, _user_name, $user_name, user$name1 Se attributi di classi, sono inizializzati a false (i boolean), 0 (i tipi numerici) o null (il resto)
MATRICI Vettori monodimensionali di dati, semplici o composti, di tipo omogeneo –Semplici - char a[]; char[] b; char array[]; –Composti (oggetti) - String args[]; String[] other_args; –Composti (matrici) - char chess[][]; char[][] checkers; Voxel human_body[][][]; Indicizzati con int (indice primo elemento = 0), con controllo di validità degli indici a run-time Vanno create e dimensionate esplicitamente, anche contestualmente alla dichiarazione (la quale di per sé non alloca nulla, ma si limita a definire il tipo di un nome, inizializzato a null)
MATRICI: CREAZIONE INIZIALIZZAZIONE Dichiarazione e successiva creazione int array[][];/* qui array == null */ array = new int[10][3]; Dichiarazione e contestuale creazione int array[][] = new int[10][3]; Dichiarazione e parziale creazione int array[][] = new int[10][]; Dichiarazione, creazione e inizializzazione –String names[] = {"Tic","Tac"}/* equivalente a... */ –String names[] = new String[2]; names[0] = "Tic"; names[1] = "Tac";
ERRORI CON LE MATRICI Tentare di dimensionare una matrice nella (e non contestualmente alla) sua dichiarazione int list [50]; Tentare di inizializzare una matrice prima di averla creata int list[]; for (int i=0 ; i<9 ; i++) { list[i] = i; } Prima della sua creazione (operatore new) la matrice non esiste: la dichiarazione ha l'unico scopo di specificare il tipo della variabile (che comprende solo il numero delle dimensioni)
MATRICI COME CLASSI Dalla classe Object, la capostipite di tutte le classi, deriva la classe Array, dotata di un metodo length ereditato dalle sue sottoclassi int a[][] = new int[10][3]; a.length;/* 10 */ a[0].length;/* 3 */ Per ogni tipo (semplice o composto) esiste implicitamente una sottoclasse di Array che comprende tutte le matrici di oggetti di quel tipo (N.B. da Array non si possono derivare sottoclassi esplicitamente) Se B è sottoclasse di A, allora B[] lo è di A[]
CLASSI, ATTRIBUTI E METODI Esempio di classe public class MyClass { int i; /* attributo */ public MyClass () { /* metodo costruttore: ha lo i = 10; stesso nome della classe */ } public void Add_to_i (int j) { /* altro metodo */ i = i+j; } Ricordiamo che Java non adotta il modello a oggetti puro: "non tutti i dati sono oggetti"
FUNZIONI E POLIMORFISMO: OVERLOADING DI METODI Variante dell'esempio precedente public class MyNewClass { int i; /* attributo */ public MyNewClass () { /* metodo costruttore: ha lo i = 10; stesso nome della classe */ } public MyNewClass (int j) { /* altro metodo costruttore i = j; diverso dal precedente */ }..... } Metodi omonimi devono essere distinguibili per la quantità e/o per il tipo degli argomenti
ATTRIBUTO this Implicitamente presente in ogni oggetto, è sempre legato all'oggetto medesimo public class MyNewClass { int i; /* attributo */ public MyNewClass () { i = 10; } public MyNewClass (int i) { /* parametro */ this.i = i; /* disambiguazione */ }..... } Risolve omonimie e.g. tra attributi e parametri
CREAZIONE DI OGGETTI Creazione e uso di un oggetto con MyClass MyClass mc; /* dichiarazione, non creazione */ mc = new MyClass(); /* creazione: l'attributo i vale 10 */ mc.i++; /* ora i vale 11 */ mc.Add_to_i(10); /* e ora i vale 21 */ Creazione di un oggetto con MyNewClass MyNewClass mc0, mc1; /* dichiarazione, non creazione */ mc0 = new MyNewClass(); /* creazione: l'attributo i vale 10 */ mc0.i++; /* ora i vale 11 */ mc1= new MyNewClass(20); /* creazione: l'attributo i vale 20 */ mc1.i++; /* ora i vale 21 */
DISTRUZIONE DI OGGETTI Java non supporta "distruttori" espliciti: gli oggetti non si distruggono, si "riciclano" Un oggetto privo di puntatori incidenti non è più accessibile alle applicazioni e la memoria che esso occupa può essere "riciclata" String s; /* dichiarazione, non creazione */ s = new String ("abc"); /* creazione: s punta a "abc" */ s = "def"; /* "abc" non è più puntata da s */ Il "riciclatore" (garbage collector, o GC) opera in un thread indipendente (i.e. "in parallelo" con altri thread, sia di utente che di sistema)
FINALIZZATORI DI OGGETTI Un oggetto "riciclabile" (i.e. privo di puntatori incidenti) potrebbe trovarsi in uno stato poco "pulito" (e.g. in passato potrebbe aver aperto dei file che non sono stati ancora chiusi) Prima di procedere alla fase cruenta, il GC invoca il metodo finalize di ciascun oggetto "riciclabile", definibile da utente protected void finalize () /* per protected vedi oltre */ { close (); } /*chiudi tutti i file aperti */ Il metodo finalize esiste sempre: se non è stato ridefinito viene ereditato da Object
CLASSI: SINTASSI [Doc comment][Modifiers] class ClassName [extends SuperClassName] [implements InterfaceName [, InterfaceName]] {ClassBody} Doc comment- commento "di documentazione" Modifiers- uno o più dei qualificatori abstract, final, public/protected/private, native, synchronized, transient, volatile extends- la classe è sottoclasse di un'altra (e di una sola: ereditarietà singola) implements- la classe realizza una o più interfacce (può "simulare" ereditarietà multipla) ClassBody- gli attributi e i metodi della classe
CLASSI E SOTTOCLASSI Le zebre come particolari tipi di cavallo... public class Horse { int Head, Tail, Legs, Body; public Horse () { Head = 1; Tail = 1; Legs = 4; Body = 1; } public void AddLegs (int Legs) { this.Legs += Legs;} } public class Zebra extends Horse { int Stripes; public Zebra (int Stripes) { this.Stripes = Stripes; } public void AddLegs (int Legs) /* ridefinizione */ { this.Legs += Legs + Stripes; } }
SOTTOCLASSI E super Manipoliamo le zampe di una nuova zebra Zebra mz; mz = new Zebra( ); /* ha 4 zampe */ mz.AddLegs(4); /* ora ne ha */ Il vecchio metodo AddLegs è ancora usabile mz.super.AddLegs(4); /* ora ne ha */ L'attributo super, implicitamente presente in ogni oggetto, permette di "ricuperare" un attributo/metodo da una (sopra)classe dopo che è stato ridefinito in una sua (sotto)classe
SOTTOCLASSI E CASTING Proseguendo con l'esempio precedente Horse mh = (Horse)mz; /* ora è vista come cavallo */ mh.AddLegs(4); /* ora ha zampe */ (Zebra)mh.AddLegs(4); /* ora ne ha */ Il cast da una classe a una sottoclasse (anche non diretta) è possibile se l'oggetto è davvero un esemplare della sottoclasse (il controllo è fatto a run-time); il viceversa è sempre OK Il cast per linea di discendenza non diretta è un errore (il controllo è fatto a compile-time) Il cast non altera gli oggetti: influisce solo sul modo di "osservare" un puntatore all'oggetto
METODI: SINTASSI [Doc comment][Modifiers] ReturnType MethodName (ParameterList) {MethodBody} Modifiers- uno o più dei qualificatori abstract, final, public/protected/private, native, synchronized, transient, volatile ReturnType- va omesso nei costruttori, ma va indicato (eventualmente void) negli altri casi; deve essere identico al tipo ritornato da metodi sovrascritti ParameterList- deve essere coerente con quella dei metodi sovrascritti MethodBody- le variabili locali devono essere inizializzate prima di essere usate
ACCESSI E CONDIVISIONE Quattro tipi di diritti d'accesso –publicvoid AnyOneCanAccess() {... } –protectedvoid OnlySubclassesCanAccess() {... } –privatevoid NoOneElseCanAccess() {... } – void OnlyFromWithinMyPackage() {... } L'ultimo tipo si chiama "friendly" Attributi statici: condivisi da tutte le istanze; metodi statici: operano solo su dati statici class NewDocument extends Document { static int Version = 10; int Chapters; static void NewVersion () { Version++; } /* OK */ static void AddChapter () { Chapters++; } } /* KO */
TIPI DI CLASSE Quattro tipi di classi –abstract: tra i vari metodi, deve contenerne almeno uno abstract, ma non può contenere metodi private o static; deve dare origine a sottoclassi; non può essere istanziata –final: termina una catena di classi-sottoclassi, non può dare origine a ulteriori sottoclassi –public: può essere usata senza formalità nel package che ne contiene la dichiarazione; disponibile anche in altri package, purché vi venga "importata" –synchronized: tutti i metodi interni sono synchronyzed Classi astratte public abstract class Graphics { /* generica */ public abstract void DrawLine(int x1,y1,x2,y2); } public class VGA extends Graphics { /*specifica */ public void DrawLine(int x1,y1,x2,y2) { }}
INTERFACCE Alternativa alle classi astratte: non richiede la derivazione esplicita di sottoclassi public interface AudioClip { void play ();/* avvia i'esecuzione */ void loop ();/* esegui ciclicamente una audio clip */ void stop (); }/* interrompi l'esecuzione */ class MyAudio implements AudioClip { void play () { } void loop () { } void stop () { } } class YourAudio implements AudioClip { } I metodi possono essere public o abstract, gli attributi public, static o final
OPERATORI E PRECEDENZA Operatori (ordine di precedenza decrescente):. [] () ! ~ instanceof * / % + - /* anche: String name = "Luigi" + "Stringa" */ > >>> = == != /* ritornano un booleano */ & ^ | && || ?... :... = += -= *= /= &= |= ^= %= >= >>>=
ISTRUZIONI CONDIZIONALI If –if (boolean) { statements; } else { statements; } Case –switch (expr) { case tag1: statements; break;..... case tagn: statements; break; default: statements; break; }
ISTRUZIONI ITERATIVE For –for (init expr1 ; test expr2 ; incr expr3) { statements; } While –while (boolean) { statements; } Do –do { statements; } while (boolean) Controllo di flusso –break [label] –continue [label] –return expr; –label: statement
FOR, SWITCH, CONTINUE E BREAK: PRECISAZIONI label: for (int i =0, int j=0 ; i<10 && j<20; i++, j++) { /* exp */ for (int z=0 ; z<100 ; z++) { /* scope è solo il for */ switch (expr) { case tag: statements; break; /* prosegue con penultima riga */..... default: statements; break label; } /* prosegue da label */ if (z==15) continue; /* prosegue il for z */ if (z==i+j) continue label; /* prosegue da label */ }