









Studia grazie alle numerose risorse presenti su Docsity
Guadagna punti aiutando altri studenti oppure acquistali con un piano Premium
Prepara i tuoi esami
Studia grazie alle numerose risorse presenti su Docsity
Prepara i tuoi esami con i documenti condivisi da studenti come te su Docsity
Trova i documenti specifici per gli esami della tua università
Preparati con lezioni e prove svolte basate sui programmi universitari!
Rispondi a reali domande d’esame e scopri la tua preparazione
Riassumi i tuoi documenti, fagli domande, convertili in quiz e mappe concettuali
Studia con prove svolte, tesine e consigli utili
Togliti ogni dubbio leggendo le risposte alle domande fatte da altri studenti come te
Esplora i documenti più scaricati per gli argomenti di studio più popolari
Ottieni i punti per scaricare
Guadagna punti aiutando altri studenti oppure acquistali con un piano Premium
Riassunto dei concetti più importanti di prog 3 con esempi
Tipologia: Appunti
1 / 15
Questa pagina non è visibile nell’anteprima
Non perderti parti importanti!










La java reflection è un potente meccanismo di Java che permette di manipolare e ottenere informazioni sulle classi a runtime, in particolare sui metodi, sui campi, sui costruttori e sul nome della classe dell'oggetto. Si basa su 4 classi: Method, Field, Constructor e Class, ognuna delle quali offre dei metodi per ottenere le informazioni citate sopra (ad esempio: getMethods() restituisce un array con i metodi della classe, invoke e getParameters(), getFields() restituisce un array con i campo della classe, getConstructors() restituisce un array con i costruttori delle classi e getClass() permette di sapere a runtime il nome della classe dell'oggetto su cui viene invocato). Ha moltissime applicazioni ed usi, è usata durante il debugging per accedere ai campi e i metodi privati delle classi, usando metodi come getDeclaredFields() e getDeclaredMethods(), per fare dei test, è usata all'interno di molte librerie ed è usata per aumentare la robustezza del codice, ad es. può essere sfruttata, come instanceof, per verificare se due oggetti hanno la stessa classe, instanceof è più flessibile perché restituisce true anche se l'oggetto è di una sottoclasse, quindi considera le relazioni di ereditarietà, getClass() invece no.
es. class Quadrato { private int x;
public Quadrato(int lato) { x = lato; }
public int getLato() { return x; }
@Override public boolean equals(Object o) { if (o != null && o.getClass() == this.getClass()) { //al posto di o.getClass() == this.getClass() si sarebbe potuto usare o instanceof Quadrato Quadrato aus = (Quadrato) o; return aus.getLato() == this.getLato(); } else { return false; } } }
il modello di programmazione guidata dagli eventi, ovvero un modello event-driven, è caratterizzato dalla reattività. Il programma, infatti, al verificarsi di un evento, generato da una sorgente che può essere di vario tipo, nelle interfacce grafiche è tipicamente un componente grafico, deve reagire, ovvero deve far eseguire il codice predisposto alla gestione di quell'evento. (handler/listener registrati presso la sorgente e associati al
verificarsi di un evento). Ciò fa sì che, a differenza della programmazione sequenziale, quella ad eventi è asincrona e non segue quindi un flusso di esecuzione lineare in quanto, al verificarsi di un evento, esegue il codice degli handler.
Nelle GUI in java, che si usi SWING o javaFX non importa poichè cambia la sintassi ma il concetto alla base è il medesimo, ai componenti grafici( bottoni, finestre, caselle di testo ect..) vengono associati e registrati dei listener, istruzioni che devono essere eseguite al verificarsi di un evento che, nelle GUI java, è tipicamente un'azione dell'utente. La programmazione ad eventi è reattiva in quanto, all'azione di un utente deve corrispondere una gestione e una risposta specifica. ciclo di vita degli eventi: evento generato da una sorgente evento "ascoltato" da un listener registrato presso la sorgente il listener esegue il codice associato all'evento.
es. SWING
class ButtonEs extends JFrame{
public ButtonEs(){ JFrame frame = new JFrame("finestra"); JPanel panel = new JPanel(); JButton btn = new JButton("cambia background"); btn.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e){ panel. setBackground(Color.YELLOW); } });
frame.add(panel); panel.add(btn); frame.setSize(300,300); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true);
}
}
con lambda
btn.addActionListener( e -> {setBackground(Color.YELLOW);});
es con javaFX:
public class BottoneEs extends Application{ public static void main(String[] args){
che entrano in conflitto (se non quelli che operano sull'oggetto stesso). Questo modello di sincronizzazione vuole proteggere l'accesso e la modifica di variabili condivise di un oggetto e funziona sfruttando il fatto che ogni oggetto abbia un proprio lock. I metodi quindi vengono dichiarati utilizzando la parola synchronized dopo lo specificatore di visibilità del metodo: es. public synchronized void add(..){..}
3)es.
public class Stampante{
private int i; public Stampante(){ i = 0; } public synchronized void stampa(int j){ System.out.println(i +j); i++; } public static void main(String[]args){ Stampante st= new Stampante(); Thread t1 = new StampaThread(st); Thread t2 = new StampaThread(st); t1.start(); t2.start(); }
}
class StampaThread extends Thread{ private Stampante st; public StampaThread(Stampante st){ this.st = st; } public void run(){
for(int j = 0; j++; j<100){ st.stampa(j); } } }
domanda: Si descriva in dettaglio il pattern Observer-Observable e si faccia un semplice esempio di utilizzo di tale pattern in un’applicazione java. NB: per dimostrare l’uso del pattern NON è necessario sviluppare un’applicazione con interfaccia grafica, si può sviluppare un’applicazione senza interfaccia.
la mia risposta: Il pattern Observer-Observable è un pattern molto usato e svincolato da un linguaggio di programmazione specifico. Permette a un oggetto (Observable) di notificare automaticamente ai suoi osservatori (Observer) qualsiasi cambiamento nel suo stato, mantenendo un basso accoppiamento tra gli oggetti. Non è un pattern che si usa solamente nelle GUI. Noi lo abbiamo visto a proposito delle GUI java. L'esempio e le istruzioni che userò sono quelle della classe Observable e dell'interfaccia Observer, ormai deprecate e superate con PropertyChangeListener in JavaBeans o ObservableList in JavaFX. Si creano due classi una, quella dell'oggetto da monitorare, che estende la classe Observable e l'altra, quello dll'oggetto che osserva, che implementa Observer. Ogni volta che avviene una modifica dello stato dell'oggetto Observable, si devono invocare due istruzioni: setChanged() che rende persistenti i cambiamenti e notifyObservers(//eventuale parametro) che notifica che è avvenuto un cambiamento agli Observer registrati presso di esso (con istruzione addObserver tipicamente invocata nel main o dove si creano gli oggetti), l'oggetto Observer invece, quando viene invocata la notifyObservers(), invoca il metodo update (public void update(Observable o, Object arg)) ed esegue il codice opportuno.
es.
class Prezzo extends Observable{ private int prezzo; public Prezzo(int prezzo){ this.prezzo = prezzo; } public void setPrezzo(int nuovoPrezzo){ prezzo =nuovoPrezzo; setChanged(); notifyObservers(); } public int getPrezzo(){ return prezzo;
La clausola extends in un tipo parametrico specifica un upper bound (limite superiore) per il tipo generico. Questo significa che il tipo generico può essere sostituito solo con: ● La classe specificata dopo extends. ● Le sue sottoclassi (se è una classe). ● Le implementazioni (se è un'interfaccia). Ad esempio, se si definisce un tipo generico come T extends Number, T può essere sostituito solo con Number o con le sue sottoclassi (ad esempio Integer, Double, ecc.).
Tipo grezzo (raw type):
Un tipo grezzo (raw type) è un tipo generico utilizzato senza specificare un tipo parametrico. L'uso di raw type è sconsigliato perché bypassa i controlli di tipo introdotti dai generics, rendendo il codice meno sicuro e più soggetto a errori a runtime.
A tempo di compilazione : Il compilatore effettua il type checking e garantisce la sicurezza del tipo.
● A tempo di esecuzione : Le informazioni sui tipi generici vengono rimosse tramite l'erasure. Questo significa che, a runtime, tutti i tipi generici sono trattati come Object o come il tipo specificato nella clausola extends.
Ecco un esempio di una classe generica con la clausola extends:
class Lista { // T può essere Number o una sua sottoclasse private List lista; // Lista di elementi di tipo T private int num_el; // Numero di elementi
public Lista() { lista = new ArrayList<>(); // Inizializza la lista num_el = 0; }
// Aggiunge un elemento alla lista public void add(T el) { lista.add(el); num_el++; }
// Trova l'elemento massimo nella lista public T max() { if (lista != null && num_el > 0) { T max = lista.get(0); // Assume che il primo elemento sia il massimo for (T el : lista) { if (el.doubleValue() > max.doubleValue()) { // Confronta i valori max = el; } } return max; // Restituisce l'elemento massimo } return null; // Se la lista è vuota, restituisce null }
Overloading è la possibilità di avere più metodi all'interno di una stessa classe con lo stesso nome, essi devono però differire per parametri di input, nel tipo e/o nel numero, ed eventualmente nel tipo di ritorno, n.b: il tipo di ritorno da solo non è sufficiente per distinguere due metodi in overloading. Devono differire per i parametri. L'overloading nei linguaggi OO si applica quindi ai metodi. L'overriding si applica sempre ai metodi ma consiste nella modifica e/o estensioni di metodi delle sopraclassi, in Java della classe Object o delle classi che vengono estese tramite la cluasula extends, La firma dei metodi ereditati quindi rimane uguale e si può modificare il corpo del metodo, se si vuole estendere, ovvero aggiungere qualche istruzione rispetto a quello della classe genitore, si può non copiare tutto il codice del metodo ereditato ma creare un riferimento utilizzando super() e poi aggiungendo le istruzioni necessarie.
es.
class Prodotto{
private String name; private int prezzo;
public Prodotto(String name, int prezzo){ this.prezzo = prezzo; this.name = name;
il sovraccarico associato alla creazione e distruzione di thread, migliorando le prestazioni e la scalabilità dell'applicazione.
Elementi architetturali: Basato su un numero fisso di thread pre-creati. Un numero fisso di thread esegue i task. Se tutti i thread sono occupati, i task vengono messi in una coda in attesa che un thread diventi disponibile. Garantisce un numero massimo di thread in esecuzione simultaneamente, prevenendo l'eccesso di utilizzo delle risorse di sistema.
Elementi architetturali: Basato su un pool di thread che possono eseguire task dopo un certo ritardo o periodicamente. I task possono essere schedulati per essere eseguiti una sola volta dopo un ritardo specificato o ripetutamente a intervalli fissi. Adatto per task che devono essere eseguiti periodicamente o con ritardi predefiniti.
I thread pool, in java, sono gestiti tramite le classi del framework concurrent, in particolare attraverso le factory della classe Executors.
Il pattern Model-View-Controller (MVC) è un pattern architetturale utilizzato per organizzare il codice in applicazioni software, rendendolo più modulare, mantenibile e scalabile. È particolarmente utile nelle applicazioni con interfacce utente, come le applicazioni web o desktop. Il pattern MVC separa la logica dell'applicazione in tre componenti principali:
○ Gestisce l'input dell'utente (ad esempio, clic su pulsanti o inserimento di dati in un form), esegue eventuali operazioni di elaborazione e aggiorna il Model di conseguenza. ○ Il Controller decide anche quale View mostrare in base alle azioni dell'utente e allo stato dell'applicazione. Vantaggi del pattern MVC : ● Separazione delle responsabilità : Ogni componente ha un ruolo ben definito, il che rende il codice più organizzato e facile da mantenere. ● Riusabilità : Il Model e il Controller possono essere riutilizzati in diverse View, facilitando lo sviluppo di applicazioni con interfacce multiple (ad esempio, web e mobile). ● Testabilità : La separazione delle logiche rende più semplice testare i singoli componenti in modo isolato. Relazione con altri pattern : ● Il pattern MVC è spesso utilizzato in combinazione con il pattern Observer (o Publish-Subscribe ), dove il Model notifica le View dei cambiamenti di stato. Questo permette di mantenere un basso accoppiamento tra i componenti, rendendo il sistema più flessibile e modulare.
visibilità in java: private, protected, public e di default (senza specificatore) private: visibile solo dalla stessa classe public: visibile da tutte le classi indipendentemente dalla locazione protected: visibile solo da classi nello stesso package o sottoclassi default: visibile solo da classi nello stesso package
Vengono usate, per esempio, da javaFX per legare i dati del model tramite binding alle componenti dell'interfaccia grafica che devono reagire ai cambiamenti in automatico In sintesi, le properties e i JavaBean sono concetti fondamentali in Java per garantire un design pulito, modulare e rispettoso dei principi della programmazione orientata agli oggetti.
In Java, l' I/O (Input/Output) è un meccanismo essenziale per gestire il flusso di dati tra un programma e diverse fonti, come file, dispositivi di rete, o la console. Java offre due approcci principali per l'I/O: il tradizionale I/O basato su stream e il più moderno I/O basato su canali (NIO). Entrambi gli approcci hanno i loro vantaggi e sono adatti a scenari diversi.
L'I/O tradizionale in Java si basa sul concetto di stream , che rappresenta un flusso di dati. Gli stream possono essere di due tipi: byte stream e character stream. I byte stream sono progettati per gestire dati binari, come immagini o file eseguibili, mentre i character stream sono ottimizzati per dati testuali, come file di testo. Le classi principali per i byte stream sono InputStream e OutputStream, mentre per i character stream si utilizzano Reader e Writer. Queste classi astratte sono estese da implementazioni concrete, come FileInputStream, FileOutputStream, FileReader, e FileWriter, che permettono di lavorare direttamente con file. L'I/O basato su stream è semplice e intuitivo, ma può essere inefficiente per operazioni su grandi volumi di dati, poiché legge o scrive i dati un byte o un carattere alla volta. Per migliorare le prestazioni, è possibile utilizzare stream con buffer, come BufferedInputStream o BufferedReader, che riducono il numero di accessi alla sorgente di dati.
Il pacchetto java.nio (New I/O) introduce un approccio più avanzato e performante per l'I/O, basato su buffer e canali. I buffer sono contenitori di dati che permettono di leggere o scrivere grandi blocchi di informazioni in una sola operazione, riducendo il numero di accessi alla sorgente. I canali, invece, rappresentano una connessione a una fonte o destinazione di dati, come file o socket, e permettono di trasferire dati in modo efficiente. Un altro vantaggio di NIO è la gestione avanzata dei file e delle directory attraverso le classi Path e Files. Queste classi semplificano operazioni come la creazione, la copia, la cancellazione e la lettura di file, offrendo metodi di alto livello che riducono la complessità del codice.
Il binding in Java è un concetto che si riferisce alla capacità di collegare due proprietà o componenti in modo che i cambiamenti in uno si riflettano automaticamente nell'altro. Questo meccanismo è particolarmente utile in contesti come lo sviluppo di interfacce grafiche, dove è necessario sincronizzare lo stato di un'interfaccia utente con i dati sottostanti, o in applicazioni che richiedono una gestione reattiva dei dati.
Il binding può essere di due tipi: unidirezionale o bidirezionale. Nel binding unidirezionale, una proprietà segue i cambiamenti di un'altra, ma non viceversa. Ad esempio, se si collega un'etichetta (Label) a un campo di testo (TextField), l'etichetta si aggiornerà automaticamente quando il contenuto del campo di testo cambia, ma non è vero il contrario.
Nel binding bidirezionale, invece, due proprietà sono sincronizzate reciprocamente. Un cambiamento in una delle due proprietà si riflette immediatamente nell'altra. Questo è particolarmente utile in scenari in cui due componenti devono rimanere sempre allineati, come nel caso di due campi di testo che rappresentano la stessa informazione in formati diversi.
JavaFX, il framework per la creazione di interfacce grafiche in Java, utilizza estensivamente il concetto di binding attraverso le properties. Le properties in JavaFX non sono semplici variabili, ma oggetti che supportano notifiche di cambiamento e binding. Ogni property può essere collegata a un'altra, permettendo una sincronizzazione automatica dei dati.
Ad esempio, in un'interfaccia utente, è possibile collegare una proprietà di un controllo grafico (come il testo di un'etichetta) a una proprietà di un modello dati (come il nome di un utente). Quando il valore del nome cambia nel modello, l'etichetta si aggiorna automaticamente senza bisogno di intervento manuale.
Il binding offre numerosi vantaggi:
Sebbene il binding sia spesso associato alle interfacce grafiche, il concetto può essere applicato anche in contesti non grafici. Ad esempio, in un'applicazione che gestisce dati complessi, è possibile utilizzare il binding per sincronizzare diverse parti del modello dati, garantendo che i cambiamenti in un'area si propaghino automaticamente alle altre.
Il binding è un potente strumento in Java che permette di collegare proprietà o componenti in modo dinamico, garantendo una sincronizzazione automatica e una gestione reattiva dei