Laboratorio 09 — Entrada Salida (I O) en Java - jorgealbertopineda78-del/java GitHub Wiki
Unidad: 9 — Entrada/Salida
Duración estimada: 90 minutos
Nivel: Intermedio-Avanzado
En el Lab08 la bitácora del gremio vivía en un ArrayList en memoria:
private ArrayList<String> bitacora = new ArrayList<>();Cuando el programa termina, esa información desaparece. En un sistema real necesitamos que los datos persistan: que el roster del gremio, el inventario y el historial de batallas se guarden en archivos y se puedan recargar la próxima vez que inicie el juego.
- Leer y escribir archivos de texto con
BufferedReaderyBufferedWriter - Verificar existencia de archivos y directorios con
File - Manejar excepciones de I/O con
IOException - Persistir y recuperar datos del sistema RPG en archivos
.txt
El Sistema de Gestión de Gremio ahora necesita guardar su estado en disco:
- El roster se guarda en
roster.txtal cerrar el programa - El inventario se guarda en
inventario.txt - La bitácora de batallas se acumula en
bitacora.txt(modo append) - Al iniciar, el sistema recarga roster e inventario desde los archivos
┌─────────────────────────────────────────────────────────┐
│ PersistenciaGremio │
│ - CARPETA: String = "datos_gremio" │
│ │
│ + guardarRoster(ArrayList<Personaje>) │
│ throws IOException │
│ + cargarRoster(): ArrayList<String> │
│ throws IOException │
│ │
│ + guardarInventario(HashMap<String, Integer>) │
│ throws IOException │
│ + cargarInventario(): HashMap<String, Integer> │
│ throws IOException │
│ │
│ + agregarEntradaBitacora(String entrada) │
│ throws IOException │
│ + mostrarBitacora() │
│ throws IOException │
│ │
│ - crearCarpetaSiNoExiste() │
└─────────────────────────────────────────────────────────┘
Archivos generados:
datos_gremio/
├── roster.txt ← un personaje por línea
├── inventario.txt ← formato: item,cantidad
└── bitacora.txt ← entradas acumuladas (append)
Crea PersistenciaGremio.java:
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
public class PersistenciaGremio {
private static final String CARPETA = "datos_gremio";
public PersistenciaGremio() {
crearCarpetaSiNoExiste();
}
// Crea la carpeta si todavía no existe
private void crearCarpetaSiNoExiste() {
File carpeta = new File(CARPETA);
if (!carpeta.exists()) {
carpeta.mkdir();
System.out.println("[IO] Carpeta '" + CARPETA + "' creada.");
}
}
// ──────────────────────────────────────────────────────
// GUARDAR ROSTER
// Formato de línea: Sylva,10,300
// nombre,nivel,puntosVida
// ──────────────────────────────────────────────────────
public void guardarRoster(ArrayList<Personaje> roster) throws IOException {
File archivo = new File(CARPETA + "/roster.txt");
BufferedWriter writer = new BufferedWriter(new FileWriter(archivo));
for (Personaje p : roster) {
writer.write(p.getNombre() + "," +
p.getNivel() + "," +
p.getPuntosVida());
writer.newLine(); // salto de línea independiente del SO
}
writer.close();
System.out.println("[IO] Roster guardado: " +
roster.size() + " personajes.");
}
// ──────────────────────────────────────────────────────
// CARGAR ROSTER — retorna lista de líneas sin parsear
// (el Main decide qué hacer con cada línea)
// ──────────────────────────────────────────────────────
public ArrayList<String> cargarRoster() throws IOException {
File archivo = new File(CARPETA + "/roster.txt");
ArrayList<String> lineas = new ArrayList<>();
if (!archivo.exists()) {
System.out.println("[IO] roster.txt no encontrado.");
return lineas;
}
BufferedReader reader = new BufferedReader(new FileReader(archivo));
String linea;
while ((linea = reader.readLine()) != null) {
if (!linea.trim().isEmpty()) {
lineas.add(linea);
}
}
reader.close();
System.out.println("[IO] Roster cargado: " +
lineas.size() + " registros.");
return lineas;
}
}💡
new FileWriter(archivo)por defecto sobrescribe el archivo.
Para agregar al final (modo append) se usanew FileWriter(archivo, true).
Lo veremos en la bitácora (Parte 2).
Agrega estos métodos a PersistenciaGremio:
// ──────────────────────────────────────────────────────
// AGREGAR ENTRADA A BITÁCORA (modo append — no borra lo anterior)
// ──────────────────────────────────────────────────────
public void agregarEntradaBitacora(String entrada) throws IOException {
File archivo = new File(CARPETA + "/bitacora.txt");
// true = modo append → agrega al final del archivo
BufferedWriter writer = new BufferedWriter(
new FileWriter(archivo, true));
writer.write(entrada);
writer.newLine();
writer.close();
}
// ──────────────────────────────────────────────────────
// LEER Y MOSTRAR BITÁCORA COMPLETA
// ──────────────────────────────────────────────────────
public void mostrarBitacora() throws IOException {
File archivo = new File(CARPETA + "/bitacora.txt");
if (!archivo.exists()) {
System.out.println("[IO] La bitácora está vacía.");
return;
}
System.out.println("\n=== Bitácora de Batallas ===");
BufferedReader reader = new BufferedReader(new FileReader(archivo));
String linea;
int numero = 1;
while ((linea = reader.readLine()) != null) {
System.out.println(numero++ + ". " + linea);
}
reader.close();
}💡
new FileWriter(archivo, true)— el segundo parámetrotrue
activa el modo append. Cada llamada agrega al final sin borrar
lo que ya había. Perfecto para logs y bitácoras.
Agrega a PersistenciaGremio los métodos del inventario:
guardarInventario(HashMap<String, Integer> inventario)
- Abre
datos_gremio/inventario.txtpara escritura (sobrescribe) - Por cada entrada del mapa escribe una línea con formato:
item,cantidad - Ejemplo:
Poción de vida,8 - Cierra el writer e imprime cuántos items se guardaron
cargarInventario(): HashMap<String, Integer>
- Si el archivo no existe, retorna un HashMap vacío
- Lee línea por línea y usa
split(",")para separar nombre y cantidad -
Integer.parseInt(partes[1])convierte el texto a número - Retorna el HashMap reconstruido
Pista para cargarInventario():
String[] partes = linea.split(",");
String item = partes[0];
int cantidad = Integer.parseInt(partes[1]);
inventario.put(item, cantidad);Demuestra el sistema en 5 bloques. Rodea todo con try-catch:
try {
// todos los bloques aquí
} catch (IOException e) {
System.out.println("Error de archivo: " + e.getMessage());
}Bloque 1 — Crear personajes y guardar roster:
PersistenciaGremio persistencia = new PersistenciaGremio();
ArrayList<Personaje> roster = new ArrayList<>();
roster.add(new Druida("Sylva", 10, 300, 100));
roster.add(new Arquero("Legolas", 6, 150, "Arco", 20, 95));
roster.add(new Guerrero("Thorin", 9, 400, "Hacha", 80));
persistencia.guardarRoster(roster);Bloque 2 — Cargar roster desde archivo:
ArrayList<String> lineasRoster = persistencia.cargarRoster();
System.out.println("\n=== Roster cargado desde archivo ===");
for (String linea : lineasRoster) {
String[] partes = linea.split(",");
System.out.println("Nombre: " + partes[0] +
" | Nivel: " + partes[1] +
" | Vida: " + partes[2]);
}Bloque 3 — Guardar y cargar inventario:
HashMap<String, Integer> inventario = new HashMap<>();
inventario.put("Poción de vida", 8);
inventario.put("Flecha élfica", 30);
inventario.put("Pergamino de fuego", 3);
persistencia.guardarInventario(inventario);
HashMap<String, Integer> inventarioCargado =
persistencia.cargarInventario();
System.out.println("\n=== Inventario cargado desde archivo ===");
for (Map.Entry<String, Integer> e : inventarioCargado.entrySet()) {
System.out.println(e.getKey() + " → " + e.getValue());
}Bloque 4 — Escribir entradas en la bitácora:
persistencia.agregarEntradaBitacora("Sylva atacó a Malachar (daño: 240)");
persistencia.agregarEntradaBitacora("Legolas sin flechas — no pudo atacar");
persistencia.agregarEntradaBitacora("Thorin venció a Dragón de Hielo");
persistencia.mostrarBitacora();Bloque 5 — Verificar los archivos creados:
File carpeta = new File("datos_gremio");
System.out.println("\n=== Archivos en datos_gremio/ ===");
for (File f : carpeta.listFiles()) {
System.out.println(f.getName() +
" (" + f.length() + " bytes)");
}[IO] Carpeta 'datos_gremio' creada.
[IO] Roster guardado: 3 personajes.
[IO] Roster cargado: 3 registros.
=== Roster cargado desde archivo ===
Nombre: Sylva | Nivel: 10 | Vida: 300
Nombre: Legolas | Nivel: 6 | Vida: 150
Nombre: Thorin | Nivel: 9 | Vida: 400
[IO] Inventario guardado: 3 items.
[IO] Inventario cargado: 3 items.
=== Inventario cargado desde archivo ===
Poción de vida → 8
Flecha élfica → 30
Pergamino de fuego → 3
=== Bitácora de Batallas ===
1. Sylva atacó a Malachar (daño: 240)
2. Legolas sin flechas — no pudo atacar
3. Thorin venció a Dragón de Hielo
=== Archivos en datos_gremio/ ===
roster.txt (44 bytes)
inventario.txt (58 bytes)
bitacora.txt (97 bytes)
| Criterio | Puntos |
|---|---|
crearCarpetaSiNoExiste() usa File correctamente |
10 |
guardarRoster() escribe con BufferedWriter en formato correcto |
15 |
cargarRoster() lee con BufferedReader y maneja archivo inexistente |
15 |
guardarInventario() y cargarInventario() con formato clave,valor |
20 |
agregarEntradaBitacora() usa modo append (FileWriter(f, true)) |
15 |
mostrarBitacora() lee e imprime numerado |
10 |
Main demuestra los 5 bloques con try-catch (IOException)
|
15 |
| Total | 100 |
Responde en respuestas.docx:
- ¿Cuál es la diferencia entre
FileWriter(archivo)yFileWriter(archivo, true)? ¿Qué pasaría con la bitácora si usaras el primero? - ¿Por qué es importante verificar si un archivo existe antes de leerlo? ¿Qué excepción lanzaría Java si intentas leer un archivo que no existe sin verificar?
- ¿Qué ventaja tiene
BufferedReadersobre leer el archivo directamente conFileReader? ¿Qué hace el "buffer" en este contexto? - ¿Definir Enums en java, para que sirven investigar y listar en documento las clases similares a
BufferedReader y BufferedWriteryFileReader y FileWriter?
Sube al repositorio en la carpeta lab09_io:
lab09_io/
├── Personaje.java
├── Guerrero.java
├── Arquero.java
├── Druida.java
├── Nigromante.java
├── (excepciones del Lab07)
├── PersistenciaGremio.java
├── Main.java
└── datos_gremio/ ← se genera al ejecutar el programa
├── roster.txt
├── inventario.txt
└── bitacora.txt
Laboratorio preparado para el curso de Programación Orientada a Objetos — Ago-Dic 2025