Try-with-resources
Sissejuhatus
Mõned objektid esindavad väliseid ressursse, näiteks avatud faile, andmebaasiühendusi jne. Failide puhul on näiteks need ressurssid piiratud. See tähendab, et operatsioonisüsteem seab piirangu, kui palju faile üks protsess korraga avada saab.
Kui programm on ressurssi kasutamise lõpetanud, tuleb see ühendus sulgeda. Kui seda ei tehta, tekib ressursileke ehk olukord, kus programm hoiab ressursist kinni, kuigi selleks enam vajadust ei ole. Pikemas perspektiivis võib see põhjustada programmi töö katkemise.
Java try-with-resources tagab, et ressursid suletakse automaatselt, kui neid enam vaja ei ole. Need sulgetakse ka siis, kui tekib erind.
Faili käsitsi sulgemine
BufferedReader objekti peab peale kasutust sulgema.
Kõige otsesem viis seda teha oleks kutsuda try-ploki sees välja close() meetod:
try {
BufferedReader reader = Files.newBufferedReader(Path.of("books.txt"));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
reader.close(); // only reached if no exception is thrown
} catch (IOException e) {
System.err.println("Error: " + e.getMessage());
// reader is never closed if an exception occurred before close()
}
Samas selline lähenemine on problemaatiline.
Kui readLine() peaks mingil põhjusel erindi viskama, jäetakse try-ploki sees olev kood pooleli ja jätkatakse catch-plokis oleva koodi täitmisega.
Selle tulemusena jääb fail avatuks.
Siinkohal tuleks abiks finally-plokk, mis käivitatakse alati olenemata sellest, kas tekkis viga või mitte:
BufferedReader reader = null;
try {
reader = Files.newBufferedReader(Path.of("books.txt"));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
System.err.println("Error: " + e.getMessage());
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
System.err.println("Failed to close reader: " + e.getMessage());
}
}
}
See töötab, kuid võib tunduda väga kohmakana.
Ning ka close() kutse võib visata IOException-i, mis omakorda nõuaks käsitlemist jne.
Try-with-resources
Try-with-resources lahendab eelnevalt mainitud probleemi.
Ressursid deklareeritakse try märksõnale järgnevate sulgude sees:
try (BufferedReader reader = Files.newBufferedReader(Path.of("books.txt"))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
System.err.println("Error: " + e.getMessage());
}
Kui try-plokk lõppeb - kas tavatingimustel või erindi tõttu - kutsutakse reader.close() automaatselt välja.
Seda kasutades puudub vajadus finally, null-kontrolli ja liigsete try/catch-plokkide järgi.
AutoCloseable
Try-with-resources töötab iga klassiga, mis implementeerib AutoCloseable liidest.
AutoCloseable on liides ühe meetodiga:
public interface AutoCloseable {
void close() throws Exception;
}
BufferedReader, BufferedWriter, InputStream, OutputStream, andmebaasi Connection, PreparedStatement - kõik need implementeerivad AutoCloseable-i (või selle alamliidest Closeable).
Iga sinu kirjutatud klass, mis haldab välist ressurssi, peaks samuti teostama AutoCloseable liidest.
Multiple resources
Ühes try-lauses saab deklareerida mitu ressurssi, eraldades need semikoolonitega.
Need suletakse deklareerimisele vastupidises järjekorras - viimasena avatud ressurss suletakse esimesena:
try (
BufferedReader reader = Files.newBufferedReader(source);
BufferedWriter writer = Files.newBufferedWriter(target)
) {
String line;
while ((line = reader.readLine()) != null) {
writer.write(line);
writer.newLine();
}
} catch (IOException e) {
System.err.println("Error during copy: " + e.getMessage());
}
Kui see plokk lõpeb, suletakse esmalt writer, seejärel reader.
Millal tuleks kasutada
Alati. Siinkohal mingeid erandeid ei ole.
Iga kord, kui töötad ressursiga, mis teostab AutoCloseable liidest, kasuta try-with-resources-it.
Käsitsi close() kutsumine või finally-ploki kirjutamine pole alternatiivid - need on veaohtlikud ja raskesti loetavad lahendused, mida try-with-resources asendab.
Kui leiad end küsimas, kas peaksid try-with-resources-it kasutama, on vastus alati jah.