Ingegneria del Software Programmazione Java Overview Caratteristiche generali Il linguaggio Java Le tecnologie per il Web
Caratteristiche generali Un moderno linguaggio orientato agli oggetti Pensato per lo sviluppo di applicazioni che devono essere eseguite in un ambiente eterogeneo connesso in rete Portabile Semplice Robusto Sicuro Multithreaded Livello Semantico Portabilità Portabilità del codice sorgente Un programma scritto in un linguaggio ad alto livello per una determinata macchina virtuale (processore, s.o.) può essere riutilizzato in una macchina virtuale differente? Un esempio Ansi C Compilatore 1 Compilatore 2 Gap Semantico Compilatore N Linguaggio Macchina 1 Linguaggio Macchina 2 Linguaggio Macchina N Processore 1 Processore 2 Processore N
Portabilità (2) Il codice sorgente scritto in Ansi C è portabile! ma la portabilità del codice sorgente richiede la ricompilazione su ogni piattaforma Inoltre, cosa succede se un programma C deve invocare API del sistema operativo? La portabilità è limitata a quelle macchine virtuali che supportano quelle specifiche API Livello Semantico Portabilità di Java Java garantisce la portabilità del codice eseguibile grazie alla Java Virtual Machine (JVM) Write Once Run Everywhere! Sorgente Java Eseguibile Java (Byte Code) generato dal compilatore Java JVM Interprete Java 1 Linguaggio Macchina 1 Processore 1 JVM Interprete Java 2 Linguaggio Macchina 2 Processore 2 Gap Semantico JVM Interprete Java N Linguaggio Macchina N Processore N
Ciclo di Sviluppo Nome.java Sorgente Compilatore Class Loader Nome.class Bytecode JVM Just in Time Compiler Sistema Operativo Processore Ciclo di Sviluppo (2) Il compilatore non produce codice macchina, ma un insieme ottimizzato di istruzioni detto BYTECODE. Il sistema run-time di Java emula una macchina virtuale (Java Virtual Machine) che esegue il BYTECODE. Ogni architettura per la quale la Virtual Machine sia implementata, può eseguire lo stesso programma Java. L efficienza di esecuzione di Java è superiore rispetto agli altri linguaggi interpretati (Tcl, Perl...), anche se non raggiunge quella dei linguaggi compilati. Inoltre l interprete Java fornisce compilatori just in time per trasformare a runtime il BYTECODE in codice macchina, guadagnando in velocità, ma perdendone la portabilità.
Ancora sulla portabilità Oggi possiamo continuare a dire Write Once, Run Everywhere? Java supporta nuovi dispositivi con caratteristiche hardware molto omogenee tra loro Le diverse piattaforme Java 2 Standard Edition (J2SE) E la piattaforma base per workstation e server Java 2 Enterprise Edition (J2EE) E una estensione della piattaforma base arricchita con servizi per la sicurezza, la gestione delle transazioni, Supporta il modello a componenti Enterprise Java Bean (EJB) Java 2 Micro Edition (J2ME) E la piattaforma ridotta per palmari, cellulari,
Le diverse piattaforme (2) Portabilità legata alla piattaforma J2SE-JVM ClassLoader Serialization Reflection RMI J2ME-KVM Sicurezza Un eseguibile prodotto da altri e scaricato via rete sulla propria macchina potrebbe trasmettere virus o accedere a dati privati. Un programma Java non può violare la sicurezza! L esecuzione di un BYTECODE Java é confinata nel sistema runtime che lo interpreta. Nel linguaggio non esistono i puntatori. L applet non può scrivere né leggere sul client, né aprire connessioni con altri sistemi.
Robustezza Controlli estensivi a compile-time e a run-time, per rilevare gli errori quanto prima possibile (es.: type checking) Per questo, le caratteristiche insicure di C e C++ sono rimosse: Nessuna gestione esplicita dei puntatori (no aritmetica dei puntatori, no malloc e free esplicite, ) Gestione della memoria con garbage collection Array e stringhe veri Verifica del byte-code a load-time Dinamicità Il codice è eseguibile anche in assenza di alcuni moduli: le classi necessarie per la esecuzione di un programma Java possono essere caricate e collegate dinamicamente quando servono Esempio: nuove release di moduli caricabili automaticamente dalla rete quando servono
Concorrenza Multithreading parte integrante del linguaggio: Applicazioni interattive più facili a scriversi Migliore "reattività" (anche se non real-time) Esempio: caricamento asincrono di immagini nei browser di rete riduce i tempi di attesa Ricchezza La Standard Library Java contiene una ricca collezione di classi e di metodi preconfezionati: Language support Utilities Input/output Networking Abstract Window Toolkit (AWT)
Package Un package raggruppa un insieme di classi correlate, che possono essere importate nelle applicazioni. L appartenenza ad un pacchetto si realizza inserendo all inizio del file l istruzione : package MyPackage; Per importare in una applicazione le classi del pacchetto: import MyPackage.*; java.lang java.io java.util java.net java.applet java.awt java.awt.image java.awt.peer Semplicità Sintassi simile a C e C++ (facile da imparare) Elimina i costrutti più "pericolosi" di C e C++ aritmetica dei puntatori (de)allocazione esplicita della memoria strutture (struct) definizione di tipi (typedef) preprocessore (#define) Aggiunge garbage collection automatica Conserva la tecnologia OO di base di C++ Rivisita C++ in alcuni aspetti (C++--==)
Distribuzione Pensato per essere eseguito in rete L ambiente run-time incorpora funzioni di rete (sia di basso livello: TCP/IP, che di alto livello: HTTP, ) La rete è facilmente accessibile (come i file locali) Il linguaggio Java Caratteristiche lessicali
Commenti /* Commento tradizionale, eventualmente su più linee, non nidificato */ // Commento su di una sola linea Identificatori Sono composti da lettere Java e cifre Java, e devono iniziare con una lettera Java Possono essere di qualsiasi lunghezza Attenzione: sono lettere e cifre Unicode! _ e $ sono considerate lettere caratteri con codifica diversa (es. maiuscole e minuscole) sono considerati diversi (anche se hanno la stessa rappresentazio-ne)
Parole Chiave Le seguenti keywords non possono essere usate come identificatori: abstract double int super boolean else interface switch break extends long synchronized byte final native this case finally new throw catch float package throws char for private transient class (goto) protected try (const) if public void continue implements return volatile default import short while do instanceof static Note: - const e goto sono riservate, ma non usate - anche i letterali null, true, false sono riservati struct union enum signed unsigned extern auto register sizeof typedef Keywords Java e C C char int short long float double void if else for while do switch case default break continue return goto volatile static const Java byte boolean final try catch finally throw throws private public protected transient synchronized native abstract import class extends instanceof implements interface package this super new letterali, non keywords riservate ma non usate in Java true false null
Tipi TIPI KEYWORD NOTE boolean: boolean true, false Primitivi numerici interi byte short int long char 8 bit int. in compl. a 2 16 bit 32 bit 64 bit 16 bit Unicode floating-point float double 32 bit IEEE 754 64 bit Reference classi class interfacce interface array Null Tipi (2) Linguaggio fortemente tipato: il tipo di una espressione è sempre noto a compile-time Linguaggio a oggetti non puro: non tutti i tipi sono classi Rispetto al C, non esistono: signed, unsigned, long double, enum, puntatori, struct, union, typedef
Esempi di Tipi Primitivi Dichiarazioni: int i, float f; Inizializzazioni:double d = 3.14; Espressioni: i + 5 j = i++ Essenzialmente, gli stessi operatori del C Nota: byte e short vengono sempre promossi a int prima di essere valutati Assegnamenti:i = j + 5; Classi dichiarazione di classe: class Automobile { <campi> <metodi> dichiarazione di oggetto: Automobile a; creazione di oggetto: a = new Automobile(); uso dell oggetto: a.campo a.metodo( )... a a nu l heap I campi vengono inizializzati
Safe.java class Safe { public boolean islocked() { return (locked) public void unlock (int thiscombination) { if (thiscombination == combination) unlock(); private void unlock() { locked = false; private void setcombination(int setting) { combination = setting; Safe() { /* costruttore */ Safe (int door) { /* altro costruttore (overloading) */ doornumber = door; setcombination(doornumber); Corso di Ingegneria del Software Esempio di Classe public int doornumber = 123; private boolean locked = true; private int combination = 456; Array In Java gli array sono oggetti I componenti di un array: sono tutti dello stesso tipo possono essere di tipo primitivo o reference (inclusi altri array) sono indicizzati con int (indice primo elemento: 0), con controllo di validità degli indici a run-time Esempio: int[ ] a; /* dichiarazione */ /* anche int a[ ]; */ a = new int[3]; /* creazione */ a null a a[0] = 0; /* uso */ heap
Array (2) La lunghezza di un array è fissata al momento della sua creazione, e non può essere cambiata... ma si può assegnare un nuovo array di diversa lunghezza all array reference: int[ ] a = new int[3]; a heap a = new int[5]; a heap Array di Array short [ ] [ ] a; /* array di array di short */ short a[ ] [ ]; /* equivalente */ a = new short [3][2]; a a[2][ 0] heap a a = new short [3][ ]; heap nul l nul l nul l
Array di Oggetti class Automobile { public int targa; public int velocità;.. Automobile[ ] a=new Automobile[3]; a heap a[2].targa Strutture di Controllo
Strutture di Controllo Sequenza Selezione Iterazione if switch for while do-while Salto break uscita da un blocco try-catch- Gestione eccezioni continua return finally-throw continua un loop da un metodo Non c è goto! Sintassi boolean, non integer! if ( condition ) switch ( intexpr ) { statement; case intexpr : statement; [else [case intexpr : statement; statement; ]... default : statement; ] while ( condition ) statement; do statement; while ( condition ); for ( init; condition; increment ) statement;
Gestione delle Eccezioni È una struttura di controllo che permette la gestione di condizioni di errore senza complicare la struttura del codice (codici di ritorno, flag, ) Quando si rileva una condizione anomale (eccezione), essa viene segnalata (throw), e il controllo viene automaticamente trasferito al gestore della eccezione, che la tratta Struttura try-catch-finally try blocco-0 catch (exception_type1 id) blocco-1 catch (exception_type2 id) block-2 finally blocco-n qui dentro viene segnalata la eccezione, con throw e exception_type1 blocco-0 exception_type2 blocco-1 blocco-2 blocco-n
Segnalazione delle Eccezioni Le eccezioni possono venire segnalate: dal sistema run-time, es.: ArithmeticException IndexOutOfBoundsException SecurityException NullPointerException... dal programma: throw e; La Dichiarazione throws type method(args) throws MyException {. throw new MyException( ). Permette al compilatore di controllare quali eccezioni vengono segnalate fornisce documentazione all utente del metodo
Gestione delle Eccezioni (2) public class Person { public void set_age(int age) throws Exception { if (age < 0) throw new Exception("thrown from f()"); Person P = new Person(); try { P.age(15); Catch(Exception e) { Classi
Dichiarazione di Classi [Doc comment] [Modifiers] class ClassName [ extends SuperClassName] [ implements InterfaceName [, InterfaceName]...] {ClassBody Doc comment commento di documentazione Modifiers abstract, final, public,... extends la classe è sottoclasse di un'altra implements la classe realizza una o più interfacce ClassBody i campi e i metodi della classe Esempio Dichiarazione di una classe MyClass public class MyClass { int i; /* campo */ public void Add_to_i (int j) { /* metodo */ i = i+j; public MyClass () { /* metodo costruttore: ha lo */ i = 10; /* stesso nome della classe */ Creazione e uso di una istanza di MyClass MyClass mc; /* dichiarazione, non creazione */ mc = new MyClass(); /* creazione: l'attributo i vale 10 */ mc.i++; /* ora i vale 11 */ mc.add_to_i(10); /* e ora i vale 21 */
Dichiarazione di Campi [DocComment] [Modifiers] Type VariableDeclarator [,VariableDeclarator] ; Esempio: Doc comment Modifiers Type VariableDeclarator static int i = 5, j = 7, a[]; commento di documentazione static, public, protected,private, final,... tipo (primitivo o reference) identificatore (anche di array), con eventuale inizializzazione Dichiarazione di Metodi [Doc comment] [Modifiers] ReturnType MethodName (ParameterList) [throws ClassType [,ClassType] ] { MethodBody Doc comment Modifiers ReturnType ParameterList commento di documentazione static, public,protected,private, abstract, final,native,... può essere un tipo primitivo o reference, o void (nessun valore di ritorno). Deve essere sempre specificato (ma non per i costruttori) un metodo ha sempre un numero fisso di argomenti (ma è possibile l overloading)
Esempio int base; campo della classe public int power (int n) { int i, p; p=1; for (i=1; i<=n; ++i) p=p*base; return p; variabili locali valore della funzione Il Modificatore static Campi e metodi dichiarati static, sono associati alla classe e non a una particolare istanza class MyClass { static int a; static void MyMethod() { a = a+1; Pertanto: esiste una sola copia di un campo statico, condiviso da tutte le istanze della classe; non occorre istanziare un oggetto per usare un membro statico; metodi statici possono accedere solo a membri statici della classe Sono qualificati con il nome della classe, e non della istanza MyClass.a =MyClass.a + 1;
Esempio class Safe { static int LastSerialNumber; public int SerialNumber; int SetCombination;. Safe(int Combination) { SerialNumber = LastSerialNumber++; SetCombination = Combination; public static StartNewSeriesFrom (int Start) { LastSerialNumber = Start;. Il costruttore Safe fa sì che ogni istanza di Safe abbia un SerialNumber unico Safe.LastSerialNumber(100000); /* inizializza il numero di serie */ Safe MySafe = new Safe(23456); /* crea una Safe di SerialNumber = 100001 e combinazione 23456 */ MySafe.SerialNumber. La Keyword this Se una variabile locale ha lo stesso nome di un membro della sua classe, questo risulta invisibile ( shadowing ) a meno di usare la keyword this, che denota l oggetto corrente Esempio: class MyClass { int x, y, z; /* membri */ void MyMethod (int x) { /* parametro */ int y; /* variabile locale */ y=x+1; /* y è la variabile locale, x è il parametro */ z=this.x; /* this.x è il membro x */ NB: Poichè this denota un oggetto, non si può usare con membri dichiarati static
Passaggio di Parametri a un Metodo Tutti i tipi primitivi sono passati per valore Anche i riferimenti a oggetti di tipo reference sono passati per valore Es: int i; SomeObject obj = new SomeObject(); i obj MyMethod( i, obj );. Void MyMethod (int j, SomeObject o) { j o j = j+1; /* non altera i, passato per valore */ o = null; /* non altera obj, che si riferisce ancora all oggetto */ heap Costruttori Sono metodi che vengono chiamati quando si crea una istanza di una classe; non ritornano alcun valore Se non definisco alcun costruttore, per default viene fornito il costruttore vuoto: class MyClass { MyClass() { Possono essere overloaded; non ha senso che siano static
Esempio di Overloading Dichiarazione di una classe MyNewClass public class MyNewClass { int i; public MyNewClass () { i = 10; public MyNewClass (int j) { i = j; Overloading: metodi omonimi devono essere distinguibili per la quantità e/o per il tipo degli argomenti... Creazione di una istanza di MyNewClass MyNewClass mc0, mc1; /* dichiarazione, non creazione */ mc0 = new MyNewClass(); /* creazione: l'attributo i vale 10 */ mc0.i++; /* ora i vale 11 */ mc1= new MyNewClass(20); /* creazione: l'attributo i vale 20 */ mc1.i++; /* ora i vale 21 */ Distruzione di Oggetti Java non supporta distruttori di oggetti: gli oggetti non possono essere distrutti esplicitamente Un oggetto privo di riferimenti incidenti non è più accessibile, e la memoria che esso occupa può essere "riciclata" dal garbage collector, che opera in un thread indipendente a bassa priorità String s; /* dichiarazione, non creazione */ s = new String ("abc"); /* creazione: s punta a "abc" */ s = "def"; /* "abc" non è più puntata da s */
Finalizzatori di Oggetti Un oggetto "riciclabile" (cioè privo di puntatori incidenti) potrebbe trovarsi in uno stato poco "pulito" (ad es. potrebbe aver aperto dei file che non sono stati ancora chiusi) Prima di riciclarlo, il garbage collector invoca il metodo finalize dell oggetto, es.: protected void finalize () throws Throwable {... super.finalize(); /* meglio metterlo sempre */ Il metodo finalize esiste sempre: se non è stato definito, viene ereditato da Object Il Modificatore native Un metodo è dichiarato native quando il suo codice è dipendente dalla piattaforma (ad esempio, è scritto in un altro linguaggio) Esempio: public native void seek(long pos) throws IOException ; non deve essere specificato il body
Ereditarietà class Animale { float Peso; void Mangia () { class Mammifero extends Animale { int BattitoCardiaco; // eredita Peso void Respira() { // eredita mangia class Gatto extends Mammifero { // eredita BattitoCardiaco, Peso, Mangia, Respira void FaLeFusa() { Shadowing e Overriding shadowing un campo di una sottoclasse può nascondere un campo omonimo di una sua superclasse overriding un metodo di una sottoclasse può sovrascrivere un metodo di ugual segnatura e ugual ReturnType di una sua superclasse class SuperClass { int i; void m(int k) { nasconde sovrascrive class Subclass extends Superclass { long i: void m(int n) {
La Keyword super per denotare un membro nascosto (campo shadowed o metodo overridden non static), si può usare la keyword super class SuperClass { int i; class Subclass extends SuperClass { long i: i = super.i +1; Il Modificatore abstract Una classe è dichiarata abstract quando contiene almeno un metodo abstract (cioè senza body) Una classe abstract non può essere instanziata: occorre sovrascrivere tutti i metodi abstract in una sottoclasse, e istanziare la sottoclasse abstract class a { abstract int m(int k); class b extends a {... int m(int n) { sovrascrive, fornendo la implementazione del metodo
final ha tre significati diversi: Il Modificatore final campo final non può essere modificato: è un campo costante (deve essere inizializzato) Esempio: final int i = 5; metodo final non può essere sovrascritto classe final non può avere sottoclassi (quindi i suoi metodi sono implicitamente final) Interfacce In sostanza, sono liste di metodi di cui non è definita l implementazione, ma solo l interfaccia Una interface è definita in modo analogo ad una classe, ma ha metodi non implementati e non ha variabili di istanza. Una interface può contenere la definizione di costanti, che sono condivise dalle classi che implementano l interface. Più classi possono implementare la stessa interfaccia, definendone tutti i metodi. La loro implementazione deve essere realizzata da una classe
IntStack.java interface IntStack { void push(int item); int pop( ); DynStack.java class DynStack implements IntStack { variabili di istanza e metodi propri... public void push(int item) { corpo di push per DynStack public int pop( ) { corpo di pop per DynStack FixedStack.java class FixedStack implements IntStack { variabili di istanza e metodi propri... public void push(int item) { corpo di push per FixedStack public int pop( ) { corpo di pop per FixedStack InterfaceTest.java class InterfaceTest { public static void main (String args[]) { IntStack mystack; FixStack fs = new FixStack(8); DynStack ds = new DynStack(5); mystack = ds; for (int i=0; i<12; i++) mystack.push(i); mystack = fs; for (int i=0; i<8; i++) mystack.push(i);
Superinterfacce Una interfaccia può estendere una o più altre interfacce, es.: interface a extends b, c, d {. Ogni classe che implementa un interfaccia deve estendere anche tutte le sue superinterfacce Interfacce vs Classi abstract Le interfacce sono simili a classi che abbiano soltanto: metodi abstract campi static e final (cioè costanti) A differenza delle classi, le interfacce permettono di realizzare una forma di ereditarietà multipla