Sono metodi associati al programma, invece che a una classe.
Si fanno metodi di questo tipo quando il metodo è utile solo in un programma.
Consideriamo questo problema: stampare una serie di espressioni, separate da una linea di asterischi.
class Linea { public static void main(String args[]) { int x=12; int y=42; System.out.println("**********************************"); System.out.println(x+2); System.out.println("**********************************"); System.out.println(2*x+1); System.out.println("**********************************"); System.out.println(y/2); System.out.println("**********************************"); System.out.println(x+y/2); System.out.println("**********************************"); } }
Questa istruzione si ripete sempre uguale.
Potrei definire un metodo che fa la stampa, e poi invocare il metodo quando serve.
Però il metodo va messo in una classe.
Devo creare una classe solo per metterci dentro il metodo:
class Inutile { void stampaLinea() { System.out.println("**********************************"); } }
Devo creare un oggetto solo per poter invocare il metodo:
class ProcInutile { public static void main(String args[]) { int x=12; int y=42; Inutile i; i=new Inutile(); i.stampaLinea(); System.out.println(x+2); i.stampaLinea(); System.out.println(2*x+1); i.stampaLinea(); System.out.println(y/2); i.stampaLinea(); System.out.println(x+y/2); i.stampaLinea(); } }
Soluzione alternativa: definire il metodo dentro il programma.
Posso scrivere metodi che sono associati al programma e non a una classe.
class Proc { static void stampaLinea() { System.out.println("**********************************"); } public static void main(String args[]) { int x=12; int y=42; stampaLinea(); System.out.println(x+2); stampaLinea(); System.out.println(2*x+1); stampaLinea(); System.out.println(y/2); stampaLinea(); System.out.println(x+y/2); stampaLinea(); } }
Ci sono delle somiglianze con i metodi delle classi, e delle differenze.
La dichiarazione è quasi uguale, ma si mette static davanti.
static TipoValoreRitorno nomeMetodo(argomenti) { istruzioni }
Per invocare il metodo nel programma (=far eseguire le istruzioni), si mette il nome del metodo con gli argomenti fra parentesi.
stampaLinea(); System.out.println(x+y/2); stampaLinea();
Non c'è un oggetto di invocazione.
Quindi:
Sono tutte e due conseguenze del fatto che questi metodi non hanno un oggetto di invocazione.
Realizzare un metodo che stampa la stringa "Positivo" oppure "Negativo" oppure "Zero" a seconda del segno di un numero passato come argomento.
Realizzare anche un programma di prova.
Il metodo ha:
L'intestazione del metodo è di conseguenza:
static void stampaSegno(int n)
Devo stampare una stringa diversa a seconda se il numero è positivo, negativo, oppure zero.
Un modo possibile (ce ne sono altri):
static void stampaSegno(int n) { if(n==0) System.out.println("Zero"); else if(n>0) System.out.println("Positivo"); else System.out.println("Negativo"); }
Questo è un possibile programma che usa il metodo:
class Segno { static void stampaSegno(int n) { if(n==0) System.out.println("Zero"); else if(n>0) System.out.println("Positivo"); else System.out.println("Negativo"); } public static void main(String args[]) { int a=2; stampaSegno(a); stampaSegno(-9); stampaSegno(2*a-4); } }
Posso invocare il metodo passando il risultato di una qualsiasi espressione, per esempio:
Stampare questa figura:
* ** *** **** ***** ****** ******* ******** ********* **********
Definire un metodo che stampa una linea di
n
(per stampare un singolo asterisco,
fare System.out.print("*");)
Il metodo ha:
Per l'intestazione, è come un metodo di una classe:
static void asterischi(int n) { ... }
L'unica differenza, nell'intestazione, è la parola static
In n viene messo il numero di asterischi da stampare.
Dopo aver stampato, vado a capo.
static void asterischi(int n) { int a; for(a=0; a<n; a++) System.out.print("*"); System.out.println(); }
Se invoco il metodo come asterischi(x), viene stampata una linea di n asterischi.
Questo va fatto per x=1, 2, 3, ..., 10
class Aster { static void asterischi(int n) { int a; for(a=0; a<n; a++) System.out.print("*"); System.out.println(); } public static void main(String args[]) { int x; for(x=1; x<=10; x++) asterischi(x); } }
Quando si invoca un metodo void:
Nel corpo del metodo le variabili del programma non si possono usare.
I parametri attuali sono valori, che possono anche risultare dalla valutazione di espressioni:
asterischi(12); asterischi(a*2-b); asterischi(2+(int) p.distance(q));
Le variabili del metodo e del programma possono anche avere lo stesso nome.
Sono comunque due variabili diverse.
class Stesso { static void asterischi(int n) { int x; for(x=0; x<n; x++) System.out.print("*"); System.out.println(); } public static void main(String args[]) { int x; for(x=1; x<=10; x++) asterischi(x); } }
Prima di invocare il metodo:
Le variabili del metodo non esistono
Ogni volta che viene invocato un metodo, si crea una zona di memoria per tutte le sue variabili (variabili locali e parametri formali).
Prima di eseguire le istruzioni, c'è la copiatura dei parametri attuali in quelli formali.
Quando si eseguono le istruzioni, nei parametri attuali sono stati messi i valori dei parametri attuali.
Il valore di x va in n: questo dipende dell'invocazione!
Quindi, il valore di x viene copiato dentro n
È come se venisse fatto n=x;
Ogni metodo può accedere solo alle sue variabili:
La zona di memoria di asterischi viene rimossa, e si torna allo stato originario.
Attenzione! Quando si invoca nuovamente il metodo, le sue variabili vengono create di nuovo.
Da una invocazione all'altra, i valori delle variabili locali vengono persi.
Cosa stampa questo programma?
class Trab1 { static void prova(int x) { x=0; } public static void main(String args[]) { int x; x=12; prova(x); System.out.println(x); } }
Si deve fare la figura.
Durante l'esecuzione del programma, esiste solo la zona di memoria del programma:
Viene creata la zona di memoria del metodo:
Il valore di x del main viene copiato nella x di prova
Solo ora si esegue il corpo del metodo.
Se x=0 compare nel metodo prova, viene messo 0 nella x di prova
Notare: la x di main non può venire modificata: è inaccessibile dal metodo prova
Viene cancellata la zona di prova
Viene quindi stampato 12
Cosa stampa questo programma?
class Ordine { static void prova(int x, int y) { System.out.println(x); } public static void main(String args[]) { int x=10; int y=20; prova(y,x); } }
I nomi delle variabili sono irrilevanti.
Se invece del nome x uso il nome abcd il programma fa lo stesso.
Il passaggio avviene sulla base della posizione:
Nella x di prova viene messo il valore della y di main
Viene stampato 20
Se non è chiaro:
usate nomi di variabili diverse, se possibile, per evitare confusione
Se invece il meccanismo è chiaro, potete anche usare gli stessi nomi.
Altro suggerimento:
non modificate i parametri formali, per evitare confusione
Il programma trasmette dei valori al metodo (es. 3, 12, -2, ecc.)
Il metodo li riceve: per poterli usare, vengono messi in alcune variabili
int a=12, b=-2; nomemetodo(12*2, a-b/2, a+b);
Vengono valutate le tre espressioni.
I valori calcolati sono 24, 13, 10
L'invocazione del metodo equivale a:
nomemetodo(24, 13, 10);
Per lo stesso motivo:
int x=12; prova(x);
È equivalente a:
int x=12; prova(12);
Perchè il valore di x dovrebbe cambiare.
I parametri attuali sono valori.
Quando si invoca il metodo, le espressioni fra parentesi vengono valutate.
Il fatto che il valore 12 derivi dal fatto che in x c'era questo valore viene dimenticato:
prova(x) diventa come prova(12), che non ha nessuna relazione con x
i parametri attuali sono valori: il programma "trasmette" dei valori ai metodi; la provenienza dei valori (costanti, valori di variabili, valori di espressioni) è irrilevante
Quando faccio prova(x) è il valore contenuto in x che viene inviato. Il fatto che provenga da x non ha importanza.
La sequenza effettiva è:
I passi vengono fatti in questa sequenza.
Cosa stampa questo programma?
class Ritorno { static int prova(int x) { x=x+10; return x+2; } public static void main(String args[]) { int x=12; x=prova(x); System.out.println(x); } }
Viene stampato 24
Perchè?
Inizio del metodo:
Viene ritornato il valore di x+2
Per x si intende la x di prova, che è 22
Dato che il metodo era stato invocato con x=prova(x), e il valore di ritorno è 24, questo equivale a x=24
Il valore di ritorno viene messo in x
Se non fate modifiche ai parametri formali, non avrete questi problemi:
// programma piu' chiaro class Chiaro { static int prova(int x) { int altronome; altronome=x+10; return altronome+2; } public static void main(String args[]) { int x=12; x=prova(x); System.out.println(x); } }
x=prova(x);
Se non vi risulta chiaro, usate una variabile in più:
int y; y=prova(x); x=y;
Da questo si capisce la sequenza: prima invocazione, poi assegnamento.
Date due variabili intere a e b, stampare i valori delle espressioni a-b, a-b*b, -a/b, a/b
Stampare solo i valori di queste espressioni che risultano positivi (maggiori o uguali a zero).
Valuto ogni espressione e vedo se è positiva:
class SenzaMetodo { public static void main(String args[]) { int a=12, b=32; if(a-b >= 0) System.out.println(a-b); else System.out.println("Espressione negativa"); if(a-b*b >= 0) System.out.println(a-b*b); else System.out.println("Espressione negativa"); if(-a/b >= 0) System.out.println(-a/b); else System.out.println("Espressione negativa"); if(a/b >= 0) System.out.println(a/b); else System.out.println("Espressione negativa"); } }
Farlo usando un metodo.
Quale è l'operazione che si ripete?
Stampare un valore se positivo.
``calcolare l'espressione'' non è l'operazione che si ripete, dato che ogni volta si tratta di una operazione diversa.
Si può riscrivere il programma:
class Ripetizione { public static void main(String args[]) { int a=12, b=32; int f; f=a-b; if(f >= 0) System.out.println(f); else System.out.println("Espressione negativa"); f=a-b*b; if(f >= 0) System.out.println(f); else System.out.println("Espressione negativa"); f=-a/b; if(f >= 0) System.out.println(f); else System.out.println("Espressione negativa"); f=a/b; if(f >= 0) System.out.println(f); else System.out.println("Espressione negativa"); } }
È chiaro che la valutazione di una espressione va fatta nel programma, dato che sono quattro espressioni diverse.
Però la verifica se positivo e la stampa si ripetono sempre uguali.
Il metodo prende un valore intero, e non restituisce nulla.
class ConMetodo { static void stampaSePositivo(int val) { if(val>=0) System.out.println(val); else System.out.println("Espressione negativa"); } public static void main(String args[]) { int a=12, b=32; stampaSePositivo(a-b); stampaSePositivo(a-b*b); stampaSePositivo(-a/b); stampaSePositivo(a/b); } }
Cosa succede quando si invoca stampaSePositivo(a-b*b)?
Equivale a fare: val=a-b*b e poi eseguire il corpo del metodo.
Si intende che dove val è la variabile del metodo mentre a e b sono variabili del programma.
Scrivere un metodo che calcola il fattoriale di un numero intero.
Scrivere poi il programma che stampa i fattoriali dei valori da 1 a 4, il fattoriale di 6 e poi verifica se il fattoriale di 5 è maggiore di 50 oppure no.
Ha valore di ritorno intero.
Ha come argomento un intero.
static int fattoriale(int n)
Calcolo il fattoriale del numero che sta in n,
e ritorno il valore calcolato.
static int fattoriale(int n) { int a; int f; f=1; for(a=1; a<=n; a++) f=f*a; return f; }
Basta a questo punto invocare il metodo tutte le volte che serve.
public static void main(String args[]) { int i; for(i=1; i<=4; i++) System.out.println(fattoriale(i)); System.out.println(fattoriale(6)); if(fattoriale(5)>50) System.out.println("Il fattoriale di 5 e' maggiore di 50"); else System.out.println("Il fattoriale di 5 e' minore di 50"); }
Provare senza metodo: andava ripetuto lo stesso codice almeno tre volte.
Anche in questo modo funziona:
static int fattoriale(int n) { int f=1; for(; n>=1; n--) f=f*n; return f; }
Molto meno chiaro.
Scrivere un metodo che calcola il valore assoluto di un numero intero.
Scrivere un programma che legge da tastiera un intero, e stampa la radice del valore assoluto.
Ha come parametro un intero, e ritorna un intero.
static int valoreAssoluto(int)
Se il numero è positivo, ritorno il suo valore.
Se è negativo, ritorno l'opposto del suo valore.
static int valoreAssoluto(int n) { if(n>=0) return n; else return -n; }
Nel programma di prova, è bene fare più invocazioni, per verificare se il metodo funziona.
Nel programma vero potrebbe anche esserci solo una invocazione.
class ValAssoluto { static int valoreAssoluto(int n) { if(n>=0) return n; else return -n; } public static void main(String args[]) { int x; for(x=-10; x<=10; x++) { System.out.println(valoreAssoluto(x)); } } }
Il programma che voglio fare è quello che legge un intero da tastiera, fa la radice del valore assoluto, e stampa.
import javax.swing.*; class Radice { static int valoreAssoluto(int n) { if(n>=0) return n; else return -n; } public static void main(String args[]) { int x; String s; s=JOptionPane.showInputDialog("Dammi un intero"); x=Integer.parseInt(s); System.out.println(Math.sqrt(valoreAssoluto(x))); System.exit(0); } }
Scrivere un metodo che riceve due interi e restituisce la somma dei loro fattoriali.
Usare il metodo fattoriale.
Si può invocare un metodo anche all'interno di un altro metodo.
static int fattoriale(int n) { int a; int f; f=1; for(a=1; a<=n; a++) f=f*a; return f; }
Prende due interi e torna un intero.
static int sommaFatt(int, int)
Devo calcolare i fattoriali, e sommarli.
static int sommaFatt(int x, int y) { int f1, f2; f1=fattoriale(x); f2=fattoriale(y); return f1+f2; }
class SommaFatt { static int fattoriale(int n) { int a; int f; f=1; for(a=1; a<=n; a++) f=f*a; return f; } static int sommaFatt(int x, int y) { int f1, f2; f1=fattoriale(x); f2=fattoriale(y); return f1+f2; } public static void main(String args[]) { int i; System.out.println(sommaFatt(1,4)/2); } }