Java/Classi e oggetti: differenze tra le versioni

Wikibooks, manuali e libri di testo liberi.
Contenuto cancellato Contenuto aggiunto
Riga 3: Riga 3:
Vediamo uno dei componenti fondamentali dei programmi in Java, la '''classe'''.
Vediamo uno dei componenti fondamentali dei programmi in Java, la '''classe'''.


== Classe ==
== Introduzione ==


Una '''classe''' è un tipo di dato creato dal programmatore, ed è l'essenza della programmazione ad oggetti.
Una '''classe''' è un tipo di dato creato dal programmatore, ed è l'essenza della [[w:Programmazione orientata agli oggetti|programmazione ad oggetti]].
<br/>
Con l'evolversi dell'informatica e il crescere della complessità delle applicazioni, i programmatori hanno trovato sempre più difficile trovare gli errori nei codici già scritti, riutilizzare codice scritto per altri programmi e modificare funzionalità già esistenti senza effetti collaterali imprevedibili.


Il codice di un programma può essere lunghissimo, arrivare fino a milioni di righe di codice. Si rende necessaria, allora, una suddivisione del codice secondo certi criteri. La programmazione orientata agli oggetti, in particolare, propone di dividere il codice in parti ben precise, ciascuna delle quali occupa di un aspetto specifico dell'applicazione. L'applicazione viene costruita mettendole insieme e facendole interagire in modo appropriato.


Ciascuna di esse presenta all'esterno un'interfaccia, che specifica quali operazioni può compiere, a prescindere da come siano effettivamente realizzate (implementazione). Questo significa che, nel caso si renda necessaria una modifica sul modo in cui un certo compito viene svolto (ad esempio, per correggere un errore), è più facile per il programmatore operare la modifica, proprio perché circoscriverà la manipolazione all'implementazione. Le altre componenti del programma, che conoscono ed utilizzano solo l'interfaccia, non devono essere modificate.
La soluzione a questi problemi è rendere la programmazione più '''astratta''' e '''modulare'''. Se un codice è diviso in parti ben precise che si occupano di aspetti specifici dell'applicazione e queste parti presentano all'esterno delle interfacce che specificano '''quali''' operazioni possano compiere (specifica), indipendentemente da '''come''' poi le compiano (implementazione) poi è più facile per il programmatore individuare un errore o riutilizzare delle funzionalità, proprio perché circoscriverà la manipolazione alla parte che gli serve.


La programmazione ad oggetti propone di vedere un programma come un insieme di entità, chiamate "oggetti", che interagiscono tra loro. Ogni oggetto dovrebbe rispecchiare un elemento della vita reale.


===Altro esempio===
Immaginiamo di dover fare un programma per gestire gli stipendi: creeremo un vettore di stringhe per il nome, un altro per il cognome e un vettore di int per gli stipendi. Dopo aver creato i codici che chiedono i dati e li inseriscono o li mostrano a richiesta il programma funziona e siamo contenti.
Immaginiamo di dover fare un programma per gestire gli stipendi all'interno di un'azienda. Un programmatore proveniente da un ''bacground'' di programmazione procedurale creerà un vettore di stringhe per il nome dei dipendenti, un altro per il cognome e un vettore di <code>int</code> per gli stipendi. Dopo aver creato i codici che chiedono i dati e li inseriscono o li mostrano a richiesta, il programma funziona e siamo contenti.<br/>
Dopo qualche anno, però, l'azienda apre una filiale negli USA. Dobbiamo quindi riadattare il programma perché gestisca anche i dollari.
Dopo qualche anno, però, l'azienda apre una filiale negli USA. Poiché il nostro programma era stato scritto presupponendo che la valuta da usare fossero gli Euro, dobbiamo riadattare il programma perché gestisca anche i dollari. Facile: creiamo un vettore stringa che contiene l'indicazione della valuta e riadattiamo tutto il programma per gestirla.<br/>

Creiamo un vettore stringa che contenga l'indicazione della valuta e riadattiamo tutto il programma per gestirla. Peccato che l'azienda decida poi di aprire una filiale in India. Adattiamo il programma anche alle Rupie e i dipendenti indiani ricevono uno stipendio ridicolo.
Peccato che l'azienda decida poi di aprire una filiale in India. Adattiamo il programma anche alle Rupie e i dipendenti indiani ricevono uno stipendio ridicolo.<br/>
Perché? Perché quando avevamo due valute abbiamo creato un if di questo tipo:
Perché? Perché quando avevamo due valute abbiamo creato un if di questo tipo:


Riga 23: Riga 23:
che ora, nel caso di tre valute possibili non è più corretto.
che ora, nel caso di tre valute possibili non è più corretto.


Sarebbe bello che la funzione che stampa a video il valore della valuta potesse, in qualche modo, chiedere direttamente al dipendente qual è il valore del suo stipendio in dollari, in Euro o in rupie. C'è bisogno di qualcuno che si occupi di gestire tutti i dati relativi al dipendente. In questo modo, alla funzione di stampa non "interesserà" più se il dipendente vive in India, in America o in Europa: le interessa solo sapere che il dipendente deve ricevere un certo stipendio, e quale è il valor eid questo stipendio.
Per evitare di passare notti insonni a affrontare situazioni del genere sarebbe bello poter definire un tipo di dato "Dipendente" e dargli tutte le caratteristiche del dipendente (nome, ruolo, stipendio) in modo da condensare in esso tutto ciò che riguarda la sua logica di gestione e creare il resto del programma richiamando le funzioni interne di Dipendente. Aggiungere una nuova caratteristica al dipendente vorrebbe dire modificare solo il tipo di dato Dipendente e non tutto il programma che ne fa uso.

Per questa operazione esiste la classe.
Possiamo risolvere, quindi, il problema mettendo insieme tutti i dati relativi a un singolo dipendente. Definiamo un tipo di dato "Dipendente" e gli diamo tutte le caratteristiche del dipendente (nome, ruolo, stipendio) in modo da condensare in esso tutto ciò che riguarda la sua logica di gestione e creare il resto del programma richiamando i metodi di Dipendente. Aggiungere una nuova caratteristica al dipendente vuole dire modificare solo il tipo di dato Dipendente e non tutto il programma che ne fa uso.
Il tipo di dato che abbiamo creato è una classe.


===Sintassi===
La classe ha lo scopo di delineare le proprietà e le caratteristiche degli elementi che ci servono. Questi elementi possono poi essere creati realmente (''istanziati'') e diventano così “oggetti”: le istanze della classe sono gli oggetti.
La classe ha lo scopo di delineare le proprietà e le caratteristiche degli elementi che ci servono. Questi elementi possono poi essere creati realmente (''istanziati'') e diventano così “oggetti”: le istanze della classe sono gli oggetti.
La classe è un po' come un modello di qualcosa e l'oggetto è questo qualcosa realizzato concretamente. Gli elementi che compongono la classe possono essere: tipi primitivi, metodi, costruttori, oggetti di altre classi, classi stesse. Questi elementi sono tutti racchiusi in un contenitore (la classe) “class”. Vi è quindi la classe e gli oggetti: sono due cose diverse. Grazie all'esistenza delle classi possiamo creare gli oggetti. Gli oggetti sono allocati in memoria dalla JVM.
La classe è un po' come un modello di qualcosa e l'oggetto è questo qualcosa realizzato concretamente. Gli elementi che compongono la classe possono essere: tipi primitivi, metodi, costruttori, oggetti di altre classi, classi stesse. Questi elementi sono tutti racchiusi in un contenitore (la classe) “class”. Vi è quindi la classe e gli oggetti: sono due cose diverse. Grazie all'esistenza delle classi possiamo creare gli oggetti. Gli oggetti sono allocati in memoria dalla JVM.

Versione delle 15:01, 22 nov 2010

Indice del libro

Vediamo uno dei componenti fondamentali dei programmi in Java, la classe.

Introduzione

Una classe è un tipo di dato creato dal programmatore, ed è l'essenza della programmazione ad oggetti.

Il codice di un programma può essere lunghissimo, arrivare fino a milioni di righe di codice. Si rende necessaria, allora, una suddivisione del codice secondo certi criteri. La programmazione orientata agli oggetti, in particolare, propone di dividere il codice in parti ben precise, ciascuna delle quali occupa di un aspetto specifico dell'applicazione. L'applicazione viene costruita mettendole insieme e facendole interagire in modo appropriato.

Ciascuna di esse presenta all'esterno un'interfaccia, che specifica quali operazioni può compiere, a prescindere da come siano effettivamente realizzate (implementazione). Questo significa che, nel caso si renda necessaria una modifica sul modo in cui un certo compito viene svolto (ad esempio, per correggere un errore), è più facile per il programmatore operare la modifica, proprio perché circoscriverà la manipolazione all'implementazione. Le altre componenti del programma, che conoscono ed utilizzano solo l'interfaccia, non devono essere modificate.

La programmazione ad oggetti propone di vedere un programma come un insieme di entità, chiamate "oggetti", che interagiscono tra loro. Ogni oggetto dovrebbe rispecchiare un elemento della vita reale.

Altro esempio

Immaginiamo di dover fare un programma per gestire gli stipendi all'interno di un'azienda. Un programmatore proveniente da un bacground di programmazione procedurale creerà un vettore di stringhe per il nome dei dipendenti, un altro per il cognome e un vettore di int per gli stipendi. Dopo aver creato i codici che chiedono i dati e li inseriscono o li mostrano a richiesta, il programma funziona e siamo contenti.
Dopo qualche anno, però, l'azienda apre una filiale negli USA. Poiché il nostro programma era stato scritto presupponendo che la valuta da usare fossero gli Euro, dobbiamo riadattare il programma perché gestisca anche i dollari. Facile: creiamo un vettore stringa che contiene l'indicazione della valuta e riadattiamo tutto il programma per gestirla.
Peccato che l'azienda decida poi di aprire una filiale in India. Adattiamo il programma anche alle Rupie e i dipendenti indiani ricevono uno stipendio ridicolo.
Perché? Perché quando avevamo due valute abbiamo creato un if di questo tipo:

IF la valuta è EUR applica questo valore, ELSE quest'altro

che ora, nel caso di tre valute possibili non è più corretto.

Sarebbe bello che la funzione che stampa a video il valore della valuta potesse, in qualche modo, chiedere direttamente al dipendente qual è il valore del suo stipendio in dollari, in Euro o in rupie. C'è bisogno di qualcuno che si occupi di gestire tutti i dati relativi al dipendente. In questo modo, alla funzione di stampa non "interesserà" più se il dipendente vive in India, in America o in Europa: le interessa solo sapere che il dipendente deve ricevere un certo stipendio, e quale è il valor eid questo stipendio.

Possiamo risolvere, quindi, il problema mettendo insieme tutti i dati relativi a un singolo dipendente. Definiamo un tipo di dato "Dipendente" e gli diamo tutte le caratteristiche del dipendente (nome, ruolo, stipendio) in modo da condensare in esso tutto ciò che riguarda la sua logica di gestione e creare il resto del programma richiamando i metodi di Dipendente. Aggiungere una nuova caratteristica al dipendente vuole dire modificare solo il tipo di dato Dipendente e non tutto il programma che ne fa uso. Il tipo di dato che abbiamo creato è una classe.

Sintassi

La classe ha lo scopo di delineare le proprietà e le caratteristiche degli elementi che ci servono. Questi elementi possono poi essere creati realmente (istanziati) e diventano così “oggetti”: le istanze della classe sono gli oggetti. La classe è un po' come un modello di qualcosa e l'oggetto è questo qualcosa realizzato concretamente. Gli elementi che compongono la classe possono essere: tipi primitivi, metodi, costruttori, oggetti di altre classi, classi stesse. Questi elementi sono tutti racchiusi in un contenitore (la classe) “class”. Vi è quindi la classe e gli oggetti: sono due cose diverse. Grazie all'esistenza delle classi possiamo creare gli oggetti. Gli oggetti sono allocati in memoria dalla JVM.

Per esempio, Mario è un'istanza della classe Dipendente, ossia un oggetto di tipo Dipendente.

Per creare una classe devo usare la parola chiave class con questa sintassi:

public class Esempio {
/*
variabili e costanti...
metodi e costruttori...
cicli e controlli di flusso...
oggetti di classi e classi stesse...
*/
}

la classe Dipendente potrebbe essere


public class Dipendente {
    public int stipendio;
    public String valuta;
    public String nome, cognome;
    public void promuovi() {
     //promuovo il dipendente aumentando del 10% lo stipendio
        this.stipendio=this.stipendio*(1.1);
    }
}

Si noti che dentro la classe abbiamo definito delle variabili che rappresentano le caratteristiche (dette attributi) di un dipendente. Osserviamo che le dichiarazioni di variabili sono precedute dalla parola public: significa che il valore dell'attributo sarà modificabile dall'esterno della classe (la possibilità di leggere e scrivere un attributo è detta visibilità). Altre visibilità possibili sono:

modificatore di visibilità effetto
public visibile da qualsiasi parte del programma
private visibile solo dall'interno della classe stessa
protected visibile solo dalle classi dello stesso package
friendly visibile dallo stesso package o dalle classi che ereditano. È la visibilità assegnata di default se non viene specificato nulla.

Si riporta l'esempio funzionante del capitolo "Primo programma" della classe HelloWorld:

public class HelloWorld {
    public static void main (String args[]) {
        System.out.println("Hello, world!");
    }
}

È convenzione che i nomi delle classi comincino con la maiuscola, ed è bene rispettare questa prassi, per la leggibilità del codice, anche se non è obbligatoria.

Interfaccia pubblica e privata della classe

Una classe è un tipo di dati astratto (TDA) definito dal programmatore.

Esattamente come ci sono i tipi primitivi cosi ci sono i TDA del programmatore che si usano in maniera simile, tuttavia, mentre per i primitivi si parla di variabili, per i TDA si parla di oggetti. Inoltre, gli oggetti e i primitivi vengono allocati nella memoria in modalità e posizione diverse (rispettivamente nello heap e nello stack, per chi fosse interessato al lato più tecnico). Guardiamo come si dichiarano e inizializzano.

Per dichiarare un intero devo usare l'istruzione:

int a;

cioè devo dare un nome ad una variabile di tipo intero, quindi con "a" uso un valore intero che dovrò “inizializzare” (specificare).

Per dichiarare un oggetto devo usare invece:

NomeDellaClasse B;

cioè devo dare un nome ad un oggetto di tipo NomeDellaClasse, quindi con "B" qui uso un riferimento a un oggetto, che dovrò “inizializzare” (specificare).

Non è permesso compilare senza inizializzare gli oggetti dichiarati, si può subito dichiararli e inizializzarli poi in un secondo momento, ma bisogna comunque farlo:

int a = 10;

userò il valore 10 con il nome “a” realmente e pertanto questo sarà allocato in memoria,

Nomedellaclasse B = new Nomedellaclasse();

userò realmente la classe essendo concretizzata nel suo oggetto, con il nome “B”, pertanto questo è allocato in memoria.

Nel primo caso uso un valore, direttamente, nel secondo il riferimento ad un valore.

Il riferimento al valore è solo un indirizzo della memoria, che punta al valore reale, ma non è direttamente questo.

Le caratteristiche di base della classe sono all'interno dell'oggetto "B" e tutte le altre sono ottenibili. Adesso posso ottenere realmente ciò che questa classe consente di fare usando il nome oggetto "B" con la notazione "dot" cioè il punto "." e una chiamata ad un suo elemento, così NomeOggetto.membrodellaclasse. Ad esempio posso fare:

B.faifrase(); 

Ovvero chiamo un metodo che è scritto in Nomedellaclasse, e la JVM eseguirà realmente ciò che è scritto in questo metodo.

public class Nomedellaclasse {

    String soggetto= "egli ";
    String verbo= "fa ";
    String complemento= "qualcosa";

    public void faifrase(){
        System.out.println(soggetto + verbo + complemento);
    }
   
    public static void main(String [] Args){
    
        Nomedellaclasse B= new Nomedellaclasse();
        B.faifrase();    
// la classe Nomedellaclasse è realizzata e allocata in memoria con l'oggetto "B"
// l'oggetto "B" fa quello che la classe permette di fare con "faifrase()", nel codice è "B.faifrase()".
 
    }
}

Ottengo la scritta "egli fa qualcosa".

Ancora..., posso addirittura accedere ai singoli elementi e cambiarli:

B.soggetto= "lui ";

In questo modo

public class Nomedellaclasse {

    String soggetto= "egli ";
    String verbo= "fa ";
    String complemento= "qualcosa";

    public void faifrase(){
        System.out.println(soggetto + verbo + complemento);
    }
   
    public static void main(String [] Args){
    
        Nomedellaclasse B= new Nomedellaclasse();
        B.soggetto= "lui";   // quì, in esecuzione, cambio e modifico un membro della classe
        B.faifrase();
 
    }
}

Avrò l'output "Lui fa qualcosa".

Vi sono già migliaia di classi preconfezionate già pronte e usabili dal programmatore per la stesura del codice sorgente. Queste classi sono documentate ampiamente ed in modo ordinato dalla Sun con il nome di API nella documentazione del JDK (Java Development Kit). Il programmatore non deve far altro che usare le classi già esistenti ed adattarle ai propri scopi con modalità predefinite come l'ereditarietà.

Mediante le classi è possibile e necessario incapsulare, ereditare, polimorfizzare il codice. Incapsulamento, ereditarietà, polimorfismo, sono le modalità della programmazione orientata agli oggetti (O.O.P.) che determina la differenza rispetto ai precedenti linguaggi procedurali e strutturati, rendendola più performante in quanto diviene modulare, scalabile, riusabile. Il programmatore partendo dalle classi standard dei packages può arrivare a specificare proprie classi personali più adatte per l'uso del software previsto. Le classi esistenti sono già ottimizzate e collaudate, consentendo un risparmio di lavoro notevole, è difficile se non impossibile farne a meno, ed è anche fortemente sconsigliato. Occorre verificare e confrontare le API sempre e comunque per scegliere le classi a cui aggangiarsi per la stesura del proprio software.

Il programmatore può scrivere le classi ereditando da altre classi, nidificandole, implementando le classi astratte e interfacce. Per tutte queste pratiche e per il loro uso, vedere il capitolo sull'ereditarietà.

Queste classi (API) sono davvero numerose e per ordinarle in modo che fossero facilmente usabili sono state raggruppate in insiemi specifici, i package.

La classe può essere normale, interna, anonima, astratta, secondaria o derivata. Gli elementi all'interno della classe e la classe stessa si possono proteggere mediante i modificatori di accesso. In ogni caso non è possibile dichiarare private o pretected una classe altrimenti non potrebbe essere estesa e questo non è possibile nella O.O.P.. L'interfaccia pubblica è costituita dall'insieme dei membri e istanze pubbliche, nel nostro esempio (Nomedellaclasse) tutte le istanze sono pubbliche. Se invece di “public” vi fosse “private” allora la classe avrebbe anche un'interfaccia privata ed i membri “private” sono raggiungibili solo all'interno della classe o da classi annidate o da metodi “getter” e “setting” se presenti. Vedere il capitolo sui modificatori. Le classi si passano informazioni e dati solo tramite l'interfaccia pubblica.

Archiviare le classi

I package sono meri contenitori di classi esattamente come le directory lo sono per i files e tra l'altro vale la stessa regola: nello stesso package non vi possono essere due classi con lo stesso nome così come nella stessa directory non vi possono essere due file con lo stesso nome.

Il programmatore così come può creare sue classi così può ordinarle in suoi package. Ordinare proprie classi può servire a ritrovarle con facilità e a riusarle, vedremo poi come, con altrettanta facilità.
Il programmatore può assegnare una classe ad un suo package usando la parola chiave package seguita da un nome attribuitogli, caratterizzante l'insieme delle classi; con questa sintassi:

package nomedelpackage;

Questa istruzione deve essere la prima in assoluto del codice sorgente e inoltre il nome non deve cominciare con "java." poiché questo è riservato solo ai package standard della JDK. Il nome del package dovrebbe essere indicativo delle caratteristiche della collezione delle classi. Per archiviare la classe "HelloWorld". che abbiamo visto prima, in un package di nome "saluti" dobbiamo scrivere:

package saluti;

I package hanno un sistema di nomi strutturato.

Gli ambienti integrati di sviluppo “IDE” usano loro modalità di stoccaggio delle classi per cui occorre vedere la documentazione relativa, questi ambienti nell'automatizzare e facilitare il processo, e spesso questo è utile, purtroppo oscurano la procedura.

Compilando “a mano” la procedura diventa trasparente, più chiara, almeno per quanto riguarda la formazione di pacchetti di classi del programmatore.

I nomi del package devono avere una corrispondenza diretta nel filesystem: ad esempio, se si vuole archiviare il file helloWorld.class nella cartella “saluti” del file system, la cartella "saluti" dovrà essere presente, perché è qui che vogliamo conservare il file insieme ad altri dello stesso argomento e crearci la nostra collezione di classi.

Dovrà esserci la directory saluti e dunque si pongono degli interrogativi: dove è montata la cartella saluti? A che livello del file System? Ricordiamoci che dopo deve essere anche ritovata e occorre sapere non solo il suo nome ma anche dov'è.

Entra in gioco un percorso parziale che comincia dalla root e si interrompe al livello superiore più vicino alla directory del package, il percorso a monte della cartella “saluti”. Questo percorso è importante se vogliamo ritrovare poi le nostre classi. Questo percorso si chiama "CLASSPATH". "CLASSPATH" è anche il nome della variabile di sistema che va settata con il valore del percorso. Del CLASSPATH ne parliamo dopo.

La JVM per riutilizzare e ritrovare le classi, pur conoscendone il nome, non sa dove sono montate. La JVM sa che la classe HelloWorld è nella cartella “saluti” ma non sa dove è questa, sà mezzo percorso, quello del package, l'ultima parte dell'indirizzo assoluto. È necessario perciò che la prima parte dell'indirizzo deve essere già esistente sul disco fisso e ciò deve essere specificato anche al compilatore.

Ora poniamo che sia "/home/giovanni/mieclassi/", questo indirizzo lo dobbiamo specificare quando compiliamo le classi da archiviare, ed è questo il "CLASSPATH".

Si realizza con l'opzione "-d" di "javac" seguito dal CLASSPATH, in questo modo:

ripeto che l'indirizzo /home/giovanni/mieclassi/ deve già esistere realmente sul file system

javac -d /home/giovanni/mieclassi/ HelloWorld.java

Il compilatore leggerà nell'istruzione "package" il nome "saluti", creerà la directory e la accoderà. Automaticamente ci sarà /home/giovanni/mieclassi/saluti e lì metterà HelloWorld.class. In questo caso è il compilatore che ha creato la cartella "saluti". Non dobbiamo fare altro. Avremo archiviato la classe HelloWorld nel package saluti.

Questo indirizzo CLASSPATH dovrà essere lo stesso anche al momento della lettura delle classi, cosicché quando compiliamo un sorgente sia archiviando le classi e sia riusandole in lettura la JVM si riferirà alla medesima locazione di storage delle classi.

Il package può avere anche più directory. Nel caso produciamo molte classi esse infatti andranno stipate in cartelle diverse ed il nome della cartella, chiaramente, indicherà il contenuto.

Con più directory cambia di poco: metteremo un punto “.” tra una directory e l'altra sul codice dopo l'istruzione package. La jvm cambierà automaticamente il punto nel carattere separatore del sistema operativo usato e la classe dovrà essere archiviata nell'ultima sottodirectory.

Facciamo, per esempio, delle classi di saluto simili ad HelloWorld, in molte lingue, per tirarle fuori al bisogno, secondo la lingua parlata dagli utenti del programma.

Faremo questi file, notare il nome del package che ora sono con una directory e e una sottodirectory separate dal punto:

package saluti.inghilterra;
public class HelloWorld {
    public void faisaluto () {
       System.out.println("Hello, world!");
   }
}

e

package saluti.francia;
public class HelloWorld {
   public void faisaluto ()  {
       System.out.println("Bonjour, monde!");
   }
}

e

package saluti.italia;
public class HelloWorld {
   public void faisaluto ()  {
       System.out.println("Ciao, mondo!");
   }
}

Se compilo ognuno di questi file con l'opzione "-d" del comando "javac", il compilatore appronterà sul disco la directory “saluti” con all'interno le cartelle “inghilterra”, “francia”, “italia”, tutte e tre queste cartelle sono allo stesso livello: sottodirectory di saluti.

Per comodità possiamo anche compilarle tutte in un colpo solo, a condizione che nella dir corrente vi sono solo questi tre file, con il comando

 javac -d /home/giovanni/mieclassi/ *.java

Grazie al segno "*" asterisco, tutte e tre Le classi Helloworld saranno compilate e stoccate nei rispettivi packages.

Non cambia niente per la prima parte dell'indirizzo, il classpath, di cui parlavamo sopra che rimane invariato.

Un'altra cartella ancora.

Se archivio con un package così “packages saluti.italia.dialetto” allora ci sarà la cartella dialetto come sottodirectory della cartella italia. All'interno di dialetto dovranno esserci i file corrispondenti dei saluti italiani in dialetto, ognuno con un diverso nome. I file, anche se con nomi uguali “Helloworld.class”, sono stati messi, comunque, in directory separate e i file dal nome diverso nella medesima directory. Tutti sono comunque individuabili e riprendibili.

L' istruzione

package nomedelpackage;

è facoltativa, è possibile non metterla: se queste non c'è, il compilatore inserisce la classe in un package di default.

I files del package di default saranno inseriti dalla JVM nella radice del percorso del CLASSPATH: da questo stesso posto verranno lette le classi automaticamente dalla JVM.

Vediamo come riusare le nostre classi.

Importare le classi

Per riutilizzare le classi stoccate in precedenza è necessario ma solo per i package personali come quelli che abbiamo fatto sopra usare la variabile di sistema CLASSPATH.

La JVM per i package standard di java, ovvero le API del linguaggio java, è già a posto , se li trova automaticamente da sola, in quanto generalmente l'installazione del jDK, fatta normanlmente, include il percorso automaticamente. Quindi questo è valido per le installazioni tipiche. È possibile per installazioni personalizzate inserire classpath anche per le API standard se queste fossero in directory scelte da noi.

Il Classpath serve, per dirgli dove sono solo i nostri package.

Per esempio i file archiviati Helloworld sono in /home/giovanni/mieclassi/ .

Per usare le classi dei package bisogna scrivere la parola chiave import.

Precisamente

import nomedelpackage;

Questa istruzione va inserita dopo "package", se presente (perché è opzionale), e prima di "class".

Vogliamo riusare le classi che abbiamo già fatto. Per farlo dobbiamo dire all'interprete dove sono le classi e in contemporanea quali sono. Dove e quali...

Se volessimo richiamare le nostre classi, prima archiviate, dovremo scrivere nel codice

import saluti.inghilterra.*;

dove "saluti.inghilterra" stanno ad indicare la directory e sottodirectory e l'asterisco i nomi dei file (asterisco = vogliamo tutti i file). L'interprete inserirà, prima della directory "saluti", il percorso del classpath e così troverà lindirizzo assoluto delle classi archiviate, vedrà anche l'asterisco e quindi ad ogni chiamata andrà a prendere il file corrispondente. Se non mettessimo l'asterisco dovremo mettere il nome preciso del file.class. Invece e quasi sempre, abbiamo bisogno di riusare molte classi e perciò è comodo l'asterisco.

Faremo questo codice per i saluti in inglese:

   import saluti.inghilterra.*;

class Test{
   public static void main(String [] args){

         HelloWorld inglese = new HelloWorld();
         inglese.faisaluto();

}
}

Lo compileremo a riga di comando così:

javac -classpath /home/giovanni/mieclassi/ Test.java

Lo eseguiremo a riga di comando così:

java -classpath ./:/home/giovanni/mieclassi/ Test

Ed otteremo l'output:

Hello, world!

Attenzione tra il primo classpath e il secondo c'è una differenza ed è il "." punto.

Il classpath si può settare anche nelle variabili utente del sistema operativo e in questo modo non siamo più costretti a digitare un interminabile comando con opzioni strane con il rischio di sbagliare, ma basta solo un " "java"o un "javac".

Per windows: Pannello di Controllo - Sistema - Avanzate - Variabili d'ambiente - Variabili utente. Ad esempio:

.;C:\Java\mieclassi

Per linux da shell: ad esempio

export CLASSPATH= ./:/home/giovanni/mieclassi

Attenzione: nel capitolo installazione abbiamo parlato della variabile PATH che quì non c'entra niente, sono due cose molto diverse, alcuni le confondono. La variabile PATH fa riferimento agli eseguibili java come i programmi java, javac, jar, ed altri, mentre la CLASSPATH punta alle classi.

Attenzione si ripete che è importante il "." punto nel classpath che rimanda ai file presenti nella dir corrente: questo è importante se vogliamo eseguire (comando "java") i file. class che abbiamo compilato (comando "javac") o che sono comunque presenti in questa stessa dir . Se lo omettiamo potremmo non poter eseguire i programmi: la JVM non trova i file.class nella dir corrente perché li cerca altrove.

Spessissimo, avremo bisogno di accedere alle classi contenute nei packages del JDK. Nel JDK la sun fornisce una copiosa quantità di packages disponibili all'uso.

Alcuni package di base delle librerie standard java :

  • java.lang è il package che contiene le classi di base del linguaggio
  • java.util raccoglie classi d’utilità generale
  • java.io contiene le classi per programmare l’input-output
  • java.awt contiene classi per programmare interfacce grafiche
  • java.net contiene classi per programmare connessioni
  • java.applet contiene classi per programmare applet