In Java una classe viene dichiarata in questo modo:
class MiaClasse{
//variabili
//codice
}
Il corpo di una classe contiene tutto il codice che determina il ciclo di vita degli oggetti creati dalla classe stessa: costruttori, metodi, variabili ecc. Inoltre è possibile aggiungere dei modificatori,come public e private, rendendo più complessa la dichiarazione di una classe.
Esistono diversi tipologie di variabili:
1)Variabili di membro della classe che vengono chiamate campi
2)Variabili all’interno di un metodo o un blocco di codice (variabili locali)
3)Variabili definite nella firma di un metodo, sono chiamate parametri.
public String nome=“Pippo”;
private int anni=40;
la var nome è accessibile da tutte le classi, mentre la var anni è accessibile solamente all’interno della classe in cui è stata dichiarata.
Dichiarazione di un metodo
Un metodo può essere considerato come un sottoprogramma definito per scopi particolari, ma che non può esistere da solo, ovvero deve far parte di una classe
public int nomeMetodo(int a, String b){
//istruzioni
}
Gli unici elementi necessari nella dichiarazione di metodi sono: tipo dell’oggetto restituito dal metodo (in questo caso int),il nome del metodo i parametri , e un corpo racchiuso tra graffe dove inserire il codice.
Più in generale, le dichiarazioni di metodi hanno cinque componenti:
1)Modificatore che regola l’accesso al metodo
2)Tipo restituito
3)nome del metodo
4)Prametri (sono quelle variabili o oggetti specificate all’interno delle parentesi tonde)
5)Corpo del metodo
public int somma(int a, int b) {
int risultato = a + b;
return risultato;
}
Overloading dei metodi
l’overloading dei metodi, ovvero è in grado di distinguere i metodi non dal nome ma dalla firma. Ciò significa che i metodiall’interno diuna classe possonoavere lo stesso nome se hanno liste di parametri diversi.
Costruttore di classe
Una classe può contenere uno o più costruttori che vengono richiamati per creare oggetti del tipo della classe a cui appartiene il costruttore stesso.
La dichiarazione di un costruttore è simile a quella di un metodo con alcune eccezioni:
• Deve avere lo stesso nome della classe in cui è definito • Non deve essere specificato il tipo di oggetto restituito perché i costruttori non restituiscono ne oggetti ne variabili
public class Automobile {
private int velocitaMassima = 0;
private int cilindrata = 0;
private String alimentazione = “”;
}
Abbiamo quindi semplicemente definito una classe con tre campi dichiarati private cioè non accessibili da altre classi. La nostra classe è public questo significa che posso dichiarare degli oggetti di tipo automobile in un’altra classe. Per fare ciò si utilizzano i costruttori, c’è da sottolineare che il compilatore mette a disposizione, per ogni classe, un costruttore di default (senza parametri) che viene utilizzato ogni qualvolta si istanzia la classe. Per cui non è obbligatorio definire un costruttore senza parametri. Supponiamo di voler creare un oggetto di tipo automobile definendogli le proprietà rappresentate dalle variabili di chiarate private, ovvero: velocità massima, cilindrata e alimentazione.Queste variabili non sono accessibili direttamente da classi esterne perché dichiarate private, per poter eseguire questa operazione è sufficiente definire un costruttore che svolga questo compito, ovvero:
public class Automobile {
private int velocitaMassima = 0;
private int cilindrata = 0;
private String alimentazione = “”;
public Automobile(int vel, int cil, String alim){
velocitaMassima = vel;
cilindrata = cil;
alimentazione = alim;
}
}
Questo costruttore ci permette dall’esterno, nel momento in cui inizializziamo un oggetto di tipo automobile, di assegnare dei valori alle sue proprietà.
Automobile miaAutomobile = new Automobile(200, 2000, “benzina”);
Invocare un metodo
public class Stampa {
String nome = “Mario”;
public void stampaStringa(String miaStringa){
System.out.println(miaStringa);
}
stampaStringa(nome); //Invocazione del metodo
}
Un aspetto teorico importante, con delle ripercussioni pratiche è il concetto di passaggio delle variabili per valore. Più in particolare i tipi primitivi come ad esempio int, double etc. vengono passati ai metodi per valore.
public class PassaggioPerValore {
public static void main(String[] args) {
int x = 3;
mioMetodo(x); // invocazione del metodo
System.out.println(“dopo aver invocato il metodo, x = “+x);
}
public static void mioMetodo(int p) { // dichiarazione del metodo
p=10;
}
}
Abbiamo creato una classe che contiene un metodo main all’interno del quale viene dichiarata e inizializzata una variabile x di tipo int uguale a 3, viene invocato un metodo mioMetodo definito dopo il metodo main, ed infine viene stampato in output il valore della variabile x. Il metodo mioMetodo in particolare prende una variabile di tipo int e le assegna il valore 10.Cosa pensate che stampi in output questo esempio? Per rispondere a questa domanda bisogna ricordarsi che le variabili vengono passate ai metodi per valore ciò significa che qualsiasi modifica al valore di una variabile passata ad un metodo è valida solamente nell’ambito del metodo stesso.dopo aver invocato il metodo, x = 3. I tipi non primitivi non vengono passati per valore ma per riferimento, ovvero il metodo esegue una copia del riferimento della variabile passata al metodo.A differenza del passaggio per valore, il metodo può modificare l’oggetto a cui punta il riferimento, poiché, anche se sono usati diversi riferimenti, la posizione dei dati a cui puntano è la stessa.
public class StampaArray {
public static void main(String[] args) {
int[] mioArray = {3, 7, 9};
for(int i=0; i<3; i++){
System.out.println(mioArray[i]);
}
modificaElementi(mioArray);
for(int i=0; i<3; i++){
System.out.println(mioArray[i]);
}
}
public static void modificaElementi (int[] arr) {
for(int i=0; i<3; i++){
arr[i] = i*20;
}
} }
Quello che accade in questo caso è che l’aver invocato il metodo modificaElementi modifica l’array di partenza per cui i due cicli for che stampano in output i valori della variabile mioArray stampano valori diversi. L’output di questo programma è:
3 7 9 0 20 40
Oggetti
Una volta che un oggetto ha completato il lavoro per cui è stato creato, le sue risorse vengono riciclate per l’utilizzo da parte di altri oggetti.Ecco un piccolo programma, chiamato CreaOggettoRett, che crea un oggetto di tipo Rettangolo. Per prima cosa quindi creiamo la classe che definisce l’oggetto di tipo Rettangolo. Questa ha come variabili di classe, ovviamente, la base e l’altezza del rettangolo; inoltre sono presenti un costruttore e i metodi per calcolare il perimetro e l’area del rettangolo.La classe Rettangolo ha la seguente forma:
public class Rettangolo {
public int base = 0;
public int altezza = 0;
public Rettangolo(int b, int h) {
base = b;
altezza = h;
}
public int getArea() {
return base * altezza;
}
public int getPerimetro() {
return 2*(base + altezza);
}
}
Una volta definita la classe Rettangolo possiamo definire una classe che istanzia un oggetto di tipo Rettangolo:
public class CreaOggettoRett {
public static void main(String[] args) {
Rettangolo rett = new Rettangolo(50, 100);
int areaRettangolo = rett.getArea();
int perimetroRettangolo = rett.getPerimetro();
System.out.println(“L’area del rettangolo è: ”
+ areaRettangolo);
System.out.println(“Il perimetro del rettangolo è: ”
+ perimetroRettangolo);
} }
Vediamo in dettaglio cosa fa questa classe; per prima cosa viene creato un oggetto di tipo Rettangolo tramite:
Rettangolo rett = new Rettangolo(50, 100);
Questa istruzione definisce un oggetto di tipo Rettangolo chiamato rett. Attraverso l’operatore new
viene invocato il costruttore Rettangolo (che abbiamo definito nella classe Rettangolo) e che ci permette di creare tale oggetto.
Osserviamo che al costruttore abbiamo specificato due valori interi (50 e 100). Questo significa che non abbiamo invocato il costruttore di default ma quello da noi definito che ha come parametri la base e l’altezza dell’oggetto Rettangolo.
Abbiamo quindi creato un oggetto di tipo Rettangolo che ha la base con valore 50 e l’altezza con valore 100.
Dopo aver creato l’oggetto Rettangolo la classe CreaOggettoRett definisce due variabili di tipo: int: areaRettangolo e perimetroRettangolo.
Queste variabili vengono inizializzate subito utilizzando i metodi della classe Rettangolo che calcolano rispettivamente l’area ed il perimetro, attraverso le istruzioni:
int areaRettangolo = rett.getArea();
int perimetroRettangolo = rett.getPerimetro();
Commentiamo queste istruzioni; prima di tutto è necessario che le variabili areaRettangolo e perimetroRettangolo siano di tipo int in quanto i metodi che calcolano perimetro e area restituiscono un int.Per richiamare un metodo di un’altra classe si utilizza la forma: istanzaDellaClasse.nomeMetodo();
Creare un oggetto
Rettangolo rett = new Rettangolo(50, 100);
la creazione di un oggetto è composta di tre parti:
• Dichiarazione: viene associato un tipo ad un nome di variabile.
• Istanza: la parola chiave new è un operatore Java che crea l’oggetto.
•Inizializzazione: l’ operatore new è seguito da una chiamata ad un costruttore, che inizializza il nuovo oggetto.
Istanziare una classe
L’operatore new crea un’istanza di una classe allocando una parte di memoria per il nuovo oggetto e restituisce un riferimento a tale memoria. L’operatore new invoca anche il costruttore dell’oggetto.L’operatore new restituisce un riferimento all’oggetto creato. Questo riferimento è, di solito, assegnato ad una variabile del tipo appropriato, come:
Rettangolo ret=new Rettangolo(50,100);
Inizializzare un oggetto
Per inizializzare un oggetto è necessario, come abbiamo già detto, utilizzare il costruttore. Dopo aver creato un oggetto, probabilmente si desidera utilizzarlo per qualcosa.I campi degli oggetti sono accessibili con il loro nome.Se i campi vengono utilizzati all’esterno della classe alle quali appartengono è necessario utilizzare l’istruzione rappresentata dall’oggetto operatore punto seguito dal nome del campo al quale si vuole accedere, ovvero:
riferimentoOggetto.campo
Per cui se ad esempio volessimo stampare la base e l’altezza dell’oggetto Rettangolo all’interno della classe CreaOggettoRett:
System.out.println(“base del rettangolo: “+ rett.base);
Ricordiamo che l’operatore new restituisce un riferimento ad un oggetto. Così si potrebbe utilizzare il valore restituito per accedere ai campi di un oggetto nuovo:
int altezza = new Rettangolo().altezza;
Utilizzare un metodo di un oggetto
Abbiamo già accennato a come deve essere richiamato un metodo di un oggetto quando abbiamo utilizzato i metodi:
int areaRettangolo = rett.getArea();
Come nel caso dei campi, rett deve essere un riferimento a un oggetto (in questo caso Rettangolo). È possibile utilizzare un nome di variabile, ma è anche possibile utilizzare qualsiasi espressione che restituisce un riferimento all’oggetto.L’operatore new restituisce un riferimento all’oggetto, in modo da poter utilizzare il valore restituito e richiamare i metodi dell’ oggetto stesso:
new Rettangolo(50, 100).getArea();
La nuova espressione Rettangolo (50, 100) restituisce un riferimento ad un oggetto di tipo Rettangolo.
Come si vede, è possibile utilizzare la notazione del punto per richiamare il metodo getArea(), e calcolare l’area del nuovo rettangolo.
Garbage Collector
La gestione della memoriaè complessa e spesso si va incontro ad errori.La piattaforma Javaconsente di creare quanti oggetti si vogliono e non bisogna preoccuparsi di distruggerli.L’ambiente runtime Java elimina gli oggetti quando stabilisce che non sono più utilizzati. Questo processo è chiamato Garbage collection. Un oggetto, per la garbage collection, non è più indispensabile quando non ci sono più riferimenti a tale oggetto. E’ possibile eliminare in modo esplicito un riferimento a un oggetto impostando la variabile al valore speciale null.
Valore restituito da un metodo
Un metodo restituisce un valore al codice che lo ha invocato quando:
• Tutte le istruzione del metodo vengono completate • Viene raggiunta l’istruzione return
L’istruzione return si applica all’oggetto che deve essere restituito dal metodo, ovviamente il tipo dell’oggetto deve essere specificato nella dichiarazione del metodo stesso.l tipo del valore restituito deve corrispondere con il tipo di ritorno dichiarato con il metodo, non si può restituire un valore intero da un metodo che nella dichiarazione restituisce un valore booleano.
Ad esempio il metodo getArea() della classe restituisce un tipo intero:
public int getArea() {
return base * altezza
}
Il valore del tipo restituito viene valutato dall’espressione:
base * altezza
Il metodo getArea() restituisce un tipo primitivo ma metodo può anche restituire un qualsiasi tipo di oggetto.
Utilizzo della parola chiave this
All’interno di un metodo di una classe o di un costruttore, la parola chiave this è un riferimento all’oggetto a cui appartengono.
L’utilizzo più comune per l’utilizzo della parola chiave this è quando si vuole fare riferimento ad un campo non utilizzabile direttamente da un metodo o da un costruttore.
public class Rettangolo {
public int base = 0;
public int altezza = 0;
public Rettangolo(int base, int altezza) {
this.base = base;
this.altezza = altezza;
}
}
Controllo dell’accesso ai membri di una classe
I modificatori di accesso permettono di determinare se e in che modo altre classi possono utilizzare un particolare campo o richiamare un metodo. Ci sono due livelli di controllo di accesso:
• public, livello di accessibilità più alto
• private, protected, accessibilità limitata
Quando una classe viene dichiarata con il modificatore public è visibile a tutte le alte classi.Se una classe non ha modificatore l’impostazione di default la rende visibile solo all’interno del suo stesso package (gruppi di classi correlate).
Per i membri di una classe, ci sono due ulteriori modificatori di accesso: private e protected.
Il modificatore private specifica che il membro è accessibile solo nella classe in cui viene dichiarato.
Il modificatore protected specifica che il membro è accessibile solo dalle classi appartenenti allo stesso package della classe in cui è dichiarato. La tabella seguente mostra l’accesso ai membri riferiti con i vari modificatori.
Metodi di classe
Il linguaggio di programmazione Java supporta i metodi statici, questi hanno il modificatore static, e sono detti metodi di classe, possono essere invocati conil nome della classe, senza la necessità di creare un’istanza della classe, ad esempio:
NomeClasse.nomeMetodo(parametri)
Un uso comune dei metodi statici è quello di utilizzarli per accedere ai campi statici.
•I metodi di istanzapossono accedere direttamente alle variabili di istanza e ad altri metodi di istanza.
•I metodi di istanzapossono accedere direttamente alle variabili di classe e ai metodi di classe.
I metodi di classe possono accedere direttamente alle variabili di classe e metodi di classe.
I metodi di classenon possono accedere direttamente avariabili di istanza ometodi di istanza direttamente, devono utilizzare un riferimento ad un oggetto.
Costanti
Il modificatore static, in combinazione con il modificatore final, viene anche utilizzato per definire costanti.
Il modificatore final indica che il valore di questo campo non può cambiare.
static final double PI = 3.141592653589793;
I Pacakages
un package può essere considerato come un raggruppamento di classi e interfacce, legate logicamente tra loro, che fornisce una “protezione” di accesso ed una gestione del namespace.Un package può quindi, di primo impatto, essere considerato come una directory all’interno della quale sono presenti delle classi e/o delle interfacce.
Quando un classe o interfaccia si trova all’interno di un package è necessario specificarlo all’interno del codice della classe ne seguente modo:
package nomePackage;
public class miaClasse{}
…
}
Per utilizzare un membro pubblico, di una classe che si trova all’interno di un package, in una classe che si trova all’esterno del package è necessario importare l’intero package o la classe alla quale appartiene il membro che si vuole utilizzare.Per utilizzare un membro pubblico, di una classe che si trova all’interno di un package, in una classe che si trova all’esterno del package è necessario importare l’intero package o la classe alla quale appartiene il membro che si vuole utilizzare.Ad esempio supponiamo di avere una classe Rettangolo all’interno del package Forme e che la stessa classe abbia al suo interno definito il metodo getArea() che calcola l’area di un oggetto di tipo Rettangolo.Se volessimo creare un oggetto di tipo Rettangolo in una classe CreaRettangolo, che non di trova all’interno del package Forme, per poter richiamare il metodo getArea(), bisogna importare l’intero package Forme o la sola classe Rettangolo nella classe CreaRettangolo.
L’import ha la seguente forma:
import Forme.*; // per importare l’intero package
import Forme.Rettangolo; // per importare la sola classe
L’import deve essere specificato, in questo caso nella classe CreaRettangolo prima della definizione della classe ovvero:
import Forme.*;
public class CreaRettangolo{
//codice
}
I packages possono essere contenuti uno dentro l’altro, ad
esempio del il package Forme contenesse al suo interno altri
packages come Solide e Piane, per accedere alla classe Cubo
presente nel package Solide si esegue il seguente import: import Forme.Solide.*;
oppure
import Forme.Solide.Cubo;