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
NIO.2 (Java 7+): la API java.nio.file.Files ofrece métodos de alto nivel como Files.readAllLines(), Files.write() y Files.copy() que son más concisos para operaciones comunes. Los streams de java.io siguen siendo relevantes para APIs que los requieren y para procesamiento de streams de red.

Siguiente apartado → Gestión de memoria: heap, referencias y el GC

Índice de la sección

Índice del curso