Introduzione a Java Java • Linguaggio Object-Oriented • La maggior parte dei dati manipolati dai programmi e’ contenuta in oggetti • La principale forma di astrazione sui dati Nota • Introdurremo le caratteristiche fondamentali, partendo dalla conoscenza di un semplice linguaggio imperativo • Obiettivo di questo corso non e’ presentare il linguaggio nella sua completezza (tanto meno le librerie) • Piuttosto presentare le metodologie di programmazione usando Java Riferimenti • CAP. 2: B.Liskov, “Program Development in Java” • Cay S. Horstman, “Concetti di Informatica e Fondamenti di Java” 2 Programmi Java I programmi in Java consistono di classi (o interfacce, in seguito) Le classi sono utilizzate per due funzionalita’ differenti: • collezioni di procedure stand-alone (metodi statici) • nuovi tipi di dato, tramite oggetti (in cui variabili e metodi d’istanza definiscono lo stato interno e le operazioni) Cominciamo dal frammento “imperativo” • collezioni di procedure stand-alone (metodi statici) Espressioni: tipi primitivi • vari primitivi numerici standard: int (interi) , double (numeri in virgola mobile) • boolean: due valori possibili, true e false • char: caratteri Principali Operazioni Aritmetiche • • • • • * moltiplicazione, + somma, - sottrazione, / divisione etc… Ex: (13 * 14) + 1 Operatori booleani • • • • ! not && and ^ xor || or Ordine di precedenza: (true ^ true && false || !true) • la formula viene letta come ( (true ^ (true && false )) || (!true) ) Operatori di confronto per valori numerici • • • • >, >= maggiore, maggiore o uguale <, <= minore, minore o uguale == uguale != diverso Gli operatori di uguaglianza e diseguaglianza (== e !=) possono essere applicati a qualunque tipo di dati Java, in seguito discuteremo il loro significato Variabili e Assegnamento • ogni variabile utilizzata in un programma deve essere dichiarata prima del suo uso • nella dichiarazione bisogna indicare l'insieme dei valori che potrà memorizzare, ovvero il tipo • l’assegnamento ha la semantica ovvia Esempio int x = 10; int y = 20; boolean b = (x < y); x:=y; • dichiariamo le variabili x ed y a cui e’ associato il valore 10 e 20 rispettivamente • dichiariamo b di tipo boolean, vale true • x assume il valore associato ad y, 20 Controllo dei tipi • Il compilatore controlla staticamente che il programma sia ben tipato • Ogni uso di un identificatore deve essere conforme al tipo con cui e’ dichiarato int x = 10; int y = 20; boolean b = x+y; *errore di tipo* Comandi Principali • blocchi di istruzioni: parentesi graffe { ... } • istruzioni condizionali (decisionali, di scelta): ifelse, switch • istruzioni iterative (cicli): while, for o do-while • istruzioni di salto: break, continue, return o throw • chiamate di procedure: ??? parte diversa Classi come contenitori di procedure • In Java le procedure sono definite da metodi statici • I metodi statici vanno dichiarati all’interno di una classe contenitore • Si usa l’attributo static public class Num{ public static int gcd(int n,int d){ \\REQUIRES : n e d >0 \\EFFECTS: calcola il massimo comun divisore while (n !=d){ if (n >d) n=n-d; else d=d-n;} return n; } } Osservazioni I • La lista dei parametri formali deve contenere i tipi relativi (puo’ anche essere vuota) • Analogamente deve essere indicato il tipo del risultato che viene restituito (return) • Se un metodo termina senza restituire alcun valore allora il tipo e’ void • Le informazioni sul tipo dei parametri e sul risultato servono per realizzare typechecking Osservazioni II • La classe Num contiene la dichiarazione di un solo metodo statico • In generale puo’ contenere una lista di dichiarazioni Chiamata di Metodo • I metodi statici devono essere chiamati indicando il nome della classe ed il loro nome • Al solito fornendo i valori per i parametri (parametri attuali) • I controlli statici garantiscono che i tipi siano compatibili public class ChiamataMetodi { public static void main(String[] args){ int x= 6; int y=3; int result= Num.gcd(x,y);} •Il metodo main e’ il metodo principale, dichiarato in una classe apposita ChiamataMetodi •Per chiamare il metodo statico deve essere indicata la classe in cui il metodo e’ dichiarato public class ChiamataMetodi { public static void main(String[] args){ int x= 6; int result= Num.gcd(x,true);} •Errore di tipo rilevato dal compilatore •Analogamente se il tipo di ritorno non e’ consistente public class Num{ public static int gcd(int n,int d){ \\REQUIRES : n e d >0 \\EFFECTS: calcola il massimo comun divisore ………… } public static void prova(int x,int y){ gcd(x+1,y+1);} } •Abbiamo dichiarato un altro metodo all’interno della classe •All’interno della classe il riferimento alla classe e’ implicito (si puo’ omettere) Chiamata di Metodo: semantica intesa • Durante l'esecuzione di un programma Java, in ogni istante è in esecuzione un metodo (e uno solo), il metodo corrente • Quando si incontra l'invocazione di un altro metodo, l'esecuzione del metodo corrente viene sospesa fino al completamento del metodo invocato (che termina eventualmente restituendo un valore) Chiamata di Metodo: semantica intesa • Per modellare le chiamate di metodi annidati bisogna utilizzare uno stack di record di attivazione • Politica LIFO, l’elemento al top dello stack corrisponde al metodo corrente, gli altri sono sospesi nell’ordine • Al momento di una chimata di metodo viene inserito nello stack il record di attivazione (push) • Al momento della terminazione del metodo corrente il record di attivazione relativo viene eliminato (pop), il metodo chiamante riprende l’esecuzione Record di Attivazione • Contiene le informazioni per eseguire il corpo del metodo. Tra l’altro: • i parametri formali con il valore del corrispondente parametro attuale (call by value); • le variabili locali del metodo invocato (dichiarate all’interno del metodo); • l 'indirizzo di ritorno (IR), cioè il punto del metodo chiamante cui bisogna cedere il controllo (e restituire il risultato) alla fine dell'esecuzione del metodo invocato. public class ChiamataMetodi { public static void main(String[] args){ int num = -5; num = doubleAbs(num); ... } public static int doubleAbs(int n){ int res = abs(n); return 2 * res; } public static int abs(int n){ if (n < 0) n = -n; return n; } } •Lo stack modella la chiamata di metodi annidati Oggetti • Le classi servono anche per definire nuovi tipi di dato ----collezioni di dati ----relative operazioni • Estendendo quelli primitivi, interi, etc… Tipi di Dato • La classe definisce un tipo • Lo stato degli oggetti e’ definito da variabili d’istanza • Le operazioni sono definite da metodi d’istanza • Gli oggetti istanze della classe hanno quel tipo Esempio semplice • Vogliamo realizzare una (un tipo) classe BankAccount i cui oggetti sono dei semplici conti bancari. • Vogliamo le seguenti operazioni depositare denaro prelevare denaro chiedere il saldo corrente Come si procede? • Va definita una classe che definisce il tipo • Variabili d’istanza per rappresentare lo stato degli oggetti • Metodi d’istanza per modellare le operazioni richieste • Costruttore (o costruttori) per creare un nuovo oggetto della classe ed inizializzare le variabili relative Una possibile implementazione public class BankAccount { public double balance; • balance variabile dichiarata nella classe, variabile d’istanza • gli oggetti di tipo BankAccount hanno le variabili d’istanza definite nella classe Costruttore public BankAccount(double initial){ balance=initial; } •costruttore ha il nome della classe, viene invocato al momento della creazione di un oggetto per inizializzare le variabili •ci possono essere piu’ costruttori con parametri differenti Metodi d’istanza: le operazioni public void deposit(double amount) { balance = balance + amount; } public void withdraw(double amount) { balance = balance - amount; } public double getBalance() { return balance; } } Creare oggetti • Gli oggetti istanze della classe si creano tramite new • Hanno il tipo nome della classe new BankAccount(1000) • viene chiamato il costruttore, che inizializza la variabile balance dell’oggetto al volore 1000 Semantica Intesa • Per memorizzare gli oggetti si usa una memoria a heap (heap) • Zona di memoria ad accesso libero, in cui si puo’ allocare e deallocare in momenti arbitari • Intuitivamente un insieme di locazioni, ognuna contiene la descrizione di un oggetto • La descrizione dell’oggetto contiene le variabili d’istanza dell’oggetto ed i loro valori Semantica intesa • La new new BankAccount(1000) • viene creata una nuova locazione nella heap, che contiene lo stato dell’oggetto • viene chiamato il costruttore, che inizializza la variabile balance dell’oggetto al volore 1000 Variabili di tipo oggetto BankAccount p1= new BankAccount(1000); • la variabile p1 e’ dichiarata con tipo BankAccount • Il suo valore (nell’opportuno frame di variabili) e’ il riferimento all’oggetto relativo nell’heap (semantica intesa) Variabili d’istanza • Le variabili d’istanza appartengono agli oggetti • Ogni oggetto ha la propria variabile d’istanza balance, il cui valore e’ memorizzato nella realtiva locazione nella heap BankAccount p1= new BankAccount(1000); BankAccount p2=new BankAccount(2000); • La variabile balance dell’oggetto riferito da p1 viene inizializzata al valore 1000 • La variabile balance dell’oggetto riferito da p2 viene inizializzata al valore 2000 Assegnamento: attenzione BankAccount p1= new BankAccount(1000); BankAccount p2=p1; if (p1==p2){…} • L’oggetto e’ condiviso • L’operatore == confronta il valore (cioe’ il riferimento), e’ true. Non molto utile in generale.. Come si manipolano gli oggetti? • Per leggere/ modificare lo stato interno rappresentato dalle variabili d’istanza • Tramite le relative operazioni (metodi d’istanza) • Accedendo direttamente allo stato interno, alle variabili d’istanza • In entrambi i casi deve essere specificato l’oggetto sul quale si vuole operare Esempio BankAccount p1= new BankAccount(1000); BankAccount p2=new BankAccount(2000); • Vogliamo trasferire 1000 dal conto di p1 in quello di p2 Prima soluzione BankAccount p1= new BankAccount(1000); BankAccount p2=new BankAccount(2000); p1.withdraw(1000); p2.deposit(1000); • I metodi d’istanza vanno invocati fornendo il riferimento all’oggetto su cui si vuole effettuare l’operazione Seconda soluzione BankAccount p1= new BankAccount(1000); BankAccount p2=new BankAccount(2000); p1.balance=0; p2.balance=balance+1000; • Per accedere alle variabili d’istanza bisogna fornire il riferimento all’oggetto su cui si vuole effettuare l’operazione • Analogamente ai metodi Attenzione • I metodi d’istanza devono essere chiamati con il riferimento all’oggetto • Questo riferimento puo’ anche essere implicito se siamo in un metodo d’istanza o costruttore della classe stessa Riferimento Implicito public class BankAccount{ public double balance; //il saldo corrente // costruttore public BankAccount(double initial){ balance=initial; } •Il riferimento e’ all’oggetto di tipo BankAccount su cui stiamo eseguendo il metodo d’istanza (analogogo this.balance) This public class BankAccount{ public double balance; //il saldo corrente // costruttore public BankAccount(double balance){ this.balance=balance; } •this.balance si riferisce alla variabile d’istanza su cui stiamo eseguendo il metodo •balance e’ il parametro formale Inizializzazione •esiste un valore particolare che può essere associato ad una variabile di tipo oggetto per indicare che quella variabile non riferisce alcun oggetto •questo valore, indipendentemente dalla classe, è chiamato null •sul valore null non è possibile effettuare nessuna operazione •per default le variabili non inializzate hanno valore null. Esempio BankAccount p1; p1.withdraw(1000); •p1 e’ null, la chiamata del metodo withdraw termina lanciando una eccezione (NullPointerException) Programmi Java Le classi sono utilizzate per due funzionalita’ differenti: • collezioni di procedure stand-alone (metodi statici) • nuovi tipi di dato, tramite oggetti (in cui variabili e metodi d’istanza definiscono lo stato interno e le operazioni) • Le due funzionalita’ possono anche coesistere all’interno della stessa classe Metodi e Variabili Statici: semantica intesa • Variabili e metodi statici appartengono alla classe in cui sono dichiarati, sono globali • A differenza delle variabili e dei metodi di istanza che appartengono agli oggetti (istanze della classe) • Sono quindi condivisi tra tutti gli oggetti della classe A cosa servono? • A memorizzare informazioni comuni tra tutti gli oggetti istanze della classe • A realizzare funzionalita’ indipendenti dagli oggetti Esempio: var. statiche • Supponiamo di volere modificare BankAccount in modo che ogni conto abbia un codice identificativo, ognuno diverso dagli altri • Si puo’ utilizzare una variabile statica (globale) che mantiene il valore del primo numero intero disponibile • Introduciamo anche un metodo statico per inizializzare il valore della variabile statica Parte dell’implementazione public class BankAccount { public double balance; //il saldo corrente public int miocodice; //il codice del conto public static int codice; //il primo codice disponibile • • • • La variabile codice e’ dichiarata nella classe ed e’ statica La variabile miocodice e’ dichiarata nella classe ed e’ d’istanza Ogni oggetto ha la propria variabile miocodice La variabile codice e’ globale e condivisa tra tutti gli oggetti BankAccount Costruttore public BankAccount(double initial){ balance=initial; miocodice=codice; codice++; } • La variabile statica e’ unica e condivisa tra tutti i conti • Viene incrementata ogni volta che viene creato un nuovo conto • In questo modo mantiene sempre aggiornato il primo valore libero Metodo statico public static void modifica-codice(int val){ \\REQUIRES: val >= codice codice=val; } • Permette di inizializzare la variabile statica e di aggiornarne il valore (con un valore piu’ grande) Esempio: main BankAccount.modifica-codice(10); BankAccount p1= new BankAccount(1000); BankAccount p2=new BankAccount(2000); • La variabile statica codice prende inizialmente il valore 10 • I due oggetti avranno codici diversi e la loro creazione modifichera’ il valore di codice • Notate che al solito il metodo statico deve essere chiamato tramite il riferimento alla classe In alternativa BankAccount.codice:=10; BankAccount p1= new BankAccount(1000); BankAccount p2=new BankAccount(2000); • Si accede direttamente alla variabile statica codice, assegnando il valore 10 • Per accedere alla variabile statica deve essere indicata la classe, analogamente come nel caso dei metodi Variabili e Metodi Statici • Al solito il riferimento puo’ essere implicito dal codice dei metodi e costruttori della classe stessa public static void modifica-codice(int val){ \\REQUIRES: val >= codice codice=val; } • Non e’ necessario specificare il nome della classe Regole di Visibilita’ dei Nomi • Vengono verificate staticamente dal compilatore • Verifica che ogni accesso ad un nome (di variabile o metodo) sia legale, ovvero che ci sia un legame corretto tra le dichiarazioni e l’uso dei nomi Cosa e’ visibile da un oggetto? • Le sue variabili d’istanza • Le variabili statiche dichiarate nella classe a cui appartiene (perche’ sono condivise tra tutti gli oggetti) Cosa e’ visibile da una classe? • Le variabili statiche • Non sono visibili variabili d’istanza (sono relative agli oggetti) Esempio: un po’ strano BankAccount p1=new Bankaccount(1000); p1.codice=1; (accesso legale) p1.balance=1; (accesso legale) • Tramite il riferimento all’oggetto: variabile statica e d’istanza Visibilita’ • Le classi, come le variabili ed i metodi dichiarati all’interno della classe possono essere specificati come: • public (si puo’ accedere da qualsiasi metodo della classe o non ) • private (visibile solo all’interno della classe) Esempio • Modifichiamo BankAccount public class BankAccount { private double balance; • La variabile d’istanza balance e’ private …..resto del codice resta inalterato Esempio: da un’altra classe • Supponiamo che balance sia private allora il seguente codice nel metodo main non sarebbe legale (viene rilevato dal compilatore) BankAccount p1= new BankAccount(1000); BankAccount p2=new BankAccount(2000); p1.balance=0; p2.balance=balance+1000; Packages • Gli specificatori di acccesso come public e private servono per bloccare l’accesso ad alcune informazioni contenute all’interno di una classe • Sono essenziali per programmare e per verificare la correttezza dei programmi, in modo modulare • Esiste anche la possibilita’ di raggruppare le classi in packages, in modo che le informazioni private siano condivise tra varie classi appartenenti allo stesso package (ma non dagli altri) Esempio • Come vedremo e’ sempre consigliabile sempre usare -- variabili d’istanza private --introdurre metodi d’istanza per leggere e modificare opportunamente lo stato degli oggetti • In questo modo si crea un incapsulamento degli oggetti che possono essere modificati solo dai metodi d’istanza • La loro implementazione resta invisibile al codice che lo usa Esempio 2 BankAccount p1= new BankAccount(1000); BankAccount p2=new BankAccount(2000); p1.balance=0; p2.balance=balance+1000; • Il codice accede allo stato interno degli oggetti • Se cambia l’implementazione deve essere modicato (non e’ un esempio di buona programmazione) • Meglio manipolare gli oggetti tramite le operazioni (metodi d’istanza), servono proprio a questo!