Tudo bem, Fábio?
Quando lidamos com recursos, como acesso ao banco de dados ou leitura de arquivos, é comum que após finalizarmos as operações que temos para fazer, a conexão ou o fluxo sejam encerrados para que o recurso seja liberado.
Para fazer isto, normalmente existe um método chamado close().
Então logo após ler ou escrever em um arquivo, por exemplo, você pode chamar o método close() para liberar o recurso.
Um problema que pode acontecer aí, é que algo pode dar errado no meio do caminho, uma exception pode ocorrer. Então se você põe a chamada do close() dentro de um bloco try:
try {
InputStream is = new FileInputStream("teste.txt");
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String linha = br.readLine();
System.out.println(linha);
br.close();
} catch (IOException e) {
e.printStackTrace();
}
Se uma exception for lançado no meio do caminho, pode ser que o fluxo vá para o catch, e a linha do close() nunca seja executada. A consequência, é que pode ser o recurso fique aberto...
Para garantir que o recurso seja fechado, é comum que o pessoal faça a chamada ao método close() dentro do bloco finally, que é um bloco que temos certeza que será executado independente de haver exception ou não, independente de ele entrar ou não no catch.
Ficaria assim (meio feioso):
BufferedReader br = null;
try {
InputStream is = new FileInputStream("teste.txt");
InputStreamReader isr = new InputStreamReader(is);
br = new BufferedReader(isr);
String linha = br.readLine();
System.out.println(linha);
br.close();
} catch (IOException e) {
e.printStackTrace();
}
finally {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
É aí que entra o try-with-resources. Tudo que você põe nos parenteses dele, o método close() será chamado automaticamente. Para garantir que todo mundo que você por entre parênteses terá o método close(), ele só aceita tipos que implementam a interface AutoClosable. Dessa forma você consegue evitar ter que chamar o close() manualmente.
No seu código, para tratar todas as exceções, você pode por todo mundo dentro do try-with-resources, já que todas essas classes que lidam com arquivos possuem o método close(). Ficaria algo como:
try(InputStream is = System.in;
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
OutputStream os = new FileOutputStream("saida.txt");
OutputStreamWriter osw = new OutputStreamWriter(os);
BufferedWriter bw = new BufferedWriter(osw)) {
String linha = br.readLine();
while(linha != null) {
if(linha.equals("exit")) {
break;
}
bw.append(linha);
bw.newLine();
linha = br.readLine();
}
} catch (IOException e) {
e.printStackTrace();
}
Na prática, só chamar os métodos close() do BufferedReader e do BufferedWriter é suficiente. Mas como você precisa do InputStreamReader que por sua vez precisa do InputStream, por exemplo, vai ser preciso criar todo mundo dentro do try-with-resources...
Mas agora você garante que o método close() será sempre chamado e o recurso liberado.
Faz sentido? Abraço!