"Amigo, dieci capanne!"
"Sono felice di imparare Java, Capitano!"
"A tuo agio, Amigo. Oggi abbiamo un argomento super interessante. Parleremo di come un programma Java interagisce con risorse esterne e studieremo un'istruzione Java molto interessante. Meglio non coprirsi le orecchie."
"Sono tutto orecchie."
"Mentre un programma Java viene eseguito, a volte interagisce con entità esterne alla macchina Java. Ad esempio, con file su disco. Queste entità sono solitamente chiamate risorse esterne."
"Allora cosa sono considerate risorse interne?"
"Le risorse interne sono gli oggetti creati all'interno della macchina Java. Tipicamente, l'interazione segue questo schema:
"Il sistema operativo tiene rigorosamente traccia delle risorse disponibili e controlla anche l'accesso condiviso ad esse da diversi programmi. Ad esempio, se un programma modifica un file, un altro programma non può modificare (o eliminare) quel file. Questo principio non è limitato ai file, ma forniscono l'esempio più facilmente comprensibile.
"Il sistema operativo dispone di funzioni (API) che consentono a un programma di acquisire e/o rilasciare risorse. Se una risorsa è occupata, solo il programma che l'ha acquisita può utilizzarla. Se una risorsa è libera, qualsiasi programma può acquisire Esso.
"Immagina che un ufficio abbia condiviso tazze da caffè. Se qualcuno prende una tazza, le altre persone non possono più prenderla. Ma una volta che la tazza è stata usata, lavata e rimessa al suo posto, chiunque può riprenderla."
"Capito. È come i posti in metropolitana o altri mezzi di trasporto pubblico. Se un posto è libero, allora chiunque può prenderlo. Se un posto è occupato, allora è controllato dalla persona che lo ha preso."
"Esatto. E ora parliamo dell'acquisizione di risorse esterne . Ogni volta che il tuo programma Java inizia a lavorare con un file su disco, la macchina Java chiede al sistema operativo l'accesso esclusivo ad esso. Se la risorsa è libera, allora la macchina Java acquisisce Esso.
"Ma dopo che hai finito di lavorare con il file, questa risorsa (file) deve essere rilasciata, cioè devi notificare al sistema operativo che non ne hai più bisogno. Se non lo fai, la risorsa continuerà ad essere tenuto dal vostro programma."
"Sembra giusto."
"Per mantenerlo in questo modo, il sistema operativo mantiene un elenco di risorse occupate da ciascun programma in esecuzione. Se il tuo programma supera il limite di risorse assegnato, il sistema operativo non ti fornirà più nuove risorse.
"È come i programmi che possono consumare tutta la memoria..."
"Qualcosa del genere. La buona notizia è che se il tuo programma termina, tutte le risorse vengono automaticamente rilasciate (lo fa il sistema operativo stesso)."
"Se questa è la buona notizia, significa che ci sono cattive notizie?"
"Proprio così. La cattiva notizia è che se stai scrivendo un'applicazione server..."
"Ma scrivo queste applicazioni?"
"Molte applicazioni server sono scritte in Java, quindi molto probabilmente le scriverai per lavoro. Come dicevo, se stai scrivendo un'applicazione server, allora il tuo server deve funzionare ininterrottamente per giorni, settimane, mesi, eccetera."
"In altre parole, il programma non termina e ciò significa che la memoria non viene rilasciata automaticamente."
"Esattamente. E se apri 100 file al giorno e non li chiudi, in un paio di settimane la tua applicazione raggiungerà il limite di risorse e andrà in crash."
"Sono ben pochi mesi di lavoro stabile! Cosa si può fare?"
"Le classi che utilizzano risorse esterne hanno un metodo speciale per rilasciarle: close()
.
"Ecco un esempio di un programma che scrive qualcosa su un file e poi chiude il file quando ha finito, cioè libera le risorse del sistema operativo. Assomiglia più o meno a questo:
Codice | Nota |
---|---|
|
Il percorso del file. Ottieni l'oggetto file: acquisisci la risorsa. Scrivi nel file Chiudi il file - rilascia la risorsa |
"Ah... Quindi, dopo aver lavorato con un file (o altre risorse esterne), devo chiamare il close()
metodo sull'oggetto collegato alla risorsa esterna."
"Sì. Sembra tutto semplice. Ma possono verificarsi eccezioni durante l'esecuzione di un programma e la risorsa esterna non verrà rilasciata."
"E questo è molto brutto. Cosa fare?"
"Per assicurarci che il metodo close()
venga sempre chiamato, dobbiamo avvolgere il nostro codice in un blocco try
-- e aggiungere il metodo al blocco. Sarà simile a questo:catch
finally
close()
finally
try
{
FileOutputStream output = new FileOutputStream(path);
output.write(1);
output.close();
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
output.close();
}
"Hmm... c'è qualcosa che non va?"
"Giusto. Questo codice non verrà compilato, perché la output
variabile è dichiarata all'interno del try{}
blocco, e quindi non è visibile nel finally
blocco.
Risolviamolo:
FileOutputStream output = new FileOutputStream(path);
try
{
output.write(1);
output.close();
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
output.close();
}
"Adesso va tutto bene?"
"Va bene, ma non funzionerà se si verifica un errore quando creiamo l' FileOutputStream
oggetto, e questo potrebbe accadere abbastanza facilmente.
Risolviamolo:
FileOutputStream output = null;
try
{
output = new FileOutputStream(path);
output.write(1);
output.close();
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
output.close();
}
"E adesso funziona tutto?"
"Ci sono ancora alcune critiche. Innanzitutto, se si verifica un errore durante la creazione dell'oggetto FileOutputStream
, la output
variabile sarà nulla. Questa possibilità deve essere presa in considerazione nel finally
blocco.
"In secondo luogo, il close()
metodo viene sempre chiamato nel finally
blocco, il che significa che non è necessario nel try
blocco. Il codice finale sarà simile a questo:
FileOutputStream output = null;
try
{
output = new FileOutputStream(path);
output.write(1);
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
if (output!=null)
output.close();
}
"Anche se non consideriamo il catch
blocco, che può essere omesso, le nostre 3 righe di codice diventano 10. Ma fondamentalmente abbiamo appena aperto il file e scritto 1."
"Uff... È una buona cosa che conclude la questione. Relativamente comprensibile, ma un po' noioso, no?"
"Così è. Ecco perché i creatori di Java ci hanno aiutato aggiungendo un po' di zucchero sintattico. Ora passiamo al clou del programma, o meglio, a questa lezione:
try
-con-risorse
"A partire dalla sua settima versione, Java ha una nuova try
dichiarazione -with-resources.
"È stato creato proprio per risolvere il problema con la chiamata obbligatoria al close()
metodo."
"Sembra promettente!"
"Il caso generale sembra abbastanza semplice:
try (ClassName name = new ClassName())
{
Code that works with the name variable
}
"Quindi questa è un'altra variazione dell'affermazione try
? "
"Sì. È necessario aggiungere le parentesi dopo la try
parola chiave, quindi creare oggetti con risorse esterne all'interno delle parentesi. Per ogni oggetto tra parentesi, il compilatore aggiunge una finally
sezione e una chiamata al close()
metodo.
"Di seguito sono riportati due esempi equivalenti:
Codice lungo | Codice con try-with-resources |
---|---|
|
|
"Fantastico! Il codice che utilizza try
-with-resources è molto più breve e più facile da leggere. E meno codice abbiamo, minori sono le possibilità di commettere errori di battitura o di altro genere."
"Sono contento che ti piaccia. A proposito, possiamo aggiungere catch
e finally
blocchi all'istruzione try
-with-resources. Oppure non puoi aggiungerli se non sono necessari.
Più variabili contemporaneamente
"Spesso potresti incontrare una situazione in cui devi aprire più file contemporaneamente. Supponiamo che tu stia copiando un file, quindi hai bisogno di due oggetti: il file da cui stai copiando i dati e il file in cui stai copiando i dati .
"In questo caso, l' try
istruzione -with-resources ti consente di creare uno ma più oggetti al suo interno. Il codice che crea gli oggetti deve essere separato da punti e virgola. Ecco l'aspetto generale di questa istruzione:
try (ClassName name = new ClassName(); ClassName2 name2 = new ClassName2())
{
Code that works with the name and name2 variables
}
Esempio di copia di file:
Codice corto | Codice lungo |
---|---|
|
|
"Bene, cosa possiamo dire qui? try
-con-risorse è una cosa meravigliosa!"
"Quello che possiamo dire è che dovremmo usarlo."
GO TO FULL VERSION