• Non ci sono risultati.

Il ciclo while

N/A
N/A
Protected

Academic year: 2021

Condividi "Il ciclo while"

Copied!
32
0
0

Testo completo

(1)

1

Iterazioni (Capitolo 6)

SETTIMANA 4

2

Problema

Riprendiamo un problema visto nella prima lezione,  per il quale abbiamo individuato un algoritmo  senza realizzarlo

 Problema: Avendo depositato ventimila euro in un  conto bancario che produce il 5% di interessi all’anno,  capitalizzati annualmente, quanti anni occorrono  affinché il saldo del conto arrivi al doppio della cifra  iniziale?

 Abbiamo bisogno di un programma che ripeta degli  enunciati (capitalizzazione degli interessi annuali,  incremento del conto degli anni), finchè non si realizza  la condizione desiderata

Algoritmo che risolve il problema

2. All’anno 0 il saldo è 20000

3.  Ripetere i passi 3 e 4 finché il saldo è minore del  doppio di 20000, poi passare al punto 5

4. Aggiungere 1 al valore dell’anno corrente 5. Il nuovo saldo è il valore del saldo precedente 

moltiplicato per 1.05 (cioè aggiungiamo il 5%) 6. Il risultato è il valore dell’anno corrente

L’enunciato while consente la realizzazione di  programmi che devono eseguire ripetutamente una  serie di azioni finché è verificata una condizione

Il ciclo while

Sintassi:

Scopo:

 eseguire un enunciato finché la  condizione è vera

Il corpo del ciclo while può  essere un enunciato qualsiasi,  quindi anche un blocco di  enunciati

L’enunciato while realizza un  ciclo

 per capire cosa significa questa  espressione è utile osservare la  rappresentazione del codice  mediante diagrammi di flusso

while (condizione) enunciato

(2)

5

Soluzione: classe Investment

public class Investment {

public Investment(double aBalance, double aRate) { balance = aBalance;

rate = aRate;

years = 0;

}

public void waitForBalance(double targetBalance) { while (balance < targetBalance)

{ years++;

double interest = balance * rate / 100;

balance = balance + interest;

} }

public double getBalance() { return balance; } public int getYears() { return years; } private double balance;

private double rate;

private int years;

}

6

Soluzione: classe InvestmentTester

public class InvestmentTester {

public static void main(String[] args) {

final double INITIAL_BALANCE = 10000;

final double RATE = 5;

Investment invest

= new Investment(INITIAL_BALANCE, RATE);

invest.waitForBalance(2 * INITIAL_BALANCE);

int years = invest.getYears();

System.out.println("The investment doubled after "

+ years + " years");

} }

Cicli infiniti

Esistono errori logici che impediscono la terminazione  di un ciclo, generando un ciclo infinito

 l’esecuzione del programma continua ininterrottamente

Bisogna arrestare il programma con un comando del  sistema operativo, o addirittura riavviare il computer

int year = 0;

È tutto chiaro? …

1. Quante volte viene eseguito il seguente ciclo?

while (false) // enunciato

2. Cosa succede nel programma 

InvestmentTester se RATE nel metodo main 

(3)

9

Cicli for

10

Ciclo for

Molti cicli hanno questa forma

Per comodità esiste il ciclo for equivalente

Non è necessario che l’incremento sia di una sola  unità, né che sia positivo, né che sia intero

i = inizio; while (i < fine) { enunciati i++;

}

for (i = inizio; i < fine; i++) { enunciati

}

for (double x = 2; x > 0; x = x - 0.3) { enunciati

}

L’enunciato for

Sintassi:

Scopo: eseguire un’inizializzazione, poi ripetere  l’esecuzione di un enunciato ed effettuare un  aggiornamento finché la condizione è vera

Nota: l’inizializzazione può contenere la definizione di  una variabile, che sarà visibile soltanto all’interno  del corpo del ciclo

for (inizializzazione; condizione; aggiornamento) enunciato

for (int y = 1; y <= 10; y++) { …

}// qui y non è più definita

Il metodo waitYears di Investment

Vogliamo sapere quale sarà il saldo finale del nostro  investimento dopo 20 anni, con tasso di interesse  costante al 5%

public class Investment {

...

public void waitYears(int n) {

for (int i = 1; i <= n; i++) {

double interest = balance * rate / 100;

balance = balance + interest;

}

years = years +n;

} ...

(4)

13

Esempio: invertire una stringa

public class ReverseTester

{ public static void main(String[] args) { Scanner in = new Scanner(System.in);

System.out.println("Inserire una stringa:");

String s = in.nextLine();

String r = "";

for (int i = 0; i < s.length(); i++) { char ch = s.charAt(i);

// aggiungi all’inizio r = ch + r;

}

System.out.println(s + " invertita è " + r);

}}

14

Visibilità delle variabili

Se il valore finale di una variabile di controllo del ciclo  deve essere visibile al di fuori del corpo del ciclo,  bisogna definirla prima del ciclo

Poiché una variabile definita nell’inizializzazione di un  ciclo non è più definita dopo il corpo del ciclo, è 

possibile (e comodo) usare di nuovo lo stesso nome  in altri cicli

double b = ...;

for (int i = 1; i < 10 && b < c; i++) { ...

modifica b

}// qui b è visibile, mentre i non lo è for (int i = 3; i > 0; i--)

System.out.println(i);

È tutto chiaro? …

1. Riscrivere sotto forma di ciclo while il ciclo for del  metodo waitYears

2. Quante volte viene eseguito il seguente ciclo for?

for (i = 0; i <= 10; i++) System.out.println(i * i);

Cicli annidati

(5)

17

Problema

Vogliamo stampare una tabella con i valori delle  potenze xy, per ogni valore di x tra 1 e 4 e per ogni  valore di y tra 1 e 5

Innanzitutto, bisogna stampare 4 righe

1 1 1 1 1 2 4 8 16 32 3 9 27 81 243 4 16 64 256 1024

for (int x = 1; x <= 4; x++)

{ // stampa la riga x-esima della tabella ...

}

18

Problema

Per stampare la riga x­esima, bisogna calcolare e  stampare i valori x1, x2, x3, x4 e x5, cosa che si può  fare facilmente con un ciclo for

Ogni iterazione del ciclo stampa un valore, seguito  da uno spazio bianco per separare valori successivi; 

al termine si va a capo

// stampa la riga x-esima della tabella for (int y = 1; y <= 5; y++)

{ int p = (int)Math.round(Math.pow(x, y));

System.out.print(p + " ");

}System.out.println(); // va a capo

Soluzione

Mettendo insieme le due “soluzioni parziali” si risolve  il problema, mediante due cicli “annidati” (nested) ,  cioè “uno dentro l’altro”

for (int x = 1; x <= 4; x++)

{ // stampa la riga x-esima della tabella for (int y = 1; y <= 5; y++)

{ // stampa il valore y-esimo // della riga x-esima

int p = (int)Math.round(Math.pow(x, y));

System.out.print(p + " ");

} System.out.println(); // va a capo

}

1 1 1 1 1 2 4 8 16 32 3 9 27 81 243 4 16 64 256 1024

Le colonne  non sono  allineate!

Soluzione

Incolonnare dati di lunghezza variabile è un problema  frequente

final int COLUMN_WIDTH = 5;

for (int x = 1; x <= 4; x++) { for (int y = 1; y <= 5; y++)

{ // converte in stringa il valore

String p = "" + (int)Math.round(Math.pow(x, y));

// aggiunge gli spazi necessari while (p.length() < COLUMN_WIDTH) p = " " + p;

System.out.print(p);

} System.out.println();

}

Ora ci sono  tre cicli  annidati!

1 1 1 1 1 2 4 8 16 32 3 9 27 81 243 4 16 64 256 1024

(6)

21

Il problema del “ciclo e  mezzo”

22

Il problema del “ciclo e mezzo”

Un ciclo del tipo

 “fai qualcosa, verifica una condizione, fai 

qualcos’altro e ripeti il ciclo se la condizione era vera”

non ha una struttura di controllo predefinita in Java e  deve essere realizzata con un “trucco”, come quello  di usare una variabile booleana, detta variabile di  controllo del ciclo

Una struttura di questo tipo si chiama anche “ciclo e  mezzo” o ciclo ridondante (perché c’è qualcosa di 

“aggiunto”, di innaturale…)

Il problema del “ciclo e mezzo”

Situazione tipica: l’utente deve inserire un insieme di  valori, la cui dimensione non è predefinita

Si realizza un ciclo while, dal quale si esce soltanto  quando si verifica la condizione all’interno del ciclo

boolean done = false;

while (!done)

Ciclo e mezzo: il programma QEater

import java.util.Scanner;

public class QEater

{ public static void main(String[] args) {

Scanner in = new Scanner(System.in);

boolean done = false;

while(!done) {

System.out.println("Voglio una Q");

(7)

25

Break, continue, e codice spaghetti

Soluzione alternativa alla struttura “ciclo e mezzo”: 

usare un ciclo infinito while(true) e l’enunciato break

 L’enunciato break provoca la terminazione del ciclo

 Esiste anche l'enunciato continue, che fa proseguire  l'esecuzione dalla fine dell'iterazione attuale del ciclo

L'uso di break e continue è non necessario e   sconsigliabile

 perchè contribuisce a creare codice spaghetti 

 ovvero rappresentato da diagrammi di flusso pieni di  linee, difficili da leggere e comprendere

while (true)

{ String input = in.next();

if (input.equalsIgnoreCase(“Q”)) break;

else

... // elabora line }

26

Materiale di complemento (Capitolo 7)

Variabili non inizializzate

Variabili non inizializzate

È buona regola fornire sempre un valore di  inizializzazione nella definizione di variabili

Cosa succede altrimenti?

 la definizione di una variabile “crea” la variabile, cioè le  riserva uno spazio nella memoria primaria (la quantità  di spazio dipende dal tipo della variabile)

 tale spazio di memoria non è “vuoto”, una condizione  che non si può verificare in un circuito elettronico, ma  contiene un valore “casuale” (in realtà contiene l’ultimo  valore attribuito a quello spazio da un precedente  programma… valore che a noi non è noto)

int lit;

(8)

29

Variabili non inizializzate

Se si usasse il valore di una variabile prima di averle  assegnato un qualsiasi valore, il programma si 

troverebbe ad elaborare quel valore che 

“casualmente” si trova nello spazio di memoria  riservato alla variabile

public class Coins6 // NON FUNZIONA!

{ public static void main(String[] args) { int lit;

double euro = 2.35;

double totalEuro = euro + lit / 1936.27;

System.out.print("Valore totale in euro ");

System.out.println(totalEuro);

} }

ERRORE

30

Variabili non inizializzate

Questo problema provoca insidiosi errori di  esecuzione in molti linguaggi di programmazione

 il compilatore Java, invece, segnala come errore  l’utilizzo di variabili a cui non sia mai stato assegnato  un valore (mentre non è un errore la sola definizione...)

 questi errori non sono sintattici, bensì logici, ma 

vengono comunque individuati dal compilatore, perché  si tratta di errori semantici (cioè di comportamento del  programma) individuabili in modo automatico

Coins6.java:5: variable lit might not have been initialized

Cicli do

Ciclo do

Capita spesso di dover eseguire il corpo di un ciclo  almeno una volta, per poi ripeterne l’esecuzione se è  verificata una particolare condizione 

Esempio tipico: leggere un valore in ingresso, ed  eventualmente rileggerlo finché non viene introdotto  un valore “valido”

(9)

33

Ciclo do

Si può usare un ciclo while “innaturale”

ma per comodità esiste il ciclo do

double rate;

do{ System.out.println("Inserire il tasso:");

rate = console.readDouble();

} while (rate <= 0);

// si usa un’inizializzazione "ingiustificata"

double rate = 0;

while (rate <= 0)

{ System.out.println("Inserire il tasso:");

rate = console.readDouble();

}

34

Cicli: errori e consigli

Errori per scarto di uno

Come evitare questi errori per scarto di uno?

 Provare con alcuni semplici casi di prova

 Investimento iniziale 100 euro, tasso 50%

Allora years deve essere inizializzato a 0

 Tasso di interesse 100%

Allora la condizione deve essere < e non <=

int years = 0; // o forse int years =1; ???

while (balance < 2 * initialBalance)

{ // o forse balance <= 2 * initialBalance ???

years++;

double interest = balance * rate / 100;

balance = balance + interest;

}

System.out.println("Investimento raddoppiato dopo"

+ years + " anni.");

Cicli for di “cattivo gusto”

È un ciclo for sintatticamente corretto

 ma contiene condizioni estranee, che producono  risultati difficilmente comprensibili

L'intestazione è corretta

 ma il contatore viene modificato nel corpo del ciclo

Sono cicli for di cattivo gusto!

for (rate = 5; years-- > 0; System.out.println(balance)) { enunciati

}

for (int i = 1; i <= years; i++) { if (balance >= targetBalance)

i = i + 1;

else...

}

(10)

37

Punti e virgola mancanti o di troppo

C'è un “;” di troppo!

 il corpo del ciclo è vuoto

L'istruzione di aggiornamento di sum viene eseguita  una sola volta, dopo l'uscita dal ciclo

In questo caso tutte le operazioni necessarie sono  nell'intestazione del ciclo

 Il corpo del ciclo deve essere vuoto

 Ma abbiamo dimenticato il “;” dopo l'intestazione

 Quindi l'istruzione di stampa viene considerata parte  del corpo

sum = 0;

for (int i = 1; i <= 10; i++);

sum = sum + i;

System.out.println(sum);

for (years = 1; (balance = balance + balance * rate / 100)

< targetBalance; years++) // ; System.out.println(years);

38

Definire bene i limiti

E se n fosse negativo?

La condizione i!=n sarebbe sempre vera

Molto meglio i<=n

Imparare a contare le iterazioni 

Il primo ciclo (asimmetrico) viene eseguito (b­a)/c volte

Il secondo (simmetrico) viene eseguito (b­a)/c +1 volte

Due intestazioni 

 Sono equivalenti

 Nella prima i limiti sono simmetrici

 Nella seconda sono asimmetrici ma il codice è più  leggibile 

for (int i = 1; i != n; i++)

for (i = a; i < b; i+=c) // oppure

for (i = a; i <= b; i+=c)

for (i = 0; i <= s.length() - 1; i++) // è corretta, ma è meglio questa:

for (i = 0; i < s.length(); i++)

Progettazione di classi (capitolo 8)

(capitolo 3 – sez. 3.7 e 3.8)

(11)

41

Parametri espliciti/impliciti Il parametro this

42

I parametri dei metodi

Cosa succede quando invochiamo il metodo?

L’esecuzione del metodo dipende da due valori

 il riferimento all’oggetto account

 il valore 500

Quando viene eseguito il metodo, il suo parametro  esplicito amount assume il valore 500

 esplicito perché compare nella firma del metodo

public void deposit(double amount) { balance = balance + amount;

}

account.deposit(500);

I parametri dei metodi

Abbiamo già detto che un metodo può avere  parametri espliciti e/o impliciti

amount è il parametro esplicito del metodo

balance si riferisce alla variabile di esemplare balance  della classe BankAccount, ma sappiamo che di tale  variabile esiste una copia per ogni oggetto

Alla variabile balance di quale oggetto si riferisce il  metodo?

 si riferisce alla variabile che appartiene all’oggetto con  cui viene invocato il metodo, ma come fa?

public void deposit(double amount) { balance = balance + amount;

}

Il parametro implicito dei metodi

All’interno di ogni metodo il riferimento all’oggetto con  cui viene eseguito il metodo si chiama parametro  implicito e si indica con la parola chiave this

in questo caso, this assume il valore di account  all’interno del metodo deposit

Ogni metodo ha sempre uno ed un solo parametro  implicito, dello stesso tipo della classe a cui 

appartiene il metodo

 eccezione: i metodi statici non hanno parametro  implicito

Il parametro implicito non deve essere dichiarato e  si chiama sempre this

momsavings.deposit(500);

(12)

45

Uso del parametro implicito

La vera sintassi del metodo dovrebbe essere

Ma Java consente una comoda scorciatoia:

 se un metodo si riferisce ad un campo di esemplare, il  compilatore costruisce automaticamente un riferimento  al campo di esemplare dell’oggetto rappresentato dal  parametro implicito this

public void deposit(double amount) { this.balance = this.balance + amount;

}

// this è di tipo BankAccount

46

È tutto chiaro? …

1. Quanti e quali sono i parametri impliciti ed 

espliciti del metodo withdraw di BankAccount? 

Quali sono i loro nomi e tipi?

2. Qual è il significato di this.amount nel metodo 

deposit? Oppure, se l’espressione è priva di 

significato, per quale motivo lo è?

Membri di classe “statici”

Classi “di utilità” e metodi statici

Esistono classi che non servono a creare oggetti ma  contengono metodi statici e costanti.

 Queste si chiamano solitamente classi di utilità

La classe Math è un esempio di questo tipo di classi

La classe Numeric scritta da noi è un altro esempio

Esempio: una classe Financial

public class Financial

(13)

49

Variabili statiche

Vogliamo modificare BankAccount in modo che

 il suo stato contenga anche un numero di conto

 il numero di conto sia assegnato dal costruttore

ogni conto deve avere un numero diverso

i numeri assegnati devono essere progressivi, iniziando  da 1

public class BankAccount { ...

private int accountNumber;

}

50

Soluzione

Prima idea (che non funziona…)

 usiamo una variabile per memorizzare l’ultimo numero  di conto assegnato

public class BankAccount { ...

private int accountNumber;

private int lastAssignedNumber;

...

public BankAccount() { lastAssignedNumber++;

accountNumber = lastAssignedNumber;

}}

Soluzione

Questo costruttore non funziona perché la variabile  lastAssignedNumber è una variabile di esemplare

 ne esiste una copia per ogni oggetto

 il risultato è che tutti i conti creati hanno il numero di  conto uguale a 1

public class BankAccount { ...

private int accountNumber;

private int lastAssignedNumber;

...

public BankAccount() { lastAssignedNumber++;

accountNumber = lastAssignedNumber;

}}

Variabili statiche

Ci serve una variabile condivisa da tutti gli  oggetti della classe

 una variabile con questa semantica si ottiene con  la dichiarazione static

Una variabile static (variabile di classe) è  condivisa da tutti gli oggetti della classe

Ne esiste un’unica copia indipendentemente da  quanti oggetti siano stati creati (anche zero)

public class BankAccount { ...

private static int lastAssignedNumber;

}

(14)

53

Variabili statiche

Ora il costruttore funziona

Ogni metodo (o costruttore) di una classe può  accedere alle variabili statiche della classe e  modificarle

public class BankAccount { ...

private int accountNumber;

private static int lastAssignedNumber = 0;

...

public BankAccount() { lastAssignedNumber++;

accountNumber = lastAssignedNumber;

}}

54

Variabili statiche

Le variabili statiche non possono (da un punto di  vista logico) essere inizializzate nei costruttori

 Il loro valore verrebbe inizializzato di nuovo ogni  volta che si costruisce un oggetto, perdendo il  vantaggio di avere una variabile condivisa!

Bisogna inizializzarle quando si dichiarano

Questa sintassi si può usare anche per le variabili  di esemplare, anziché usare un costruttore

non è una buona pratica di programmazione

private static int lastAssignedNumber = 0;

Variabili statiche

Nella programmazione ad oggetti, l’utilizzo di  variabili statiche deve essere limitato

 metodi che leggono variabili statiche ed agiscono di  conseguenza, hanno un comportamento che non  dipende soltanto dai loro parametri (implicito ed  espliciti)

Variabili statiche

È invece pratica comune (senza 

controindicazioni) usare costanti statiche, come  nella classe Math

public class Math { ...

public static final double PI =

(15)

57

È tutto chiaro? …

1. Citare due variabili statiche della classe System

58

Categorie di variabili (sez. 3.7) Ciclo di vita di una variabile

Ciclo di vita di una variabile

Sappiamo che in Java esistono quattro diversi tipi di  variabili

 variabili locali (all’interno di un metodo)

 variabili parametro (dette parametri formali)

 variabili di esemplare o di istanza

 variabili statiche o di classe

Hanno in comune il fatto di contenere valori  appartenenti ad un tipo ben preciso.

Differiscono per quanto riguarda il loro ciclo di vita

 cioè l’intervallo di tempo in cui, dopo essere state  create, continuano ad occupare lo spazio in memoria  riservato loro

Ciclo di vita di una variabile

Una variabile locale

viene creata quando viene eseguito l’enunciato in cui è  definita

viene eliminata quando l’esecuzione del programma  esce dal blocco di enunciati in cui la variabile è definita

se non è definita all’interno di un blocco di enunciati, viene  eliminata quando l’esecuzione del programma esce dal  metodo in cui la variabile viene definita

Una variabile parametro (formale)

viene creata quando viene invocato il metodo

viene eliminata quando l’esecuzione del metodo  termina

(16)

61

Ciclo di vita di una variabile

Una variabile statica

viene creata quando la macchina virtuale Java carica  la classe per la prima volta 

viene eliminata quando la classe viene scaricata  dalla macchina virtuale Java

ai fini pratici, possiamo dire che esiste sempre...

Una variabile di esemplare

viene creata quando viene creato l’oggetto a cui  appartiene

viene eliminata quando l’oggetto viene eliminato

62

Eliminazione di oggetti

Un oggetto è a tutti gli effetti “inutilizzabile” quando  non esiste più nessun riferimento ad esso

Se un programma abbandona molti oggetti in  memoria, puo’ esaurire la memoria a disposizione

La JVM effettua automaticamente la gestione della  memoria durante l’esecuzione di un programma

 Usa un meccanismo di garbage collection

Viene periodicamente riciclata, cioè resa di nuovo  libera, la memoria eventualmente occupata da oggetti  che non abbiano piu’ un riferimento nel programma

Allocazione della memoria in java

Al momento dell’esecuzione, a ciascun programma  java viene assegnata un’area di memoria

 Una parte della memoria serve per memorizzare il  codice; quest’area è statica, ovvero non modifica le sue  dimensioni durante l’esecuzione del programma

 La Java Stack è un’area dinamica (ovvero che cambia 

Modello della memoria in java

(17)

65

Ambiti di visibilità di variabili

66

Visibilità di variabili locali

Per evitare conflitti, dobbiamo conoscere l’ambito di  visibilità di ogni tipo di variabile

Ovvero la porzione del programma all’interno della  quale si può accedere ad essa

Esempio: due variabili locali con lo stesso nome

 Funziona perché gli ambiti di visibilità sono disgiunti

public class RectangleTester {

public static double area(Rectangle rect)

{ double r = rect.getWidth() * rect.getHeight();

return r; }

public static void main(String[] args)

{ Rectangle r = new Rectangle(5, 10, 20, 30);

double a = area(r);

System.out.println(r); } }

Visibilità di variabili locali

Anche qui gli ambiti di visibilità sono disgiunti:

Invece l’ambito di visibilità di una variabile non può  contenere la definizione di un’altra variabile locale con  lo stesso nome:

if (x >= 0)

{ double r = Math.sqrt(x);

. . . } // la visibilitò di r termina qui else

{ Rectangle r = new Rectangle(5, 10, 20, 30);

// OK, questa è un’altra variabile r . . . }

Rectangle r = new Rectangle(5, 10, 20, 30);

if (x >= 0)

{ double r = Math.sqrt(x);

// Errore: non si può dichiarare un’altra var. r qui . . . }

Visibilità di membri di classe

Membri private hanno visibilità di classe

 Qualsiasi metodo di una classe può accedere a variabili  e metodi della stessa classe

Membri public hanno visibilità al di fuori della classe

 A patto di renderne qualificato il nome, ovvero:

Specificare il nome della classe per membri static

Math.PI, Math.sqrt(x)

Specificare l’oggetto per membri non static

account.getBalance()

Non è necessario qualificare i membri appartenenti ad  una stessa classe

 Perché ci si riferisce automaticamente al parametro  implicito this

(18)

69

Visibilità di membri di classe

Esempio: qualifica sottintesa dal parametro implicito

public class BankAccount {

public void transfer(double amount, BankAccount other) { withdraw(amount); // cioè this.withdraw(amount);

other.deposit(amount);

}

public void withdraw(double amount)

{ if (balance > amount) // cioè this.balance balance = balance – OVERDRAFT_FEE;

//cioè BankAccount.OVERDRAFT_FEE else ...

} ...

private static double OVERDRAFT_FEE = 5;

...

}

70

Visibilità sovrapposte

Purtroppo gli ambiti di visibilità di una variabile locale  e di una variabile di esemplare possono sovrapporsi

Viene segnalato un errore in compilazione?

public class Coin {

...

public double getExchangeValue(double exchangeRate) {

double value; // Variabile locale ...

return value;

}

private String name;

private double value; // Campo di esemplare omonimo }

Visibilità sovrapposte

Non viene segnalato alcun errore in compilazione

 Java specifica che in casi come questo prevale il  nome della variabile locale

 La variabile di esemplare viene “messa in ombra” 

(shadowed)

Questa scelta è giustificata dal fatto che la variabile  di esemplare può sempre essere qualificata usando il 

Visibilità sovrapposte

Errore molto comune:

 utilizzare accidentalmente lo stesso nome per una  variabile locale e un campo di esemplare

public class Coin {

(19)

73

È tutto chiaro? …

1. Qual è l’ambito di visibilità delle variabili amount  e newBalance del metodo deposit di 

BankAccount?

2. Qual è l’ambito di visibilità del campo di  esemplare balance di BankAccount?

74

Chiamate per valore e  riferimento

75

Chiamate per valore e riferimento

Un nuovo metodo transfer per la classe BankAccount:

Proviamo ad usarlo:

Non funziona!

 All’inizio dell’esecuzione del metodo la variabile 

parametro otherBalance ha il valore di savingsBalance

 La modifica del valore di otherBalance non ha effetto su  savingsBalance perché sono variabili diverse

Alla fine dell’esecuzione otherBalance non esiste più

void transfer(double amount, double otherBalance) {

balance = balance - amount;

otherBalance = otherBalance + amount;

}

double savingsBalance = 1000;

harrysChecking.transfer(500, savingsBalance);

System.out.println(savingsBalance);

76

Chiamate per valore e riferimento

1000

(20)

77

Chiamate per valore e riferimento

78

Chiamate per valore e riferimento

A volte si dice impropriamente che in Java i numeri  sono passati per valore e gli oggetti per riferimento

In realtà in Java il passaggio è sempre “per valore”

 il valore del parametro effettivo è assegnato al  parametro formale, ma

 passando per valore una variabile oggetto, si passa  una copia del riferimento in essa contenuto

 l’effetto “pratico” di questo passaggio è la possibilità di  modificare lo stato dell’oggetto stesso, come  avviene con il passaggio “per riferimento”

Altri linguaggi (come C++) consentono il passaggio  dei parametri “per riferimento”, rendendo possibile  la modifica dei parametri effettivi

Materiale di complemento

(Capitoli 3/8) Errori tipici con i costruttori

(21)

81

In questo caso il compilatore non è in grado di compilare la  classe Program, perché non sa a quale oggetto applicare il  metodo deposit, che non è un metodo statico e quindi  richiede sempre un riferimento ad un oggetto da usare come  parametro implicito

public class Program

{ public static void main(String[] args) { BankAccount account = new BankAccount();

deposit(500); // ERRORE }

}

Invocare metodi senza oggetto

82

Invocare metodi senza oggetto

Ci sono, però, casi in cui è ammesso (ed è pratica molto  comune!) invocare metodi non statici senza specificare un  riferimento ad oggetto

Questo accade quando si invoca un metodo non statico di  una classe da un altro metodo non statico della stessa  classe

viene usato il parametro implicito this

public void withdraw(double amount) { balance = getBalance() - amount;

}

this.getBalance()

Tentare di ricostruire un oggetto

A volte viene la tentazione di invocare un  costruttore su un oggetto già costruito con  l’obiettivo di riportarlo alle condizioni iniziali

Il messaggio d’errore è un po’ strano… il compilatore  cerca un metodo BankAccount, non un 

costruttore, e naturalmente non lo trova!

BankAccount account = new BankAccount();

account.deposit(500);

account.BankAccount(); // NON FUNZIONA!

cannot resolve symbol

symbol     : method BankAccount ( ) location    : class BankAccount

Tentare di ricostruire un oggetto

Un costruttore crea un nuovo oggetto con  prefissate condizioni iniziali

un costruttore può essere invocato soltanto con  l’operatore new

La soluzione è semplice

assegnare un nuovo oggetto alla variabile oggetto  che contiene l’oggetto che si vuole “ricostruire”

BankAccount account = new BankAccount();

account.deposit(500);

account = new BankAccount();

(22)

85

Progettare classi: accoppiamento,  coesione, effetti collaterali

(sezioni 8.2, 8.4)

86

Coesione e accoppiamento

Una classe dovrebbe rappresentare un singolo  concetto

 Metodi pubblici e costanti elencati 

nell’interfaccia pubblica devono avere una  buona coesione

 Ovvero devono essere strettamente correlate al  singolo concetto rappresentato dalla classe

Quando una classe usa un'altra classe  all'interno del proprio codice si parla di  accoppiamento tra le due classi

 Visualizziamo accoppiamento con diagrammi  UML (Unified Modeling Language)

 La linea tratteggiata con freccia aperta punta  alla classe nei cui confronti esiste dipendenza

Accoppiamento

Problema: se una classe viene modificata, tutte quelle  che da essa dipendono possono richiedere modifiche

Problema: per usare una classe in un programma,  vanno usate anche tutte quelle da cui essa dipende

 Bisogna eliminare tutti gli accoppiamenti non necessari

Effetti collaterali

Effetto collaterale di un metodo:

 Qualsiasi tipo di comportamento osservabile al di fuori  del metodo stesso

Qualsiasi metodo modificatore ha effetti collaterali

Modificano il proprio parametro esplicito

Esistono altri tipi di effetto collaterale:

(23)

89

Effetti collaterali

Un altro tipo di effetto collaterale: visualizzazione di  dati in uscita

Esempio: un metodo printPurchase per CashRegister

Problema: abbiamo ipotizzato che qualsiasi utente  parli la lingua inglese

Problema: abbiamo realizzato accoppiamento con le  classi System e PrintStream

 Evitiamo di inserire attività di input/output all’interno di  metodi di una classe che ha altre finalità

public void printPurchase() // Non consigliato {

System.out.println("The purchase is now $" + purchase);

}

90

È tutto chiaro? …

1. Se a è un riferimento ad un oggetto 

BankAccount, allora a.deposit(100) modifica 

l’oggetto. È un effetto collaterale?

2. Immaginiamo di avere scritto il seguente metodo  in una classe Dataset: produce effetti collaterali?

void read(Scanner in)

{ while (in.hasNextDouble()) add(in.nextDouble);

}

Pacchetti

I pacchetti di classi (package)

Tutte le classi della libreria standard sono raccolte in  pacchetti (package) e sono organizzate per 

argomento e/o per finalità

Esempio: la classe Rectangle appartiene al pacchetto  java.awt (Abstract Window Toolkit)

Per usare una classe di una libreria, bisogna  importarla nel programma, usando l’enunciato

import nomePacchetto.NomeClasse

Le classi System e String appartengono al  pacchetto java.lang

 il pacchetto java.lang viene importato  automaticamente

(24)

93

Importare classi da un pacchetto

Sintassi:

 Scopo: importare una classe da un pacchetto, per  poterla utilizzare in un programma

Sintassi:

 Scopo: importare tutte le classi di un pacchetto, per  poterle utilizzare in un programma

Nota: le classi del pacchetto java.lang non hanno  bisogno di essere importate

Attenzione: non si possono importare più pacchetti  con un solo enunciato

import nomePacchetto.NomeClasse;

import nomePacchetto.*;

import java.*.*; // ERRORE

94

Stili per l’importazione di classi

Un enunciato import per ogni classe importata

Un enunciato import per tutte le classi di un pacchetto

 non è un errore importare classi che non si usano!

Se si usano più enunciati di questo tipo

 non è chiaro il pacchetto di appartenenza delle classi

 Ci possono essere classi omonime in pacchetti diversi

 La classe Timer, appartiene ad entrambi i pacchetti

Se si definisce una variabile di tipo Timer il compilatore  segnala un errore di “riferimento ambiguo”

import java.math.BigInteger;

import java.math.BigDecimal;

import java.math.*;

import java.util.*;

import javax.swing.*;

Stili per l’importazione di classi

È possibile non usare per nulla gli enunciati import,  ed indicare sempre il nome completo delle classi  utilizzate nel codice

java.math.BigInteger a =

new java.math.BigInteger("123456789");

(25)

97

La gestione delle eccezioni (capitolo 11)

98

Argomenti inattesi (precondizoni)

Spesso un metodo richiede che i suoi argomenti

 siano di un tipo ben definito

questo viene garantito dal compilatore

abbiano un valore che rispetti certi vincoli, ad  esempio sia un numero positivo

in questo caso il compilatore non aiuta...

Come deve reagire il metodo se riceve un  parametro che non rispetta i requisiti richiesti  (chiamati precondizioni)?

Argomenti inattesi (precondizioni)

Ci sono quattro modi per reagire ad argomenti inattesi

non fare niente

il metodo termina la propria esecuzione senza segnalare  errori. In questo caso la documentazione del metodo deve  indicare chiaramente le precondizioni. La responsabilità di  ottemperare alle precondizioni e’ del metodo chiamante.

Eseguire solo se le precondizioni sono soddisfatte:

questo però si può fare solo per metodi con valore di ritorno  void, altrimenti che cosa restituisce il metodo?

Se restituisce un valore casuale senza segnalare un errore,  chi ha invocato il metodo probabilmente andrà incontro a un  errore logico

if (precondizioni) {... corpo del metodo ...}

Argomenti inattesi (precondizioni)

Terminare il programma con System.exit(1)

questo è possibile soltanto in programmi non  professionali

La terminazione di un programma attraverso il metodo  statico System.exit() non fornisce un meccanismo  standard per informare dell’avvenuto

Usare una asserzione

Il meccanismo delle “asserzioni” si usa solo per i  programmi in fase di sviluppo e collaudo. 

Noi non lo esaminiamo

Lanciare un’eccezione

System.exit(0) invece termina l’esecuzione  segnalando che tutto è andato bene

(26)

101

Lanciare una eccezione

Il meccanismo generale di segnalazione di errori in  Java consiste nel “lanciare” (throw) un’eccezione

si parla anche di sollevare o generare un’eccezione

Lanciare un’eccezione in risposta ad un parametro  che non rispetta una precondizione è la soluzione  più corretta in ambito professionale

 la libreria standard mette a disposizione tale  eccezione: IllegalArgumentException

public void deposit(double amount) { if (amount <= 0)

throw new IllegalArgumentException();

balance = balance + amount;

} 102

Enunciato throw

Sintassi:

Scopo: lanciare un’eccezione

Nota:

 di solito l’oggettoEccezione viene creato con new  ClasseEccezione( )

 Non è necessario memorizzare il riferimento in  una variabile oggetto

throw oggettoEccezione;

Le eccezioni in Java

Quando un metodo lancia un’eccezione

 l’esecuzione del metodo viene interrotta

 l’eccezione viene “propagata” al metodo chiamante, la  cui esecuzione viene a sua volta interrotta

 l’eccezione viene via via propagata fino al metodo main, 

Gestire le eccezioni

Esaminiamo un problema tipico:

 convertire stringhe in numeri

Supponiamo che la stringa sia stata introdotta  dall’utente

 se la stringa non contiene un numero valido, viene  generata un’eccezione NumberFormatException

(27)

105

L’enunciato che contiene l’invocazione del metodo  che può generare l’eccezione deve essere 

racchiuso tra due parentesi graffe, precedute dalla  parola chiave try

Bisogna poi sapere di che tipo è l’eccezione  generata dal metodo

 nel nostro caso NumberFormatException

Gestire le eccezioni

try { ...

n = Integer.parseInt(line);

...

}

106

Il blocco try è seguito da una clausola catch, 

definita in modo simile ad un metodo che riceve un  solo parametro, del tipo dell’eccezione che si  vuole gestire

Nel blocco catch si trova il codice che deve essere  eseguito nel caso in cui si verifichi l’eccezione

l’esecuzione del blocco try viene interrotta nel punto in  cui si verifica l’eccezione e non viene più ripresa

Gestire le eccezioni

catch (NumberFormatException e) { System.out.println("messaggio");

}

Esempio: gestire NumberFormatException

import java.util.Scanner;

public class IntEater

{ public static void main(String[] args) {

System.out.println("Passami un numero int!");

Scanner in = new Scanner(System.in);

int n = 0;

boolean done = false;

while (!done)

{ try{ String line = in.nextLine();

n = Integer.parseInt(line);

// l'assegnazione seguente e` eseguita // solo se NON viene lanciata l'eccezione

done = true; }

catch(NumberFormatException e)

{ System.out.println("No, voglio un int");

// all'uscita del blocco catch done e` false }

}

System.out.println("Grazie, " + n + " e` un numero int!");

} }

Enunciati try/catch

Sintassi:

Scopo: eseguire enunciati che possono generare  una eccezione

se si verifica l’eccezione di tipo ClasseEccezione,  eseguire gli enunciati contenuti nella clausola catch

altrimenti, ignorare la clausola catch

try

{ enunciatiCheForseGeneranoUnaEccezione }

catch (ClasseEccezione oggettoEccezione) { enunciatiEseguitiInCasoDiEccezione }

(28)

109

Enunciati try/catch

Se gli enunciati nel blocco try possono generare più  eccezioni di diverso tipo, è necessario prevedere un  blocco catch per ogni tipo di eccezione

try

{ enunciatiCheForseGeneranoPiu’Eccezioni }

catch (ClasseEccezione1 oggettoEccezione1) { enunciatiEseguitiInCasoDiEccezione1 }

catch (ClasseEccezione2 oggettoEccezione2) { enunciatiEseguitiInCasoDiEccezione2 }

110

Perché usare eccezioni?

Idea generale: per gestire un errore di esecuzione ci  sono due compiti da svolgere 

Individuazione

Ripristino

Problema: il punto in cui viene individuato l’errore di  solito non coincide con il punto di ripristino

Esempio: il metodo parseInt fallisce (ovvero la stringa in  esame non può essere convertita in intero)

Il metodo parseInt non sa come gestire questo  fallimento: dipende dal contesto!

Soluzione: il meccanismo di lancio/cattura di eccezioni

 Consente di gestire un errore di esecuzione in un punto  diverso rispetto a dove questo si è generato

Obbliga a gestire l'errore, altrimenti l'esecuzione termina

Come gestire le eccezioni?

Lanciare presto, catturare tardi

Quando un metodo incontra un problema che non  è in grado di gestire al meglio

 Conviene lanciare un’eccezione piuttosto che mettere  in atto una soluzione imprecisa/incompleta

“Lanciare presto”

Tipi di 

eccezioni

(29)

113

Diversi tipi di eccezioni

In Java esistono diversi tipi di eccezioni (cioè diverse  classi di cui le eccezioni sono esemplari)

eccezioni di tipo Error

eccezioni di tipo Exception

un sottoinsieme sono di tipo RuntimeException

Eccezioni di tipo Error e di tipo RuntimeException  sono eccezioni non controllate

 La loro gestione è facoltativa, ovvero lasciata alla  scelta del programmatore

 se non vengono gestite e vengono lanciate, provocano  la terminazione del programma

Le altre sono eccezioni controllate

 la loro gestione è obbligatoria

114

Eccezioni controllate e non

Le eccezioni controllate

 Descrivono problemi che possono verificarsi prima o poi,  indipendentemente dalla bravura dell programmatore

Per questo motivo le eccezioni di tipo IOException sono  controllate

Se si invoca un metodo che può lanciare un’eccezione  controllata, è obbligatorio gestirla con try/catch

Altrimenti viene segnalato un errore in compilazione

Le eccezioni non controllate

 Descrivono problemi dovuti ad errori del programmatore  e che quindi non dovrebbero verificarsi (in teoria…)

Per questo motivo le eccezioni di tipo RuntimeException  sono non controllate

Non è obbligatorio catturarle tramite try/catch

Eccezioni controllate

import java.io.FileNotFoundException; // QUESTO CODICE import java.io.FileReader; // NON COMPILA!

import java.util.Scanner;

public class MyInputReader {

public static void main(String[] args) { String filename = “filename.txt”;

FileReader reader = new FileReader(filename);

Scanner in = new Scanner(reader);

} }

Gestire le eccezioni di Input/Output

MyInputReader.java:9:

unreported exception java.io.FileNotFoundException;

must be caught or declared to be thrown

Scanner può essere usata anche per leggere file (lo  vedremo prossimamente); in questo caso il parametro  esplicito e’ un oggetto di classe FileReader

Se non trova il file, il costruttore FileReader()  lancia  l’eccezione controllata java.io.FileNotFoundException

(30)

117

Perché le eccezioni di IO sono controllate?

Perchè sono associate a problemi che si possono  verificare indipendentemente dalla bravura del  programmatore

Quando si leggono dati da un flusso di input

 se l’input proviene da un file su disco, il disco può  essere difettoso

 se l’input proviene da una connessione di rete, la  connessione può interrompersi durante la lettura

 se l’input proviene dalla tastiera, l'utente può inserire  informazioni non corrette (ad esempio può non  inserire input)

118

Ci sono varie strategie per gestire eccezioni di IO

Il modo più semplice

intercettiamo (catch) l’eccezione quando si verifica,  usando un blocco try con una clausola catch, e  terminiamo l’esecuzione del programma con una  segnalazione d’errore

Gestire le eccezioni di IO

try {

String name = buffer.readLine();

}

catch (IOException e) { System.out.println(e);

System.exit(1);

}

Se viene generata l’eccezione, il programma stampa  sull’output standard la descrizione testuale standard  dell’eccezione

Poi il programma termina l’esecuzione segnalando 

Gestire eccezioni di IO

catch (IOException e) { System.out.println(e);

System.exit(1);

} System.out.println(e);

System.exit(1);

Propagare eccezioni di IO:  throws

Come per le eccezioni non controllate, un metodo  può non gestire un’eccezione controllata e propagare  l’eccezione

 Il metodo termina la propria esecuzione

 E lascia la gestione al metodo chiamante

Attenzione: Per dichiarare che un metodo propaga  un’eccezione controllata, si contrassegna il metodo 

(31)

121

Propagare eccezioni di IO da main

Se non si vuole gestire un’eccezione controllata, si  può dichiarare che il metodo main la propaga

 Questo è in assoluto il modo più semplice (anche se  non il migliore) di scrivere un main in cui si possono  generare eccezioni di IO

import java.io.FileNotFoundException; // QUESTO CODICE import java.io.FileReader; // COMPILA!

import java.util.Scanner;

public class MyInputReader {

public static void main(String[] args) throws IOException { String filename = “filename.txt”;

FileReader reader = new FileReader(filename);

Scanner in = new Scanner(reader);

} }

122

Le eccezioni di Scanner

Scanner è una classe di utilità. Non appartiene al  pacchetto IO e non lancia eccezioni di IO!

Le principali eccezioni di Scanner

NoSuchElementException lanciata ad es. da next e  nextLine se l'input non è disponibile

InputMismatchException lanciata da nextInt nextDouble,  ecc., se l'input non corrisponde al formato richiesto

IllegalStateException lanciata da molti metodi se invocati  quando lo Scanner e` “chiuso” (ovvero dopo un'invocazione  del metodo close)

Suggerimento: quando possibile usiamo solo i metodi  next e nextLine di Scanner, se necessario convertiamo  stringhe in numeri usando Double.parseDouble e 

Integer.parseInt 

Materiale di complemento (capitolo 11 – eccezioni di IO)

Gestire le eccezioni di input

Abbiamo detto che un flusso va sempre chiuso  (usando il metodo close) per rilasciare le risorse

 E se l’esecuzione viene interrotta da una eccezione  prima dell’invocazione del metodo close?

 Si verifica una situazione di potenziale instabilità

Si può usare la clausola finally

Il corpo di una clausola finally viene eseguito  comunque, indipendentemente dal fatto che  un’eccezione sia stata generata oppure no

(32)

125

Gestire eccezioni di input

Esempio: uso di try/finally

Se 

FileReader reader = new FileReader(filename);

Scanner in = new Scanner(reader);

readData(in);

reader.close(); // può darsi che non si arrivi mai qui FileReader reader = new FileReader(filename);

try {

Scanner in = new Scanner(reader);

readData(in);

} finally

{ reader.close(); } // questa istruzione viene eseguita // comunque prima di passare

// l’eccezione al suo gestore

126

Progettare nuovi tipi di eccezioni

Ora sappiamo

 programmare nei nostri metodi il lancio di eccezioni  appartenenti alla libreria standard (throw new …)

 gestire eccezioni (try/catch)

 lasciare al chiamante la gestione di eccezioni sollevate  nei nostri metodi (throws nell’intestazione del metodo)

Non sappiamo ancora creare eccezioni solo nostre

 Ad esempio, se vogliamo lanciare l’eccezione  LaNostraEccezioneException come facciamo?

Per ora saltiamo questo argomento (sezione 11.6)

Lo riprenderemo quando avremo studiato il concetto di  ereditarietà in Java

Riferimenti

Documenti correlati

Infine, definire un metodo che restituisce una stringa che descrive un calciatore convocato nella squadra nazionale.

Un curriculum vitae (cv) pu`o essere caratterizzato dal nome della persona, il luogo e anno di nascita, il titolo di studio posseduto e l’elenco delle borse di studio

La presentazione della tesi di un laureando davanti ad una commissione di laurea pu`o essere descritta tramite il nome e la matricola del laureando, il titolo della tesi, il nome

La sessione di una conferenza pu`o essere caratterizzata dal nome della con- ferenza, dal numero di sessione, dal nome del coordinatore della sessione e dall’elenco delle

Inoltre, definire un metodo per modificare l’anno di pubblicazione ed il numero dell’edizione, un metodo equals che restituisce true se due libri sono uguali (altrimenti

Una societ`a sportiva pu`o essere caratterizzata tramite il nome, l’indirizzo, il nome del responsabile e l’elenco degli atleti tesserati. Scrivere una classe SocSportiva, il

Inoltre, definire un metodo per modificare il prezzo, un metodo equals che verifica se due articoli sono uguali ed un metodo che restituisce una stringa che descrive un articolo

Inoltre, definire un metodo per modificare il nome della via, un metodo per modificare lo status di via pedonale o meno, un metodo che restituisce una stringa che descrive una