OBIETTIVI DELLA LEZIONE
In questa lezione impareremo a conoscere:
Alla fine della lezione saremo in grado di scrivere autonomamente la nostra prima semplice classe Java.
Abbiamo visto che la piattaforma Java si basa su tre componenti principali. Il linguaggio, la libreria di classi e la Java Virtual Machine (JVM). Rivediamo con un breve esempio cosa serve la JVM e perché ha avuto tanto successo
Vediamolo con una metafora. La vostra azienda Cremasca ha spostato parte della produzione in numerosi paesi dell'est asiatico. Dopo i primi tempi in cui numerose difficoltà di comunicazione sono emerse, la dirigenza ha ideato un innovativo meccanismo per comunicare gli ordini ai siti produttivi.
Il flusso degli ordini di produzione è quindi ad oggi molto efficace:
Anologamente, per quanto riguarda Java:
Per quanto ci riguarda, un "dato" è una quantità minimale di informazione che vogliamo elaborare con un nostro programma. Può essere il saldo del nostro conto corrente, il numero di amici che abbiamo invitato alla nostra festa o il colore del gatto del vicino.
Come nell'algebra e in altri linguaggi di programmazione, anche in Java chiamiamo variabile il contenitore simbolico di un certo "dato". Le variabili vengono manipolate con degli operatori che ne modificano il valore. Frasi che coinvolgono variabili ed operatori, vengono chiamate espressioni.
Supponiamo di dover preparare un modulo cartaceo da compilare per un sondaggio; (l'utente compilerà il modulo con una penna, nessun computer). L'utente deve inserire il proprio sesso, l'età e la marca delle caramelle preferite. Probabilemente prevederemmo una cosa del genere:
Sesso: _ Età: __ Marca caramelle preferite: ____________________
Infatti, per il sesso basta inserire un carattere, M o F, due numeri per l'età e per la marca preferita, che non conosciamo a priori, lasciamo 20 spazi vuoti. Ciascun dato occuperà un certo "spazio" sul foglio, ad esempio la variabile "Sesso" occuperà uno spazio.
In un programma Java, come del resto in ogni linguaggio di programmazione, i dati vengono immagazzinati in memoria da qualche parte (per oggi non ci preoccuperemo di capire dove). Per ogni variabile viene predisposto lo spazio opportuno: come sul foglio venivano predisposti degli spazi per le lettere scritte a penna, allo stesso modo nella memoria del calcolatore viene riservato un numero di byte adeguato.
Per poter fare questo, Java richiede che venga specificato sempre il tipo di dato che vogliamo sia contenuto in una variabile. Ad esempio:
int questo; short mese;
Il tipo di dato non può essere cambiato nel corso del programma. Java è un linguaggio fortemente tipizzato (non lo sarà affatto il PHP).
Una variabile può essere:
In Java, i tipi di dati primitivi sono i mattoni base a partire dai quali vengono costruiti tutti gli altri oggetti. NON sono oggetti. Sono stati pensati a partire da quelli definiti dal C, con alcuni miglioramenti e semplificazioni efficaci. Eccoli:
Tipo | Valore | Default | Occupa | Intervallo |
---|---|---|---|---|
boolean | true, false | false | 1 bit | |
char | carattere Unicode | \u0000 | 2 bytes | da \u0000 a \uFFFF |
byte | intero (con segno) | 0 | 1 byte | da -128 a 127 |
short | intero (con segno) | 0 | 2 bytes | da -32768 a 32767 |
int | intero (con segno) | 0 | 4 bytes | da -2147483648 a 2147483647 |
long | intero (con segno) | 0 | 8 bytes | da -9223372036854775808 a 9223372036854775807 |
float | numeri a virgola mobile (IEEE 754) | 0.0 | 4 bytes | da ±1.4E-45 a ±3.4028235E+38 |
double | numeri a virgola mobile (IEEE 754) | 0.0 | 8 bytes | da ±4,9E-324 a ±1.7976931348623157E+308 |
Il valore di default verrà utilizzato per i campi degli oggetti e per gli array [per adesso non sappiamo cosa siano; lo capiremo più avanti].
In Java assumono i valori true o false e NON sono dei numeri interi ne possono essere convertiti in numeri interi come succede in C o C++.
I numeri interi sono tutti con il segno. NON esistono i numeri unsigned come in C. Inoltre hanno una dimensione che è fissata dal linguaggio (quella della tabella sopra) e non variabile con il tipo di architettura.
I numeri vengono rappresentati in base 2 e non in base decimale. Questo perché, come ben sapete, le memorie sono bistato e possono solo memorizzare due valori, che per convenzione si assumono essere 0 o 1.
Ripasso
In base 2 ci sono solo due simboli a disposizione, ad esempio, 7 diventa 111 mentre 37 diventa 100101. In base 16 (esadecimale) ci sono invece 16 simboli a disposizione e per convenzione si utilizzano le lettere da A a E per rappresentare i numeri da 10 a 15. Ad esempio, 37 diventerebbe 25.
Cosa rappresenta il numero 155 in base 10? 155 = 1 * 102 + 5 * 101 + 5 * 100. Cioè ogni cifra ha un valore in base alla posizione (infatti si chiama notazione posizionale e non è l'unico modo di rappresentare i numeri, basti ad esempio pensare alla rappresentazione romana in cui 155 = CLV).
Come si converte un numero decimale in un altra base? Lo si deve dividere ripetutamente per la base fino ad ottenere 0. I resti delle divisioni intere, letti dall'ultimo al primo, sono le cifre del numero convertito.
Esempio: convertiamo 155 in base 2.
155/2 = 77/2 = 38/2 = 19/2 = 9/2 = 4/2 = 2/2 = 1/2 = 0 1 1 0 1 1 0 0 1 <--- Leggiamo i resti in questa direzione ----- 10011011
Esempio: convertiamo 155 in base 16.
155/16 = 9/16 = 0 11 9 <--- Leggiamo i resti in questa direzione ----- 9B
Per convertire da base 2 o 16 a base decimale, dobbiamo semplicemente moltiplicare ciascuna cifra del numero per il suo valore posizionale.
Esempio: convertiamo 10011011 in base 10.
10011011 = 1*2^7 + 0*2^6 + 0*2^5 + 1*2^4 + 1*2^3 + 0*2^2 + 1*2^1 + 1*2^0 = = 1*128 + 0* 64 + 0* 32 + 1* 16 + 1* 8 + 0* 4 + 1* 2 + 1* 1 = = 128 + 0 + 0 + 16 + 8 + 0 + 2 + 1 = = 155 (il simbolo ^ indica qui l'elevamento a potenza, ma non è un operatore JAVA!)
La base 16 è un multiplo della base 2. Dato un numero in base due, per passare ad un numero in base 16 basta convertirlo 4 cifre alla volta.
Esempio: convertiamo 10011011 in base 16.
10011011 --> 1001 e 1011 8+0+0+1 e 8+0+2+1 9 e B 9B 9B
I numeri negativi vengono rappresentati in complemento a 2. Per prima cosa ricordiamoci che il complemento a 1 di un numero si costruisce invertendo tutti i bit della sua rappresentazione binaria.
Esempio: calcoliamo il complemento a 1 dell'intero 155.
155 è un intero, quindi sarà contenuto in 32 bits: 00000000 00000000 00000000 10011011 invertiamo tutti i bits 11111111 11111111 11111111 01100100
Il complento a 2 del numero A si ottiene sommando 1 al suo complemento a 1 (che in Java è l'operatore ~ come in C).
Esempio: calcoliamo il complemento a 2 di 155.
~155 + 1 = 11111111 11111111 11111111 01100100 + 00000000 00000000 00000000 00000001 = 11111111 11111111 11111111 01100101
La notazione in complemento a 2 si utilizza in modo che A+(-A)=0.
Esempio: calcoliamo 155 + (-155).
155 --> 00000000 00000000 00000000 10011011 + -155 --> 11111111 11111111 11111111 01100101 + 00000000 00000000 00000000 00000000
I numeri negativi in Java sono rappresentati in complemento a due. Attenzione alla circolarità indotta dalla rappresentazione: se allo short A = 32767 aggiungiamo 1 otteniamo -32768! Vediamolo:
32767 è uno short, dunque è memorizzato in 16 bits 32767 --> 01111111 11111111 + 1 --> 00000000 00000001 = 10000000 00000000Che è la rappresentazione in complemento a due di -32768 su 16 bits.
class EsempioCircolarita { public static void main(String[] args) { short a = 32767; a += 1; System.out.println(a); short b = -32768; b -= 1; System.out.println(b); } }
>java EsempioCircolarita -32768 32767
Nei nostri programmi utilizzeremo solo numeri interi di tipo int e long, visto che per quanto ci riguarda possiamo fare a meno delle altre tipologie.
I numeri a virgola mobile sono una rappresentazione dei numeri razionali. NON sono un insieme denso cioè tra due numeri non è detto che ce ne sia un terzo né sono illimitati.
L'aritmetica a virgola mobile di Java è un sottoinsieme dello standard IEEE-754. Per i più curiosi, all'indirizzo http://www.h-schmidt.net/FloatApplet/IEEE754.html potete trovare un applet Java che vi permette di capire meglio la rappresentazione in virgola mobile.
Durante il corso utilizzaremo solo numeri a virgola mobile di tipo double (64 bits).
Ripasso
Nella memoria di un calcolatore possiamo memorizzare solo numeri binari. Di certo non possiamo memorizzare un segno grafico come un carattere. Lo stesso problema lo affrontarono coloro che organizzarono le trasmissioni telegrafiche: come trasmettere un messaggio con uno strumento in grado di trasmettere solo punti e linee? Ovviamente è necessaria una codifica: ad ogni carattere associamo un numero che trasmettiamo o, nel nostro caso, memorizziamo.
Una codifica che si affermò in modo pressoché universale fu quella standard ASCII, che comprendeva 128 caratteri (7 bits) tra cui alcuni caratteri di controllo che erano utilizzati per le trasmissioni e la gestione del terminale. 128 caratteri non sono molti ma erano sufficienti per codificare la lingua inglese che non usa le lettere accentate. La codifica ASCII è la seguente:
NUL( 0) SOH( 1) STX( 2) ETX( 3) EOT( 4) ENQ( 5) ACK( 6) BEL( 7) BS ( 8) HT ( 9) NL ( 10) VT ( 11) FF ( 12) CR ( 13) SO ( 14) SI ( 15) DLE( 16) DC1( 17) DC2( 18) DC3( 19) DC4( 20) NAK( 21) SYN( 22) ETB( 23) CAN( 24) EM ( 25) SUB( 26) ESC( 27) FS ( 28) GS ( 29) RS ( 30) US ( 31) SP ( 32) ! ( 33) " ( 34) # ( 35) $ ( 36) % ( 37) & ( 38) ' ( 39) ( ( 40) ) ( 41) * ( 42) + ( 43) , ( 44) - ( 45) . ( 46) / ( 47) 0 ( 48) 1 ( 49) 2 ( 50) 3 ( 51) 4 ( 52) 5 ( 53) 6 ( 54) 7 ( 55) 8 ( 56) 9 ( 57) : ( 58) ; ( 59) < ( 60) = ( 61) > ( 62) ? ( 63) @ ( 64) A ( 65) B ( 66) C ( 67) D ( 68) E ( 69) F ( 70) G ( 71) H ( 72) I ( 73) J ( 74) K ( 75) L ( 76) M ( 77) N ( 78) O ( 79) P ( 80) Q ( 81) R ( 82) S ( 83) T ( 84) U ( 85) V ( 86) W ( 87) X ( 88) Y ( 89) Z ( 90) [ ( 91) \ ( 92) ] ( 93) ^ ( 94) _ ( 95) ` ( 96) a ( 97) b ( 98) c ( 99) d (100) e (101) f (102) g (103) h (104) i (105) j (106) k (107) l (108) m (109) n (110) o (111) p (112) q (113) r (114) s (115) t (116) u (117) v (118) w (119) x (120) y (121) z (122) { (123) | (124) } (125) ~ (126) DEL(127)
Per permettere la codifica di lingue che utilizzano molti caratteri accentati lo standard ASCII fu esteso a 8 bits in vari modi differenti. Microsoft per MSDOS introdusse le "code pages": i caratteri dal 128 al 255 erano diversi a seconda della code page scelta. Ad esempio, la code page inglese era la 437 mentre quella italiana la 850.
Ad un certo punto per evitare il dilagare di approcci differenti fu introdotto lo standard a 8 bits ISO 8859-1, chiamato anche ISO Latin1. La code page di MS Window nelle lingue occidentali è la 1252, compatibile con lo standard.
La differenza tra le code pages causa effetti deleteri quando si programma in linguaggi come il C. Ad esempio la lettera 'è' viene codificata con il codice 232 da MS Windows mentre lo stesso codice corrisponde alla lettera thorn maiuscola Þ (presente nell'alfabeto finlandese) nella console DOS. Semplice no?
Fortunatamente queste sono complicazioni in estinzione. Per semplificare le comunicazioni nell'era dell'informatizzazione globale è stata introdotta una codifica detta UNICODE in cui si arrivano ad utilizzare fino a 32 bits per carattere: è possibile rappresentare anche tutti gli ideogrammi cinesi!
Compatibile con questa codifica è lo standard a 16 bits ISO/IEC 10646 utilizzato da Java.
I caratteri in Java sono codificati seguendo lo standard UNICODE a 16 bits, cioè per ciascun carattere sono utilizzati due bytes (NON uno come in C). Questo permette di includere pressoché qualunque tipo di carattere.
Per rappresentare una costante carattere la si deve includere tra due apici semplici. Per assegnare un carattere unicode ad una variabile si utilizza la forma \uXXXX dove XXXX è la codifica esadecimale. Comode sone le seguenze di escape, come in C:
\n | a capo (LF) |
\t | tabulazione orizzonatale (HT) |
\\ | backslash \ |
\' | apice ' |
\" | virgolette doppie " |
Esempi:
char carattere = 'R'; char carattere = '\n'; char carattere = '\u05D0';
Le stringhe NON sono un tipo di dato primitivo ma un oggetto della classe String. Mnemonicamente potete ricordare che String inizia con una maiuscola, come del resto tutte le classi, a differenza dei tipi primitivi che iniziano con una minuscola.
Le stringhe però a differenza di tutti gli altri oggetti possono essere inizializzate con una costante, come in C. Esempio:
String frase = "Questa è una stringa";
Le stringhe non sono terminate da un carattere NULL come in C: non ci interessa come siano rappresentate nella memoria. Questo è un tipico della programmazione ad oggetti e si chiama "incapsulazione": nascondere i dettagli implementativi.
Il Java ha un controllo dei tipi molto più stringente che il C. Permette conversioni di tipo implicite SOLO quando è impossibile perdere informazioni. Altrimenti, se si vuole forzare la converisone di tipo dobbiamo forzarla, come in C, con un cast. Esempio:
long numeroLungo = 35267; int numeroCorto; numeroCorto = (int)numeroLungo;
La seguente è la tabella delle conversioni implicite ammesse:
boolean | byte | short | int | long | float | double | char | |
boolean | - | NO | NO | NO | NO | NO | NO | NO |
byte | NO | - | SI | SI | SI | SI | SI | CAST |
short | NO | CAST | - | SI | SI | SI | SI | CAST |
int | NO | CAST | CAST | - | SI | SI | SI | CAST |
long | NO | CAST | CAST | CAST | - | SI | SI | CAST |
float | NO | CAST | CAST | CAST | CAST | - | SI | CAST |
double | NO | CAST | CAST | CAST | CAST | CAST | - | CAST |
char | NO | CAST | CAST | SI | SI | SI | SI | - |
Gli operatori Java coincidono pressoché con quelli C/C++.
Somma (+), sottrazione (-), moltiplicazione (*), divisione (/) e resto della divisione intera (%). ATTENZIONE: se la divisione è fatta tra due interi NON produce un numero a virgola mobile!
L'operatore + quando usato con due stringhe le concatena. Esempio:
String nome = "Luigi" + " " + "Rossi";
L'operatore di assegnamento è il segno di uguale (=). Come in C, la maggior parte degli operatori può essere combinata con l'operatore di assegnamento. Ad esempio, a += 3; corrisponde a a = a + 3;.
Uguale (==), diverso (!=), maggiore (>), maggiore e uguale (>=), minore (<) e minore e uguale (<=). Restituiscono il valore boolean true se la relazione è verificata, false altrimenti.
In Java è possibile inserire commenti nel codice in tre modi. I primi deu sono identici a quelli previsti dal C++. Esempio:
/* Questo è un commento su più righe. */ // Questo è un commento a singola riga
Il terzo modo serve per specificare dettagli di un programma agli altri programmatori (o anche a noi stessi). Lo vederemo più avanti.
Una istruzione in Java coincide con un comando eseguito dalla JVM. Un blocco è un insieme di istruzioni racchiuso tra parentesi graffe. E' possibile dichiarare delle variabili locali all'interno di in blocco di istruzioni, e non è necessario farlo all'inizio come in C89. I blocchi possono essere annidati a piacere: le variabili locali sono visibili dal punto della loro dichiarazione fino alla fine del blocco cui appartengono, (quindi anche nei sottoblocchi). (Queste sono chiamate in genere regole di scopo o di visibilità).
int a = 1; { int b; b = a; // b diventa 1 } // qui la variabile b cessa di esistere
Una sequenza è invece una banale successione di istruzioni.
Composta da UNA espressione e UNO/DUE/TRE blocchi di codice (chiamati i "rami" dell' if)
if (condizioneLogica) istruzione1;
oppure
if (condizioneLogica) istruzione1; else istruzione2;
oppure
if (condizioneLogica1) istruzione1; else if (condizioneLogica2) istruzione2; else if (condizioneLogica3) istruzione3; else istruzione4;
Ciuscuna istruzione può essere costituita da un istruzione singola o da un blocco.
Esercizio 0
Studiare, compilare ed eseguire il seguente programma Java. Come si chiama il nome del file in cui deve essere contenuto?
import javax.swing.*; class Somma { public static void main(String[] args) { String stringaInput; int primoNumero, secondoNumero; stringaInput = JOptionPane.showInputDialog("Inserisci il primo numero"); primoNumero = Integer.parseInt(stringaInput); stringaInput = JOptionPane.showInputDialog("Inserisci il secondo numero"); secondoNumero = Integer.parseInt(stringaInput); JOptionPane.showMessageDialog(null, "La somma dei due numeri è " + (primoNumero+secondoNumero)); System.exit(0); } }
Esercizio 1
Dopo aver studiato l'esercizio 0, utilizzando le funzioni di input/output della classe JOptionPane, scrivere la classe Java Radice che chieda all'utente un numero a virgola mobile e restituisca all'utente la sua radice quadrata.
Suggerimenti: usate la funzione Double.parseDouble(String x) per ottenere un double da una stringa di input; utilizzate la funzione Math.sqrt(double x) per ottenere la radice quadrata.
Esercizio 2
Dopo aver svolto l'esercizio 1, scrivere la classe Java EquazioneSecondoGrado che risolva una equazione di secondo grado.
©2005 Roberto Sassi