Introduzione a Java - unibo.itlia.deis.unibo.it/Courses/sot/esercitazioni/es-java/java-intro.pdf ·...

Post on 03-Jun-2020

16 views 0 download

Transcript of Introduzione a Java - unibo.itlia.deis.unibo.it/Courses/sot/esercitazioni/es-java/java-intro.pdf ·...

Introduzione a Java

Concetti base Eclipse, ereditarietà e interfacce

Il problema del progetto •  I linguaggi classici (C, Pascal, …)

❒  perfetti per problemi “piccoli”, facilmente scomponibili in funzioni di base (es: registratore di cassa, ecc…)

❒  non offrono strumenti mentali adatti ad affrontare la progettazione di sistemi software complessi (es: gestionali di magazzini, ospedali, ecc…).

•  L’uomo non è portato a pensare il mondo in termini di funzioni… semmai di oggetti che svolgono funzioni.

Cos’è Java?

•  Linguaggio di programmazione: ❒  di alto livello ❒  orientato agli oggetti ❒  progettato per essere il più possibile indipendente dal SO

❒  … e per semplificare l’attività di progetto.

Architettura run-time delle applicazioni java

•  Un’ applicazione Java è strutturata come un insieme di componenti. ❒  Alcuni sono statici (vengono caricati

prima dell’inizio del programma ed esistono per tutta la durata dello stesso)

❒  Altri sono dinamici (creati durante l’esecuzione, al momento del bisogno)

•  Poichè un’ applicazione deve avere un punto di partenza prestabilito, uno dei componeneti statici contiene il main

Classi

Oggetti

main

Inversione del punto di vista •  Questo appoccio porta a invertire il punto di vista:

❒  l’enfasi non è più sull’operazione svolta (da qualcuno) ❒  ma sull’entità che la svolge

•  In C l’enfasi è sull’OPERAZIONE, il “chi” la svolge è un dettaglio. (VISIONE PROCEDURALE)

printf(“Hello!”);!

•  In Java (APPROCCIO A OGGETTI) il punto di vista si inverte: l’enfasi si sposta su CHI offre il servizio.

•  Per esprimere questo cambiamento si usa la notazione puntata: component.operation(param); !

System.out.println(“Hello!”);!•  System.out è il componente, println è

l’operazione (metodo)

il main dal C al Java file myprogram.c

file MyProgram.java

int main(int argc, char* argv[]){ …}!!

public class MyProgram{! public static void main(String[] args){! …! }!}!

in Java il main deve essere sempre scritto dentro una classe “public”

per convenzione ogni classe deve esser definita dentro un file con lo stesso nome

Le classi devono avere l’iniziale maiuscola

Compilazione: dal mondo C…

Programma sorgente C

(testo)

Compilatore C e linker alle librerie -$ gcc myprogram.c –o program.exe

stdio.h string.h

program.exe è: •  scritto in linguaggio macchina

(illeggibile dall’uomo) •  eseguibile dal SO su cui l’abbiamo compilato: -$ ./program.exe arg1 arg2…

Librerie

Hardware S. O.

myprogram.c

program.exe

Compilazione: … al nuovo mondo: Java

Programma sorgente Java (testo)

Java Compiler -$ javac MyProgram.java!

HW

MyProgram.java

HW SO

HW SO

Java Virtual Machine (JVM)

SO

MyProgram.class è: •  scritto in bytecode (illeggibile dall’uomo) •  eseguibile dall’infrastruttura Java, indipendentemente dal SO! !-$ java MyProgram arg1 arg2 …!

consegnamo il .class all’esame? …NOOO!

Esempio 1 – Hello Pinco Pallino!

•  Creare un’applicazione java che prenda in ingresso due parametri: ❒  <nome cognome> di chi esegue il programma

•  Stampi a video: Hello <nome cognome> !!!

Esempio 1 – Soluzione dal C al Java file myprogram.c

file MyProgram.java

include <stdio.h>!int main(int argc, char* argv[]){! printf(“Hello %s!!!“, argv[1]);!}!!

public class MyProgram{! public static void main(String[] args){! System.out.println(“Hello ”+args[0]+”!!!”);! }!}! 0 è il primo parametro, non il

nome del programma, come in C

Semplifichiamoci la vita …e usiamo Eclipse! (www.eclipse.org) •  Creare un nuovo progetto:

❒  tasto dx su Package Explore ❒  New -> Java Project ❒  Inserire il nome (es: MyProject) e Finish

•  Creare una nuova classe: ❒  tasto dx sul nome del progetto ❒  New -> Class ❒  Inserire il nome (es: MyProgram) e Finish

•  Eclipse crea già la nostra prima classe public class MyProgram{ … }!

com’è fatta la cartella del progetto che Eclipse ci ha creato?

Compilazione e esecuzione con Eclipse

•  compiliamo: Project -> Build All ❒  Eclipse fa per noi: -$ javac MyProgram.java!

•  eseguiamo: ❒  senza parametri:

•  tasto dx sul .java che contiene il main -> Run as -> Java Application ❒  …ma noi dobbiamo eseguire passando dei parametri:

•  Run -> Run Configurations… -> doppio click su Java Application •  indichiamo il nome della classe che contiene il main •  tab Arguments -> scrivo i miei parametri separati da spazi in

Program Arguments ❒  Eclipse fa: !-$ java MyProgram.class “Daniela Loreti” !

cosa succede nella cartella del progetto? cosa leggo aprendo il .class?!

I tipi primitivi in Java (1/2) •  char (2 byte) UNICODE •  boolean (1bit) true/false

❒  boolean è un tipo autonomo. Non si convertono boolean in interi, neanche con un cast.

❒  le espressioni relazionali e logiche (es: if (x>2) …) danno come risultato un boolean, (non un int come in c)

Interi: •  byte (1byte) -128…+127 •  short (2byte) -32768…+32768 •  int (4byte) -2x109…+2x109 •  long (8byte) -9x1018…+9x1018

I tipi primitivi in Java (2/2) Reali: •  float (4byte) -1045…+1045(6-7 cifre sign.) •  double (8byte) -10328…+10328(14-15 cifre sign.) Esempio di guai: •  float f = Math.sin(Math.PI/3);!

Errore di compilazione: “Possible loss of precision, found float, required double” oppure “Type mismatch: cannot convert from double to float”

•  float f = (float) Math.sin(Math.PI/3);!CAST ESPLICITO. Compilazione OK! Il progettista si assume esplicitamente la responsabilità della perdita di precisione.

Esempio 2 – Il contatore Creare un’applicazione java che simuli un conta-persone. •  Quando occorre (es: fine giornata) deve poter essere

resettato. •  Chiunque lo richieda deve poter sapere quante persone

sono passate. Comportamento osservabile: è un componente caratterizzato da:

❒  un valore (intero variabile nel tempo) ❒  e tre operazioni: reset() ; inc() ; getStato()

Vorrei creare un componente riutilizzabile da diversi main

stato inaccessibile dall’esterno del file. Così nessuno potrà mai usare male il mio contatore nel suo main!

Il contatore in C

file mycounter.h void reset(void);!void inc(void);!void getStato(void);!!

esempio di file mymain.c #include <stdio.h>!#include “mycounter.h”!int main(int argc, char* argv[]){! reset(); inc(); int v = getStato();! printf(“il contatore attualmente vale %d”,v); !}!!

file mycounter.c static int stato; !void reset(void){stato=0};!void inc(void){stato++;};!void getStato(void){return stato};!!

Come faccio in C?

public class MyProgram{! public static void main(String[] args){! Contatore c = new Contatore();! c.reset(); c.inc(); int val = c.getStato();! System.out.println(“il contatore vale: ”+val);! }!}!

public class Contatore{! private int stato = 0;! public void reset(){stato=0;}! public void inc(){stato++;}! public int getStato(){return stato;} !}!!

Come faccio in Java? Il contatore in Java

esempio di file MyProgram.java

file Contatore.java

stato inaccessibile dall’esterno della classe

costruttore di default

attributo

metodi

è molto più semplice stampare un valore

E se volessi usare contemporaneamente due contatori? In C mi servirebbero due variabili stato => Mi servirebbe poter importare 2 volte nel main il file mycounter.c. ma non lo posso fare …qualcosa mi suggerisce che ho sbagliato tutto! forse …Avrei dovuto usare typedef!

int stato; !void reset(void){stato=0};!void inc(void){stato++;};!void getStato(void){return stato};!!

In Java: public class MyProgram{! public static void main(String[] args){! Contatore c1 = new Contatore();! Contatore c2 = new Contatore();! c1.reset(); c1.inc(); int val1 = c1.getStato();! c2.reset(); c2.inc(); int val2 = c2.getStato();! System.out.println(“il contatore 1 vale: ”+val1);! System.out.println(“il contatore 2 vale: ”+val2);! }!}!

esempio di file MyProgram.java

E se volessi usare contemporaneamente due contatori?

Ah! lo posso fare due

volte?!

Il Contatore non è solo un insieme di funzioni e variabili. E’ un TIPO DI DATO!

allora lo posso fare anche in C (1/2)

esempio di file mymain.c #include <stdio.h>!#include “mycounter.h”!int main(int argc, char* argv[]){! int v1, v2; contatore c1, c2;! reset(&c1); reset(&c2); inc(&c1); v1=getStato(c1);! printf(“il contatore 1 attualmente vale %d”, v1); …!}!!

file mycounter.c #include “mycounter.h”!void reset(contatore* pc){*pc=0};!void inc(contatore* pc){(*pc)++;};!void getStato(contatore c){return c};!!

typedef int contatore; !…!!

file mycounter.h

allora lo posso fare anche in C (2/2)

Definire un tipo di dato in C tramite typedef è possibile, ma: •  non c’è unitarietà tra parte-dati (espressa con typedef

nel mycounter.h) e parte-operazioni (scritte successivamente e altrove, ad es. nel mycounter.c)

•  non c’è protezione dall’uso improprio, perchè tutti vedono typedef e dunque tutti possono aggirarla

•  le signature delle operazioni fanno trasparire dettagli (I puntatori…) che non dovrebbero entrare in gioco a questo livello!

=> il livello di espressività di C non è adeguato!

Oggetti Java come istanze di Classi

Gli oggetti java sono componenti sw DINAMICI, creati a immagine e somiglianza di una classe •  si possono creare tutte le istanze che servono •  la creazione avviene run-time al momento del

bisogno

OGGETTI Contatore, ISTANZE della classe Contatore!

classe String •  In java le stringhe non sono più array di

caratteri. Sono OGGETTI, istanze della CLASSE String!

•  Java lo sottolinea impedendo di applicare gli operatori classici degli array, come []: per selezionare il carattere i-esimo si usa charAt!String s = “pippo”;!char c = s.charAt(i); //c=“i”!!

•  ci sono tantissime operazioni invocabili sulle stringhe => usare il suggest di Eclipse: ❒  nell’esempio precedente, dopo aver digitato “s.”

provare la combinazione ctrl+Space)

Contatore con 2 costruttori public class Contatore{! private int stato;! public Contatore(){stato = 0;};! public Contatore(int val){stato = val};! public void reset(){stato=0;}! public void inc(){stato++;}! public int getStato(){return stato;} !}!!

file Contatore.java

public class MyProgram{! public static void main(String[] args){! Contatore c1 = new Contatore();! Contatore c2 = new Contatore(10);…! }!}!

esempio di file MyProgram.java

Chiama il secondo costruttore. c2 è inizializzato a 10

i costruttori sono metodi particolari che dicono come devono essere inizializzati gli oggetti

la keyword this

public class Contatore{! private int stato;! public Contatore(){};! public Contatore (int stato){this.stato = stato};! public void reset(){stato=0;}! public void inc(){stato++;}! public int getStato(){return stato;} !}!!

file Contatore.java

denota l’oggetto corrente. di norma sottintesa, ma può essere specificata se occorre.

Oppure:

Riferimenti •  in C si possono definire, per ciascun tipo:

❒  sia variabili (es: int x;) ❒  sia puntatore (es: int* px;)

•  in Java il linguaggio impone due scelte: ❒  varabili per tipi primitivi

(es: int x;) passaggio di parametri per VALORE

❒  riferimenti per gli oggetti (es: Contatore c;). passaggio per RIFERIMENTO

Riferimenti: cosa ci si può fare •  Definirli senza inizializzarli Contatore c; •  Assegnare loro la costante null c=null;!

questo riferimento non punta a nulla •  Usarli per creare nuovi oggetti

! ! ! ! !c = new Contatore();!•  Assegnarli uno all’altro:

!Contatore c2 = c;! c2 adesso referenzia lo stesso oggetto di c •  Confrontarli: c == c2 !

è vera se puntano allo stesso oggetto, NON se hanno lo stesso valore!

c2

c

oggetto Contatore

gli array di java Gli array in java sono oggetti, istanze di una classe speciale denotata da [] Quindi come per ogni oggetto: •  prima di definisce un riferimento

int[] v;!Contatore[] w; !

•  poi si crea dinamicamente l’istanza v = new int[3];!w = new Contatore[6]; !

gli array di java nel primo caso ogni elemento dell’array è una variabile normale, tipo primitivo:

v = new int[3]; v[0]=4; v[2]=34;!!

nel secondo caso ogni elemento dell’array è un riferimento a oggetto: w = new Contatore[6];! w[0] = new Contatore(10); !!

!

4 34 v

w 10

Ereditarietà Spesso capita di aver bisogno di un componente simile a uno già esistente, ma non identico. es: voglio un contatore che sappia anche decrementare! public class ContatoreDec extends Contatore {! public void dec(){stato--;}!}!!public class Contatore{! protected int stato;! public Contatore(){}; …!}!

file Contatore.java

file ContatoreDec.java

c2=new ContatoreDec(30);!c2.inc(); !c2.dec();!!

MyProgram.java

con private la variabile stato non sarebbe visibile in ContatoreDec

Posso invocare tutti I metodi public di Contatore e ContatoreDec!

In Java tutte le classi ereditano implicitamente da una classe di sistema: Object. Da essa ereditano alcuni metodi di default: •  boolean equals(Object o);!c1.equals(c2); equivalente a (c1==c2). restituisce true se sono = i riferimenti. •  String toString();!System.out.println(“c=”+c); c=Contatore@38da9246!System.out.println(“c=”+c.getStato()); c=24!

Ereditarietà – tutto è Object!

implicitam. chiamato Contatore.toString()

Integer.toString()

oggetto Contatore

c2

c1

ma questi metodi di Object possono essere ridefiniti. basta aggiungerli tra i metodi del Contatore: public class Contatore{ …! public boolean equals(Object obj){! if (obj instanceof Contatore) ! return ((Contatore)obj).getStato() == stato;! else return false;! }! public String toString(){! return “Questo contatore vale ”+this.stato;! }!}!

Ereditarietà – tutto è Object!

System.out.println(“c=”+c); ! ora stampa: c=Questo contatore vale 24

Lo controllo perchè Object obj potrebbe esser qualunque cosa

Classi astratte •  Moltissime entità che usiamo per descrivere il mondo

non sono reali, ma sono utili per esprimersi. •  es: Gli Animali

❒  parlare di “animali” ci è molto utile, ma a ben pensarci non esiste il generico animale!

❒  nella realtà esistono solo animali specifici: “gatto”, “cane”, “serpente” …

•  per questo esistono le classi astratte! Sono come le classi normali, ma da qualche parte esprimiamo la convenzione di non crearne mai istanze ❒  public abstract class Animale{ … }!❒  ci posso metter dentro metodi e attributi comuni agli animali

Classi astratte …esempio concreto public abstract class Animale{! private String mioNome;! protected String verso;! protected Animale(String s){mioNome=s;}! public abstract String nomeComune();! public abstract String siMuove();! public abstract String doveVive();! public void mostra(){! System.out.println(mioNome+”: è un “ +nomeComune()+”, si muove ”+siMuove()+” e vive ” +doveVive());! }!}!

l’attributo mioNome ce l’hanno tutti! Cambia il suo valore!

metodi astratti

mostra() usa i metodi astratti anche se nessuno ha ancora definito cosa fanno. POLIMORFISMO

Ereditarità e classi astratte

•  Chiunque estenda una classe (normale o astratta) vedrà i suoi metodi e attributi protected e public.

•  Posso ereditare a cascata da classi (normali o astratte) -> le classificazioni sono spesso ad albero.

•  Java NON supporta l’ereditarietà multipla -> posso estendere una sola classe

Altro esempio •  Voglio modellare le classi Studente e Lavoratore.

•  Presumibilmente le farò estendere una classe Persona!

•  ma come modello uno StudenteLavoratore?

Interfacce •  Certe volte mi fa proprio comodo definire solo le operazioni

che una certa classe di oggetti può compiere. •  spesso occorre ereditarietà multipla …ma senza mescolare le

implementazioni!!! public interface IStudente{public int getMatricola();}!public interface ILavoratore{! public int getStipendio();!}!public class Studente implements IStudente{…}!public class Lavoratore implements ILavoratore{…}!public class StudenteLavoratore !

! ! !implements IStudente, ILavoratore{…}!!!Devo definire le operazioni di tutte le interfacce che implemento!

solo i nomi delle operazioni… non mi interessa (e non posso) dire qui cosa fanno!

Esercizio 3 - Corsa Mista Implementare un’applicazione java che simuli una gara di corsa tra concorrenti “molto diversi” Tutti i concorrenti hanno un nome proprio, una matricola (numero intero) e una velocità (float, supponiamola costante). Per tutti i concorrenti deve esser possibile: •  stampare una breve descrizione •  calcolare il tempo di percorrenza data una certa

distanza

Corsa Mista Possono gareggiare: •  concorrenti inquinanti (auto e moto): che usano un

certo tipo di carburante, hanno una marca e sono dotati di una certa autonomia in km, esaurita la quale, non potendo rifornirsi, devono ritirarsi. ❒  Le auto hanno anche un numero di passeggeri

(da 1 a 5) •  concorrenti ecologici (bicicletta e cavallo)

Corsa Mista Le biciclette e tutti i veicoli inquinanti sono a conducente umano e come tali devono riposarsi per R ore ogni G ore di guida.

R = random tra 6 e 8 G = random tra 10 e 12 calcolaOreDiSonno() calcolaOreDiGuida()

•  Le auto con più di un passeggero tuttavia non hanno bisogno di interrompere la gara.

•  I cavalli non dormono mai!

Si simuli una corsa per T Km con numero di partecipanti a piacere (anche più concorrenti dello stesso tipo) •  si stampi a video la descrizione di ciascuno in ordine di

arrivo!

Esercizio 3 - Suggerimenti •  non esiste il generico Concorrente,

ConcorrenteInquinate o ConcorrenteEcologico, come non esisteva il generico Animale

•  auto, moto, cavalli e biciclette possono essere divisi secondo due classificazioni: ❒  Inquinanti (auto, moto) – Ecologici (bici, cavallo) ❒  ConducenteUmano (auto, moto, bici) – ConducenteAltro (cavallo)

•  Con una semplice gerarchia di classi non posso mappare questa realtà perchè non esiste l’ereditarietà multipla.

•  Posso definire un insieme di operazioni che solo auto, moto e bici devono fare? -> se si, posso definire l’interfaccia IConducenteUmano!

Esercizio 3 - Suggerimenti

•  generare un numero random con Java: Math.random() restituisce un double tra 0 e 1. Quindi se invece voglio un int tra min e max:

(int) min + Math.random()*(max-min+1) •  cercate di replicare meno codice possibile. Es: se due

classi devono compiere la stessa operazione, possiamo scrivere il metodo corrispondente una volta sola in una superclasse concreta o astratta che sia.