← Volver a Tema 1

Tema 1 · Interpretación de Código

Explicación línea a línea de fragmentos de código Java de Tema 1, pensada para repasar qué hace cada instrucción. Usa el menú lateral para saltar directamente a cada código.

Página 2 · java.io

Página 2: Formas de declarar un fichero

No es un programa completo, sino varios ejemplos sueltos que muestran las distintas formas de crear un objeto File: con la ruta completa en un único String, o separando el directorio del nombre de fichero.

Ejemplos · declaración de File
//Windows
File fichero1 = new File( "C:\\EJERCICIOS\\UNI1\\ejemplo1.txt");
//Linux
File fichero1 = new File( "/home/ejercicios/uni1/ejemplo1.txt");

String directorio= "C:/EJERCICIOS/UNI1";
File fichero2 = new File(directorio, "ejemplo2.txt");

File direc = new File(directorio);

Explicación línea a línea

  1. 1
    File fichero1 = new File("C:\\EJERCICIOS\\UNI1\\ejemplo1.txt");

    Primer constructor: la ruta completa (directorio + fichero) en un único String. En Windows el separador es \, y hay que escribirlo doble (\\) porque la barra invertida es un carácter especial en Java.

  2. 2
    File fichero1 = new File("/home/ejercicios/uni1/ejemplo1.txt");

    La misma idea, pero en Linux, donde el separador de rutas es / y no hace falta escaparlo.

  3. 3
    String directorio= "C:/EJERCICIOS/UNI1";

    Se guarda solo la ruta del directorio en una variable de texto aparte, para reutilizarla.

  4. 4
    File fichero2 = new File(directorio, "ejemplo2.txt");

    Segundo constructor: se pasan dos String por separado, el directorio y el nombre del fichero; File se encarga de unirlos correctamente.

  5. 5
    File direc = new File(directorio);

    Un objeto File no tiene por qué apuntar a un fichero: aquí apunta a un directorio, usando ese mismo File como base para el siguiente ejemplo (Página 3).

En resumen: File tiene varios constructores para representar la misma ruta de distintas formas (todo junto, o directorio y nombre por separado), y puede representar tanto ficheros como directorios.
Página 3 · java.io

Página 3: VerDir

Antes del programa, la Página 3 muestra un tercer constructor de File: File fichero3 = new File(direc, "ejemplo3.txt"); (un File de directorio + un nombre de fichero). Después, el programa VerDir lista los ficheros del directorio actual y, para cada uno, comprueba si es un fichero o un directorio.

VerDir.java
import java.io.*;

public class VerDir {

    public static void main(String[] args) {
        String dir = ".";
        File f = new File(dir);
        String[] archivos = f.list();
        System.out.printf("Ficheros en el directorio actual: %d %n",
                            archivos.length);
        for (int i = 0; i < archivos.length; i++) {
            File f2 = new File(f, archivos[i]);
            System.out.printf("Nombre: %s, es fichero?: %b, es directorio?: %b %n", archivos[i], f2.isFile(),f2.isDirectory());
        }
    }
}

Explicación línea a línea

  1. 1
    String dir = ".";

    El punto "." es un atajo que representa el directorio actual (desde donde se ejecuta el programa).

  2. 2
    File f = new File(dir);

    Se crea un File que apunta a ese directorio actual.

  3. 3
    String[] archivos = f.list();

    list() devuelve un array de String con los nombres de todo lo que hay dentro de ese directorio (ficheros y subdirectorios).

  4. 4
    System.out.printf("Ficheros en el directorio actual: %d %n", archivos.length);

    Se imprime cuántos elementos se han encontrado, usando printf con %d (número entero) y %n (salto de línea).

  5. 5
    for (int i = 0; i < archivos.length; i++) {

    Se recorre cada nombre devuelto por list().

  6. 6
    File f2 = new File(f, archivos[i]);

    Se crea un nuevo File combinando el directorio f con cada nombre, para poder consultar sus propiedades individualmente (mismo constructor visto en la Página 3, directorio + nombre).

  7. 7
    System.out.printf("Nombre: %s, es fichero?: %b, es directorio?: %b %n", ...);

    Se imprime el nombre junto con el resultado de isFile() e isDirectory(), usando %s para texto y %b para booleanos.

Resultado Mostrado por Terminal (impreso en la Página 4)

Salida de Consola
Ficheros en el directorio actual: 3 Nombre: VerDir.class, es fichero?: true, es directorio?: false Nombre: VerDir.java, es fichero?: true, es directorio?: false Nombre: VerInf.java, es fichero?: true, es directorio?: false
En resumen: el programa usa list() para obtener los nombres del directorio actual y, por cada uno, crea un File aparte para poder preguntarle si es un fichero o un directorio.
Página 4 · java.io

Página 4: VerInf

Antes del programa, la Página 4 muestra dos variantes rápidas del ejemplo VerDir: apuntar a otro directorio fijo (File f = new File("d:\\db");) o a uno indicado por el usuario al ejecutar el programa (String dir=args[0];). Después, el programa VerInf muestra información detallada de un fichero concreto usando varios métodos de File.

VerInf.java
import java.io.*;

public class VerInf {

    public static void main(String[] args) {
        System.out.println("INFORMACIÓN SOBRE EL FICHERO:");
        File f = new File("D:\\ADAT\\UNI1\\VerInf.java");
        if(f.exists()){
            System.out.println("Nombre del fichero : "+f.getName());
            System.out.println("Ruta                : "+f.getPath());
            System.out.println("Ruta absoluta       : "+f.getAbsolutePath());
            System.out.println("Se puede leer       : "+f.canRead());
            System.out.println("Se puede escribir   : "+f.canWrite());
            System.out.println("Tamaño              : "+f.length());
            System.out.println("Es un directorio    : "+f.isDirectory());
            System.out.println("Es un fichero       : "+f.isFile());
            System.out.println("Nombre del directorio padre: "+f.getParent());
        }
    }
}

Explicación línea a línea

  1. 1
    System.out.println("INFORMACIÓN SOBRE EL FICHERO:");

    Se imprime una cabecera para el listado que viene a continuación.

  2. 2
    File f = new File("D:\\ADAT\\UNI1\\VerInf.java");

    Se apunta a un fichero concreto mediante su ruta absoluta completa en Windows.

  3. 3
    if(f.exists()){

    Antes de preguntar por sus propiedades, se comprueba que el fichero realmente existe; si no, no tendría sentido pedirle información.

  4. 4
    f.getName()

    Devuelve solo el nombre del fichero, sin la ruta: VerInf.java.

  5. 5
    f.getPath()

    Devuelve la ruta tal y como se usó para construir el File.

  6. 6
    f.getAbsolutePath()

    Devuelve la ruta absoluta completa en el sistema de ficheros.

  7. 7
    f.canRead() / f.canWrite()

    Indican si el fichero se puede leer y si se puede escribir, respectivamente (permisos).

  8. 8
    f.length()

    Devuelve el tamaño del fichero, en bytes.

  9. 9
    f.isDirectory() / f.isFile()

    Indican si lo que representa el objeto File es un directorio o un fichero normal.

  10. 10
    f.getParent()

    Devuelve el nombre del directorio que contiene a este fichero (su "padre").

Resultado Mostrado por Terminal (continúa en la Página 5)

Salida de Consola
INFORMACIÓN SOBRE EL FICHERO: Nombre del fichero : VerInf.java Ruta : D:\ADAT\UNI1\VerInf.java Ruta absoluta : D:\ADAT\UNI1\VerInf.java Se puede leer : true Se puede escribir : true Tamaño : 824 Es un directorio : false Es un fichero : true Nombre del directorio padre: D:\ADAT\UNI1
En resumen: el programa comprueba primero que el fichero existe y después vuelca por pantalla todas sus propiedades usando los métodos de consulta que ofrece la clase File.
Página 5 · java.io

Página 5: CrearDir

El programa CrearDir crea un directorio nuevo, crea dos ficheros vacíos dentro de él, renombra uno de esos ficheros y crea un tercer fichero usando una ruta de texto directamente. Después se muestra cómo borrar un fichero con delete().

CrearDir.java
import java.io.*;

public class CrearDir {

    public static void main(String[] args) {
        File d = new File("NUEVODIR");
        File f1 = new File(d,"FICHERO1.TXT");
        File f2 = new File(d,"FICHERO2.TXT");

        d.mkdir();

        try {
            if (f1.createNewFile())
                System.out.println("FICHERO1 creado correctamente...");
            else
                System.out.println("No se ha podido crear FICHERO1...");

            if (f2.createNewFile())
                System.out.println("FICHERO2 creado correctamente...");
            else
                System.out.println("No se ha podido crear FICHERO2...");
        } catch (IOException ioe) {ioe.printStackTrace();}

        f1.renameTo(new File(d,"FICHERO1NUEVO"));

        try {
            File f3 = new File("NUEVODIR/FICHERO3.TXT");
            f3.createNewFile();
        } catch (IOException ioe) {ioe.printStackTrace();}
    }
}

Explicación línea a línea

  1. 1
    File d = new File("NUEVODIR");

    Se declara un File que representará el directorio que se va a crear.

  2. 2
    File f1 = new File(d,"FICHERO1.TXT"); / File f2 = new File(d,"FICHERO2.TXT");

    Se declaran dos ficheros dentro de ese directorio, usando el constructor File(File, String).

  3. 3
    d.mkdir();

    Se crea físicamente el directorio NUEVODIR en disco. Hasta este punto, d solo era un objeto en memoria.

  4. 4
    if (f1.createNewFile()) ... else ...

    createNewFile() crea el fichero vacío y devuelve true si lo ha creado, o false si ya existía; se avisa por pantalla del resultado. Se repite lo mismo para f2.

  5. 5
    } catch (IOException ioe) {ioe.printStackTrace();}

    createNewFile() puede lanzar IOException, por eso va dentro de un try-catch; si algo falla, se imprime la traza del error.

  6. 6
    f1.renameTo(new File(d,"FICHERO1NUEVO"));

    Se renombra FICHERO1.TXT a FICHERO1NUEVO, indicando el nuevo nombre como otro objeto File.

  7. 7
    File f3 = new File("NUEVODIR/FICHERO3.TXT");

    Esta vez se apunta al nuevo fichero con una única ruta de texto (en vez de File+String), mezclando / como separador dentro de NUEVODIR.

  8. 8
    f3.createNewFile();

    Se crea el tercer fichero, vacío, dentro de NUEVODIR.

A continuación, el libro muestra cómo borrar el fichero f2 con delete() (no se puede borrar NUEVODIR directamente mientras contenga ficheros):

borrar fichero
if(f2.delete())
    System.out.println("Fichero borrado...");
else
    System.out.println("No se ha podido borrar el fichero...");
En resumen: mkdir() crea directorios, createNewFile() crea ficheros vacíos, renameTo() renombra y delete() borra; para borrar un directorio primero hay que borrar (o mover) todo lo que contiene.
Página 12 · java.io

Página 12: LeerFichTexto

El programa LeerFichTexto lee, carácter a carácter, el propio fichero fuente LeerFichTexto.java usando FileReader (flujo de caracteres, en vez de bytes) y muestra cada carácter por pantalla.

LeerFichTexto.java
import java.io.*;

public class LeerFichTexto {

    public static void main(String[] args) throws IOException {
        //declarar fichero
        File fichero =
            new File("C:\\EJERCICIOS\\UNI1\\LeerFichTexto.java");
        //crear el flujo de entrada hacia el fichero
        FileReader fic = new FileReader(fichero);
        int i;
        while ((i = fic.read()) != -1)
            System.out.println((char) i);
        fic.close();
    }
}

Explicación línea a línea

  1. 1
    public static void main(String[] args) throws IOException {

    Como este programa no usa try-catch, delega el manejo de la excepción hacia fuera con throws IOException.

  2. 2
    File fichero = new File("C:\\EJERCICIOS\\UNI1\\LeerFichTexto.java");

    Se apunta al propio fichero fuente del programa (ruta absoluta en Windows).

  3. 3
    FileReader fic = new FileReader(fichero);

    Se crea el flujo de entrada de caracteres hacia el fichero (a diferencia de FileInputStream, que trabaja con bytes).

  4. 4
    int i;

    Variable donde se guardará cada carácter leído (como número entero).

  5. 5
    while ((i = fic.read()) != -1)

    read() lee un carácter y lo devuelve como int; el bucle continúa mientras el valor devuelto sea distinto de -1 (fin de fichero).

  6. 6
    System.out.println((char) i);

    Se convierte i a char mediante un cast ((char) i) para mostrarlo como el carácter que realmente es, no como su valor numérico.

  7. 7
    fic.close();

    Se cierra el flujo de lectura.

El libro añade que, para leer de golpe bloques de 20 caracteres en vez de uno a uno, se puede usar la variante de read() que recibe un array:

lectura por bloques
char b[]= new char[20];
while ((i = fic.read(b)) != -1) System.out.println(b);

read(b) rellena el array b con hasta 20 caracteres de una vez y devuelve cuántos ha leído realmente (o -1 al llegar al final); es más eficiente que leer carácter a carácter.

En resumen: FileReader permite leer un fichero de texto carácter a carácter (o por bloques), haciendo cast a char para interpretarlo como texto y no como números.
Página 13 · java.io

Página 13: EscribirFichTexto

El programa EscribirFichTexto escribe, carácter a carácter, el contenido de un String en un fichero de texto usando FileWriter, y añade un * al final con append().

EscribirFichTexto.java
import java.io.*;

public class EscribirFichTexto {

    public static void main(String[] args) throws IOException {
        File fichero = new
            File("C:\\EJERCICIOS\\UNI1\\FichTexto.txt");
        FileWriter fic = new FileWriter(fichero);

        String cadena ="Esto es una prueba con FileWriter";
        char[] cad = cadena.toCharArray();
        for(int i=0; i<cad.length; i++)
            fic.write(cad[i]);

        fic.append('*');
        fic.close();
    }
}

Explicación línea a línea

  1. 1
    File fichero = new File("C:\\EJERCICIOS\\UNI1\\FichTexto.txt");

    Se apunta al fichero de texto que se va a crear/sobrescribir.

  2. 2
    FileWriter fic = new FileWriter(fichero);

    Se crea el flujo de salida de caracteres; si el fichero ya existía, su contenido anterior se borra (por defecto no se añade al final).

  3. 3
    String cadena ="Esto es una prueba con FileWriter";

    Se declara el texto que se quiere escribir en el fichero.

  4. 4
    char[] cad = cadena.toCharArray();

    Se convierte el String en un array de char, para poder recorrerlo y escribirlo carácter a carácter.

  5. 5
    for(int i=0; i<cad.length; i++) fic.write(cad[i]);

    Se recorre el array y se escribe cada carácter, uno a uno, con write(int).

  6. 6
    fic.append('*');

    append() añade un carácter (o texto) al final de lo ya escrito, sin sobrescribir nada.

  7. 7
    fic.close();

    Se cierra el flujo, asegurando que todo lo escrito queda guardado en el fichero.

El libro añade que, en vez de escribir carácter a carácter, se puede escribir un array entero de golpe con fic.write(cad), y muestra un ejemplo escribiendo varias cadenas seguidas desde un array de String (continúa en la Página 14):

escritura de un array de Strings
String prov[] =
    {"Albacete","Avila","Badajoz","Cáceres","Huelva","Jaén",
     "Madrid","Segovia","Soria","Toledo","Valladolid","Zamora"};

for(int i=0; i<prov.length; i++) fic.write(prov[i]);

Aquí write(String) escribe cada provincia completa de una vez; como no se añade ningún salto de línea entre una y otra, quedarán todas pegadas seguidas en el fichero.

En resumen: FileWriter permite escribir texto carácter a carácter, un array de caracteres de golpe, o directamente un String completo con write(String); append() añade contenido sin borrar lo anterior.
Página 14 · java.io

Página 14: LeerFichTextoBuf

Antes del programa, el libro muestra que FileWriter fic = new FileWriter(fichero, true); permite añadir al final de un fichero en vez de borrar su contenido (segundo parámetro true), y que BufferedReader se construye envolviendo un FileReader. El programa LeerFichTextoBuf usa esa combinación para leer un fichero línea a línea (en vez de carácter a carácter).

LeerFichTextoBuf.java
import java.io.*;

public class LeerFichTextoBuf {

    public static void main(String[] args) {
        try{
            BufferedReader fichero = new BufferedReader(
                            new FileReader("LeerFichTexto.java"));
            String linea;
            while((linea = fichero.readLine()) !=null)
                System.out.println(linea);

            fichero.close();
        }
        catch (FileNotFoundException fn ){
            System.out.println("No se encuentra el fichero");}
        catch (IOException io) {
            System.out.println("Error de E/S ");}
    }
}

Explicación línea a línea

  1. 1
    try{

    Aquí todo el trabajo con ficheros se agrupa dentro de un try-catch, en vez de usar throws IOException en main.

  2. 2
    BufferedReader fichero = new BufferedReader(new FileReader("LeerFichTexto.java"));

    Se envuelve un FileReader con un BufferedReader, que añade un búfer de lectura eficiente y, sobre todo, el método readLine() (leer líneas completas), algo que FileReader no tiene por sí solo.

  3. 3
    String linea;

    Variable donde se guardará cada línea leída.

  4. 4
    while((linea = fichero.readLine()) !=null)

    readLine() lee una línea completa y la devuelve, o devuelve null al llegar al final del fichero; el bucle continúa mientras haya líneas.

  5. 5
    System.out.println(linea);

    Se imprime la línea leída tal cual, sin necesidad de cast (ya es un String, no un número como con read()).

  6. 6
    fichero.close();

    Se cierra el flujo dentro del propio bloque try, tras terminar de leer.

  7. 7
    catch (FileNotFoundException fn ){ ... } catch (IOException io) { ... }

    Se capturan por separado dos posibles errores: que el fichero no exista (FileNotFoundException), o cualquier otro error de entrada/salida (IOException), mostrando un mensaje distinto para cada caso.

En resumen: BufferedReader envuelve a FileReader para poder leer el fichero línea a línea con readLine(), en vez de carácter a carácter; el fin de fichero se detecta aquí con null, no con -1.
Páginas 14-15 · java.io

Páginas 14-15: EscribirFichTextoBuf

Antes del programa, el libro muestra que BufferedWriter se construye envolviendo un FileWriter, igual que pasaba con BufferedReader. El programa EscribirFichTextoBuf escribe 10 líneas de texto en un fichero, saltando de línea después de cada una con newLine().

EscribirFichTextoBuf.java
import java.io.*;

public class EscribirFichTextoBuf {

    public static void main(String[] args) {
        try{
            BufferedWriter fichero = new BufferedWriter
                        (new FileWriter("FichTexto.txt"));

            for (int i=1; i<11; i++){
                fichero.write("Fila numero: "+i);
                fichero.newLine();
            }
            fichero.close();
        }
        catch (FileNotFoundException fn ){
                System.out.println("No se encuentra el fichero");}
        catch (IOException io) {
                System.out.println("Error de E/S ");}
    }
}

Explicación línea a línea

  1. 1
    BufferedWriter fichero = new BufferedWriter(new FileWriter("FichTexto.txt"));

    Se envuelve un FileWriter con un BufferedWriter, que añade un búfer para escribir caracteres de forma eficiente y aporta el método newLine().

  2. 2
    for (int i=1; i<11; i++){

    Bucle que recorre los números del 1 al 10 (10 vueltas en total).

  3. 3
    fichero.write("Fila numero: "+i);

    Se escribe el texto "Fila numero: " concatenado con el número de la vuelta actual.

  4. 4
    fichero.newLine();

    Se escribe un salto de línea, para que la siguiente fila quede en una línea nueva.

  5. 5
    fichero.close();

    Se cierra el flujo una vez escritas las 10 líneas.

En resumen: BufferedWriter añade a FileWriter un búfer eficiente y el método newLine() para no tener que escribir el salto de línea a mano.
Página 15 · java.io

Página 15: Reescritura con PrintWriter

El libro presenta PrintWriter (también deriva de Writer) como alternativa más cómoda: tiene los métodos print() y println(), igual que System.out, y println() añade el salto de línea automáticamente. Se construye envolviendo un FileWriter. Con esto, el ejemplo de EscribirFichTextoBuf se puede reescribir así, mucho más corto:

reescritura con PrintWriter
PrintWriter fichero = new PrintWriter
                (new FileWriter("FichTexto.txt"));
for(int i=1; i<11; i++)
        fichero.println("Fila numero: "+i);
fichero.close();

Explicación línea a línea

  1. 1
    PrintWriter fichero = new PrintWriter(new FileWriter("FichTexto.txt"));

    Se envuelve un FileWriter con un PrintWriter, que aporta los métodos print() y println().

  2. 2
    for(int i=1; i<11; i++)

    Mismo bucle de antes: del 1 al 10.

  3. 3
    fichero.println("Fila numero: "+i);

    Se escribe la línea y se salta de línea en la misma instrucción; ya no hace falta llamar a newLine() por separado como con BufferedWriter.

  4. 4
    fichero.close();

    Se cierra el flujo, igual que en los ejemplos anteriores.

En resumen: PrintWriter hace lo mismo que BufferedWriter + newLine(), pero en menos código, reutilizando los métodos print()/println() que ya se conocen de System.out.
Página 16 · java.io

Página 16: EscribirFichBytes

Este programa escribe los números del 1 al 99 en un fichero byte a byte usando FileOutputStream, y a continuación vuelve a leer ese mismo fichero con FileInputStream, mostrando cada valor leído por consola.

EscribirFichBytes.java
import java.io.*;

public class EscribirFichBytes {

    public static void main(String[] args) throws IOException {

        File fichero = new File("c:\\...");

        FileOutputStream fileout = new FileOutputStream(fichero);

        FileInputStream filein = new FileInputStream(fichero);

        int i;

        for (i = 1; i < 100; i++) {
            fileout.write(i);
        }

        fileout.close();

        while ((i = filein.read()) != -1) {
            System.out.println(i);
        }

        filein.close();
    }
}

Explicación línea a línea

  1. 1
    import java.io.*;

    Se importan todos los paquetes de java.io, donde están las clases para trabajar con ficheros y flujos de entrada/salida (File, FileOutputStream, FileInputStream...).

  2. 2
    public class EscribirFichBytes {

    Se declara una clase pública llamada EscribirFichBytes.

  3. 3
    public static void main(String[] args) throws IOException {

    Se declara el método main: es estático, no devuelve nada (void) y recibe como argumento un array de String llamado args. Además, puede lanzar una excepción de tipo IOException.

  4. 4
    File fichero = new File("c:\...");

    Se crea una variable fichero de tipo File, indicando como argumento la ruta del archivo.

  5. 5
    FileOutputStream fileout = new FileOutputStream(fichero);

    Se crea la variable fileout de tipo FileOutputStream, asignándole un nuevo objeto que recibe fichero como argumento: este flujo permitirá escribir bytes en el archivo.

  6. 6
    FileInputStream filein = new FileInputStream(fichero);

    Se crea la variable filein de tipo FileInputStream, asignándole un nuevo objeto que recibe fichero como argumento: este flujo permitirá leer bytes del archivo.

  7. 7
    int i;

    Se declara una variable entera llamada i, que se reutilizará más abajo como contador y como valor leído.

  8. 8
    for (i = 1; i < 100; i++) {

    Se inicia un bucle for en el que i empieza en 1 y se incrementa de 1 en 1 mientras sea menor que 100 (es decir, recorre del 1 al 99).

  9. 9
    fileout.write(i);

    En cada vuelta del bucle se escribe el valor de i dentro del flujo de salida fileout, es decir, se guarda ese byte en el fichero.

  10. 10
    fileout.close();

    Se cierra el flujo de salida fileout una vez terminado el bucle, liberando el recurso y asegurando que todo lo escrito quede guardado en el fichero.

  11. 11
    while ((i = filein.read()) != -1) {

    Se inicia un bucle while en el que a i se le asigna lo que devuelve filein.read() (el siguiente byte leído). El bucle continúa mientras ese valor sea distinto de -1, que es lo que devuelve read() al llegar al final del fichero.

  12. 12
    System.out.println(i);

    Se muestra por pantalla el valor almacenado en i, es decir, cada byte leído del fichero.

  13. 13
    filein.close();

    Se cierra el flujo de entrada filein, liberando el recurso una vez terminada la lectura.

En resumen: el programa escribe los bytes del 1 al 99 en un fichero y luego los vuelve a leer y a imprimir por consola, mostrando así cómo se combinan FileOutputStream y FileInputStream para escribir y leer datos byte a byte en Java.
Página 17 · java.io

Página 17: EscribirFichData

Aquí en vez de FileOutputStream a secas (que solo escribe bytes sueltos), se envuelve ese flujo con un DataOutputStream, que añade métodos como writeUTF() y writeInt() para escribir directamente tipos de datos primitivos (cadenas, enteros...) en el fichero binario FichData.dat.

EscribirFichData.java
import java.io.*;

public class EscribirFichData {

    public static void main(String[] args) throws IOException {

        File fichero = new File("FichData.dat");
        FileOutputStream fileout = new FileOutputStream(fichero);
        DataOutputStream dataOS = new DataOutputStream(fileout);

        String nombres[] =
                    {"Ana","Luis Miguel","Alicia","Pedro","Manuel",
                     "Andrés", "Julio","Antonio","María Jesús"};

        int edades[] = {14,15,13,15,16,12,16,14,13};

        for (int i=0; i<edades.length; i++){
            dataOS.writeUTF(nombres[i]);
            dataOS.writeInt(edades[i]);
        }
        dataOS.close();
    }
}

Explicación línea a línea

  1. 1
    import java.io.*;

    Se importan todos los paquetes de java.io.

  2. 2
    public class EscribirFichData {

    Se declara una clase pública llamada EscribirFichData.

  3. 3
    public static void main(String[] args) throws IOException {

    Se declara el método main, estático, sin retorno y que puede lanzar IOException.

  4. 4
    File fichero = new File("FichData.dat");

    Se crea el objeto File que apunta al fichero FichData.dat (ruta relativa).

  5. 5
    FileOutputStream fileout = new FileOutputStream(fichero);

    Se crea un flujo de salida de bytes fileout enlazado al fichero.

  6. 6
    DataOutputStream dataOS = new DataOutputStream(fileout);

    Se envuelve fileout con un DataOutputStream, que permite escribir tipos de datos primitivos (no solo bytes sueltos) mediante métodos como writeUTF() y writeInt().

  7. 7
    String nombres[] = {"Ana", "Luis Miguel", ...};

    Se declara y se rellena un array de String con los nombres de las personas.

  8. 8
    int edades[] = {14,15,13,15,16,12,16,14,13};

    Se declara y se rellena un array de int con las edades, en el mismo orden que el array de nombres (posición a posición forman parejas).

  9. 9
    for (int i=0; i<edades.length; i++){

    Se inicia un bucle for que recorre todas las posiciones del array edades (y, por tanto, también las del array nombres, que tiene la misma longitud).

  10. 10
    dataOS.writeUTF(nombres[i]);

    Se escribe en el fichero el nombre de la posición i, codificado como texto UTF.

  11. 11
    dataOS.writeInt(edades[i]);

    Justo después, se escribe la edad de la posición i como número entero. Nombre y edad quedan guardados seguidos, en parejas.

  12. 12
    dataOS.close();

    Se cierra el flujo dataOS, lo que fuerza a que todo lo escrito quede realmente guardado en FichData.dat.

En resumen: el programa recorre dos arrays en paralelo (nombres y edades) y va guardando cada pareja en el fichero FichData.dat usando DataOutputStream, que sabe escribir tipos primitivos directamente (a diferencia de FileOutputStream, que solo trabaja con bytes sueltos). Es imprescindible recordar el orden en que se escribe cada dato, porque habrá que leerlo exactamente en ese mismo orden (ver Página 18).
Página 18 · java.io

Página 18: LeerFichData

Este programa lee de vuelta el fichero FichData.dat generado en la Página 17, usando DataInputStream para recuperar los datos con readUTF() y readInt(), respetando el mismo orden en el que se escribieron: primero el nombre y luego la edad.

LeerFichData.java
import java.io.*;

public class LeerFichData {

    public static void main(String[] args) throws IOException {
        File fichero = new File("FichData.dat");
        FileInputStream filein = new FileInputStream(fichero);
        DataInputStream dataIS = new DataInputStream(filein);
        String n;
        int e;

        try {
         while (true) {
             n = dataIS.readUTF();
             e = dataIS.readInt();
             System.out.println("Nombre: " + n + ", edad: " + e);
         }
         }catch (EOFException eo) {}

        dataIS.close();
    }
}

Explicación línea a línea

  1. 1
    import java.io.*;

    Se importan todos los paquetes de java.io.

  2. 2
    public class LeerFichData {

    Se declara una clase pública llamada LeerFichData.

  3. 3
    public static void main(String[] args) throws IOException {

    Se declara el método main, estático, sin retorno y que puede lanzar IOException.

  4. 4
    File fichero = new File("FichData.dat");

    Se apunta al mismo fichero que se generó en la Página 17.

  5. 5
    FileInputStream filein = new FileInputStream(fichero);

    Se crea un flujo de entrada de bytes filein enlazado al fichero.

  6. 6
    DataInputStream dataIS = new DataInputStream(filein);

    Se envuelve filein con un DataInputStream, que permite leer tipos primitivos con métodos como readUTF() y readInt().

  7. 7
    String n; int e;

    Se declaran las variables n y e, que irán guardando, en cada vuelta del bucle, el nombre y la edad leídos.

  8. 8
    try {

    Se abre un bloque try porque no se sabe de antemano cuántos registros hay: se leerá hasta que salte una excepción al llegar al final del fichero.

  9. 9
    while (true) {

    Bucle infinito: seguirá leyendo registros hasta que lo interrumpa la excepción de fin de fichero.

  10. 10
    n = dataIS.readUTF();

    Se lee el siguiente dato del fichero como texto UTF y se guarda en n (el nombre).

  11. 11
    e = dataIS.readInt();

    A continuación se lee el siguiente dato como número entero y se guarda en e (la edad). Debe leerse justo después del nombre, porque así se escribió.

  12. 12
    System.out.println("Nombre: " + n + ", edad: " + e);

    Se muestra por pantalla la pareja nombre-edad que se acaba de leer.

  13. 13
    } catch (EOFException eo) {}

    Cuando ya no quedan más datos, readUTF()/readInt() lanzan EOFException (fin de fichero); se captura con un bloque vacío, que simplemente corta el bucle sin mostrar ningún error.

  14. 14
    dataIS.close();

    Se cierra el flujo de entrada una vez terminada la lectura.

Resultado Mostrado por Terminal

Salida de Consola
Nombre: Ana, edad: 14 Nombre: Luis Miguel, edad: 15 Nombre: Alicia, edad: 13 Nombre: Pedro, edad: 15 Nombre: Manuel, edad: 16 Nombre: Andrés, edad: 12 Nombre: Julio, edad: 16 Nombre: Antonio, edad: 14 Nombre: María Jesús, edad: 13
En resumen: el programa lee el fichero binario que generó la Página 17 en parejas nombre-edad, respetando el orden de escritura, y usa una excepción (EOFException) como forma "normal" de detectar que se ha llegado al final del fichero, ya que DataInputStream no tiene un equivalente directo al -1 de read() para tipos compuestos.
Páginas 19-20 · java.io

Páginas 19-20: Persona y EscribirFichObject

La serialización convierte un objeto en una secuencia de bytes que luego se puede restaurar. La clase debe implementar Serializable. Se escribe con ObjectOutputStream (sobre un FileOutputStream) y writeObject().

Persona.java + EscribirFichObject.java
import java.io.Serializable;
public class Persona implements Serializable {
    private String nombre;
    private int edad;
    public Persona(String nombre, int edad) { this.nombre = nombre; this.edad = edad; }
    public Persona() { this.nombre = null; }
    // set/get de nombre y edad ...
}

import java.io.*;
public class EscribirFichObject {
  public static void main(String[] args) throws IOException {
    Persona persona;
    File fichero = new File("FichPersona.dat");
    FileOutputStream fileout = new FileOutputStream(fichero);
    ObjectOutputStream dataOS = new ObjectOutputStream(fileout);
    String nombres[] = {"Ana","Luis Miguel","Alicia","Pedro","Manuel","Andrés","Julio","Antonio","María Jesús"};
    int edades[] = {14,15,13,15,16,12,16,14,13};
    for (int i=0; i<edades.length; i++) {
      persona = new Persona(nombres[i], edades[i]);
      dataOS.writeObject(persona);
    }
    dataOS.close();
  }
}

Explicación línea a línea

  1. 1
    public class Persona implements Serializable

    Implementar Serializable (interfaz sin métodos) es lo que permite guardar objetos de esta clase en un fichero binario. Tiene dos atributos privados, dos constructores y sus get/set.

  2. 2
    FileOutputStream fileout = new FileOutputStream(fichero);

    Flujo de bytes hacia el fichero en disco.

  3. 3
    ObjectOutputStream dataOS = new ObjectOutputStream(fileout);

    Flujo que procesa objetos y los vuelca sobre el FileOutputStream. Se puede abreviar anidando: new ObjectOutputStream(new FileOutputStream(fichero)).

  4. 4
    String nombres[] / int edades[]

    Dos arrays paralelos con los datos: la posición i de cada uno corresponde a la misma persona.

  5. 5
    persona = new Persona(nombres[i], edades[i]);

    En cada vuelta se crea una Persona con los datos de esa posición.

  6. 6
    dataOS.writeObject(persona);

    Escribe el objeto completo en el fichero.

  7. 7
    dataOS.close();

    Cierra el flujo de salida.

En resumen: para guardar objetos hace falta una clase Serializable, un ObjectOutputStream y writeObject().
Páginas 20-21 · java.io

Páginas 20-21: LeerFichObject

Lee los objetos Persona con ObjectInputStream y readObject(). Como no se sabe cuántos objetos hay, se lee en un while(true) hasta que salta EOFException.

LeerFichObject.java
import java.io.*;
public class LeerFichObject {
  public static void main(String[] args) throws IOException, ClassNotFoundException {
    Persona persona;
    File fichero = new File("FichPersona.dat");
    FileInputStream filein = new FileInputStream(fichero);
    ObjectInputStream dataIS = new ObjectInputStream(filein);
    try {
      while (true) {
        persona = (Persona) dataIS.readObject();
        System.out.printf("Nombre: %s, edad: %d %n", persona.getNombre(), persona.getEdad());
      }
    } catch (EOFException eo) {
      System.out.println("FIN DE LECTURA.");
    }
    dataIS.close();
  }
}

Explicación línea a línea

  1. 1
    throws IOException, ClassNotFoundException

    readObject() puede lanzar ambas excepciones (ClassNotFoundException si no encuentra la clase del objeto leído).

  2. 2
    FileInputStream / ObjectInputStream

    Igual que al escribir, pero de entrada: el FileInputStream lee bytes del fichero y el ObjectInputStream los reconstruye como objetos.

  3. 3
    while (true) { ... }

    Bucle infinito: la salida no se controla con una condición, sino con la excepción del catch.

  4. 4
    persona = (Persona) dataIS.readObject();

    readObject() devuelve un Object, por eso se hace cast a Persona.

  5. 5
    catch (EOFException eo)

    Al llegar al final del fichero se lanza EOFException: es la forma de terminar la lectura y se avisa con "FIN DE LECTURA.".

  6. 6
    dataIS.close();

    Cierra el flujo de entrada.

En resumen: con objetos el fin de fichero se detecta con EOFException (no con -1 ni null) y hay que hacer cast del resultado de readObject().
Páginas 21-22 · java.io

Páginas 21-22: MiObjectOutputStream (añadir objetos)

Problema: cada new ObjectOutputStream(fichero) escribe una cabecera. Si se reabre el fichero para añadir, queda una segunda cabecera en medio y al leer salta StreamCorruptedException. Solución: heredar de ObjectOutputStream y anular writeStreamHeader().

MiObjectOutputStream.java + uso
public class MiObjectOutputStream extends ObjectOutputStream {
    public MiObjectOutputStream(OutputStream out) throws IOException { super(out); }
    protected MiObjectOutputStream() throws IOException, SecurityException { super(); }
    // Redefinición del método de escribir la cabecera para que no haga nada.
    protected void writeStreamHeader() throws IOException { }
}

File fichero = new File(nombrefichero);
ObjectOutputStream dataOS;
if (!fichero.exists()) {
    FileOutputStream fileout = new FileOutputStream(fichero);
    dataOS = new ObjectOutputStream(fileout);
} else {
    dataOS = new MiObjectOutputStream(new FileOutputStream(fichero, true));
}

Explicación línea a línea

  1. 1
    public class MiObjectOutputStream extends ObjectOutputStream

    Clase propia que hereda todo el comportamiento de ObjectOutputStream.

  2. 2
    MiObjectOutputStream(OutputStream out) { super(out); }

    Constructor que pasa el flujo al constructor de la clase padre. El segundo constructor (protected, sin parámetros) llama a super().

  3. 3
    protected void writeStreamHeader() throws IOException { }

    Se sobrescribe el método que escribe la cabecera y se deja vacío: esta clase ya no añade cabecera.

  4. 4
    if (!fichero.exists()) { ... }

    Si el fichero no existe (primera vez) se usa el ObjectOutputStream normal, que sí escribe la cabecera necesaria.

  5. 5
    else { dataOS = new MiObjectOutputStream(new FileOutputStream(fichero, true)); }

    Si ya existe se usa la clase redefinida (sin cabecera) y true en el FileOutputStream para añadir al final en vez de sobrescribir.

En resumen: primera vez → ObjectOutputStream; para añadir → MiObjectOutputStream con append = true, así el fichero conserva una única cabecera.
Páginas 22-23 · java.io

Páginas 22-23: EscribirFichAleatorio

RandomAccessFile permite acceder a cualquier posición del fichero (no solo secuencialmente). Modos: "r" (solo lectura, el fichero debe existir) y "rw" (lectura y escritura, lo crea si no existe). Métodos clave: getFilePointer(), seek(pos), length() y skipBytes(n). Cada registro mide 36 bytes: id (int, 4) + apellido (10 chars × 2 = 20) + departamento (int, 4) + salario (double, 8).

EscribirFichAleatorio.java
import java.io.*;
public class EscribirFichAleatorio {
  public static void main(String[] args) throws IOException {
    File fichero = new File("AleatorioEmple.dat");
    RandomAccessFile file = new RandomAccessFile(fichero, "rw");
    String apellido[] = {"FERNANDEZ","GIL","LOPEZ","RAMOS","SEVILLA","CASILLA","REY"};
    int dep[] = {10, 20, 10, 10, 30, 30, 20};
    Double salario[] = {1000.45, 2400.60, 3000.0, 1500.56, 2200.0, 1435.87, 2000.0};
    StringBuffer buffer = null;
    int n = apellido.length;
    for (int i = 0; i<n; i++) {
      file.writeInt(i+1);
      buffer = new StringBuffer(apellido[i]);
      buffer.setLength(10);
      file.writeChars(buffer.toString());
      file.writeInt(dep[i]);
      file.writeDouble(salario[i]);
    }
    file.close();
  }
}

Explicación línea a línea

  1. 1
    RandomAccessFile file = new RandomAccessFile(fichero, "rw");

    Abre (o crea) el fichero en modo lectura/escritura. El puntero empieza en 0.

  2. 2
    String apellido[] / int dep[] / Double salario[]

    Arrays paralelos con los datos de 7 empleados. No hace falta seek() porque se escribe en orden y el puntero avanza solo.

  3. 3
    file.writeInt(i+1);

    Escribe el identificador (1, 2, 3...), 4 bytes. Se usa i+1 para que sea mayor que 0.

  4. 4
    buffer = new StringBuffer(apellido[i]); buffer.setLength(10);

    Se fuerza el apellido a exactamente 10 caracteres (rellena con espacios/nulos o recorta) para que todos los registros midan lo mismo.

  5. 5
    file.writeChars(buffer.toString());

    Escribe los 10 caracteres a 2 bytes cada uno: 20 bytes.

  6. 6
    file.writeInt(dep[i]); file.writeDouble(salario[i]);

    Escribe departamento (4 bytes) y salario (8 bytes). Total por registro: 4 + 20 + 4 + 8 = 36 bytes.

En resumen: con registros de longitud fija (36 bytes) se puede calcular la posición de cualquier empleado; por eso el apellido se ajusta siempre a 10 caracteres.
Página 24 · java.io

Página 24: LeerFichAleatorio

Recorre todos los registros del fichero anterior. Empieza en la posición 0 y suma 36 (tamaño del registro) para pasar al siguiente.

LeerFichAleatorio.java
import java.io.*;
public class LeerFichAleatorio {
  public static void main(String[] args) throws IOException {
    File fichero = new File("AleatorioEmple.dat");
    RandomAccessFile file = new RandomAccessFile(fichero, "r");
    int id, dep, posicion;
    Double salario;
    char apellido[] = new char[10], aux;
    posicion = 0;
    for (;;) {
      file.seek(posicion);
      id = file.readInt();
      for (int i = 0; i < apellido.length; i++) {
        aux = file.readChar();
        apellido[i] = aux;
      }
      String apellidos = new String(apellido);
      dep = file.readInt();
      salario = file.readDouble();
      if (id > 0)
        System.out.printf("ID: %s, Apellido: %s, Departamento: %d, Salario: %.2f %n",
                          id, apellidos.trim(), dep, salario);
      posicion = posicion + 36;
      if (file.getFilePointer() == file.length()) break;
    }
    file.close();
  }
}

Explicación línea a línea

  1. 1
    new RandomAccessFile(fichero, "r");

    Modo solo lectura: el fichero debe existir y escribir lanzaría IOException.

  2. 2
    char apellido[] = new char[10], aux; posicion = 0;

    Array de 10 caracteres para reconstruir el apellido; posicion empieza en 0 (inicio del primer registro).

  3. 3
    for (;;) { file.seek(posicion); id = file.readInt();

    Bucle infinito. Cada vuelta coloca el puntero al inicio del registro y lee el id (4 bytes).

  4. 4
    aux = file.readChar(); apellido[i] = aux;

    Lee uno a uno los 10 caracteres del apellido (20 bytes) y los guarda en el array.

  5. 5
    String apellidos = new String(apellido);

    Convierte el array de char en String.

  6. 6
    dep = file.readInt(); salario = file.readDouble();

    Lee departamento (4 bytes) y salario (8 bytes), que siguen justo después del apellido.

  7. 7
    if (id > 0) System.out.printf(...)

    Solo imprime si el id es mayor que 0: así se saltan los registros borrados lógicamente (id = -1). trim() quita los espacios de relleno y %.2f muestra 2 decimales.

  8. 8
    posicion = posicion + 36;

    Avanza al siguiente registro (cada empleado ocupa 36 bytes).

  9. 9
    if (file.getFilePointer() == file.length()) break;

    Si el puntero llegó al final del fichero, sale del bucle.

Resultado Mostrado por Terminal

Salida de Consola
ID: 1, Apellido: FERNANDEZ, Departamento: 10, Salario: 1000.45 ID: 2, Apellido: GIL, Departamento: 20, Salario: 2400.6 ID: 3, Apellido: LOPEZ, Departamento: 10, Salario: 3000.0 ID: 4, Apellido: RAMOS, Departamento: 10, Salario: 1500.56 ID: 5, Apellido: SEVILLA, Departamento: 30, Salario: 2200.0 ID: 6, Apellido: CASILLA, Departamento: 30, Salario: 1435.87 ID: 7, Apellido: REY, Departamento: 20, Salario: 2000.0
En resumen: se recorre el fichero de 36 en 36 bytes con seek(), leyendo los campos en el mismo orden y tipo en que se escribieron.
Páginas 25-26 · java.io

Páginas 25-26: Consultar, añadir, insertar y modificar

Fragmentos sueltos (no programas completos) que usan la fórmula (id - 1) * 36 para saltar directamente a un registro sin recorrer el fichero.

Fragmentos · acceso directo a un registro
// CONSULTAR el empleado 5
int identificador = 5;
posicion = (identificador - 1) * 36;
if (posicion >= file.length())
    System.out.printf("ID: %d, NO EXISTE EMPLEADO...", identificador);
else {
    file.seek(posicion);
    id = file.readInt();
    // obtener resto de los datos, como en el ejemplo anterior
}

// AÑADIR al final
long posicion = file.length();
file.seek(posicion);

// INSERTAR empleado con id 20
StringBuffer buffer = null;
String apellido = "GONZALEZ"; Double salario = 1230.87; int id = 20; int dep = 10;
long posicion = (id - 1) * 36;
file.seek(posicion);
file.writeInt(id);
buffer = new StringBuffer(apellido);
buffer.setLength(10);
file.writeChars(buffer.toString());
file.writeInt(dep);
file.writeDouble(salario);
file.close();

// MODIFICAR departamento y salario del empleado 4
int registro = 4;
long posicion = (registro - 1) * 36;
posicion = posicion + 4 + 20;
file.seek(posicion);
file.writeInt(40);
file.writeDouble(4000.87);

Explicación línea a línea

  1. 1
    posicion = (identificador - 1) * 36;

    Calcula dónde empieza el registro: el empleado 1 está en 0, el 2 en 36, el 5 en 144...

  2. 2
    if (posicion >= file.length())

    Si la posición está fuera del fichero, ese empleado no existe; si no, se hace seek() y se lee con normalidad.

  3. 3
    long posicion = file.length(); file.seek(posicion);

    Coloca el puntero al final para añadir registros después del último.

  4. 4
    long posicion = (id - 1) * 36; ... writeInt / writeChars / writeInt / writeDouble

    Para insertar el id 20 se calcula su posición y se escriben los cuatro campos en el mismo orden de siempre (id, apellido de 10 chars, dep, salario).

  5. 5
    posicion = posicion + 4 + 20;

    Para modificar, se salta el id (4 bytes) y el apellido (20 bytes) y se cae justo en el departamento.

  6. 6
    file.writeInt(40); file.writeDouble(4000.87);

    Sobrescribe departamento (40) y salario (4000.87) del empleado 4. Aquí el fichero debe abrirse en modo "rw".

La página 26 propone la Actividad 1.3 (consulta e inserción por línea de comandos) y la Actividad 1.4 (modificar el salario sumando un importe, y borrado lógico: id = -1, apellido = id borrado, dep y salario = 0, más un programa que liste los borrados). Después empieza el apartado 1.7 de ficheros XML, con este ejemplo de estructura:

Empleados.xml
<?xml version="1.0"?>
<Empleados>
    <empleado>
        <id>1</id>
        <apellido>FERNANDEZ</apellido>
        <dep>10</dep>
        <salario>1000.45</salario>
    </empleado>
    <!-- más empleados ... -->
</Empleados>

XML es un metalenguaje de marcas jerárquicas: una raíz (<Empleados>) que contiene elementos (<empleado>), cada uno con sus etiquetas de apertura y cierre. Se procesa con un parser: DOM (carga todo el árbol en memoria) o SAX (secuencial, por eventos).

En resumen: en ficheros aleatorios con registros fijos, (id - 1) * tamaño da la posición de cualquier registro, y sumando los tamaños de los campos anteriores (4 + 20) se llega a un campo concreto.