Los flujos de entrada/salida (I/O streams) son uno de los conceptos básicos de Java, que apareció desde las primeras versiones del lenguaje. Este concepto fue inicialmente diseñado con el propósito de abstraer los procesos de lectura y escritura de datos: un flujo puede estar vinculado a un archivo, una red o incluso a la consola; para el código, esto se ve igual.
El problema surge cuando el desarrollador gestiona incorrectamente los flujos, olvida cerrarlos o confunde diferentes tipos de flujos (de caracteres y de bytes), lo que a menudo conduce a fugas de recursos, corrupción de datos o errores en tiempo de ejecución.
La solución es una comprensión y aplicación adecuada de la jerarquía de flujos de entrada/salida (InputStream/OutputStream para bytes, Reader/Writer para caracteres), así como el cierre obligatorio de flujos después de su uso, preferiblemente a través de try-with-resources a partir de Java 7.
Ejemplo de código para leer y escribir un archivo:
try (BufferedReader reader = new BufferedReader(new FileReader("input.txt")); BufferedWriter writer = new BufferedWriter(new FileWriter("output.txt"))) { String line; while ((line = reader.readLine()) != null) { writer.write(line); writer.newLine(); } }
Características clave:
¿Qué sucederá si no se cierra un flujo de entrada/salida en Java?
Los flujos manejan recursos de bajo nivel del sistema operativo, y no cerrar un flujo puede resultar en fugas de memoria, bloqueo de archivos, fallos en la aplicación o incluso agotamiento de descriptores de archivos a nivel del sistema operativo.
¿Se puede usar el mismo OutputStream para escribir en diferentes archivos?
No, la clase OutputStream está estrictamente asociada a una única fuente/destino de datos. Para diferentes archivos, se deben usar diferentes objetos de OutputStream.
¿Cuál es la diferencia entre PrintWriter y BufferedWriter? ¿Cuándo usar cada uno?
PrintWriter se especializa en complementar la salida con métodos de impresión formateados, como println(), y puede trabajar con el autocierre. BufferedWriter, por otro lado, principalmente mejora el rendimiento mediante el uso de búferes. En la mayoría de las tareas de aplicación, PrintWriter es preferible para la salida de texto, pero si solo se necesita escribir rápidamente caracteres o cadenas sin formateo, BufferedWriter es más adecuado.
Un desarrollador lee datos de un archivo grande usando FileInputStream, no usa búfer y olvida cerrar el flujo.
Pros:
Contras:
Se utiliza la combinación de BufferedReader con try-with-resources, la lectura y el procesamiento se realizan en lotes por líneas, el flujo se cierra automáticamente.
Pros:
Contras: