E/S con streams
InputStream/OutputStream, Reader/Writer
y try-with-resources
Jerarquía de streams
Java tiene dos familias de streams de E/S: los de bytes
(InputStream/OutputStream) para cualquier dato binario, y los de caracteres (Reader/Writer) para texto con encoding. Nunca mezcles ambas familias sin el
adaptador
InputStreamReader/OutputStreamWriter, y especifica siempre el charset
explícitamente.
// Jerarquía de streams de E/S en Java
// BYTES (datos binarios):
// InputStream → FileInputStream, ByteArrayInputStream, BufferedInputStream...
// OutputStream → FileOutputStream, ByteArrayOutputStream, BufferedOutputStream...
// CARACTERES (texto, tienen encoding):
// Reader → FileReader, BufferedReader, StringReader, InputStreamReader...
// Writer → FileWriter, BufferedWriter, PrintWriter, OutputStreamWriter...
// Decoradores (Decorator pattern): envuelven otro stream para añadir funcionalidad
// BufferedInputStream envuelve InputStream para añadir buffer
// InputStreamReader envuelve InputStream para convertir bytes a chars con encoding Leer ficheros de texto
import java.io.*;
import java.nio.charset.StandardCharsets;
// Leer un fichero de texto línea a línea (patrón estándar)
try (BufferedReader br = new BufferedReader(
new InputStreamReader(new FileInputStream("config.txt"), StandardCharsets.UTF_8))) {
String linea;
while ((linea = br.readLine()) != null) {
System.out.println(linea);
}
} // try-with-resources: cierra automáticamente en orden inverso al abrirse
// No necesita finally { br.close() } — AutoCloseable lo maneja
// Alternativa Java 7+: Files.newBufferedReader (más conciso)
try (BufferedReader br = java.nio.file.Files.newBufferedReader(
java.nio.file.Paths.get("config.txt"), StandardCharsets.UTF_8)) {
br.lines().forEach(System.out::println);
} Escribir ficheros de texto
import java.io.*;
import java.nio.charset.StandardCharsets;
// Escribir en un fichero de texto
try (BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(new FileOutputStream("salida.txt"), StandardCharsets.UTF_8))) {
bw.write("Primera línea");
bw.newLine();
bw.write("Segunda línea");
}
// PrintWriter: más conveniente para texto formateado
try (PrintWriter pw = new PrintWriter(
new BufferedWriter(new FileWriter("salida.txt", true)))) { // true = append
pw.println("Añadiendo línea");
pw.printf("Valor: %d%n", 42);
} Streams binarios
import java.io.*;
// Streams binarios: para datos que no son texto (imágenes, ficheros, protocolos)
// Copiar un fichero binario byte a byte con buffer
try (InputStream in = new BufferedInputStream(new FileInputStream("origen.bin"));
OutputStream out = new BufferedOutputStream(new FileOutputStream("destino.bin"))) {
byte[] buffer = new byte[8192]; // buffer de 8KB
int bytesLeidos;
while ((bytesLeidos = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesLeidos);
}
} // ambos streams se cierran automáticamente
// DataOutputStream: escribir primitivos en formato binario
try (DataOutputStream dos = new DataOutputStream(new FileOutputStream("datos.bin"))) {
dos.writeInt(42);
dos.writeDouble(3.14);
dos.writeUTF("hola");
} try-with-resources
Cualquier clase que implemente AutoCloseable puede usarse en un
try-with-resources. La JVM garantiza que se llama a close()
incluso si ocurre una excepción. Es el patrón obligatorio para todos los recursos que requieren cierre explícito:
ficheros, conexiones JDBC, sockets, etc.
// try-with-resources (Java 7): cualquier AutoCloseable se cierra automáticamente
// Funciona con múltiples recursos: se cierran en orden inverso al de apertura
try (Connection con = dataSource.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT * FROM users");
ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
System.out.println(rs.getString("nombre"));
}
} catch (SQLException e) {
// rs, ps y con ya están cerrados aquí — incluso si lanzaron excepciones
throw new RuntimeException("Error al consultar usuarios", e);
}
// Equivalente sin try-with-resources (Java 6): 30+ líneas de finally anidados
// Usar siempre try-with-resources cuando el recurso implementa AutoCloseable