Java är ett snällt språk som tar hand om skräpsamling för oss. Vi behöver inte tänka på att avallokera minnet efter vi använt det.. jättesmidigt! Dock verkar det som att denna automatiska skräpuppsamling har gjort oss lata och glömmer bort att vi faktiskt måste städa upp efter oss ibland!

Strömmar av alla olika slag är nämligen väldigt viktiga att stänga. Framförallt strömmar till filer är viktiga, eftersom en öppen ström mot en fil tar upp systemresurser. Om man inte stänger filströmmarna efter sig, kommer ditt Java-program äta upp systemresurser tills minnet tar slut, eller tills operativsystemet sätter stopp för att du har öppnat för många filer (känns felmeddelandet ”Too many open files” igen?). Om du kör ditt program på en Linuxmaskin räknas både filer och TCP-anslutningar som filkopplingar. Detta gör att om du öppnar för många filer på disk, så kommer det heller inte att gå att öppna nya sockets.

Det finns många sätt att stänga sin ström på, men vad är då det mest korrekta sättet?

Det vanligaste jag sett är följande:

public void writeToFile(TextProvider textProvider) {
    try {
        FileWriter fw = new FileWriter("fil.txt");
        fw.write(textProvider.getText());
        fw.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

Om anropet till TextProvider skulle kasta ett exception skulle aldrig strömmen stängas. Exekveringen av try-blocket skulle avslutas och koden i catch-blocket skulle då köras med en stacktrace och en öppen fil som följd.

För att stänga strömmen på rätt sätt måste du istället göra såhär:

public void writeToFile(TextProvider textProvider) {
    FileWriter fw = null;
    try {
        fw = new FileWriter("fil.txt");
        fw.write(textProvider.getText());
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (fw != null) {
            try {
                fw.close();
            } catch (IOException e){
                // Ignore
            }
        }
    }
}

Följande regler gäller:

1. Deklarera din ström utanför try-blocket
2. Tilldela ström-variabeln i try-blocket
3. Utför din(a) I/O operation(er) på strömmen
4. Stäng inte strömmen i try-blocket, utan i finally-blocket
5. Se till att stängningen sker på ett säkert sätt

Det viktiga är att man stänger strömmen i finally-blocket. Den kod som finns i finally-blocket kommer alltid anropas, oavsett om något exception kastas i try-blocket eller inte. Om anropet till TextProvider skulle gå fel här, skulle exekveringen av try-blocket avslutas, koden i catch-blocket köras och sist (men inte minst) kommer finally-blocket exekveras där strömmen stängs. Stängningen sker här på ett säkert sätt, med först en null-kontroll (ifall aldrig tilldelning av ström-variabeln skedde) och sedan en try-catch runt close-anropet för att garantera att inget exception kastas vidare.

Det blir dock rätt jobbigt att alltid skriva dessa rader i finally-blocket på alla ställen där man stänger strömmar. För att slippa skriva den långa harangen kod i finally-blocket varje gång, brukar jag göra en metod av det. Följande kodsnutt är lite bättre:

public void writeToFile(TextProvider textProvider) {
    FileWriter fw = null;
    try {
        fw = new FileWriter("fil.txt");
        fw.write(textProvider.getText());
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        close(fw);
    }
}

private void close(Closeable closeable) {
    if (closeable != null) {
        try {
            closeable.close();
        } catch (IOException e){
            // Ignore
        }
    }
}

Om man vill kan man skriva close-metoden på detta sätt också för att slippa null-kontrollen:

private void close(Closeable closeable) {
    try {
        closeable.close();
    } catch (Exception e){
        // Ignore
    }
}

Jag brukar bryta ut denna metod till en egen klass vid namn IOUtils, eller liknande. Om man använder Apache Commons i sitt projekt finns redan metoder för att stänga strömmar i en klass med just detta namn (IOUtils under paketet org.apache.commons.io).

Happy closing!