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!
Scarica

Lucidi - Dipartimento di Informatica