Docsity
Docsity

Prepara i tuoi esami
Prepara i tuoi esami

Studia grazie alle numerose risorse presenti su Docsity


Ottieni i punti per scaricare
Ottieni i punti per scaricare

Guadagna punti aiutando altri studenti oppure acquistali con un piano Premium


Guide e consigli
Guide e consigli


Algoritmi e programmazione: classi e istanze, Appunti di Algoritmi E Programmazione Avanzata

Appunti del corso di ALGORITMI E PROGRAMMAZIONE del prof. Fabio Sartori. Classi e istanze in java

Tipologia: Appunti

2022/2023

In vendita dal 05/06/2023

rossimartaa
rossimartaa 🇮🇹

4.3

(18)

156 documenti

1 / 13

Toggle sidebar

Questa pagina non è visibile nell’anteprima

Non perderti parti importanti!

bg1
CLASSI E ISTANZE IN JAVA
Abbiamo introdotto il mondo degli oggetti come una classificazione che parte dal mondo che ci
circonda e per astrazione, a partire dagli oggetti concreti, ci porta a definire il concetto di classe. La
classe è proprio quell’entità che ci permette di definire la rappresentazione di qualsiasi oggetto
Chi chiediamo quindi come il continuo scambio fra entità astratta e concreta viene realizzato.
DEFINIZIONE DI CLASSE IN JAVA
Finora abbiamo usato le classi come definizione di programma, snaturando quindi il costrutto di
classe. In realtà la classe, nel mondo del linguaggio ad oggetti, serve come modello per creare quelli
che sono gli oggetti, tramite l’istanziazione.
A meno che non si usino classi già scritte da altri, prima di poter creare un qualsiasi oggetto devo
creare la sua rappresentazione: cioè la classe.
Si ricorda che un programma ad oggetti consiste di oggetti creati a partire da classi diverse che
interagiscono fra loro
public [altri modificatori] class NomeClasse{
// Definizione Attributi di istanza e/o di classe
// Implementazione Costruttori
// Implementazione Metodi di istanza e/o di classe
}
Ognuno dei commenti andrà successivamente implementato.
RAPPRESENTAZIONE IN UML
La classe Punto in un diagramma delle classi di design è rappresentata come in figura
Sintassi:
Modificatore_di_visibilità Identificatore_Dell’attributo: tipo
Modificatore_di_visibilità:
+ indica public (appartenente all’interfaccia della classe).
Permette di attribuire modificatori di visibilità pubblico all’attributo (appartiene
all’interfaccia della classe).
(La rappresentazione è coerente con la nostra idea di punto: una rappresentazione di punto prevede
l’associazione di due coordinate nel piano)
pf3
pf4
pf5
pf8
pf9
pfa
pfd

Anteprima parziale del testo

Scarica Algoritmi e programmazione: classi e istanze e più Appunti in PDF di Algoritmi E Programmazione Avanzata solo su Docsity!

CLASSI E ISTANZE IN JAVA

Abbiamo introdotto il mondo degli oggetti come una classificazione che parte dal mondo che ci circonda e per astrazione, a partire dagli oggetti concreti, ci porta a definire il concetto di classe. La classe è proprio quell’entità che ci permette di definire la rappresentazione di qualsiasi oggetto Chi chiediamo quindi come il continuo scambio fra entità astratta e concreta viene realizzato. DEFINIZIONE DI CLASSE IN JAVA Finora abbiamo usato le classi come definizione di programma, snaturando quindi il costrutto di classe. In realtà la classe, nel mondo del linguaggio ad oggetti, serve come modello per creare quelli che sono gli oggetti, tramite l’istanziazione. A meno che non si usino classi già scritte da altri, prima di poter creare un qualsiasi oggetto devo creare la sua rappresentazione: cioè la classe. Si ricorda che un programma ad oggetti consiste di oggetti creati a partire da classi diverse che interagiscono fra loro public [altri modificatori] class NomeClasse{ // Definizione Attributi di istanza e/o di classe // Implementazione Costruttori // Implementazione Metodi di istanza e/o di classe } Ognuno dei commenti andrà successivamente implementato. RAPPRESENTAZIONE IN UML La classe Punto in un diagramma delle classi di design è rappresentata come in figura Sintassi: Modificatore_di_visibilità Identificatore_Dell’attributo: tipo Modificatore_di_visibilità:

  • indica public (appartenente all’interfaccia della classe). Permette di attribuire modificatori di visibilità pubblico all’attributo (appartiene all’interfaccia della classe). (La rappresentazione è coerente con la nostra idea di punto: una rappresentazione di punto prevede l’associazione di due coordinate nel piano)

ESEMPIO: LA CLASSE PUNTO

Definisco la classe e il nome della classe. Gli attributi sono chiamati attributi di istanza perchè caratterizzano ciascuna istanza della classe punto: si rappresentano con variabili definite all’interno della classe (diverso dalle variabili locali, non definite all’interno del main). Gli attributi di istanza caratterizzano in maniera specifica l’oggetto creato a partire dalla classe. Le variabili cambiano la propria funzione a seconda di dove vengono dichiarate, anche se dal punto di vista logico rimangono variabili. Il modificatore di visibilità viene definito prima della dichiarazione del tipo. Gli attributi della classe x e y appartengono all’interfaccia pubblica della classe: visibili pubblicamente nel sistema. COME SI CREA UN OGGETTO? Abbiamo già creato oggetti: esempio della scanner o degli array. Infatti gli array, tipi non primitivi, si implementano come degli oggetti. Gli oggetti si creano con l’operatore new : il principio di base è simile alla creazione e implementazione di variabili di tipo primitivo. Quando si creano oggetti si passa attraverso la creazione di un reference. L’istruzione NomeClasse nomeReference dichiara, ma non crea l’oggetto: non ho allocazione di memoria. Sto quindi indicando il fatto che voglio creare un oggetto che fisicamente non esiste ancora: esisterà nel momento in cui a runtime (dando il comando Java) il new NomeClasse verrà effettivamente istanziato, con tutte le caratteristiche proprie definite dalla classe. A questo punto si usa la “dot notation” per avere accesso all’interfaccia pubblica: tutti gli accessi all’interfaccia pubblica dell’oggetto definita dalla classe derivano dalla dot notation (stringhe). Tutto ciò che appartiene all’interfaccia pubblica dell’oggetto può essere interrogato tramite la dot notation. Quest’anno scriveremo classi in maniera indipendente come nomeReference.Comandi() I comandi saranno quindi implementati come metodi, mentre gli attributi conterranno variabili.

Punto p2 = new Punto(); //int k; //v = k; int k = 3; boolean esito; esito = (v == k); System.out.println(esito); esito = (p1 == p2); confronta gli oggetti System.out.println(esito); p1.x = 1; 1 p1.y = 2; 2 p2.x = 2; 1 p2.y = 3; 2 System.out.println(p2.y); System.out.println(p2); } } Il codice crea due punti e li confronta. Il primo output che verrà stampato sarà true perchè v è uguale a k (han lo stesso valore). Nel momento in cui confronto p1 e p2, il risultato del confronto sarà false perchè p1 e p2 sono due entità diverse, hanno diversi indirizzi di memoria; con il comando new ho creato i due oggetti ma non so ancora che valore assumono (assegno il loro valore successivamente). In ogni caso, anche provando ad assegnare alle coordinate di x e y gli stessi valori (in grassetto ), l’esito dell’operazione rimarrà false, appunto perché il comando usato confronta gli indirizzi di memoria. Due oggetti con stesso stato e comportamento, sono diversi: l’identità dei due oggetti sarà diversa a meno che non costruisca un alias (quando assegno un reference ad un altro reference). Quindi se creassi un punto p3 = p2 e li confrontassi, l’esito dell’operazione sarebbe true. ESEMPIO: UTILIZZO DELLA CLASSE PUNTO Ogni attributo di istanza possiede un tipo!

ESEMPIO

Creare due punti e assegnare i valori per x e y 3 e 4 e 50 e 90. Stampare a video quale punto ha x maggiore. p1.x = 3 : assegna il valore 3 all’ascissa x del punto p1. Posso fare ciò perchè x appartiene all'interfaccia pubblica del punto p1. Quando vado ad estrarre il valore, quei valori sono a tutti gli effetti dei valori interi. Nel momento in cui creo p1, quello è un reference. Se avessi due attributi, l’estrazione della variabile di x e y, estrae due variabili di tipo primitivo che si comportano come variabili di tipo primitivo dell’anno scorso. Ho quindi x e y, valori interi, su cui posso applicare gli stessi operatori. OGGETTI E REFERENCE Variabili di tipo classe (tipo p1 o p2 dell’esempio precedente) sono molto differenti rispetto a variabili di tipo primitivo (come int o char) Quando creo un alias, da quel momento in avanti, tutte le modifiche che faccio su un oggetto, verranno viste anche dall’altro oggetto. Quindi se in un flusso come questo trovassi un’istruzione del tipo p2 = p1 (che unifica l’identità), le istruzioni andranno di pari passo (meccanismo del garbage collector): la memoria di p2 verrà cancellata e sostituita da quella di p1. p1 e p2 saranno due reference che andranno a vedere la stessa area di memoria.

VARIABILI, STACK, HEAP, PARAMETRI

Vediamo bene cosa succede in memoria: Esempi informali di “cosa succede” quando

  • si dichiara una variabile
  • si stanzia una classe
  • si effettuano assegnamenti
  • si passano parametri Gli esempi
  • sono in forma intuitiva
  • non hanno la pretesa di essere completamente rigorosi
  • sono basati su Java ma valgono in generale anche per altri linguaggi (es. C++) Suggerimento: nel dubbio fare disegni. VARIABILI E MEMORIA A livello “linguaggio macchina” (o Assembler) la memoria è organizzata logicamente come un insieme di locazioni accessibili attraverso un indirizzo
  • NB: in realtà non è la vera organizzazione fisica (vedi “Memoria virtuale” in Architetture e Sistemi Operativi). A livello di linguaggio evoluto (Java etc.) la memoria è organizzata come un insieme di zone gestite in modo specifico (zone diverse hanno meccanismi di allocazione diversi). Una variabile corrisponde a un indirizzo di memoria
  • che identifica una locazione (o un insieme di locazioni) allocate nella zona opportuna
  • zone diverse hanno meccanismi di allocazione diversi… Quindi ho una differenza di rappresentazione fra ciò che è la macchina e ciò che è il linguaggio.

VARIABILI: DOVE SONO, QUANDO ESISTONO?

Se rappresentiamo la memoria come un tutt’uno, dobbiamo ricordarci che non tutte le variabili sono uguali: avremo variabili differenti a seconda del loro ciclo di vita. Alcune variabili saranno in vita per tutta la durata del programma (dichiarate nel main) e altre che avranno una vita temporanea (dichiarate in un blocco di istruzioni). Dal punto di vista della classificazione, posso avere

  • variabili statiche ( attributi di classe ): sono allocate al lancio del programma e il loro tempo di vita equivale all’esecuzione del programma. Vengono definite in un’area di memoria che prende il nome di static.
  • variabili dinamiche ( e parametri ): sono allocate all’atto della chiamata di un metodo e hanno tempo di vita equivalente all’esecuzione del metodo. Vengono definite in un’area di memoria che prende il nome di stack : ogni volta che chiamo un metodo viene allocato nello stack (come in una pila).
  • heap : contiene variabili dinamiche e vengono allocate a fronte di un’azione new () in Java. Tutto ciò che viene dichiarato new in Java viene allocato in quest’area di memoria: la gestione della memoria dinamica è automatica in Java. Quando l’oggetto non mi serve più, viene deallocato automaticamente. IL METODO MAIN
  • main è un metodo chiamato automaticamente al momento del lancio di un programma Quindi le variabili dichiarate nel main sono allocate in stack e rimangono allocate fino all’uscita dal main (che di solito coincide con la terminazione del programma)
  1. Con il comando Persona a,b; vengono create nello stack due celle di memoria per contenere reference delle due persone, che ancora non ho.
  2. Quando faccio a = new Persona(); nell’heap vengono create due celle di memoria per a, una per il nome e una per il costo. Dato che persona è un oggetto il nome sarà null (stringa) e il costo 0 : agli attributi di persona vengono dati dei valori standard che dipendono dalla natura dell’attributo stesso. Ognuno avrà il suo indirizzo di memoria.
  3. L’uguaglianza fa sì che venga creato il reference fra l’indirizzo di memoria nell’heap e la casella nello stack: ho quindi creato il reference effettivo e il rapporto fra le due aree di memoria.
  4. Quando poi assegno il costo, seguo il reference: da a seguo il reference fino all’indirizzo nell’heap. Con la dot notation estraggo il costo ed assegno un valore.
  5. a.nome = Rossi: seguo il reference e questo mi porta ad una stringa. La stringa è un oggetto immutabile (una volta definita, non posso cambiarne il nome): Java la considera come se fosse un oggetto: crea una cella di memoria nell’heap, le attribuisce il valore Rossi (stringa) e un indirizzo di memoria. Nel campo della persona a, non vedo direttamente Rossi ma vedrò il reference all’oggetto Rossi (è tutto gestito a runtime). - Quando ho un oggetto, viene sempre creato un reference. - Quando ho un valore primitivo, viene scritto il suo valore all’interno della memoria.
  6. Itero il procedimento con la persona b.
  7. Quando costruisco l’alias b=a, mi porta alla sovrascrittura del reference. Il reference che prima in b era 41917 diventa 37412: questo significa che l’area che prima era quella di b non ha più il reference. Verrà quindi eliminata con il meccanismo di garbage collector. All'inizio Verdi rimane perché ha ancora un reference che lo punta; nel momento in cui elimino il reference che punta Verdi, anche Verdi viene eliminato. Da questo momento, a e b vanno di pari passo.

LA COSTANTE NULL

Si indica con null la costante utilizzata per inizializzare un reference ad un valore di default

  • usato anche per verificare se il reference è stato correttamente inizializzato dopo la dichiarazione. Null non identifica un valore ma identifica il vuoto: inizializzando a null un oggetto, devo sempre controllare che nel momento in cui utilizzo l’oggetto, questo non sia null. Esempio : Punto p1 = null; if (p1 == null) { p1 = new Punto(); L’oggetto a cui mi riferisco deve essere diverso da null. Se il reference è null, l’oggetto va inizializzato. In questo caso ho un’eccezione: p1 è stato caratterizzato come coordinate x e y. Se non creo il punto, non avrò le sue coordinate e quindi non potrò accedervi. Per questo l’eccezione. } else { System.out.println(“oggetto già istanziato”); } Se immaginassi di stampare le coordinate del punto p4, con Punto p4 = null; System.out.println(p4.x); System.out.println(p4.y); Quando provo a fare l’accesso alle variabili x e y, mi darà eccezione. Devo crearle prima. Il compilatore deve controllare che dal punto di vista grammaticale sia tutto corretto: formalmente, dal punto di vista grammaticale, non ho errori. Ho infatti un assegnamento di un punto ai suoi valori di default e poi la stampa delle sue coordinate. A runtime non si trova la rappresentazione dell’oggetto in memoria: viene quindi sollevata un’eccezione. p4 non ha una rappresentazione di memoria. Quando l’interprete cerca di tradurre ed eseguire le istruzioni, trova un’eccezione. Se invece avessi scritto Punto p4; System.out.println(p4.x); System.out.println(p4.y); Il compilatore avrebbe rilevato il fatto che la variabile p4 non è stata inizializzata, pur essendo formalmente la stessa cosa del caso precedente. Non posso utilizzare la chiamata nel contributo x perchè non l’ho inizializzata precedentemente. (Se il valore fosse una stringa, stamperebbe null) Se volessi rimuovere un oggetto dalla memoria, dovrei metterle un reference analogo a null: cancello quindi la sua memoria. Elimino il reference dalla sua rappresentazione e quindi, grazie al garbage collector, elimino la sua reference nell’heap.

L’OPERATORE == ALTRO ESEMPIO

La prima stampa false, la seconda true LA KEYWORD INSTANCEOF Permette di verificare se un oggetto è stato istanziato a partire dalla classe specificata: quando non so se un oggetto è istanza di una certa classe lo posso chiedere esplicitamente. Ritorna true se un oggetto è istanza di una determinata classe. Sintassi: reference instanceof NomeClasse Esempio: Punto p1; p1 = new Punto (); if (p1 instanceof Punto) System.out.println (“p1 referenzia un oggetto” + “di tipo Punto”);