I/O Foundations

The I/O Universe: Bytes vs Characters

On disk everything is binary. The distinction lies in whether the JVM reads raw 8-bit bytes or decodes them into 16-bit Unicode characters via a Charset.

Binary Stream InputStream / OutputStream

Handles raw 8-bit bytes. Best for PNG images, audio, PDFs, and binary blobs.

01001011 11100010 00001111
Text Stream Reader / Writer

Decodes bytes into Unicode text (16-bit char) applying a Charset (UTF-8, ISO-8859-1).

'C' 'a' 'n' 'c' 'i'
Simulator: The Encoding Trap Try reading "Canción" with different encodings
Canción ✓ Correct Decoding
Design Architecture

The Decorator Pattern: The Stream Onion

Instead of class explosion, java.io stacks concentric wrappers over physical sources: RAM buffering and typed parsing.

DataInputStream (Tipos: readDouble, readUTF) BufferedInputStream (Caché 8 KB en RAM) FileInputStream "datos.bin" (Disco SSD / HDD)
Impact on 10,000 Byte Reads:
Disk I/O Calls: 2 calls 1.8 ms (Ultra Fast!)

The buffer loads 8192 bytes into RAM in one OS kernel call. The next 8191 reads execute in memory.

Platform Evolution

Modern Java NIO.2: java.nio.file

Java 7 replaced the fragile java.io.File with immutable Path, atomic Files operations, and lazy streams via Files.walk().

Legacy java.io.File (Java 1.0) 12 lines, manual stream handling
BufferedReader br = null;
try {
    br = new BufferedReader(new FileReader("file.txt"));
    String linea;
    while ((linea = br.readLine()) != null) {
        System.out.println(linea);
    }
} finally {
    if (br != null) br.close();
}
Modern java.nio.file (NIO.2) 1 line, atomic, explicit charset
Path path = Paths.get("file.txt");

// Lectura directa en 1 línea
List<String> lineas = Files.readAllLines(path, StandardCharsets.UTF_8);

// O Lazy Stream para archivos gigantes (GBytes):
try (Stream<Path> stream = Files.walk(path)) {
    stream.filter(Files::isRegularFile).forEach(System.out::println);
}
Binary Object Persistence

Serialization: Flattening the Heap to Disk

ObjectOutputStream converts live object graphs into a binary sequence prefixed with the signature 0xAC ED 00 05.

Object in RAM (Heap)
class Usuario implements Serializable
id = 101
nombre = "Facundo"
transient password = "secret_hash"
ObjectOutputStream
Disk File (usuario.ser)
0xAC ED 00 05 (Magic Header) 0x73 0x72 0x00 0x07 Usuario id: 101 | nombre: "Facundo" password: NULL (Not persisted!)
Security Key: The transient keyword instructs the JVM to omit sensitive fields (passwords, sockets, DB connections) during serialization.
Compatibility Contract

The Critical Role of serialVersionUID

If a class changes without a declared serialVersionUID, JVM computes a dynamic hash and deserialization fails with InvalidClassException.

Paso 1
Versión 1 en Disco

Guardamos Factura(id, monto). Hash dinámico calculado: 0x84A1

Paso 2
Evolución de Código

Agregamos emailCliente al código fuente. Nuevo hash: 0x32B9

Paso 3
Lectura / Deserialización

❌ InvalidClassException: local class incompatible (stream: 0x84A1, local: 0x32B9)

Mandatory Architectural Solution
// Declarar siempre un identificador de versión explícito:
private static final long serialVersionUID = 1L;
Artifact Packaging

Anatomy of a JAR (Java Archive)

A .jar file is not magic: it is a standard ZIP compressed archive holding compiled bytecode and a MANIFEST.MF descriptor.

mi-aplicacion.jar (Estructura ZIP)
  • 📁 META-INF/
    • 📄 MANIFEST.MF
  • 📁 com/empresa/app/
    • 📄 Main.class
    • 📄 Servicio.class
    • 📄 Modelo.class
  • 📁 resources/
    • 🖼️ logo.png
META-INF/MANIFEST.MF
Manifest-Version: 1.0
Main-Class: com.empresa.app.Main
Created-By: 21.0.2 (Eclipse Adoptium)
Execution Command:
$ java -jar mi-aplicacion.jar

The JVM opens the ZIP, reads Main-Class from MANIFEST.MF, and launches public static void main(String[] args).

Modern Distribution

Native Distribution with jpackage

The era of asking users to "Install Java first" is over. jpackage builds native installers (.exe, .dmg, .deb) bundled with a custom JRE.

1
Compilación javac *.java

Genera archivos de bytecode .class.

→
2
Empaquetado jar -cfe app.jar

Genera el archivo ejecutable .jar con su manifiesto.

→
3
jlink (Tree-Shaking) jlink --strip-debug

Corta un JRE mínimo (30 MB en vez de 300 MB del JDK).

→
4
jpackage jpackage --type exe

Produce instaladores .exe, .dmg o .deb 100% autónomos.