Introduzione linguaggio
Linguaggio ad oggetti
Java è un linguaggio di programmazione ad oggetti. I linguaggi di programmazione ad oggetti vengono ideati intorno alla fine degli anni 60, il primo linguaggio ad oggetti il Simula-67 serviva per eseguire simulazioni, da qui il nome. Il linguaggio Simula-67 introduce per primo la parola chiave class. Linguaggi comuni precedentemente erano quelli procedurali come PASCAL e C, in cui il programmatore definiva strutture dati e algoritmi (funzioni) che agivano su queste strutture dati.
Alan Key (il padre del linguaggio di programmazione Smalltalk) ha riassunto le 5 caratteristiche base di Smalltalk il primo linguaggio ad oggetti di successo e uno dei linguaggi di ispirazione di Java. Queste caratteristiche rappresentano l'approccio puro alla programmazione ad oggetti:
Ogni cosa è un oggetto. Pensate a un oggetto come a una strana variabile: racchiude un dato ma potete "fare una richiesta" a un oggetto chiedendo di fare operazioni su se stesso. In teoria, potete prendere ogni componente concettuale di un vostro problema che state tentando di risolvere (gatti, costruzioni o servizi) e rappresentarli come oggetti nel vostro programma;
Il programma è un insieme di oggetti che si dicono cosa fare spedendosi dei messaggi: Per fare una richiesta a un oggetto, invii un messaggio a quell'oggetto. Più concretamente possiamo pensare a un messaggio come la chiamata di un metodo che appartiene a quel particolare oggetto;
Ogni oggetto ha uno stato composto da altri oggetti. Detto in un altro modo, possiamo creare un nuovo tipo di oggetto da l'aggregazione di altri oggetti. In questo modo possiamo costruire strutture complesse nei nostri programmi nascondendoli dietro la semplicità degli oggetti;
Ogni oggetto appartiene a un tipo. Ogni oggetto è un' istanza di una classe, detto in altro modo classe e tipo sono sinonimi. La cosa principale che distingue una classe è "quali messaggi gli posso inviare?";
Tutti gli oggetti di un particolare tipo possono ricevere gli stessi messaggi. Vedremo in seguito la profondità di questo concetto. Siccome un oggetto di tipo "cerchio" è anche un oggetto di tipo "forma".
In sintesi si può descrivere un oggetto:
Un oggetto ha uno stato, un comportamento e un' identità.
Facciamo un esempio: abbiamo un oggetto di tipo Lampadina, che ha lo stato possibile acceso o spento e risponde ai messaggi (espone dei metodi) accendi e spegni che modificano lo stato dell'oggetto. Oggetti che hanno medesime caratteristiche appartengono alla stessa classe, ad esempio la classe Lampadina o la classe Studente.
Due oggetti della stessa classe ad esempio Lampadina possono avere stato diverso: una magari è accesa e l'altra spenta, rispondono agli stessi messaggi, accendi e spegni, ma dare risultati diversi in base al loro stato di ogni oggetto.
Ogni cosa in java è un oggetto (vedremo che ci sono anche delle eccezioni - i tipi primitivi). Come si creano gli oggetti in un programma? Dalle classi attraverso l'operazione di new che crea un oggetto dalla classe invocando il metodo costruttore definito nella classe.
Quindi scrivendo il codice in java prima si definiscono le classi di oggetti necessari per descrivere il nostro problema e poi si creano, dalle classi, gli oggetti ognuno con un proprio stato.
Nell'esempio delle lampadine definisco la classe Lampadina ed creo due oggetti lampadina1 e lampadina2.
In Unified Modeling language (UML) descrivo la classe Light
Dichiarazione di variabili e tipi
Un programma manipola dati. Una variabile è usata per contenere un dato che può essere manipolato. Per essere utilizzata, una variabile deve prima essere dichiarata dandogli un nome e specificando anche il tipo.
Una variabile contiene un valore del tipo con cui è stata dichiarata. Es: una variabile di tipo int
può contenere solo valori di tipo interi come 123 ma non di tipo floating point come 11113.21.
Il seguente disegno fa vedere l'utilizzo di variabili di tre tipi: int
, double
e String
. Una variabile di tipo int
immagazzina interi, una variabile di tipo double
numeri decimali e una di tipo String
contiene del testo.
Riprendendo l'esempio precedente la variabile lampadina1 è di tipo Lampadina
(classe definita dal programmatore):
Tipi primitivi
Come detto questi sono le uniche eccezioni al fatto che in java tutto è composto da oggetti.
Type
Contains
Default
Size
Range
boolean
true or false
false
1 bit
NA
char
Unicode character
\u0000
16 bits
\u0000 to \uFFFF
byte
Signed integer
0
8 bits
-128 to 127
short
Signed integer
0
16 bits
-32,768 to 32,767
int
Signed integer
0
32 bits
-2,147,483,648 to 2,147,483,647
long
Signed integer
0
64 bits
-9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
float
IEEE 754 floating point
0.0
32 bits
1.4E-45 to 3.4028235E+380
double
IEEE 754 floating point
0.0
64 bits
4.9E-324 to 1.7976931348623157E+308
La dimensione del tipo non cambia a seconda dell'architettura della macchina, come avviene in C ad esempio, garantendo in questo modo maggiore portabilità del codice.
Importante sapere che per ogni tipo primitivo esiste il corrispettivo tipo wrapper: classe corrispettiva che permette di creare oggetti corrispondenti al tipo primitivo.
Type
Wrapper type
boolean
Boolean
char
Character
byte
Byte
short
Short
int
Integer
long
Long
float
Float
double
Double
Esempio utilizzo tipo primitivo e corrispettivo wrapper:
I tipi wrapper sono classi che incapsulano i tipi primitivi: per istanziare l'oggetto di tipo wrapper (come per ogni oggetto) si utilizza la new che alloca la memoria per l'oggetto di quel tipo (classe). L' operazione di new restituisce l'oggetto e lo associa alla reference (vedremo più avanti in dettaglio i tipi reference).
Con questa immagine vediamo la differenza tra un tipo primitivo e uno reference, come i tipi wrapper.
Utilizzati all'interno di un metodo, i tipi primitivi vengono allocati nell'area di memoria dello stack e la variabile contiene il valore direttamente, i tipi corrispondenti di tipo reference, istanziati con l'operazione di new, sono allocati invece, nell'area di memoria dello heap.
La scelta di mantenere tipi primitivi, utilizzati nelle operazioni matematiche, all'interno del linguaggio Java, è stata giustificata proprio per questioni di performance: allocazione di dati sullo stack è più veloce rispetto alla new
che alloca nell'area di memoria dello heap
, inoltre l'accesso al tipo primitivo è diretto, mentre quello all'oggetto wrapper avviene tramite reference.
I tipi wrapper servono per inserire i dati nelle java collection (Contenitori): tipi di dati astratti come Liste, Stack, Map, Set che servono per lavorare su collezioni di oggetti. Per analogia sulle collezioni, con quanto già visto in C# Commonly Used Collection Types.
Tipi collezioni, strutture dati, fornite con il JDK: classi e interfacce, utili per contenere insieme di oggetti omogenei.
Gli array sono il primo tipo di collezioni che vediamo, una collezione di tipi ordinata, sono la struttura dati più efficiente per mantenere dati sia primitivi che reference a oggetti omogenei ma hanno il limite che una volta creati hanno dimensione fissa.
Per questo, per avere strutture dati che abbiano dimensione variabile in base alle esigenze sono state introdotte nel JDK le java collections (contenitori), un insieme di classi con diverse caratteristiche per tenere gli oggetti che sono stati creati nel programma. Queste classi sono nel package java.util.*
.
Esempio di creazione di un array vs. un java.util.ArrayList
, struttura dati che fa parte delle java collections:
Tipo booleano (boolean type)
The boolean type represents truth values. This type has only two possible values, representing the two Boolean states: on or off, yes or no, true or false. Java reserves the words true
and false
to represent these two Boolean values.
Tipo carattere (Char type)
Escape sequence
Character value
\b
Backspace
\t
Horizontal tab
\n
Newline
\f
Form feed
\r
Carriage return
"
Double quote
'
Single quote
\\
Backslash
\ xxx
The Latin-1 character with the encoding xxx, where xxx is an octal (base 8) number between 000 and 377. The forms \ x and \ xx are also legal, as in \0, but are not recommended because they can cause difficulties in string constants where the escape sequence is followed by a regular digit. This form is generally discouraged in favor of the \uXXXX form.
\u xxxx
The Unicode character with encoding xxxx, where xxxx is four hexadecimal digits. Unicode escapes can appear anywhere in a Java program, not only in character and string literals.
Letterali di Stringhe
Non è uno dei tipi primitivi, infatti in java c'è la classe String, ma siccome le stringhe sono molto utilizzate all'interno dei programmi, Java include una sintassi speciale per includere valori stringa nel codice sorgente.
Tipo intero
byte
, short
, int
e long
E' possibile esprimere il valore anche in notazione ottale ( 0
- zero davanti) ed esadecimale 0x
o 0X
.
Per il tipo long
il valore si esprime mettendo il carattere l
o L
alla fine del numero.
N.B: Dividere un intero per 0
o eseguire l'operazione modulo 0
genera l'errore a runtime java.lang.ArithmeticException.
Ogni tipo intero ha la corrispondente classe wrapper di tipo Byte
, Short
, Integer
e Long
.
Ognuna di queste classi definisce le costanti MIN_VALUE
e MAX_VALUE
che descrivono il range (intervallo) del tipo.
Inoltre queste classi wrapper includono dei metodi statici come java.lang.Byte.parseByte o java.lang.Integer.parseInt per convertire da stringa a tipo primitivo.
ESERCIZIO: Utilizzo di alcune classi standard della JDK per convertire stringhe di input nei numeri corrispondenti come interi.
INPUT STRING
TIPO INPUT
RETURN VALUE INTERO
"495"
decimale
495
"0X1EF"
esadecimale
495
"0757"
ottale
495
Verificare cosa succede se do in input la stringa "pippo" che non corrisponde a nessun numero.
Tipi decimali (Floating-point types)
I valori dei numeri decimali sono di default di tipo double
, per esprimerli come tipo float
aggiungere la lettera f
o F
a fine numero.
I numeri decimali possono anche essere espressi in notazione scientifica, con esponente, in cui il numero è seguito dalla lettera e
o E
(per esponente) e un altro numero. Quest'altro numero rappresenta l'esponente base 10 per il quale il primo numero è moltiplicato:
Valori di tipo float e double espressi in notazione scientifica:
I tipi reference
Mentre una variabile di tipo primitivo contiene un semplice valore, una variabile a un tipo reference, contiene una reference a un oggetto o un array: i tipi reference includono classi, interfacce, enumerativi e array.
Java has three kinds of types:
Primitive type: There are eight primitive types in Java:
byte
,short
,int
,long
,float
,double
,char
, andboolean
. A primitive-type variable holds a simple value.Reference type: Reference types include
class
,interface
,enum
andarray
. A reference-type variable holds a reference to an object or array.A special
null
type, holding a specialnull
reference. It could be assigned to a reference variable that does not reference any object.
A primitive variable holds a primitive value (in this storage). A reference variable holds a reference to an object or array in the heap, or null. A references variable can hold a reference of the type or its sub-type (polymorphism). The value null is assigned to a reference variable after it is declared. A reference is assigned after the instance is constructed. An object (instance) resides in the heap. It must be accessed via a reference.
Java implicitly defines a reference type for each possible array type - one for each of the eight primitive types and an object array.
Finora abbiamo visto esempi di reference creati dalle classi wrapper dei tipi primitivi, classi già definite all'interno del JDK.
Definiamo ora noi una classe con alcuni semplici attributi.
questa classe non fa niente se non contenere dei dati. Se creiamo un oggetto da questa classe, la variabile dataObj
è la reference all'oggetto:
tramite la reference possiamo modificare l'oggetto (modificare il suo stato):
Per esempio:
Vediamo passo passo gli statement eseguiti a livello di memoria.
DOMANDA: Ma la classe DataOnly
è come una struct
in C?
DataOnly
è come una struct
in C?Si, ma quando creo l'oggetto in java con la new
, l'oggetto reference è come un puntatore alla struct
. In java, tranne che per i tipi primitivi, lavoro sempre per reference.
Vedi StructInC-JavaClass per raffronto tra codice in linguaggio C e corrispettivo Java.
ESERCIZIO:
Scrivete il codice relativo all'esempio di sopra - Classe
DataOnly
, eseguite istanziazione di un oggetto della classe e modificate dello stato; Eseguite la print dello stato modificato; Fate l'override del metodotoString()
ereditato da Object per fare una visualizzazione più utile rispetto alla visualizzazione di default.
Altri tipi di reference type, oltre alle classi, sono gli array e i tipi enumerativi.
Assegnamento dei tipi reference
Esempio:
Fin qui sembra tutto semplice, andiamo avanti facendo un po’ di assegnazioni:
Adesso p1 e p2 puntano allo stesso oggetto: abbiamo una condivisione di memoria che può risultare pericolosa se non gestita adeguatamente. Infatti richiamando un metodo setter su uno dei due oggetti (per esempio p1.setNome("Fabio")
) si modificherà l’oggetto condiviso dai due puntatori. Non è sicuramente un dramma (e a volte può essere una cosa utile da sfruttare) ma è comunque una cosa da tenere presente.
N.B: Ora l'oggetto Persona con nome 'Mario' e cognome 'Verdi' non avendo più nessuna reference da cui è puntato, può essere eliminato dal thread, sempre attivo nella JVM, del garbage collector.
Il garbage collector, letteralmente collettore della spazzatura, si occupa di liberare la memoria dello heap degli oggetti non più referenziati all'interno del processo.
Assegnamento tipi reference vs tipi primitivi
Esempio di assegnamenti di tipi primitivi:
valB
contiene intero 5 mentre valA
il valore 6. Nulla di inaspettato in tutto questo.
Vedendo a livello di aree di memoria, durante l'esecuzione del codice:
Ora un esempio di assegnamento tra variabili con tipi reference:
p1.eta
vale 46 come p2.eta
.
Visualizzando a livello di memoria:
Valori di default dei tipi primitivi come attributi di una classe
Quando un tipo primitivo è membro di una classe, se non inizializzato prende i seguenti valori:
Primitive type
Default
boolean
false
char
‘\u0000’ (null)
byte
(byte)0
short
(short)0
int
0
long
0L
float
0.0f
double
0.0d
Questo non è garantito invece per le variabili locali che prendono un valore arbitrario (come in C e C++), è compito del programmatore inizializzarle prima dell'utilizzo altrimenti il compilatore dà errore di compilazione.
Es:
Metodi, argomenti e valori di ritorno
I metodi in java determinano il messaggio che un oggetto può ricevere.
Esempio di chiamata di un metodo:
Esempio con parametro di ritorno:
Il tipo del ritorno deve essere compatibile con il tipo di x. La chiamata di un metodo è spesso detto inviare un messaggio all'oggetto. Nell'esempio precedente, il messaggio è f() e l'oggetto è a. La programmazione ad oggetti è spesso sintetizzata con il concetto di 'spedire un messaggio a un oggetto'.
Passaggio parametri di tipo primitivi vs tipo reference
Vedi: http://www3.ntu.edu.sg/home/ehchua/programming/java/J3c_OOPWrappingUp.html
Paragrafo: 8. Passing Argument into Methods
Passaggio di tipi primitivi come argomento del metodo
Se l'argomento è un tipo primitivo (ad esempio un int o un double) , una copia con un valore identico è creata e passata al metodo. Il metodo lavora sulla copia clonata. Non ha accesso alla variabile originaria e tutte la modifiche all'interno del metodo agiscono sulla copia e non sulla variabile originaria lasciandola quindi inalterata.
Per esempio:
Passaggio di un tipo reference come argomento del metodo
Se l'argomento è di tipo reference (esempio un array o in'istanza di una classe), una copia della reference è creata e passata dentro il metodo. Poichè sia l'oggetto chiamante che il parametro del metodo hanno quindi la stessa reference, se il metodo cambia il valore della variabile membro di un oggetto, i cambiamenti sono visibili anche fuori dal metodo.
Per esempio:
Riassegnamento di una reference dentro un metodo
Poiché la copia della reference è passata all'interno del metodo, se il metodo riassegna un nuovo oggetto alla reference, l'oggetto chiamante e la reference nel metodo non hanno la stessa reference. Cambiamenti all'interno del metodo non si riflettono sull'oggetto chiamante.
Per esempio:
Attributi e metodo statici di una classe
Vedi: http://www3.ntu.edu.sg/home/ehchua/programming/java/J3c_OOPWrappingUp.html
Paragrafo: 1 The "static" Variables and Methods
Anche quando creiamo due oggetti distinti abbiamo una sola zona di memoria per StaticTest.i
: i due oggetti condividono la stessa i
. Esempio:
A questo punto entrambi gli oggetti st1.i
e st2.i
condividono la stessa zona di memoria contenete il valore 47.
A questo punto st1.i
e st2.i
valgono 48.
L'utilizzo del nome della classe è il modo preferito per riferirsi alle variabili static.
Logica simile è utilizzata per i metodi static. La sintassi è ClassName.method( )
. Esempio:
O lo puoi invocare, direttamente dalla classe, così:
Sebbene static quando applicato agli attributi di una classe, cambia il modo in cui il dato è creato (uno per ogni classe rispetto a uno per ogni oggetto per le classi non-static), quando applicato ai metodi, il cambiamento non è così drammatico. Un uso dei metodi static è quello di invocare un metodo senza creare l'oggetto.
Costruzione di un programma
Vedi progetto IntroduzioneJava per esempi di semplici classi, script di compilazione ed esecuzione.
Visibilità dei nomi
Organizzare il sorgente in package.
La struttura di directory e sottodirectory deve rispettare il nome del package.
Se abbiamo la seguente classe:
il codice sarà in un file nella directory: it/isisgallarate/base/TestSimpleClass.java
Se per una questione di ordine, nel progetto mettiamo il codice sorgente nella directory src
, per compilare, eseguiamo da prompt il seguente comando:
javac -d target src\it\isisgallarate\base\TestSimpleClass.java
Il codice sorgente .java viene compilato creando i file .class (bytecode) nella directory di nome target perché abbiamo specificato l'opzione -d (opzione che sta per directory di destinazione).
Per eseguire il programma:
java -classpath target it.isisgallarate.base.TestSimpleClass
DA NOTARE senza specificare l'opzione -classpath, non trova la classe:
Utilizzo di altri componenti
Per importare la definizione di una classe utilizzare import.
oppure
Per le classi del JDK questo basta, siamo sicuri che il nostro codice potrà essere compilato (javac) ed eseguito (java).
Se utilizziamo librerie esterne al JDK queste devono essere visibili: l'opzione -classpath
al comando javac e java permette di specificare il path di queste librerie.
Costruzione del primo programma
Uguaglianza per riferimento vs. Uguaglianza per valore
La discussione seguente riguarda i tipi reference, per i tipi primitivi l'unica tipo di uguaglianza è per valore e si verifica tramite l'operatore ==
.
Mentre per i tipi reference, se utilizzo l'operatore ==
verifico l'uguaglianza per riferimento, cioè se le due reference si riferiscono alla stessa zona di memoria.
Nell'esempio della figura sopra p1 e p2 sono uguali per riferimento perché condividono la stessa zona di memoria, mentre p3 e p4 non sono uguali per riferimento anche se come oggetti contengono lo stesso valore.
In caso che due oggetti sono uguali per reference è automatico che siano anche uguali per valore, riferendosi alla stessa zona di memoria. Non è detto il contrario come si vede nell'esempio sopra: p3 e p4 riferiscono a due oggetti distinti di tipo Persona
ma hanno identico valore della proprietà eta, quindi sarebbero uguali come valori.
Il metodo utilizzato per verificare l'uguaglianza per valore è equals
della classe Object
: la classe Object
, classe padre di tutte le classi ha il metodo boolean equals(Object obj)
che può essere utilizzato, se rimplementato (overridding nel gergo della programmazione ad oggetti) nelle nostre classi, per eseguire l'uguaglianza per valore.
Di default anche il metodo equals
di Object
esegue ugualianza per riferimento.
Vediamo esempio, che utilizza il metodo equals
di Object
, e vediamo che l'implementazione di base, esegue l'uguaglianza per riferimento:
Qualsiasi classe da noi creata, in quanto implicitamente sottoclasse di Object
erediterebbe lo stesso comportamento del metodo equals
. E' quindi necessario (compito del programmatore) per ottenere un confronto tra valori, reimplementare equals
per restituire true
, quando i valori sono uguali.
Esempio della classe Vino
in cui c'è l'override del metodo equals
per eseguire il confronto sui valori:
L'implementazione di equals
nell'esempio esegue il confronto sugli attributi nome
e anno
e non sull'attributo prezzo
(due vini con lo stesso nome e annatta ma prezzi diversi risulteranno uguali per valore).
ESERCIZIO
Verifica il comportamento del metodo equals
per i tipi wrapper dei tipi primitivi (Integer
, Boolean
,....) e per la classe String
costruendo dei programmi che mostrino il comportamento.
APPROFONDIMENTO
Vedi anche: https://codingjam.it/tutorial-java-il-metodo-equals-e-loperatore/ per ulteriore spiegazione.
Array
Supponete che volete calcolare la media di una classe di 30 studenti, voi certamente non volete creare 30 variabili, mark1
, mark2
...mark30
, per contenere i voti. Invece potete usare una singola variabile chiamata array per contenere i 30 elementi.
Un array è una collezione ordinata di elementi dello stesso tipo, identificata da una coppia di parentesi quadre []. Per usare un array dovete:
Dichiarare un array con un nome e un tipo. Usa i nomi plurali per gli array, es:
marks
,rows
,numbers
. Tutti gli elementi appartengono allo stesso tipo.Allocare l'array usando l'operatore new o tramite l'inizializzazione, es:
Quando un array è creato tramite l'operatore new, tutti gli elementi sono inizializzati con i loro valori di default, ad esempio 0 per int, 0.0 per i double, false per boolean, e null per gli oggetti. [Diversamente dal C/C++ che non inizializza il contenuto degli array].
Indice array
Array length
Per paragone con il linguaggio C: Array length in C
Stringhe
http://www3.ntu.edu.sg/home/ehchua/programming/java/J3d_String.html
RISORSE
ESERCIZI:
Verificate che il metodo
equals
è stato reimplementato nelle classiString
e dei tipi wrapper dei tipi primitivi (Integer
,Float
,Double
...), costruendo dei programmi di esempio. Compilateli ed eseguiteli (fate dei semplici file .bat per eseguire le due operazioni).Definite due classi con diverso nome ma praticamente uguali come attributi, l'unica differenza è che in una il metodo
equals
è reimplementato. Implementate quindi in una classe il metodoequals
per fare il confronto sui valori, mentre nell'altra vi basate sull'implementazione ereditata dalla classeObject
. Fate vedere il diverso comportamento di oggetti creati da queste due classi.Dato l'esempio con classi
Enoteca
eCassaDiVini
, all'inizio avete utilizzato la struttura array per contenere l'elenco delle casse; fare la prova a utilizzare invece la classejava.util.Vector
.Utilizzare i metodi
contains
eindexOf
della classejava.util.Vector
per cercare se c'è una certo tipo di cassa di vini.
Last updated