15. Le date in Java
Andrea Pastore 19/03/2020 0
Le date in Java sono memorizzate in oggetti appositi, che contengono il numero di secondi dal 01/01/1970. Ovviamente le date possono essere formattate in vari formati, ad esempio quello italiano che è gg/mm/aaaa, oppure quello inglese che è aaaa/mm/gg. Java mette a disposizione la classe Date per rappresentare le date e la classe Calendar per recuperare una data. Vediamo un esempio:
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone( "Europe/Rome"),Locale.ITALY);
La creazione di un oggetto di tipo Calendar è un po’ particolare: viene invocato il metodo getInstance come mostrato sopra, che prende in input la zona temporale che ci interessa e il formato della data. Fatto questo è possibile recuperare le informazioni sulla data, con la riga di codice sottostante:
Date dataOggi = calendar.getTime();
Stampare una data
Per stampare una data dobbiamo creare un oggetto di tipo DateFormat e poi invocare il suo metodo format, passandogli come parametro la data.
DateFormat dateFormat = DateFormat.getDateInstance (DateFormat.SHORT, Locale.ITALY);
System.out.println("data oggi: "+dateFormat.format(dataOggi));
Il metodo getDateInstance della classe DateFormat prende in input come primo parametro il tipo di visualizzazione, nel nostro caso DateFormat.SHORT, che indica la stampa classica della data, ovvero nel formato gg/mm/aaaa. Ovviamente il formato è dato anche dal secondo parametro: se avessimo scritto:
DateFormat dateFormat = DateFormat.getDateInstance (DateFormat.SHORT, Locale.ENGLISH );
System.out.println("data oggi: "+dateFormat.format(dataOggi));
Avremmo ottenuto una stampa nel formato inglese ovvero mm/gg/aa.
Creare una data da una stringa
Possiamo creare un oggetto di tipo date a partire da una stringa, grazie alla classe SimpleDateFormat, ad esempio:
String s = "01/01/2019";
Date initDate = new SimpleDateFormat("dd/MM/yyyy").parse (s);
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
System.out.println(formatter.format(initDate));
Il costruttore di SimpleDateFormat prende in input il formato della data in input, nel nostro caso abbiamo scritto dd/mm/yyyy. Questo codice, però, può provocare un’eccezione se la data inserita in input non è corretta. Per questo motivo dobbiamo gestirlo usando il costrutto try catch:
try {
String s = "01/01/2019";
Date initDate = new SimpleDateFormat("dd/MM/yyyy").parse (s);
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
System.out.println(formatter.format(initDate));
} catch (ParseException e) {
e.printStackTrace();
}
Potrebbero interessarti anche...
Andrea Pastore 19/03/2020
6. Il costrutto switch
Ci sono casi in cui per prendere una decisione dobbiamo prendere in esame mole opzioni. Ad esempio supponiamo di dover gestire un livello di allarme: abbiamo una variabile coloreAllarme che può assumere i seguenti valori: rosso, arancione, giallo, verde. Se il colore è rosso stampiamo la frase “allerta critica”, se il colore è arancione stampiamo la frase “stato di allerta”, se il colore è giallo stampiamo “stato di pre allerta”, se è verde stampiamo “situazione tranquilla”. Il codice potrebbe essere questo:
if(coloreAllarme.equals("rosso")){
System.out.println("allerta critica");
}
else if(coloreAllarme.equals("arancione")) {
System.out.println("stato di allerta");
}
else if(coloreAllarme.equals("giallo")){
System.out.println("stato di pre-allerta");
}
else {
System.out.println("situazione tranquilla");
}
Quando però abbiamo tutte queste combinazioni possiamo usare il costrutto swithc, che consente di fare la stessa cosa, ma con meno righe di codice:
switch(coloreAllarme){
case "rosso":
System.out.println("Allerta critica");
break;
case "arancione":
System.out.println("stato di allerta");
break;
case "giallo":
System.out.println("stato di pre-allerta");
break;
case "verde":
System.out.println("situazione tranquilla");
break;
}
Il break serve per terminare le operazioni da eseguire con quella casistica. Se non viene inserito verranno eseguite anche le operazioni della casistiche successive fino a quando non si trova un break.
In caso di variabili numeriche non è necessario mettere le virgolette: ad esempio supponiamo di avere questo codice che a seconda del valore della variabile numeroPersone stampa se sono troppe, giuste, poche o troppo poche:
switch(numeroPersone){
case 2:
System.out.println("Troppo poche");
break;
case 3:
System.out.println("poche");
break;
case 4:
System.out.println("giuste");
break;
case 5:
System.out.println("troppe");
break;
default :
System.out.println("Nessuna opzione è stata selezionata");
}
la parola chiave default indica l’operazione da fare quando la variabile inserita nello switch non corrisponde a nessuno dei casi presenti.
Andrea Pastore 19/03/2020
12. Polimorfismo in Java
Alcuni linguaggi di programmazione consentono di decidere il comportamento dei metodi e costruttori in base al numero di variabili che vengono passate loro come parametro. Supponiamo ad esempio di avere una classe Java chiamata Utente, con tre variabili (id, nome, cognome) e due costruttori: il primo prenderà in input solo una variabile id, il secondo prenderà in input una variabile id, una variabile nome e una variabile cognome.
I due costruttori in java saranno i seguenti
public Utente(int id) {
this.id = id;
this.nome = “”;
this.cognome = “”;
this.email = “”;
}
public Utente(int id, String nome, String cognome) {
this.id = id;
this.nome = nome;
this.cognome = cognome;
}
Quando noi invochiamo il costruttore a seconda del numero di parametri che inseriamo Java userà il costruttore che combacia con i parametri inseriti ed eseguirà il codice contenuto all’interno. Java darà un errore solo se non trova un costruttore con il numeor di parametri da noi specificato. Ad esempio se scriviamo:
Utente utente = new Utente(3,”Mario”,”Rossi”,”mario@gmail.com”);
Otterremo un errore, perché non abbiamo definito un costruttore che prende in input quattro parametri. Tutto quello che abbiamo detto per i costruttori è valido anche per i metodi (del resto il costruttore non è altro che un metodo speciale). Prendiamo in esame il seguente metodo che stampa tutti i dati dell’utente:
public void stampaDati() {
System.out.println(this.id + “ ” + this.nome + “ ” + this.cognome + “ ” +this.email);
}
Come per i costruttori possiamo creare un altro metodo che ha lo stesso nome, ma prende in input un parametro per stabilire come devono essere stampati questi elementi. Stabiliamo due tipologie di stampa: essenziale che stampa solo nome e cognome, completa che stampa tutto. Se il parametro passato in input non corrisponde a nessuna di queste due cose allora questo metodo invoca a sua volta il metodo stampaDati senza parametri, che stamperà i dati senza formattazione.
public void stampaDati(String tipoVisualizzazione) {
if(tipoVisualizzazione.equals("essenziale")) {
System.out.println("Nome:"+this.nome+"; Cognome:"+this.cognome);
}
else if (tipoVisualizzazione.equals("completa")){
System.out.println("Id: "+this.id + "; Nome:"+this.nome+"; Cognome:"+this.cognome+"; Email:"+this.email);
}
else {
stampaDati();
}
}
Ereditarietà
L’ereditarietà è una caratteristica di molti linguaggi di programmazione moderni che consente di ottimizzare il codice scritto ed evitare ripetizioni. Ci sono casi in cui dobbiamo creare classi molto simili tra loro. Per questo molti linguaggi di programmazione consentono di usare il meccanismo dell’ereditarietà. Supponiamo di avere bisogno di questa due classi:
- Utente
id, nome, cognome, email,telefono - Fornitore
id, nome, cognome, email,telefono,partitaIva
notiamo che Fornitore è quasi uguale alla classe Utente, quindi non ha senso riscriverla. Quando creiamo la classe Fornitore che estenderà la classe Utente. Scriviamo le due classi per renderci conto del codice e delle differenze tra le due. Qui sotto vediamo la classe utente:
public class Utente {
private int id ;
private String nome;
private String cognome;
private String email;
private String telefono;
public Utente(int id, String nome, String cognome, String email, String telefono) {
this.id = id;
this.nome = nome;
this.cognome = cognome;
this.email = email;
this.telefono = telefono;
}
// Getter & setter
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
… tutti gli altri getter e setter
}
Vediamo ora la classe Fornitore. Come abbiamo capito Java ci consente di evitare di ricopiare la classe, basta dichiarare, al momento della creazione, che la nuova classe estende la classe Utente. Questo avviene con la parola chiave extends.
public class Fornitore extends Utente {
private String partitaIva;
public Fornitore(String partitaIva, int id, String nome, String cognome, String email, String telefono) {
super(id, nome, cognome, email, telefono, via, citta, cap);
this.partitaIva = partitaIva;
}
public String getPartitaIva() {
return partitaIva;
}
public void setPartitaIva(String partitaIva) {
this.partitaIva = partitaIva;
}
}
Notiamo alcune differenze con la classe Utente scritta sopra: innanzitutto vediamo che nel costruttore c’è la parola chiave super, seguita da una serie di parametri. Si tratta dei parametri del costruttore della classe Utente, che viene richiamato con il comando super. Ogni volta che noi estendiamo una classe il costruttore della nuova classe deve richiamare il costruttore della classe che estende. Fornitore estende la classe Utente, quindi il suo costruttore prende in input anche i parametri che richiede il costruttore di Utente, che invocherà con il metodo super().
Nota: quando una classe estende un’altra classe, la classe estesa è definita superclasse
Le interfacce in Java
Vediamo ora un altro concetto fondamentale della programmazione: le interfacce, che servono per standardizzare il codice.
Interfacce
Le interfacce vengono utilizzate per definire un tipo di dato e una classe, che però non viene implementato. Vengono definite solo le firme dei metodi che poi saranno implementate in altre classi. Vediamo un esempio di interfaccia:
public interface Libro {
public void stampaLibro();
public void setTitolo(String titolo);
public String getTitolo();
public void setNumeroPagine(int numeroPagine);
public int getNumeroPagine();
}
Per usare una determinata interfaccia dopo il nome della classe dobbiamo usare la parola chiave implements seguita dal nome dell'interfaccia. Appena facciamo questo il nostro ide ci segnalerà che i metodi dell'interfaccia non sono ancora implementati, clicchiamo su add unimplementated methods (potrebbe cambiare a seconda dell’ide usato) e il nostro ide li aggiungerà per noi, dobbiamo solo completarli con il codice all'interno.
In sostanza le interfacce consentono di standardizzare il codice: dueprogrammatori si accordano sui metodi e su come vengono chiamati, e poiai fini dell'interoperabilità delle classi non è importante come vieneimplementato. Nel tempo le implementazioni possono cambiare, ma rispettandole interfacce non ci saranno problemi di incompatibilità
Il paradigma MVC
La programmazione ad oggetti è utilizzata nel paradigma MVC (MODEL, VIEW, CONTROLLER), che consente una separazione delle logiche. Vediamo i tre elementi nel dettaglio:
- model
sono le classi cosi come le abbiamo viste. Quando creiamo una classe infatti definiamo degli oggetti ma non li usiamo in quell’istante - view
le viste rappresentano la parte del progetto che mostra i dati visivamente - controller
sono i file che consentono di rendere permanenti le operazioni sugli oggetti: creazione di nuovi elementi, modifica di elementi esistenti, eliminazione di elementi e anche recupero dei dati di un elemento. I controller nascondono le logiche sottostanti alle altre classi: potremmo voler salvare i dati in un file oppure nel database, per le classi esterne viene chiamato lo stesso metodo. Facendo passare tutte le operazioni di salvataggio, modifica, caricamento per il controller avremo solo una classe che gestisce queste operazioni, che si traduce in un vantaggio per la manutenibilità del codice, in quanto per apportare modifiche alla gestione degli oggetti dovremmo modificare solo la classe relativa.
Andrea Pastore 19/03/2020
10. Strutture di dati
Finora abbiamo visto solo tipi di dati semplici: int, double, boolean e cosi via. Tuttavia i programmi reali devono elaborare grandi quantità di dati, e sarebbe scomodo e limitante memorizzarli uno per volta. Per questo motivo già dagli anni Ottanta sono state create le strutture di dati, tipi di variabili più elaborati che possono contenere al loro interno delle variabili.
Gli array
La struttura dati più semplice è l’array, che consente di memorizzare un numero stabilito di elementi di un determinato tipo. Ad esempio la riga sottostante crea un array di 10 numeri interi
int[] arrayInt = new int[10];
se non conosciamo la dimensione dell’array (perché magari ci è stato passato in input da un programma che non abbiamo realizzato noi) possiamo recuperarla con la proprietà length:
int arraySize = arrayInt.length;
Se sono noti tutti gli elementi che dobbiamo inserire nell’array possiamo anche inizializzarlo in questo modo:
int[] array2 = {2, 5, 6, 7, 11};
Essendo l’array un contenitore di variabili, quando lo utilizziamo dobbiamo specificare a quale variabile vogliamo accedere, ad esempio la seguente riga di codice:
System.out.println(arrayInt[5]);
Stampa l’elemento alla posizione 5 dell’array.
Nota: la prima posizione di un array non è 1, ma 0; quindi stampare la quinta posizione significa in realtà stampare il sesto elemento dell’array.
Frequentemente capita di dover elaborare in sequenza tutti gli elementi di un array, in questo caso possiamo utilizzare un ciclo for. Ad esempio il ciclo for sottostante inizializza tutti gli elementi dell’array creato precedentemente al valore 5.
for(int i=0;i<arraySize;i++) {
arrayInt[i] = 5;
}
Notiamo che nel ciclo abbiamo usato una variabile arraySize dove è memorizzata la dimensione dell’array. Questa variabile è stata creata qualche riga sopra. È anche possibile scrivere il ciclo for in questo modo:
for(int i=0;i<arrayInt.length;i++) {
arrayInt[i] = 5;
}
in questo modo evitiamo di scrivere la variabile esterna, ma stiamo sprecando tempo di calcolo, perché ad ogni ciclo Java dovrà calcolare la dimensione dell’array.