mattone
dopo mattoneLA COLLANA
IT/EN
← Guida Java

Java 25 · 34/39

Attività C19–C24, prima stesura

Java 25 · Guida completa · Bozza in revisione

Questa guida conserva lo stato di revisione del libro. La verifica editoriale e le prove di comprensione con lettori indipendenti sono ancora da completare.

Cerca in tutta la guida →

Queste attività accompagnano le bozze. Si svolgono su copie dei sorgenti con JDK 25. Le prove C19–C21 e C24 usano API stabili; C23 richiede il modulo incubator dichiarato nel capitolo. I criteri aiutano a distinguere un risultato osservato da una garanzia dell'API. L'apparato completo e la prova con lettori restano da verificare con i gate delle esercitazioni.

C19-A01 – Un ripiego non richiesto

Consegna. Nel programma Optional, prevedi quante volte viene chiamato nomeDiDefault() quando la ricerca trova un libro e il chiamante usa prima orElse(nomeDiDefault()), poi orElseGet(DemoOptional::nomeDiDefault). Esegui e confronta. Ripeti con una ricerca vuota.

Criterio e soluzione. Nel caso presente orElse valuta comunque l'argomento; orElseGet non chiama il fornitore. Nel caso vuoto entrambe le forme calcolano il ripiego una volta per chiamata. La differenza riguarda il momento di valutazione dell'argomento, non il valore finale se il fornitore restituisce sempre la stessa stringa.

C19-P01 – Quale assenza stiamo descrivendo?

Consegna. Aggiungi un libro senza autore e confronta cerca(codice).map(DemoOptional::autore) con cerca(codice).flatMap(DemoOptional::autore). Scrivi il tipo di entrambe le espressioni e spiega cosa significa un risultato vuoto dopo flatMap.

Criterio e soluzione. map produce Optional<Optional<String>>; flatMap produce Optional<String>. Quest'ultimo può essere vuoto perché manca il libro o perché il libro non ha autore. Se l'applicazione deve spiegare quale dei due casi è avvenuto, l'appiattimento da solo non basta.

C20-A01 – run non avvia il thread

Consegna. Prima di eseguire AvvioEJoin, prevedi le righe con il nome del thread e gli stati prima di start, dopo run diretto e dopo join. Spiega perché join rende affidabile l'ultimo stato osservato.

Criterio e soluzione. Il lavoro chiamato direttamente stampa main e non cambia NEW. Il lavoro dopo start stampa lavoratore; dopo join l'esecuzione è conclusa e lo stato è TERMINATED. La spiegazione deve riferirsi al contratto di start e join, non alla velocità della macchina.

C20-P01 – Contare senza affidarsi alla fortuna

Consegna. Nel contatore protetto, individua il monitor usato dai due thread e spiega perché il risultato è 20000. In una copia rimuovi synchronized soltanto da incrementa, esegui alcune volte e annota i risultati, senza assumere che ogni esecuzione mostri il difetto.

Criterio e soluzione. La versione corretta protegge gli incrementi con lo stesso monitor e main attende entrambi i lavoratori. La versione modificata contiene una data race e non garantisce 20000, anche se può stamparlo per caso. Il test non deve fallire perché una singola esecuzione difettosa appare corretta.

C20-D01 – La fine di due consumatori

Consegna. Nel produttore-consumatore, immagina di avviare due consumatori sulla stessa coda. Quante sentinelle di fine servono perché entrambi possano uscire dal ciclo? Spiega perché non puoi prevedere quale consumatore prenderà ciascun valore.

Criterio e soluzione. Con quel protocollo serve una sentinella per ogni consumatore. La coda assegna ciascun elemento a un solo take, ma la scelta del consumatore dipende dalla pianificazione. Un test corretto verifica che i valori siano consumati una volta e che i due lavoratori terminino, non l'ordine dei loro nomi nelle stampe.

C20-V01 – Quando scegliere un thread virtuale

Consegna. Un servizio riceve in media 2.000 richieste al secondo. Ciascuna dura 50 millisecondi, soprattutto perché aspetta un archivio remoto. Stima quante richieste sono contemporaneamente in corso e spiega quale limite dei thread di piattaforma motiva l'uso dei thread virtuali. L'archivio accetta però al massimo 20 accessi simultanei: proponi un limite esplicito. Ripeti infine la scelta per un lavoro che calcola senza attese su tutti i core disponibili.

Criterio e soluzione. In media sono in corso circa 100 richieste (2.000 × 0,050). Un thread di piattaforma per richiesta trattiene un thread del sistema operativo anche durante l'attesa; un thread virtuale può sospendersi e liberare il carrier, mantenendo il codice lineare. Il maggior numero di thread virtuali non aumenta la capacità dell'archivio: un Semaphore con 20 permessi può limitare gli accessi. Per un lavoro che usa continuamente la CPU, il limite è nei core e non nell'attesa; creare molti thread virtuali non accelera il singolo calcolo. La risposta deve distinguere concorrenza, throughput e latenza.

C21-A01 – Il null del bootstrap

Consegna. Esegui OsservaClassLoader. Individua quale classe restituisce null da getClassLoader() e spiega perché questo non indica una classe mancante. Evita di usare il suffisso dell'oggetto loader stampato come dato da memorizzare.

Criterio e soluzione. Nell'ambiente verificato ArrayList è definita dal bootstrap, rappresentato da null in questa API. I nomi interni e le identità stampate dei loader dipendono dall'implementazione e dall'esecuzione.

C21-P01 – Due errori, due richieste

Consegna. Spiega il diverso punto di partenza di Class.forName("esempio.Mancante") e di una classe B già compilata che contiene new A() ma viene eseguita dopo la rimozione di A.class. Distingui tipo e natura del fallimento.

Criterio e soluzione. La ricerca esplicita può lanciare ClassNotFoundException, eccezione controllata. L'uso di un riferimento necessario nel bytecode può produrre NoClassDefFoundError, un Error; il messaggio può contenere una ClassNotFoundException come causa. La compilazione precedente di B non carica A nella JVM che eseguirà il programma.

C23-A01 – Il resto del vettore

Consegna. Compila SommaVector con javac --release 25 --add-modules jdk.incubator.vector, eseguilo con la stessa opzione di modulo e varia la lunghezza degli array. Spiega la funzione di loopBound e del ciclo scalare finale.

Criterio e soluzione. I gruppi completi vengono elaborati dal ciclo vettoriale; gli elementi rimanenti dal ciclo scalare. Il numero di corsie non è un output fisso da confrontare fra computer. L'avviso sul modulo incubator è atteso; la presenza dell'API non la rende stabile.

C23-P01 – La maschera dell'ultimo gruppo

Consegna. Esegui SommaVectorMascherata con cinque elementi. Se la specie preferita ha quattro corsie, disegna la maschera del secondo giro. Ripeti il ragionamento se la specie ne ha otto e confronta il risultato con il programma che usa una coda scalare.

Criterio e soluzione. Con quattro corsie il secondo giro ha una sola corsia valida; con otto corsie anche il primo giro ha cinque corsie valide e tre escluse. Il risultato numerico è uguale in entrambi gli algoritmi. Il numero di corsie è una proprietà della piattaforma osservata, non un valore garantito dal libro.

C24-A01 – Metadati disponibili a runtime

Consegna. Nel programma Reflection, cambia la retention dell'annotazione da RUNTIME a CLASS in una copia. Prima di eseguire, prevedi il risultato di getAnnotation(Etichetta.class) e la prima istruzione che non potrà proseguire.

Criterio e soluzione. La ricerca restituisce null; la successiva chiamata a etichetta.value() produce una NullPointerException. La soluzione robusta controlla null oppure conserva RUNTIME se il contratto richiede lettura a esecuzione.

C24-P01 – Pubblico e dichiarato

Consegna. Aggiungi un metodo privato al record Libro. Confronta le domande poste da getMethod e getDeclaredMethod, senza assumere che ottenere il descrittore dia automaticamente il diritto di invocare il metodo. Scrivi come cambierebbe il problema in un modulo nominato non aperto.

Criterio e soluzione. getMethod riguarda i metodi pubblici visibili; getDeclaredMethod trova un metodo dichiarato direttamente, anche se privato. L'accesso all'invocazione segue regole distinte e l'incapsulamento del modulo può impedire l'accesso profondo. La prova va fatta in un progetto di test, senza usare un'apertura indiscriminata dei pacchetti.

Prova gli esempi

Per eseguire i programmi serve JDK 25. Puoi scaricare i singoli file Java collegati nel capitolo oppure il progetto completo, che contiene istruzioni e uno script di avvio. Le spiegazioni confrontano anche l’output atteso: prevedilo prima di eseguire il programma.

Massimiliano Tarquini · CC BY-NC 4.0

Torna all’inizio ↑