Più variabili dello stesso tipo (tutte int o tutte char o tutte String, ecc) possono essere organizzate in un unica struttura con un nome collettivo detto array. Le singole variabili sono dette elementi dell'array.
Un array è dichiarato scrivendo il nome del tipo degli elementi seguito da coppie di parentesi in numero corrispondente alla dimensione dell'array.
Per indicare che una variabile è un array unidimensionale la specifica di tipo va seguita da una coppia di parentesi quadre; se l'array è bidimensionale, le coppie di quadre devono essere due, ecc.
I singoli elementi del contenitore sono individuati tramite uno o più indici interi: ognuno di questi indici va posto tra parentesi quadre. L'indice del primo elemento è 0 (non 1), per cui in un array di n elementi l'indice può variare da 0 a n-1.
Gli arrays possono essere inizializzati direttamente nel modo mostrato nell'esempio.
Esempio di array unidimensionale: String[] musicista={"Gioacchino","Rossini"}; String nome, cognome; ........................... nome = musicista[0]; cognome = musicista[1];
Viceversa, si può assegnare un valore (ovviamente di tipo adeguato) ad ogni elemento dei un array.
String[] musicista={"",""}; ........................... musicista[0] = "Gioacchino"; musicista[1] = "Rossini";
Esempio di array bidimensionale: String[][] musicisti={{"Gioacchino","Rossini"},{"Giuseppe","Verdi"}}; String nome, cognome; ........................... // nome e cognome assumono i valori "Gioacchino" e "Rossini" nome = musicisti[0][0]; cognome = musicisti[0][1]; ........................... // nome e cognome assumono i valori "Giuseppe" e "Verdi" nome = musicisti[1][0]; cognome = musicisti[1][1];
In generale, un array bidimensionale può essere dichiarato ed inizializzato nel seguente modo, usando due cicli for annidati uno nell'altro.
Esempio: la tavola pitagorica int[][] tavpit = new int[10][10]; for (int i = 0; i < 10; i++ ) for (int j = 0; j < 10; j++ ) tavpit[i][j] = (i+1)*(j+1); /* La prima riga di tavpit, quella di indice i=0, contiene i numeri da 1 a 10. La seconda riga, con indice i=1, contiene i numeri pari da 2 a 20, ecc. La settima riga, con indice i=6, contiene i multipli di 7. */ int settexotto = tavpit[6][7];
L'assegnazione è l'attribuzione di un valore ad una variabile. Variabile e valore devono essere dello stesso tipo.
L'operatore di assegnazione è = .
Di norma la sequenza è identificatore = valore ma sono possibili forme contratte di assegnazione.
Un blocco è una sequenza di istruzioni che vengono eseguite dalla prima all'ultima, a meno che il flusso di esecuzione non venga forzatamente deviato da un'istruzione di controllo di flusso.
Un blocco è delimitato da una coppia di parentesi graffe.
Se il blocco non è il corpo di un metodo ed è composto da un'unica istruzione, le graffe sono facoltative.
Una variabile è detta campo (o variabile di oggetto) rappresenta un dato informativo comune a tutta la classe ma con valore specifico per ogni singolo oggetto generato
Per ogni campo vanno specificati
tipo
nome (identificatore)
Ad ogni campo può essere assegnato un valore.
Il valore di un campo è accessibile a tutti i metodi che l'oggetto eredita della classe. Questi metodi ne possono cambiare il valore con nuove assegnazioni che però non influiscono sul valore dello stesso campo in altri oggetti della stessa classe.
Se un campo è dichiarato pubblico (public) il valore del campo è accessibile anche dall'esterno della classe.
Se si prevede che un blocco di istruzioni possa o debba essere reiterato, conviene inserirlo in una adeguata struttura iterativa.
A questo scopo possono essere usate tre diverse forme:
In un ciclo a controllo finale il blocco di istruzioni del ciclo viene eseguito almeno una volta. Infatti il controllo sulla condizione di ripetizione del blocco viene fatto al termine del blocco stesso: il blocco viene ripetuto se il valore logico di controllo risulta vero. Per evitare cicli infiniti è necessario che una reiterazione del blocco cambi il valore del controllo.
Se nel blocco è presente l'istruzione break il ciclo viene interrotto immediatamente.
La struttura del ciclo è
do { ...............; ...............; } while (controllo);
Esempio: float a = 3.2f, b = 1.5f, p; do { a += a; b += b; p = a*b; } while (p < 1000);
Il ciclo con contatore è una variante del ciclo a controllo iniziale. In esso l'inizializzazione della variabile di controllo, il controllo e l'incremento da attribuire alla variabile di controllo ad ogni iterazione sono elencate nell'intestazione del ciclo, separate da punto e virgola.
Nella versione più semplice ed usuale del ciclo la variabile di controllo è di tipo intero e viene incrementata (o decrementata) di un'unità ad ogni iterazione.
Esempio: int i, fine = 100; for ( i = 0; i < fine; i++) { ................; ................; } for ( i = fine; i >0; i--) { ................; ................; }
La variabile di controllo può essere dichiarata nell'intestazione in modo da essere definita solo all'interno del ciclo. L'incremento può essere maggiore di 1.
Esempio: for ( int i = 1; i <= 100; i+= 3) { ................; ................; }
La variabile di controllo può essere anche di tipo reale.
Esempio: for ( float a = 0f; a <= 3.14f; a += 0.1f) { ................; ................; }
Si possono usare più variabili di controllo.
Esempio: float a, b; for ( a = 0f, b=6.26f; (a <= 3.14f) && (b > 0f); a += 0.1f, b -= 0.3f ) { ................; ................; }
Se nel blocco è presente l'istruzione break il ciclo viene interrotto immediatamente.
In un ciclo a controllo iniziale il blocco di istruzioni del ciclo viene eseguito solo se è vera un valore logico controllato preliminarmente all'esecuzione del ciclo. Per evitare cicli infiniti è necessario che ad un certo momento qualche istruzione del ciclo modifichi il valore della condizione di iterazione.
Se nel blocco è presente l'istruzione break il ciclo viene interrotto immediatamente.
Le la condizione di controllo è inizialmente falsa il blocco non viene mai eseguito.
La struttura del ciclo è
while (condizione) { ...............; ...............; }
Esempio: float a = 3f, b = 1.5f; while ( a < 15 ) { ................; ................; b += b; }
Una classe è la descrizione generale delle caratteristiche e delle modalità di elaborazione comuni a tutta una categoria di unità di informazione.
La dichiarazione di una classe è fondamentalmente composta dal suo identificatore seguito da un blocco contenente le dichiarazioni dei campi e dei metodi.
Campi e metodi possono essere statici o dinamici.
Una classe i cui membri siano accessibili dall'esterno deve essere dichiarata pubblica (public).
Un sorgente Java può contenere solo una classe pubblica. Il nome del sorgente deve coincidere con quello della classe seguito dal suffisso .java.
Una classe può ereditare i suoi membri da una classe descritta nello stesso sorgente o contenuta in un pacchetto importato.
In questo caso la dichiarazione della classe deve essere preceduta dalla dichiarazione di importazione del pacchetto 'import nome_pacchetto' e l'identificatore deve essere seguito dalla dichiarazione 'extends nome_superclasse'.
Classi fondamentali.
Un commento in un sorgente Java è un testo che non fa parte del codice eseguibile, utile ai programmatori per illustrare le fasi di elaborazione.
Sono possibili due tipi di commento
Un commento in riga è introdotto da una coppia di barre ( // ) e termina alla fine della riga di testo in cui è posto.
Un commento esteso può espandersi su più righe di testo. Il segnale di inizio è barra+asterisco ( /* ); il segnale di fine è asterisco+barra ( */ ).
Un commento di documentazione è finalizzato
all'uso di javadoc
e può pure espandersi su
più righe di testo. Il segnale di inizio è
barra+asterisco+asterisco
(
/** ) ; il segnale di fine è asterisco+barra
( */
).
Un componente è un oggetto dotato di rappresentazione grafica (spesso citata come GUI = 'Graphics User Interface') che può essere mostrata sullo schermo e che può interagire con l'utente.
I packages della piattaforma Java definiscono classi per molti tipi di componenti. Le classi più evolute sono quelle del package javax.swing. Limitandosi alla costruzione di applet, quelle di uso più comune, tutte discendenti della classe Component, di cui ereditano campi e metodi, sono raggruppabili nelle seguenti categorie.
Ogni componente è dotato di vari campi che possono essere assegnate con opportuni metodi. Se ne ricordano alcuni, quelli di uso più frequente.
I contenitori sono inoltre dotati dei seguenti metodi di uso molto frequente.
Ogni classe è in grado di generare oggetti in quanto dotata di appositi metodi detti costruttori.
Ogni costruttore ha come identificatore il nome della classe.
Tutte le classi, in quanto discendenti dalla classe 'capostipite' Object ereditano da questa il costruttore senza parametri NomeClasse() che quindi non è necessario esplicitare.
Gli altri eventuali costruttori si diversificano tra di loro per le diverse associazioni di parametri.
La costruzione di un oggetto della classe data si effettua con l'operatore new seguito dal costruttore adeguato.
Esempio: JButton btn = new JButton("OK");
Un'espressione è un valore determinato dall'azione di uno o più operatori su uno o più valori o immediati o espressi da variabili o prodotti da metodi.
Se un'espressione contiene più operatori, la precedenza nell'esecuzione può essere data da regole generali (come nel caso delle operazioni matematiche) o esplicitata con l'uso di parentesi tonde.
Il tipo di operatori che si possono usare in un'espressione dipende dal tipo degli operandi. In linea di massima gli operandi devono essere dello stesso tipo e un'espressione produce un risultato dello stesso tipo degli operandi. Ma ci sono notevoli eccezioni, trattate nelle descrizioni delle varie categorie di operatori.
Un etichetta è un identificatore usato per marcare la posizione di inizio di un ciclo. L'etichetta deve terminare con i due punti.
Le etichette sono usate come riferimento per le istruzioni di interruzione.
String Lancio(long n) { long r; int i,j; boolean uscito = false; start: for ( i = 0; i < 100; i++) for ( j = 0; j < 100; j++) { r = Math.round(1000*Math.random()); if (r==n) { uscito = true; break start; } } if (uscito) return (n+" non è uscito."); else return (n+" è uscito.") }
Le istruzioni di controllo di flusso modificano l'esecuzione sequenziale delle istruzioni presenti in un blocco.
Queste istruzioni si possono raggruppare in quattro tipologie:
Un'identificatore è costituito da una sequenza di caratteri alfabetici e/o numerici.
Il primo carattere deve essere alfabetico.
Sono ammessi anche i caratteri di dollaro '$' e di sottolineatura '_'.
Non sono ammessi caratteri accentati.
Caratteri maiuscoli e minuscoli della stessa lettera sono considerati diversi.
Un interfaccia è un complesso di metodi e campi costanti che possono essere aggiunti a quelli di una classe.
Le interfacce hanno fondamentalmente lo scopo di stabilire standard comuni (stessi metodi, stesse costanti) nella comunicazione tra due classi, che altrimenti, se avessero proprie modalità nell'elaborazione di particolari informazioni, non se le potrebbero scambiare.
Quando una classe deve acquisire i metodi dichiarati in una interfaccia, deve far seguire il proprio identificatore dalla clausola implements seguita dal nome dell'interfaccia.
I metodi di una interfaccia, diversamente da quelli di una classe, non sono implementati, cioè ne è definita solo l'intestazione ma non il corpo. La classe che implementa l'interfaccia è tenuta a implementare i metodi in essa contenuti.
Le interfacce non sono incluse nella gerarchia delle classi, quindi
Le istruzioni di interruzione di flusso interrompono l'esecuzione sequenziale delle istruzioni presenti in un blocco.
Queste istruzioni sono
L'istruzione return può essere usata nel corpo di un metodo per terminarlo prima di arrivare all'ultima istruzione.
Nel corpo di un metodo possono essere presenti, in vari blocchi, diverse istruzioni return.
L'istruzione break è usata:
L'istruzione continue è usata:
Un'istruzione (ingl. 'statement') è la descrizione delle elaborazioni da compiere sui valori immediati o su quelli delle variabili.
Le istruzioni sono collocate in un blocco e sono terminate da un punto e virgola. Se un'istruzione è l'ultima di un blocco, la terminazione è facoltativa.
I tipi di istruzione più importanti sono i seguenti.
Le variabili locali sono dichiarate all'interno di un blocco. Sono dette locali perché la loro visibilità è limitata all'ambito del blocco.
Variabili, anche di diverso tipo in blocchi diversi possono avere lo stesso identificatore senza confliggere.
Le espressioni matematiche si scrivono in linea secondo le convenzioni usuali in molti linguaggi di programmazione per quanto riguarda la notazione degli operatori matematici, le regole di precedenza e l'uso delle parentesi, solo tonde, eventualmente annidate.
Le funzioni matematiche in Java sono metodi statici della classe Math, compresa nel package java.lang, che non è necessario importare esplicitamente perché viene importato per default da compilatori ed esecutori.
In quanto metodi statici, le funzioni matematiche devono essere invocate sempre usando il nome della classe (Math) seguito dal punto selettore di campo e quindi dal nome della funzione.
Esempio: double rad2 = Math.sqrt(2d); double alfa = Math.asin(Math.sqrt(3d)/2);
Alcune funzioni, pur con nome uguale, sono diverse per il tipo dell'argomento e del risultato.
Ricordare che gli argomenti e gli output delle funzioni circolari (trigonometriche) sono sempre intesi in radianti.
tipo | funzione | descrizione |
---|---|---|
double | E | e (n. di Nepero) |
double | PI | π (pi greca) |
double | abs(double x) | valore assoluto |
float | abs(float x) | valore assoluto |
int | abs(int n) | valore assoluto |
long | abs(long n) | valore assoluto |
double | acos(double x) | arcocoseno |
double | asin(double x) | arcoseno |
double | atan(double x) | arcotangente |
double | atan2(double y, double x) | anomalia |
double | ceil(double x) | arrotondamento all'intero successivo |
double | cos(double x) | coseno |
double | exp(double x) | ex |
double | floor(double x) | arrotondamento all'intero precedente |
double | log(double x) | logaritmo naturale |
double | max(double a, double b) | il maggiore tra a e b |
float | max(float a, float b) | il maggiore tra a e b |
int | max(int a, int b) | il maggiore tra a e b |
long | max(long a, long b) | il maggiore tra a e b |
double | min(double a, double b) | il minore tra a e b |
float | min(float a, float b) | il minore tra a e b |
int | min(int a, int b) | il minore tra a e b |
long | min(long a, long b) | il minore tra a e b |
double | pow(double a, double b) | ab |
double | random() | y casuale, 0≤ y< 1 |
double | rint(double x) | arrotondamento all'intero |
long | round(double x) | arrotondamento all'intero |
int | round(double x) | arrotondamento all'intero |
double | sin(double x) | seno |
double | sqrt(double x) | radice quadrata |
double | tan(double x) | tangente |
double | toDegrees(double radianti) | radianti --> gradi |
double | toRadians(double gradi) | gradi --> radianti |
Un metodo (o funzione) di una classe è un blocco di istruzioni comprese tra parentesi graffe. Il blocco deve essere preceduto dall'identificatore del metodo seguito a sua volta da una coppia di parentesi tonde eventualmente contenenti i parametri.
Un metodo può essere statico: in questo caso la dichiarazione di tipo deve essere preceduta dalla dichiarazione static, altrimenti per default il metodo è dinamico.
I metodi statici sono definiti sulla classe mentre i metodi
dinamici sono definiti sugli oggetti. Ad esempio, per la classe
String il metodo
valueOf(double x) è statico.
Quindi per assegnare ad un oggetto di classe
String l'espressione in caratteri decimali
della costante matematica π si
procede nel seguente modo:
String a = String.valueOf(Math.PI);
Invece, per la stessa classe il metodo toUpperCase() è dinamico: se si vuole che una stringa b abbia gli stessi caratteri della stringa a ma tutti in versione maiuscola si invoca il metodo toUpperCase() nel seguente modo:
String a = "Italia"; String b = a.toUpperCase(); //il valore di b è "ITALIA"
Un metodo può anche produrre un valore di un determinato tipo. In questo caso l'identificatore del metodo va preceduto dal tipo di risultato prodotto e il flusso di elaborazione all'interno del metodo deve sempre terminare con una istruzione return seguita da valore prodotto.
Se il metodo non produce nessun valore l'identificatore va preceduto da void e l'istruzione return, che non è necessaria, serve eventualmente, da sola, per interrompere l'elaborazione prima che sia raggiunta l'ultima istruzione del metodo.
Un oggetto è una particolare esemplificazione (ingl. 'instance') di tutte le caratteristiche definite nella classe di appartenenza.
Un oggetto viene generato, cioè codificato in memoria, assegnando al suo identificatore il valore generato dall'operatore new seguito da uno dei costruttori della classe.
Nell'oggetto sono realizzati tutti i membri (campi e metodi) dinamici della classe mentre i membri statici sono unici per tutti gli oggetti generati dalla classe.
Il valore di un membro dell'oggetto è accessibile indicando il nome dell'oggetto seguito dal punto (selettore di campo) e dal nome del campo.
Esempio: Poeta poeta1 = new Poeta("Giovanni","Pascoli"); String cognome = poeta1.cognome;
Gli operatori di confronto analizzano l'ordinamento di due valori e producono un valore di tipo logico (true o false).
Quindi il risultato di un'espressione contenente un confronto può essere eventualmente assegnato solo a variabili di tipo boolean.
operatore | uso | descrizione |
---|---|---|
== | a == b | Vero se a è uguale a b, falso se a è diverso da b |
!= | a != b | Vero se a è diverso da b, falso se a è uguale a b |
> | a > b | Vero se a è maggiore di b, falso se a è minore o uguale a b |
>= | a >= b | Vero se a è maggiore o uguale a b, falso se a è minore di b |
< | a < b | Vero se a è minore di b, falso se a è maggiore o uguale a b |
<= | a <= b | Vero se a è minore o uguale a b, falso se a è maggiore di b |
Gli operatori digitali (ingl. 'bitwise operators') agiscono sui singoli bit che rappresentano la codifica del valore di un tipo primitivo intero.
Gli operandi e il risultato degli operatori binari devono essere dello stesso tipo, devono cioè avere lo stesso numero di bit, poiché gli operatori operano sulle coppie di bit nella stessa posizione (primo-primo, secondo-secondo, ecc.).
Nella tabella seguente a[i] è lo i-esimo bit di a, b[i] è lo i-esimo bit di b, r[i] è lo i-esimo bit di r.
operatore | nome | uso | descrizione |
---|---|---|---|
& | and | r = a & b | r[i]=1 solo se se a[i]==1 e b[i]==1 |
| | or | r = a | b | r[i]=0 solo se a[i]==0 e b[i]==0 |
^ | xor | r = a ^ b | r[i]=0 solo se a[i]==b[i] |
Due operatori unari sono operatori di 'slittamento' (ingl. 'shift'): spostano la sequenza di bit dell'operando verso destra o verso sinistra di tanti posti quante sono le unità indicate a destra del segno. Non ha senso che lo spostamento superi il numero di bit dell'operando.
Slittare a destra un valore di un posto equivale a dividerlo per 2 e, viceversa, slittare a sinistra un valore di un posto equivale a moltiplicarlo per 2.
Il terzo operatore citato produce il complemento a due dell'operando. Nella notazione binaria di un intero, il complemento a due dell'intero è l'intero (di ugual numero di bit) che sommato a quello dato dà zero: in pratica il suo opposto.
Esempio: Il decimale 10, codificato in binario su 8 bit, è 00001010 Il suo complemento a due è 11110110 (cioè la notazione binaria, su 8 bit, di -10) Infatti 00001010 + 11110110 = __________ 00000000
operatore | nome | uso | descrizione |
---|---|---|---|
>> | shift destro | r = a >> b | r=a*2-b |
<< | shift sinistro | r = a << b | r=a*2b |
˜ | compl. a 2 | r = ˜a | r = - a |
Gli operatori logici (o booleani) agiscono su operandi di tipo logico (boolean) e producono un valore logico.
Solo l'operatore di negazione è unario. Gli altri agiscono su coppie di operandi.
Congiunzione e disgiunzione hanno due operatori: la forma semplice valuta sempre entrambi gli operandi, la forma raddoppiata è più veloce perché evita valutazioni superflue. In pratica si usa quasi sempre la forma raddoppiata.
operatore | nome | uso | descrizione |
---|---|---|---|
! | negazione | r = ! a | se a==true, r=false e viceversa |
& | congiunzione | r = a & b | r = true solo se a==true e b == true |
&& | congiunzione | r = a && b | r = true solo se a==true e b == true |
| | disgiunzione | r = a | b | r = false solo se a==false e b == false |
|| | disgiunzione | r = a || b | r = false solo se a==false e b == false |
^ | disgiunzione escl. | r = a ^ b | r = true solo se a != b |
Gli operatori matematici binari, che agiscono sui tipi interi e reali, sono, oltre a quelli usuali di somma (+), sottrazione (-), prodotto (*), divisione (/), l'operatore di modulo (%) che produce il resto della divisione tra gli operandi.
Il linguaggio Java prevede inoltre vari operatori unari, che agiscono cioè su un solo argomento.
operatore | uso | descrizione |
---|---|---|
+ | +a | Trasforma il tipo di a in int se a è byte, short, char |
- | -a | Opposto di a |
++ | ++a | Incrementa a di 1 e poi lo valuta |
++ | a++ | Valuta a e poi lo incrementa di 1 |
- - | - -a | Decrementa a di 1 e poi lo valuta |
- - | a- - | Valuta a e poi lo decrementa 1 |
Le forme postfisse degli operatori ++ e -- sono molto usate nelle implementazioni dei cicli.
Sono possibili inoltre forme contratte di operazione-assegnazione.
operatore | uso | equivalente a |
---|---|---|
+ = | a + = b | a = a + b |
- = | a - = b | a = a - b |
* = | a * = b | a = a * b |
/ = | a / = b | a = a / b |
% = | a % = b | a = a % b |
In un'espressione matematica possono apparire mescolati i vari tipi numerici interi e reali. Il tipo del risultato è quello del tipo più generale che appare nell'espressione.
Le stringhe non sono di tipo primitivo ma sono oggetti della classe String, tuttavia in diverse situazioni i loro valori possono essere concatenati con l'uso dell'operatore + .
Esempio: String s = "Giuseppe"+" "+"Verdi"; //s ha valore "Giuseppe Verdi" String n = "Giuseppe"; String c = "Verdi"; String s = n+" "+c; //s ha valore "Giuseppe Verdi"
Una stringa può essere concatenata anche con un valore di tipo numerico: in questi casi Java invoca implicitamente il metodo statico String.valueOf(): il risultato è una stringa in cui il valore numerico è stato sostituito dalla sua espressione alfanumerica.
Esempio: int gm = 31; String s = "Giorni del mese = "+gm; //s ha valore "Giorni del mese = 31"
Un pacchetto ((package)) è una raccolta organizzata ad albero di dichiarazioni di classi, tale che le classi delle diramazioni sono sottoclassi delle classi nei nodi.
Il programmatore può importare l'intero pacchetto o solo un suo ramo o anche una singola classe.
La piattaforma Java rende disponibili al programmatore numerosi pacchetti; quelli di uso più frequente, almeno ad un primo approccio, sono:
java.lang java.awt java.io javax.swing
Il pacchetto java.lang è importato per default da compilatori ed esecutori Java; non serve quindi importarlo esplicitamente.
Ma se, ad esempio, il programmatore deve creare un applet swing, all'inizio del sorgente della sua classe deve apparire la clausola
import javax.swing.JApplet;
Se deve usare numerose classi swing scriverà
import javax.swing.*;
Il programmatore può produrre pacchetti personalizzati, intestando ogni singolo file contenente il sorgente di una classe pubblica con il nome del pacchetto in cui immettere la classe.
Esempio: package Geografia.Stato public class Stato { String continente, nome, capitale; float superficie; long popolazione; public Stato(String n) { super(); nome = n; } void setCapitale(String c) { capitale = c; } .......................... }
I metodi possono ricevere dall'esterno i valori da assegnare a una o più delle loro variabili locali.
Queste variabili locali che vengono inizializzate dall'esterno sono dette parametri e vanno dichiarate in una lista, separate da virgole, all'interno della coppia di parentesi tonde che accompagna l'identificatore del metodo.
Il linguaggio Java, per le variabili di tipo primitivo, ammette solo passaggio di valori.
Per passare indirizzi bisogna usare variabili di tipo classe, localizzate appunto dal loro indirizzo.
Una proprietà (dette anche variabile di classe o campo statico) rappresenta un dato informativo tipico della classe (non di qualche suo particolare oggetto: è quindi la stessa per tutti gli oggetti desunti dalla classe).
Per ogni proprietà vanno specificati
Ad ogni proprietà può essere assegnato un valore.
I valori delle proprietà di una classe accessibili a tutti i metodi della classe che ne possono cambiare il valore con nuove assegnazioni, ma questa è un'operazione da fare con attenzione perché il valore cambia per tutti gli altri oggetti.
Se una proprietà è dichiarata pubblica (public) il valore della proprietà è accessibile anche dall'esterno della classe e viene indicato dal nome della classe seguito dal punto selettore di campo seguito dall'identificatore della proprietà.
L'istruzione di selezione binaria permette di scegliere tra due diversi successivi blocchi di istruzioni a seconda del valore booleano di controllo.
La struttura dell'istruzione è
if (controllo) { } [ else { } ]
Le parentesi quadre indicano che la clausola else è facoltativa.
Se controllo è vero, viene eseguito il blocco che segue if, altrimenti, se è presente la clausola else, viene eseguito il blocco che segue else.
Spesso risulta utile la forma contratta della selezione binaria. La struttura è la seguente:
controllo? istruzione_si: istruzione_no;
Esempio: boolean ePari (int n) { (n % 2 == 0)? return true: return false; }
Il metodo ePari() potrebbe essere scritto, più usualmente, così:
boolean ePari (int n) { if (n % 2 == 0) return true; else return false; }
Ma sarebbe meglio:
boolean ePari (int n) { return (n % 2 == 0); }
E ancor meglio:
boolean ePari (int n) { return (n & 1 == 0); }
L'istruzione di selezione multipla è una generalizzazione dell'istruzione di selezione binaria è permette di diramare il flusso di esecuzione in più di due direzioni, a seconda dei valori assunti da una variabile di controllo, solitamente di tipo intero, che appare come argomento della clausola switch.
La struttura dell'istruzione è la seguente:
switch(controllo) { case val1: ...............; ...............; break; case val2: ...............; ...............; break; .................. .................. [ default: ................; ................; ] }
Se controllo ha valore val1, viene eseguita la sequenza di istruzioni situata dopo case val1. Questa sequenza, come le successive deve terminare con l'istruzione break, altrimenti vengono eseguite anche le istruzioni del caso seguente.
La clausola default è facoltativa e, se presente, deve essere l'ultima e viene eseguita se non si è verificata nessuna delle evenienze esplicitamente considerate.
Le sequenze di caratteri (stringhe) in Java sono rappresentate come oggetti della classe String.
La classe String è inclusa nel pacchetto java.lang che non è necessario importare esplicitamente.
Una stringa può essere creata in modo implicito, senza cioè invocare esplicitamente un costruttore semplicemente assegnando ad una variabile di tipo String un valore espresso da una sequenza di caratteri compresa tra virgolette doppie.
Esempio: String nome = "Paolo";
Nella tabella seguente si elencano alcuni metodi della classe String. Negli esempi a e b sono variabili di tipo String, c di tipo char, n di tipo int.
L'indice dei caratteri nella stringa comincia da 0.
I metodi indexOf, se falliscono, dànno risultato - 1.
I metodi valueOf() sono statici: si usano con il nome della classe.
tipo | funzione | descrizione |
---|---|---|
costruttori | ||
String | String() | stringa vuota: a = new String() |
String | String(String sorg) | clone di sorg: b = new String(a) |
metodi | ||
char | charAt(int i) | carattere i-esimo: c = a.charAt(3) |
String | concat(String sorgente) | concatenazione di b ad a: a.concat(b) |
int | indexOf(String ss) | posizione di ss da 0: n = a.indexOf(b) |
int | indexOf(String ss, int ix) | ricerca da ix: n = a.indexOf(b,ix) |
int | lastIndexOf(String ss) | come indexOf ma a rovescio |
int | lastIndexOf(String ss, int ix) | come indexOf ma a rovescio |
int | length() | lunghezza: n = a.length() |
String | replace(char x, char y) | rimpiazza x con y: b = a.replace('x','y') |
String | substring(int i, int f) | sottostringa: b = a.substring(3,6) |
String | toLowerCase() | tutto minuscolo: b = a.toLowerCase() |
String | toUpperCase() | tutto maiuscolo: b = a.toUpperCase() |
String | trim() | elimina spazi iniziali e finali: b = a.trim() |
String | valueOf(double x) | x in stringa: a = String.valueOf(Math.PI) |
String | valueOf(float x) | x in stringa: a = String.valueOf(1.2f) |
String | valueOf(int n) | n in stringa: a = String.valueOf(10) |
String | valueOf(long n) | n in stringa: a = String.valueOf(10l) |
Per tutte le variabili e i metodi di Java va sempre stabilito tipo, cioè l'indicazione della modalità di codificazione in memoria o in altri supporti digitali.
Il tipo di un dato è detto primitivo quando il dato è elementare (numero intero, reale, carattere o logico) e codificato in un numero prefissato di bytes.
I tipi primitivi hanno un dominio di valori limitato.
tipo | descrizione | formato |
---|---|---|
interi | ||
byte | intero da 1 byte | 8 bit |
short | intero da 2 bytes | 16 bit |
int | integer da 4 bytes | 32 bit |
long | intero da 8 bytes | 64 bit |
reali | ||
float | singola precisione | 32 bit (IEEE 754) |
double | doppia precisione | 64 bit (IEEE 754) |
altri tipi | ||
char | carattere | 16 bit (Unicode) |
boolean | logico | true o false |
Il tipo è detto indirizzo (o puntatore) quando il dato è un oggetto codificato in una opportuna locazione di memoria cui si accede tramite il suo indirizzo che rappresenta il valore effettivo dell'oggetto.
Metodi che non producono valori di nessun tipo sono dichiarati void.
In alcuni casi è possibile la conversione di tipo (ingl. 'casting') cioè la ricodifica di in valore di un tipo in quello di un'altro tipo compatibile, che si effettua preponendo al nome di una variabile il nome del nuovo tipo compreso tra parentesi tonde.
Esempio: int a = 10; long aa = (long)a; JPanel p = new JPanel(); Container c = (Container)p;
Quando un metodo che può essere esposto a situazioni problematiche non solleva direttamente una delle eccezioni previste della piattaforma Java in uso, il programmatore può scrivere un metodo che, in particolari situazioni invochi l'eccezione.
In questo metodo l'intestazione deve essere seguita dalla clausola throws seguita a sua volta dal nome della classe di eccezione prevista.
Nel corpo del metodo sarà inserita l'istruzione throw seguita da new e dal costruttore dell'eccezione.
Il programmatore può anche derivare nuove classi di eccezioni estendendo la classi della piattaforma.
String leggeNome(JTextField nome) { String ris = nome.getText(); try { controlloNome(ris) } catch (IllegalArgumentException e) { ris = ""; } finally { return ris } } //Il metodo controlloNome() solleva l'eccezione 'argomento non valido' //eventualmente rilevata dal metodo precedente void controlloNome(String s) throws IllegalArgumentException { for (int i=0;i < s.length(); i++) if (Character.isDigit(s.charAt(i))) { throw new IllegalArgumentException("Carattere improprio"); break; } }
Volendo personalizzare l'eccezione, si può procedere come nel seguente esempio.
String leggeNome(JTextField nome) { String ris = nome.getText(); try { controlloNome(ris) } catch (EccNomeSbagliato e) { ris = ""; } finally { return ris } } void controlloNome(String s) throws EccNomeSbagliato { for (int i=0;i < s.length(); i++) if (Character.isDigit(s.charAt(i))) { throw new EccNomeSbagliato("Carattere improprio."); break; } } class EccNomeSbagliato extends IllegalArgumentException { EccNomeSbagliato(String m) { super(m); } }
Quando si prevede che l'esecuzione di un'istruzione risulti problematica, ad esempio per inadeguatezza di qualche parametro, l'istruzione va 'tentata' preparando una o più risposte alle prevedibili situazioni di inadeguatezza, dette eccezioni (Exception) ed, eventualmente, dando le opportune istruzioni per la conclusione del problema.
Molte eccezioni sono descritte da classi dei pacchetti Java e possono essere usate immediatamente. Ma il programmatore può descrivere nuove eccezioni estendendo le classi Java.
La struttura da usare in questi casi è la seguente:
try { ..................; ..................; } catch(Exception1 e) { ..................; ..................; } catch(Exception2 e) { ..................; ..................; } ....................... ....................... [ finally { ...................; ...................; } ]
Ad esempio, dovendo trasformare una stringa in un valore numerico primitivo, può succedere che la stringa risulti inadeguata perché non composta da caratteri che possano rappresentare un numero.
boolean eStringaNumerica(String s) { boolean ris=true; try { int n = new Integer(s).intValue(); } catch(NumberFormatException e) { ris = false; } finally { return ris; } }
Ad ogni variabile può essere assegnato un valore, cioè la codificazione del dato attualmente rappresentato.
Il valore può essere rappresentato immediatamente oppure desumibile dal valore di un'altra variabile, dal calcolo di un'espressione o prodotto da un metodo e deve essere dello stesso tipo assegnato all'identificatore della variabile.
Per variabili di tipo intero (byte, short, int e long) il valore immediato è normalmente espresso da una sequenza di cifre decimali.
Per specificare che la sequenza va inteso come long si può far seguire l'ultima cifra dal suffisso letterale l o L (meglio, per non confonderlo con 'uno').
Per le variabili di tipo reale (float e double) il valore immediato è normalmente espresso da una sequenza di cifre decimali in virgola fissa o in notazione esponenziale in linea (es. 1.6E-19).
Per specificare che la sequenza va intesa come float si può far seguire l'ultima cifra dal suffisso letterale f o F mentre per specificare che la sequenza va inteso come double si può far seguire l'ultima cifra dal suffisso letterale d o D.
I valori di tipo char sono singoli caratteri compresi in una coppia di apici (es. 'a', 'W').
I valori di tipo logico (boolean) sono solo due: true e false.
Anche agli oggetti di classe String può essere assegnato un valore immediato con le stesse modalità usate per i tipo primitivi. Il valore immediato di una stringa è una sequenza di caratteri alfanumerici racchiusa da doppie virgolette. Es. String nome = "Paolo".
Il valore associato all'identificatore di un oggetto è fornito dall'operatore new seguito dal nome di un costruttore che, dopo aver generato l'oggetto un una certa locazione di memoria, ne passa l'indirizzo all'identificatore.
Il valore dell'indirizzo di un identificatore non ancora inizializzato dall'operatore new è null.
Una variabile è la memorizzazione codificata di una o più informazioni.
Per ogni variabile deve esser dichiarata in un blocco, definendone il tipo e l'identificatore.
La dichiarazione può essere fatta in qualunque punto del blocco, ma, in linea di massima, per chiarezza, è bene che le dichiarazioni siano all'inizio del blocco.
Ad ogni variabile può essere assegnato un valore o nella stessa istruzione in cui viene dichiarata o in una successiva istruzione del blocco.
Dopo questa assegnazione il valore è rappresentato dall'identificatore.
Nel linguaggio Java sono definite tre tipologie di variabili, distinte dal loro ambito di azione (ingl. 'scope')