Escritura y Lectura#

Introducción#

En el manejo de datos, es común encontrarse con la necesidad de guardar y recuperar información de manera eficiente. Para lograr esto, Java proporciona herramientas para serialización y manejo de archivos. La serialización permite convertir un objeto en una secuencia de bytes para almacenarlo o transmitirlo, mientras que el manejo de archivos permite leer y escribir datos de archivos en el sistema.

Esta clase explorará en detalle los conceptos fundamentales de serialización, deserialización, y operaciones de archivos en Java, proporcionando ejemplos prácticos para entender su funcionamiento.

Objetivos#

  1. Comprender el concepto de serialización en Java y su importancia en el almacenamiento y transmisión de objetos.

  2. Aplicar operaciones de serialización y deserialización en objetos de Java.

  3. Entender el manejo de archivos en Java, incluyendo la lectura y escritura de archivos.

  4. Implementar ejemplos prácticos que combinen la serialización y manejo de archivos en Java.

Lectura y Escritura de Archivos#

Java proporciona varias clases para trabajar con archivos, como FileInputStream, FileOutputStream, BufferedReader, y BufferedWriter. A este proceso se le suele conocer por E/S de Texto (Text I/O)

¿Cómo Java lee y escribe archivos?

Trulli

Texto vs Binarios

Trulli

BufferedInputStream/BufferedOutputStream#

BufferedInputStream/BufferedOutputStream puede utilizarse para acelerar la entrada y salida reduciendo el número de lecturas y escrituras en disco. Con BufferedInputStream, todo el bloque de datos del disco se lee una vez en el búfer de la memoria. Los datos individuales son entonces cargados a tu programa desde el buffer, como se muestra en la Figura 17.12a. Usando BufferedOutputStream, los datos individuales se escriben primero en el buffer de la memoria. Cuando el buffer está lleno, todos los datos en el buffer se escriben en el disco una vez, como se muestra en la Figura 17.12b.

Trulli
Trulli
DataOutputStream output = new DataOutputStream(new BufferedOutputStream(new FileOutputStream("temp.dat")));

Esta clase es muy completa y robusta, sin embargo, usarla requiere de experticie. Además de esta gran clase, existen otras formas, métodos y clases, que permiten hacer la escritura y lectura de archivos de forma más amigable BufferedReader y BufferedWriter.

Ejemplo de escritura en archivo#

import java.io.*;

public class EscrituraArchivo {
    public static void main(String[] args) {
        try (BufferedWriter writer = new BufferedWriter(new FileWriter("archivo.txt"))) {
            writer.write("Este es un ejemplo de escritura en un archivo.");
            System.out.println("Texto escrito en el archivo.");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
// Prueba el texto aquí

Existen muchas clases de E/S para diversos fines. En general, se pueden clasificar en clases de entrada y clases de salida. Una clase de entrada contiene los métodos para leer datos, y una clase de salida contiene los métodos para escribir datos. PrintWriter es un ejemplo de una clase de salida que suele utilizar mucho para escribir archivos de html o css para páginas web, y Scanner es un ejemplo de una clase de entrada. El siguiente código crea un objeto de entrada para el archivo temp.txt y lee datos del archivo:

PrintWriter output = new PrintWriter("temp.txt");

output.print("Java 101");
output.print("Java 102");
output.close();
// Leer el archivo utilizando Scanner
Scanner input = new Scanner(new File("temp.txt"));
System.out.println(input.nextLine());
Java 101Java 102

Clases de E/S Binarios#

El InputStream abstracto es la clase raíz para la lectura de datos binarios, y el OutputStream abstracto es la clase raíz para la escritura de datos binarios.

FileInputStream/FileOutputStream#

FileInputStream/FileOutputStream son para leer/escribir bytes de/a archivos. Todos los métodos de estas clases se heredan de InputStream y OutputStream. FileInputStream/FileOutputStream no introducen nuevos métodos. Para construir un FileInputStream, utilice los constructores mostrados en la Figura 17.6. Se producirá una java.io.FileNotFoundException si se intenta crear un FileInputStream con un fichero inexistente.

Nota

Los datos de texto se leen con la clase Scanner y se escriben con la clase PrintWriter.

DataInputStream input = new DataInputStream(new BufferedInputStream(new FileInputStream("temp.dat")));
import java.io.*;
public class TestFileStream {
  public static void main(String[] args) throws IOException {
    try (
      // Create an output stream to the file
      FileOutputStream output = new FileOutputStream("temp.dat"); // output stream
    ) {
      // Output values to the file
      for (int i = 1; i <= 10; i++)
      output.write(i);
    }
    try (
      // Create an input stream for the file
      FileInputStream input = new FileInputStream("temp.dat"); // input stream
    ) {
      // Read values from the file
      int value;
      while ((value = input.read()) != -1) // input
      System.out.print(value + " ");
    }
  }
}
// Prueba el código aquí

Ejemplo de lectura de archivo#

DataInputStream input = new DataInputStream(new BufferedInputStream(new FileInputStream("temp.dat")));
import java.io.*;

public class LecturaArchivo {
    public static void main(String[] args) {
        try (BufferedReader reader = new BufferedReader(new FileReader("archivo.txt"))) {
            String linea;
            while ((linea = reader.readLine()) != null) {
                System.out.println(linea);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
// Prueba el texto aquí

DataInputStream/DataOutputStream#

DataInputStream lee bytes del flujo y los convierte en valores de tipo primitivo o cadenas. DataOutputStream convierte valores de tipo primitivo o cadenas en bytes y envía los bytes al flujo.

DataInputStream extiende FilterInputStream e implementa la interfaz DataInput. DataOutputStream extiende FilterOutputStream e implementa la interfaz DataOutput.

// Prueba el texto aquí

E/S de Texto vs a E/S Binaria#

La E/S binaria no implica codificación ni descodificación, por lo que es más eficiente que la E/S de texto.

Los ordenadores no distinguen entre archivos binarios y archivos de texto. Todos los archivos se almacenan en formato binario y, por tanto, todos los archivos son esencialmente binarios. La E/S de texto se basa en la E/S binaria para proporcionar un nivel de abstracción para la codificación y descodificación de caracteres. La codificación y descodificación se realizan automáticamente para la E/S de texto.

La clase abstracta InputStream es la clase raíz para la lectura de datos binarios, y la clase abstracta OutputStream es la clase raíz para escribir datos binarios.

El diseño de las clases de E/S de Java es un buen ejemplo de aplicación de la herencia, donde las operaciones comunes se generalizan en superclases, y las subclases proporcionan operaciones especializadas.

import java.io.*;
public class TestDataStream {
  public static void main(String[] args) throws IOException {
    try ( // Create an output stream for file temp.dat
      DataOutputStream output = new DataOutputStream(new FileOutputStream("temp.dat"));
    ) {
      // Write student test scores to the file
      output.writeUTF("John");
      output.writeDouble(85.5);
      output.writeUTF("Susan");
      output.writeDouble(185.5);
      output.writeUTF("Kim");
      output.writeDouble(105.25);
    }
    try ( // Create an input stream for file temp.dat
    DataInputStream input = new DataInputStream(new FileInputStream("temp.dat"));
    ) {
      // Read student test scores from the file
      System.out.println(input.readUTF() + " " + input.readDouble());
      System.out.println(input.readUTF() + " " + input.readDouble());
      System.out.println(input.readUTF() + " " + input.readDouble());
    }
  }
}
// Prueba el texto aquí

Conclusión#

La serialización y el manejo de archivos son fundamentales para el desarrollo de aplicaciones en Java que requieren persistencia y comunicación de datos. La serialización permite convertir objetos en secuencias de bytes, facilitando su almacenamiento en archivos o su transmisión a través de redes. Esta técnica es clave cuando se necesita mantener el estado de un objeto más allá de la ejecución de un programa. Por otro lado, el manejo de archivos permite leer y escribir datos desde y hacia archivos, lo cual es esencial para cualquier aplicación que interactúe con sistemas de almacenamiento.

Dominar estas técnicas no solo mejora la capacidad de un programador para crear aplicaciones que gestionen datos de manera efectiva, sino que también le permite construir soluciones más robustas y eficientes, asegurando que los datos persistan entre ejecuciones y que puedan ser transferidos entre diferentes sistemas de forma segura y controlada.

Recursos Adicionales#

Libros#

  • Y. Daniel Liang. «Introduction to Java Programming and Data Structures, Comprehensive Version». Addison Wesley. Edición 12 (2019). Capítulo 17.

  • Bloch, Joshua. Effective Java. Addison-Wesley, 2008.

  • Eckel, Bruce. Thinking in Java. Prentice Hall, 2006.

Guias y Tutoriales#

Videos#