USIL · Facultad de Ingeniería e Inteligencia Artificial 2026-1

Programación Orientada
a Objetos en Java

Domina los 4 pilares de la POO, interfaces gráficas con Swing, manejo de archivos y proyectos completos con diagramas UML y su implementación en Java.

🧬 Herencia🔷 Abstracción 🖥️ Swing GUI📁 Archivos 📐 UML + Java
4
Temas centrales
22
Preguntas de quiz
20+
Ejemplos de código
5
Casos UML completos
Contenido del dashboard
🧬

Tema 1 — Herencia y Polimorfismo

Relación "es un" entre clases. extends, super(), @Override. Polimorfismo dinámico, casting. Clases abstractas como base jerárquica.

🔷

Tema 2 — Abstracción e Interfaces

abstract class vs interface. Contratos de comportamiento con implements. Diferencias clave, múltiple implementación, uso real en Java.

🖥️

Tema 3 — GUI con Swing (USIL)

JFrame, JPanel, componentes (JLabel, JButton, JTextField, JComboBox, JTextArea). Layouts (BorderLayout, FlowLayout, BoxLayout). ActionListener y eventos.

📁

Tema 4 — Manejo de Archivos

File, FileReader/Writer, BufferedReader/Writer, FileInputStream/OutputStream. Try-with-resources, IOException. Lectura y escritura de archivos de texto y binarios.

📐

Tema 5 — Casos UML Integradores

5 proyectos completos con contexto, diagrama UML (todas las relaciones) y código Java completo. Del análisis al código.

1 de 8
Pilares POO Tema 01
01

Herencia y Polimorfismo

La herencia permite crear jerarquías de clases reutilizando código. El polimorfismo permite que un objeto se comporte de formas distintas según su tipo real.

🧬 extendssuper()@Override🔄 Polimorfismo
¿Qué es la Herencia?

Definición

La herencia es el mecanismo por el cual una clase (subclase/hija) adquiere automáticamente los atributos y métodos de otra clase (superclase/padre). Modela la relación "ES UN".

📌 Beneficios clave

Reutilización de código · Organización jerárquica · Extensibilidad · Base para el polimorfismo

Sintaxis Java:

public class Subclase extends Superclase { // atributos propios // métodos propios y sobreescritos }

🔗 Analogía

La herencia biológica: un Perro es un Animal. Hereda características de Animal (respirar, moverse) pero añade las suyas propias (ladrar, raza).

Pilares relacionados:

Concepto¿Qué hace?
extendsHereda de una sola clase padre
super()Llama al constructor del padre
super.met()Llama a un método del padre
@OverrideSobreescribe un método heredado
finalImpide que se sobreescriba/herede
Polimorfismo
🔄

Polimorfismo de sobreescritura (Override)

Una subclase redefine un método de la superclase. En tiempo de ejecución se llama al método de la clase real del objeto, no el de la referencia (binding dinámico).

Animal a = new Perro(); a.sonido(); // llama Perro.sonido() // aunque referencia sea Animal

Polimorfismo de sobrecarga (Overload)

Varios métodos con el mismo nombre pero distintos parámetros (tipo/cantidad) en la misma clase. Se resuelve en compilación.

int sumar(int a, int b) double sumar(double a, double b) int sumar(int a, int b, int c)
⚠️ Java NO tiene herencia múltiple de clases

Una clase Java solo puede extender una clase. Para comportamientos múltiples se usan interfaces (implements). Esto evita el "problema del diamante".

Ejemplo completo: jerarquía Animal
// Superclase public class Animal { private String nombre; private int edad; public Animal(String nombre, int edad) { this.nombre = nombre; this.edad = edad; } public void sonido() { System.out.println("Sonido genérico de animal"); } public void mostrarInfo() { System.out.println("Animal: " + nombre + ", Edad: " + edad); } // getters y setters... }
// Subclase — Hereda de Animal public class Perro extends Animal { private String raza; public Perro(String nombre, int edad, String raza) { super(nombre, edad); // llama constructor padre this.raza = raza; } @Override public void sonido() { // polimorfismo System.out.println("¡Guau! Guau!"); } public void buscarPalo() { System.out.println("Buscando el palo..."); } }
// Polimorfismo en acción public class Main { public static void main(String[] args) { Animal[] animales = { new Perro("Rex", 3, "Labrador"), new Gato("Mishi", 2), new Pez("Nemo", 1) }; for (Animal a : animales) { a.mostrarInfo(); a.sonido(); // binding dinámico → método real } } }
Casos Aplicados
🏦Caso 1 — Sistema bancario: jerarquía de cuentas
Herencia + Polimorfismo

Contexto: Un banco maneja diferentes tipos de cuentas. Todas comparten atributos comunes pero calculan el interés de forma distinta. Usar herencia para modelar la jerarquía y polimorfismo para calcular intereses.

✅ Resolución:
public class Cuenta { protected String numeroCuenta; protected double saldo; protected String titular; public Cuenta(String nro, double saldo, String titular) { this.numeroCuenta = nro; this.saldo = saldo; this.titular = titular; } public void depositar(double monto) { saldo += monto; } public void retirar(double monto) { saldo -= monto; } public double calcularInteres() { return 0; } // será sobreescrito public void mostrar() { System.out.printf("%s | Saldo: S/%.2f | Interés: S/%.2f%n", numeroCuenta, saldo, calcularInteres()); } } public class CuentaAhorros extends Cuenta { private double tasaInteres = 0.035; // 3.5% anual public CuentaAhorros(String nro, double saldo, String t) { super(nro, saldo, t); } @Override public double calcularInteres() { return saldo * tasaInteres; } } public class CuentaCorriente extends Cuenta { private double limiteDescubierto; public CuentaCorriente(String nro, double saldo, String t, double limite) { super(nro, saldo, t); this.limiteDescubierto = limite; } @Override public double calcularInteres() { return saldo * 0.01; } // 1% } // Uso polimórfico: Cuenta[] cuentas = { new CuentaAhorros("CA-001", 5000, "Ana"), new CuentaCorriente("CC-001", 8000, "Beto", 2000) }; for (Cuenta c : cuentas) c.mostrar(); // polimorfismo
🎓Caso 2 — Sistema de empleados con jerarquía y pago
Herencia + super + polimorfismo

Una empresa tiene empleados de planta y contratistas. Todos tienen nombre y horas trabajadas, pero calculan su pago de forma diferente: empleados × tarifa fija + bono, contratistas × tarifa por hora sin bono.

✅ Resolución:
public abstract class Empleado { protected String nombre; protected int horasTrabajadas; public Empleado(String nombre, int horas) { this.nombre = nombre; this.horasTrabajadas = horas; } public abstract double calcularPago(); // abstracto public void mostrar() { System.out.printf("%s → Pago: S/%.2f%n", nombre, calcularPago()); } } public class EmpleadoPlanta extends Empleado { private double tarifaHora; private double bono; public EmpleadoPlanta(String n, int h, double tarifa, double bono) { super(n, h); this.tarifaHora = tarifa; this.bono = bono; } @Override public double calcularPago() { return horasTrabajadas * tarifaHora + bono; } } public class Contratista extends Empleado { private double tarifaHora; public Contratista(String n, int h, double tarifa) { super(n, h); this.tarifaHora = tarifa; } @Override public double calcularPago() { return horasTrabajadas * tarifaHora; } }
Preguntas Frecuentes
¿Qué diferencia hay entre @Override y la sobrecarga (overloading)?
@Override (sobreescritura): redefines un método heredado con la MISMA firma (nombre + parámetros). Se resuelve en tiempo de ejecución (binding dinámico). Overloading (sobrecarga): mismo nombre pero DIFERENTE firma dentro de la misma clase. Se resuelve en compilación.
¿Cuándo usar protected vs private en herencia?
Usa private para encapsulamiento total (solo la propia clase accede). Usa protected cuando las subclases necesitan acceder directamente al atributo o método. Es más común ver private con getters/setters, o protected para atributos que las hijas usan en sus cálculos.
¿Qué es el "casting" en polimorfismo?
Upcasting (automático): Animal a = new Perro() — la referencia Animal apunta a un Perro. Downcasting (manual): Perro p = (Perro) a — recuperas la referencia específica. Usar instanceof antes para evitar ClassCastException: if (a instanceof Perro) { ... }
¿Siempre hay que llamar a super() en el constructor?
Si el padre tiene un constructor con parámetros, la subclase DEBE llamar a super(args) en la primera línea de su constructor. Si el padre tiene un constructor sin parámetros (default), Java lo llama automáticamente. Omitir super() cuando es requerido genera error de compilación.
2 de 8
Pilares POO Tema 02
02

Abstracción e Interfaces

Las clases abstractas definen una base incompleta que las subclases deben completar. Las interfaces definen contratos de comportamiento que cualquier clase puede cumplir.

🔷 abstract class📋 interfaceimplementsContrato
Clase Abstracta

¿Qué es?

Una clase abstracta (abstract class) es una clase que no puede instanciarse directamente. Sirve como plantilla base. Puede tener métodos concretos (con cuerpo) y abstractos (sin cuerpo).

public abstract class Figura { protected String color; // método abstracto: sin cuerpo public abstract double calcularArea(); // método concreto: con cuerpo public void mostrarColor() { System.out.println("Color: " + color); } }

Interfaz

Una interface define un contrato: qué métodos debe implementar quien la use. Los métodos son implícitamente public abstract. Las variables son public static final.

public interface Serializable { void guardar(); // public abstract void cargar(); } // Clase implementa la interfaz: public class Factura implements Serializable { public void guardar() { /* código */ } public void cargar() { /* código */ } }
Comparación: Clase Abstracta vs Interfaz
AspectoClase AbstractaInterfaz
InstanciaciónNo puede instanciarseNo puede instanciarse
Métodos concretos✅ Sí✅ Solo con default/static (Java 8+)
Atributos de instancia✅ Sí❌ Solo constantes (public static final)
Constructor✅ Sí❌ No
Herencia múltiple❌ Solo una clase padre✅ Una clase puede implements múltiples
Palabra claveextendsimplements
Uso típico"ES UN tipo de""PUEDE HACER" (contrato)
Ejemplo: Figuras geométricas
public abstract class Figura { protected String color; public Figura(String color) { this.color = color; } public abstract double calcularArea(); public abstract double calcularPerimetro(); public void describir() { System.out.printf("%s | Área: %.2f | Perímetro: %.2f%n", this.getClass().getSimpleName(), calcularArea(), calcularPerimetro()); } } public class Circulo extends Figura { private double radio; public Circulo(String color, double radio) { super(color); this.radio = radio; } @Override public double calcularArea() { return Math.PI * radio * radio; } @Override public double calcularPerimetro() { return 2 * Math.PI * radio; } } public class Rectangulo extends Figura { private double base, altura; public Rectangulo(String c, double b, double h) { super(c); base=b; altura=h; } @Override public double calcularArea() { return base * altura; } @Override public double calcularPerimetro() { return 2 * (base + altura); } }
// Interfaz: múltiples comportamientos public interface Dibujable { void dibujar(); } public interface Redimensionable { void redimensionar(double factor); } // Una clase puede implementar múltiples interfaces public class Cuadrado extends Figura implements Dibujable, Redimensionable { private double lado; public Cuadrado(String c, double lado) { super(c); this.lado=lado; } @Override public double calcularArea() { return lado*lado; } @Override public double calcularPerimetro() { return 4*lado; } @Override public void dibujar() { System.out.println("□"); } @Override public void redimensionar(double f) { lado *= f; } }
Casos Aplicados
🚗Caso 3 — Vehículos con interfaz de mantenimiento
Abstracta + Interface

Modelar una jerarquía de vehículos (Auto, Moto, Camión). Todos son vehículos (clase abstracta), pero algunos pueden implementar la interfaz Mantenible con métodos de revisión técnica.

✅ Resolución:
public interface Mantenible { void programarRevision(); void registrarMantenimiento(String descripcion); } public abstract class Vehiculo { protected String marca, placa; protected int anio; public Vehiculo(String marca, String placa, int anio) { this.marca=marca; this.placa=placa; this.anio=anio; } public abstract double calcularImpuesto(); public void mostrar() { System.out.printf("%s [%s] %d → Impuesto: S/%.2f%n", marca, placa, anio, calcularImpuesto()); } } public class Auto extends Vehiculo implements Mantenible { private double valorComercial; public Auto(String m, String p, int a, double v) { super(m,p,a); valorComercial=v; } @Override public double calcularImpuesto() { return valorComercial * 0.03; } @Override public void programarRevision() { System.out.println("Revisión técnica programada para " + placa); } @Override public void registrarMantenimiento(String desc) { System.out.println("Mantenimiento [" + placa + "]: " + desc); } }
Preguntas Frecuentes
¿Cuándo elijo clase abstracta y cuándo interfaz?
Usa clase abstracta cuando las subclases comparten atributos y comportamientos concretos (hay código reutilizable) y la relación es "es un". Usa interfaz cuando quieres definir un contrato de comportamiento que clases no relacionadas pueden cumplir ("puede hacer"), o cuando necesitas que una clase cumpla múltiples contratos.
¿Puedo tener métodos concretos en una interfaz?
Desde Java 8, las interfaces pueden tener métodos default (con implementación) y static. El default provee una implementación por defecto que las clases pueden sobreescribir o no. Esto permite añadir métodos a interfaces sin romper las implementaciones existentes.
¿Puede una interfaz extender otra interfaz?
Sí, y además puede extender múltiples interfaces: public interface C extends A, B. Esto es distinto a las clases. Las interfaces forman su propia jerarquía de herencia múltiple.
3 de 8
Interfaces Gráficas Tema 03
03

GUI con Swing

Swing es la biblioteca Java para crear interfaces gráficas. Proporciona JFrame, JPanel, botones, campos de texto, listas y más. Independiente de plataforma.

🖥️ JFrameJPanelJButtonJTextFieldActionListener
¿Qué es Swing?

Definición

Swing es una biblioteca de clases Java para crear interfaces gráficas de usuario (GUI). Surge como mejora de AWT (Abstract Window Toolkit). Es independiente de la plataforma: el mismo código produce la misma apariencia en Windows, Linux y macOS.

📦 Importación

import javax.swing.*; y import java.awt.*;

Jerarquía de componentes

Component
└── Container
    ├── JComponent
    │   ├── JLabel
    │   ├── JButton
    │   ├── JTextField
    │   ├── JTextArea
    │   └── JPanel
    └── Window
        └── Frame → JFrame
Componentes principales
ComponenteClase JavaDescripción
Ventana principalJFrameContenedor top-level con barra de título, minimizar/maximizar/cerrar
Panel / ContenedorJPanelAgrupa componentes. Permite subdividir la ventana en zonas
EtiquetaJLabelTexto estático, no recibe eventos
BotónJButtonBotón clickeable. Genera ActionEvent
Campo de texto (1 línea)JTextFieldEl usuario escribe una línea de texto
ContraseñaJPasswordFieldCampo de texto que oculta los caracteres
Área de texto (multi-línea)JTextAreaMúltiples líneas de texto editable
Lista desplegableJComboBoxSelección de una opción de una lista desplegable
Casilla de verificaciónJCheckBoxMarcar/desmarcar opciones
Radio buttonJRadioButtonSelección exclusiva de opciones (con ButtonGroup)
Gestores de Layout

BorderLayout

Divide en 5 zonas: NORTH, SOUTH, EAST, WEST, CENTER. Layout por defecto de JFrame.

FlowLayout

Coloca componentes en fila de izquierda a derecha. Layout por defecto de JPanel.

GridLayout

Cuadrícula de filas × columnas. Todos los componentes del mismo tamaño.

BoxLayout

Apila horizontal (X_AXIS) o vertical (Y_AXIS). Flexible para formularios.

GridBagLayout

El más flexible. Control exacto de posición y tamaño con restricciones.

null (AbsoluteLayout)

Posicionamiento absoluto con setBounds(x,y,w,h). Usar con cautela.

Eventos
⚡ ¿Qué es un evento?

Un evento es una acción del usuario (clic, tecla, movimiento del ratón) que genera un objeto que notifica a un escuchador (Listener). El escuchador implementa una interfaz y ejecuta el código de respuesta.

EventoInterfaz ListenerMétodoCuándo se genera
ActionEventActionListeneractionPerformed()Clic en botón, Enter en campo
KeyEventKeyListenerkeyPressed(), keyReleased()Actividad del teclado
MouseEventMouseListenermouseClicked(), mouseEntered()Actividad del ratón
WindowEventWindowListenerwindowClosing()Cerrar/minimizar ventana
ItemEventItemListeneritemStateChanged()Selección en JComboBox/JCheckBox
Código: MarcoSencillo (del PDF USIL)
import javax.swing.*; public class MarcoSencillo extends JFrame { private static final int ANCHO = 200, ALTO = 150; public MarcoSencillo() { setTitle("Marco vacío"); setSize(ANCHO, ALTO); setLocation(ANCHO / 2, ALTO / 2); } public static void main(String[] args) { MarcoSencillo marco = new MarcoSencillo(); marco.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); marco.setVisible(true); } }
Código: Campo de texto con botón y ActionListener (del PDF)
import javax.swing.*; import java.awt.event.*; public class CampoTextoConBoton extends JFrame { private JTextField campoTexto; private JButton boton; public CampoTextoConBoton() { setTitle("Ejemplo Campo + Botón"); setSize(300, 150); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLayout(new FlowLayout()); campoTexto = new JTextField(15); add(campoTexto); boton = new JButton("Mostrar Texto"); add(boton); // Agregar escuchador de evento al botón boton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { String texto = campoTexto.getText(); System.out.println("Texto: " + texto); // También puede usar JOptionPane: JOptionPane.showMessageDialog(null, texto); } }); } public static void main(String[] args) { CampoTextoConBoton ventana = new CampoTextoConBoton(); ventana.setVisible(true); } }
Casos Aplicados
🖥️Caso 4 — Calculadora básica con Swing
GUI + ActionListener

Crear una ventana con dos campos numéricos, un JComboBox para seleccionar la operación (+, -, *, /) y un botón "Calcular" que muestra el resultado en un JLabel.

✅ Resolución:
import javax.swing.*; import java.awt.*; import java.awt.event.*; public class Calculadora extends JFrame { private JTextField num1, num2; private JComboBox<String> operacion; private JLabel resultado; public Calculadora() { setTitle("Calculadora"); setSize(350, 200); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLayout(new GridLayout(4, 2, 5, 5)); add(new JLabel("Número 1:")); num1 = new JTextField(); add(num1); add(new JLabel("Número 2:")); num2 = new JTextField(); add(num2); add(new JLabel("Operación:")); operacion = new JComboBox<>(new String[]{"+","-","*","/"}); add(operacion); JButton calcular = new JButton("Calcular"); resultado = new JLabel("Resultado: ---"); add(calcular); add(resultado); calcular.addActionListener(e -> { try { double a = Double.parseDouble(num1.getText()); double b = Double.parseDouble(num2.getText()); String op = (String) operacion.getSelectedItem(); double res = switch(op) { case "+" -> a+b; case "-" -> a-b; case "*" -> a*b; case "/" -> a/b; default -> 0; }; resultado.setText("Resultado: " + res); } catch (NumberFormatException ex) { resultado.setText("Error: ingresa números"); } }); setVisible(true); } public static void main(String[] a) { new Calculadora(); } }
🔐Caso 5 — Login con JPasswordField y validación
GUI + JPasswordField + condicional

Ventana de login con JTextField (usuario), JPasswordField (contraseña) y botón "Ingresar". Si las credenciales son correctas, mostrar mensaje de bienvenida; si no, mostrar error.

✅ Resolución:
import javax.swing.*; import java.awt.*; public class VentanaLogin extends JFrame { private JTextField txtUsuario; private JPasswordField txtClave; private JLabel lblMensaje; public VentanaLogin() { setTitle("Inicio de Sesión"); setSize(300,220); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLayout(new GridLayout(5,1,8,8)); add(new JLabel("Usuario:")); txtUsuario = new JTextField(); add(txtUsuario); add(new JLabel("Contraseña:")); txtClave = new JPasswordField(); add(txtClave); JButton btnIngresar = new JButton("Ingresar"); lblMensaje = new JLabel("", SwingConstants.CENTER); add(btnIngresar); add(lblMensaje); btnIngresar.addActionListener(e -> { String usuario = txtUsuario.getText(); String clave = new String(txtClave.getPassword()); if (usuario.equals("admin") && clave.equals("1234")) { lblMensaje.setText("✅ Bienvenido, " + usuario); lblMensaje.setForeground(Color.GREEN); } else { lblMensaje.setText("❌ Credenciales incorrectas"); lblMensaje.setForeground(Color.RED); } }); setVisible(true); } public static void main(String[] a) { new VentanaLogin(); } }
Preguntas Frecuentes
¿Cuál es la diferencia entre AWT y Swing?
AWT (Abstract Window Toolkit): usa componentes nativos del sistema operativo, menos portable. Swing: dibuja sus propios componentes en Java, independiente del SO, más flexible y personalizable. Swing hereda de AWT (usa su sistema de eventos) pero substituye los componentes visuales. Swing prefija las clases con J (JFrame, JButton vs Frame, Button).
¿Para qué sirve setDefaultCloseOperation?
Define qué hace la aplicación cuando el usuario cierra la ventana. Las opciones son: EXIT_ON_CLOSE: termina la aplicación (más común), HIDE_ON_CLOSE: oculta la ventana pero la app sigue corriendo, DISPOSE_ON_CLOSE: libera recursos de la ventana, DO_NOTHING_ON_CLOSE: no hace nada (debes manejar el evento manualmente).
¿Puedo usar lambda en lugar de ActionListener anónimo?
Sí, desde Java 8. Como ActionListener es una interfaz funcional (solo tiene un método abstracto), puedes usar lambda: boton.addActionListener(e -> { /* tu código */ }); Es más corto y legible que la clase anónima con @Override.
¿Qué es JOptionPane y cuándo usarlo?
JOptionPane muestra cuadros de diálogo emergentes sin crear un JFrame. Útil para mensajes rápidos y entradas simples: JOptionPane.showMessageDialog(null, "Mensaje") muestra info, JOptionPane.showInputDialog("Ingresa:") pide datos, JOptionPane.showConfirmDialog(null,"¿Seguro?") pide Sí/No.
4 de 8
Manejo de Archivos Tema 04
04

Archivos en Java

Java permite leer y escribir archivos de texto y binarios. La clase File gestiona rutas. FileReader/Writer y BufferedReader/Writer trabajan con texto. Streams con binarios.

📁 FileFileReader/WriterBufferedReader/WriterIOException
La clase File

File — Representación de rutas

La clase File NO lee ni escribe datos. Es una representación abstracta de una ruta (path) de archivo o directorio. Se usa para gestionar la existencia, crear, borrar y listar.

File archivo = new File("datos.txt"); archivo.exists(); // ¿existe? archivo.createNewFile(); // crear vacío archivo.delete(); // borrar archivo.isFile(); // ¿es archivo? archivo.isDirectory(); // ¿es carpeta? archivo.length(); // tamaño en bytes

Jerarquía de clases de archivos

Archivos de TEXTO:
├── FileReader / FileWriter
└── BufferedReader / BufferedWriter

Archivos BINARIOS:
├── FileInputStream / FileOutputStream
├── DataInputStream / DataOutputStream
└── ObjectInputStream / ObjectOutputStream
Comparación de clases
ClaseTipo de datoCaracterística principalUso típico
FileWriterTexto (char)Escribe carácter por carácter. Sin buffer.Archivos de texto pequeños
FileReaderTexto (char)Lee carácter por carácter. Sin buffer.Leer texto simple
BufferedWriterTexto (char)Envuelve FileWriter. Buffer mejora rendimiento. Método newLine().Archivos de texto grandes
BufferedReaderTexto (char)Envuelve FileReader. Método readLine() lee línea completa.Lectura línea por línea
FileOutputStreamBinario (byte)Escribe bytes crudos.Imágenes, ejecutables
FileInputStreamBinario (byte)Lee bytes crudos.Copiar archivos binarios
⚠️ IOException — siempre debe manejarse

Toda operación con archivos puede lanzar IOException. En Java moderno usa try-with-resources para garantizar que el archivo se cierre automáticamente, incluso si hay error.

Escribir y leer archivos de texto
// Escribir con BufferedWriter (try-with-resources) try (BufferedWriter bw = new BufferedWriter( new FileWriter("alumnos.txt"))) { bw.write("Ana Pérez,17"); bw.newLine(); // salto de línea bw.write("Luis Ríos,19"); bw.newLine(); bw.write("Carla Soto,16"); } catch (IOException e) { System.out.println("Error al escribir: " + e.getMessage()); } // Leer con BufferedReader try (BufferedReader br = new BufferedReader( new FileReader("alumnos.txt"))) { String linea; while ((linea = br.readLine()) != null) { System.out.println(linea); // procesa cada línea } } catch (IOException e) { System.out.println("Error al leer: " + e.getMessage()); }
// Append: agregar al final sin borrar try (BufferedWriter bw = new BufferedWriter( new FileWriter("alumnos.txt", true))) { // true = append bw.write("Diego Vargas,18"); bw.newLine(); } catch (IOException e) { e.printStackTrace(); }
// FileInputStream/FileOutputStream — copiar archivo binario try (FileInputStream fis = new FileInputStream("foto.jpg"); FileOutputStream fos = new FileOutputStream("copia.jpg")) { byte[] buffer = new byte[1024]; int bytesLeidos; while ((bytesLeidos = fis.read(buffer)) != -1) { fos.write(buffer, 0, bytesLeidos); } System.out.println("Copia realizada exitosamente"); } catch (IOException e) { e.printStackTrace(); }
Casos Aplicados
📋Caso 6 — Registro de notas en archivo de texto
File + BufferedWriter + GUI

Un sistema académico permite ingresar el nombre del alumno y su nota, y los guarda en un archivo "notas.txt". Al presionar "Ver notas" lee el archivo y muestra el contenido en un JTextArea.

✅ Resolución:
import javax.swing.*; import java.awt.*; import java.io.*; import java.awt.event.*; public class RegistroNotas extends JFrame { private JTextField txNombre, txNota; private JTextArea areaResultados; private static final String ARCHIVO = "notas.txt"; public RegistroNotas() { setTitle("Registro de Notas"); setSize(400, 350); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLayout(new BorderLayout(10,10)); JPanel panelForm = new JPanel(new GridLayout(3,2,5,5)); panelForm.add(new JLabel("Alumno:")); txNombre = new JTextField(); panelForm.add(txNombre); panelForm.add(new JLabel("Nota:")); txNota = new JTextField(); panelForm.add(txNota); JButton btnGuardar = new JButton("Guardar"); JButton btnVer = new JButton("Ver Notas"); panelForm.add(btnGuardar); panelForm.add(btnVer); add(panelForm, BorderLayout.NORTH); areaResultados = new JTextArea(); add(new JScrollPane(areaResultados), BorderLayout.CENTER); btnGuardar.addActionListener(e -> guardarNota()); btnVer.addActionListener(e -> verNotas()); setVisible(true); } private void guardarNota() { try (BufferedWriter bw = new BufferedWriter( new FileWriter(ARCHIVO, true))) { bw.write(txNombre.getText() + "," + txNota.getText()); bw.newLine(); txNombre.setText(""); txNota.setText(""); } catch (IOException ex) { JOptionPane.showMessageDialog(this, "Error: " + ex.getMessage()); } } private void verNotas() { areaResultados.setText(""); try (BufferedReader br = new BufferedReader( new FileReader(ARCHIVO))) { String linea; while ((linea = br.readLine()) != null) areaResultados.append(linea + "\n"); } catch (IOException ex) { areaResultados.setText("No hay notas registradas aún."); } } public static void main(String[] a) { new RegistroNotas(); } }
Preguntas Frecuentes
¿Qué pasa si no cierro un archivo?
Si no cierras el archivo: puede que los datos no se escriban en disco (el buffer no se vacía → flush), el archivo quede bloqueado impidiendo que otros programas lo accedan, y se produzca una fuga de recursos. Usa siempre try-with-resources que llama automáticamente a close() al salir del bloque.
¿Cómo puedo agregar al final del archivo sin borrar el contenido?
Al crear el FileWriter pasa true como segundo parámetro (modo append): new FileWriter("archivo.txt", true). Sin este parámetro, el FileWriter sobreescribe el archivo desde el principio (borra todo el contenido previo).
¿Qué es la serialización de objetos?
La serialización convierte un objeto Java completo a bytes para guardarlo en un archivo o transmitirlo por red. La clase debe implementar Serializable. Se usa ObjectOutputStream para escribir y ObjectInputStream para leer. Útil para persistir el estado de objetos entre ejecuciones del programa.
5 de 8
Proyectos Tema 05
📐

Casos Completos:
UML → Java

5 proyectos con contexto real, diagrama UML completo (todas las relaciones) y su implementación en Java. Del análisis al código.

AsociaciónAgregación ◇Composición ◆Herencia ▷Realización
Caso 1 · Sistema Universitario
🎓 Facultad con cursos, alumnos y profesores

📋 Contexto

Una universidad tiene Facultades que contienen Cursos (composición, un curso no existe sin su facultad). Los cursos tienen Alumnos inscritos (agregación, el alumno existe sin el curso). Los Profesores dictan cursos (asociación). Existe una jerarquía: Persona es la clase base de Alumno y Profesor. La interfaz Evaluable define el contrato de calificación.

📐 Diagrama UML

«interface» Evaluable
+ evaluar(): double
+ obtenerCalificacion(): String

«abstract» Persona
- nombre: String
- dni: String
+ mostrarInfo(): void (abstract)
↑ herencia
Alumno - - -▷ Evaluable
- codigo: String
- nota: double
+ evaluar(): double
Profesor
- especialidad: String
+ dictarCurso(): void

Facultad ── Curso
- nombre: String
- cursos: List<Curso> (1..*)
+ agregarCurso(): void
Curso ── Alumno
- nombre: String
- alumnos: List<Alumno> (0..*)
+ inscribir(Alumno): void
Curso también tiene asociación con Profesor (Profesor → Curso)

☕ Implementación Java

Ver código Java completo
import java.util.*; // Interfaz public interface Evaluable { double evaluar(); String obtenerCalificacion(); } // Clase abstracta base public abstract class Persona { protected String nombre, dni; public Persona(String nombre, String dni) { this.nombre = nombre; this.dni = dni; } public abstract void mostrarInfo(); } // Alumno hereda Persona, implementa Evaluable (realización) public class Alumno extends Persona implements Evaluable { private String codigo; private double nota; public Alumno(String nombre, String dni, String codigo) { super(nombre, dni); this.codigo = codigo; } public void setNota(double nota) { this.nota = nota; } @Override public double evaluar() { return nota; } @Override public String obtenerCalificacion() { return nota >= 11 ? "Aprobado" : "Desaprobado"; } @Override public void mostrarInfo() { System.out.println("Alumno: " + nombre + " | " + obtenerCalificacion()); } } // Curso: composición con Facultad, agregación con Alumnos public class Curso { private String nombre; private Profesor profesor; // asociación private List<Alumno> alumnos = new ArrayList<>(); // agregación public Curso(String nombre, Profesor profesor) { this.nombre = nombre; this.profesor = profesor; } public void inscribir(Alumno a) { alumnos.add(a); } public void mostrarAlumnos() { System.out.println("=== Curso: " + nombre + " ==="); alumnos.forEach(Alumno::mostrarInfo); } } // Facultad: composición — los cursos viven con la facultad public class Facultad { private String nombre; private List<Curso> cursos = new ArrayList<>(); // composición public Facultad(String nombre) { this.nombre = nombre; } public void agregarCurso(Curso c) { cursos.add(c); } }
Caso 2 · Tienda de productos
🛒 Inventario con herencia y GUI

📋 Contexto

Una tienda vende Productos de distintos tipos: Físicos (tienen peso y stock) y Digitales (tienen URL de descarga). El Inventario tiene una colección de productos (agregación). La interfaz Vendible define el precio final. El empleado usa una GUI Swing para registrar ventas.

📐 Diagrama UML

«interface» Vendible
+ calcularPrecioFinal(): double
+ aplicarDescuento(double): void
↑ realización
«abstract» Producto
- id: String · - nombre: String · - precio: double
+ abstract calcularPrecioFinal(): double
+ toString(): String
↑ herencia
ProductoFisico implements Vendible
- peso: double · - stock: int
ProductoDigital implements Vendible
- urlDescarga: String

Inventario ── Producto (0..*)
- productos: List<Producto>
+ agregarProducto(Producto): void
+ buscarPorId(String): Producto

☕ Implementación Java (fragmento clave)

Ver código Java
public interface Vendible { double calcularPrecioFinal(); void aplicarDescuento(double porcentaje); } public abstract class Producto { protected String id, nombre; protected double precio; public Producto(String id, String nombre, double precio) { this.id=id; this.nombre=nombre; this.precio=precio; } public abstract double calcularPrecioFinal(); @Override public String toString() { return id + " | " + nombre + " | S/" + calcularPrecioFinal(); } } public class ProductoFisico extends Producto implements Vendible { private double peso; private int stock; public ProductoFisico(String id, String n, double p, double peso, int s) { super(id,n,p); this.peso=peso; this.stock=s; } @Override public double calcularPrecioFinal() { return precio * 1.18; } // +IGV @Override public void aplicarDescuento(double pct) { precio *= (1 - pct/100); } } // Inventario: agregación de Productos public class Inventario { private List<Producto> productos = new ArrayList<>(); public void agregar(Producto p) { productos.add(p); } public void listar() { productos.forEach(System.out::println); } public Producto buscar(String id) { return productos.stream() .filter(p -> p.id.equals(id)) .findFirst().orElse(null); } }
Caso 3 · Banco USIL
🏦 Cuentas bancarias con herencia múltiple de interfaces

📋 Contexto

Un banco gestiona cuentas. Cuenta es la clase abstracta base. CuentaAhorros y CuentaCorriente la heredan. La interfaz Transaccionable define depositar/retirar y la interfaz Auditable define el registro de movimientos. Todas las cuentas de un Cliente están en composición (las cuentas son del banco, no del cliente — si el cliente se va, el banco sigue teniendo el registro). Empleado tiene asociación reflexiva (un empleado puede tener un supervisor Empleado).

📐 Diagrama UML

«interface» Transaccionable
+ depositar(double): void
+ retirar(double): boolean
«interface» Auditable
+ registrarMovimiento(String): void
+ obtenerHistorial(): List<String>

«abstract» Cuenta implements Transaccionable, Auditable
- numeroCuenta: String · # saldo: double · - historial: List<String>
+ abstract calcularInteres(): double
↑ herencia
CuentaAhorros
- tasaInteres: double
CuentaCorriente
- limiteDescubierto: double

Cliente ── Cuenta (1..*)
- nombre: String · - cuentas: List<Cuenta>
Empleado ↺ supervisa
- supervisor: Empleado (0..1)
- subordinados: List<Empleado>

☕ Implementación Java (fragmento)

Ver código Java
public abstract class Cuenta implements Transaccionable, Auditable { protected String numeroCuenta; protected double saldo; private List<String> historial = new ArrayList<>(); public Cuenta(String nro, double saldo) { this.numeroCuenta=nro; this.saldo=saldo; } @Override public void depositar(double m) { saldo+=m; registrarMovimiento("Depósito: +"+m); } @Override public boolean retirar(double m) { if(saldo>=m){ saldo-=m; registrarMovimiento("Retiro: -"+m); return true; } return false; } @Override public void registrarMovimiento(String desc) { historial.add(desc); } @Override public List<String> obtenerHistorial() { return historial; } public abstract double calcularInteres(); } // Asociación reflexiva en Empleado public class Empleado { private String nombre; private Empleado supervisor; // reflexiva 0..1 private List<Empleado> subordinados = new ArrayList<>(); // reflexiva 0..* public Empleado(String nombre) { this.nombre=nombre; } public void setSupervisor(Empleado e) { this.supervisor=e; e.subordinados.add(this); } }
Caso 4 · Hospital
🏥 Pacientes, médicos y citas médicas

📋 Contexto

Un hospital tiene Médicos y Pacientes, ambos son Personas (herencia). Se generan CitasMédicas que asocian a un médico con un paciente (asociación). El hospital tiene Departamentos (composición). La interfaz Notificable permite enviar avisos de citas.

📐 Diagrama UML

«interface» Notificable
+ enviarNotificacion(String): void

«abstract» Persona
- nombre: String · - edad: int · - telefono: String
↑ herencia
Medico implements Notificable
- especialidad: String · - cmp: String
Paciente implements Notificable
- historialMedico: String

CitaMedica ——→ Medico · CitaMedica ——→ Paciente
- fecha: String · - hora: String · - motivo: String
- medico: Medico (asociación) · - paciente: Paciente (asociación)

Hospital ── Departamento (1..*)
- departamentos: List<Departamento>
+ agendarCita(Medico, Paciente, String): CitaMedica

☕ Fragmento Java

Ver código Java
public class CitaMedica { private Medico medico; // asociación → Medico private Paciente paciente; // asociación → Paciente private String fecha, hora, motivo; public CitaMedica(Medico m, Paciente p, String f, String h, String mot) { medico=m; paciente=p; fecha=f; hora=h; motivo=mot; m.enviarNotificacion("Cita programada el "+f+" a las "+h); p.enviarNotificacion("Su cita con Dr. "+m.getNombre()+" es el "+f); } public void mostrar() { System.out.printf("Cita: %s %s | Dr.%s → %s | %s%n", fecha, hora, medico.getNombre(), paciente.getNombre(), motivo); } } public class Hospital { private String nombre; private List<Departamento> deptos = new ArrayList<>(); // composición private List<CitaMedica> citas = new ArrayList<>(); public CitaMedica agendarCita(Medico m, Paciente p, String fecha) { CitaMedica cita = new CitaMedica(m, p, fecha, "09:00", "Consulta"); citas.add(cita); return cita; } }
Caso 5 · Proyecto Integrador
🏢 Sistema de Gestión Empresarial + GUI + Archivos

📋 Contexto

Una empresa quiere gestionar sus productos y pedidos. Combina todos los temas: jerarquía de productos (herencia + abstracción), catálogo de productos (agregación), pedidos (composición con los ítems del pedido), interfaz gráfica Swing para registrar pedidos y persistencia en archivo de texto.

📐 Diagrama UML (integrador)

«interface» Persistible
+ guardarEnArchivo(String): void
+ cargarDeArchivo(String): void
«interface» Calculable
+ calcularTotal(): double
+ aplicarImpuesto(): double

«abstract» Producto
- codigo: String · - nombre: String · # precio: double
+ abstract calcularPrecioFinal(): double
↑ herencia
ProductoNacional
- igv=18%
ProductoImportado
- arancel: double

ItemPedido ——→ Producto
- producto: Producto · - cantidad: int
+ getSubtotal(): double

Pedido ── ItemPedido (1..*) · implements Calculable, Persistible
- numeroPedido: String · - items: List<ItemPedido>
+ agregarItem(ItemPedido): void
+ calcularTotal(): double · + guardarEnArchivo(String): void

VentanaPedido extends JFrame ——→ Pedido
- pedidoActual: Pedido (asociación)
- txtProducto, txtCantidad: JTextField
+ eventos: ActionListener

☕ Fragmento Java (integrador)

Ver código Java completo del caso integrador
import java.io.*; import java.util.*; import javax.swing.*; import java.awt.*; // Interfaces interface Persistible { void guardarEnArchivo(String ruta) throws IOException; } interface Calculable { double calcularTotal(); double aplicarImpuesto(); } // Jerarquía de Productos abstract class Producto { protected String codigo, nombre; protected double precio; public Producto(String c, String n, double p) { codigo=c; nombre=n; precio=p; } public abstract double calcularPrecioFinal(); public String getCodigo() { return codigo; } public String getNombre() { return nombre; } } class ProductoNacional extends Producto { public ProductoNacional(String c, String n, double p) { super(c,n,p); } @Override public double calcularPrecioFinal() { return precio*1.18; } } // ItemPedido: asociación con Producto class ItemPedido { private Producto producto; private int cantidad; public ItemPedido(Producto p, int c) { producto=p; cantidad=c; } public double getSubtotal() { return producto.calcularPrecioFinal()*cantidad; } @Override public String toString() { return producto.getNombre()+" x"+cantidad+" = S/"+getSubtotal(); } } // Pedido: composición con ItemPedido, implementa interfaces class Pedido implements Calculable, Persistible { private String numero; private List<ItemPedido> items = new ArrayList<>(); // composición public Pedido(String n) { numero=n; } public void agregarItem(ItemPedido i) { items.add(i); } @Override public double calcularTotal() { return items.stream().mapToDouble(ItemPedido::getSubtotal).sum(); } @Override public double aplicarImpuesto() { return calcularTotal(); } @Override public void guardarEnArchivo(String ruta) throws IOException { try (BufferedWriter bw = new BufferedWriter(new FileWriter(ruta, true))) { bw.write("Pedido: "+numero); bw.newLine(); for (ItemPedido i : items) { bw.write(" "+i); bw.newLine(); } bw.write("Total: S/"+calcularTotal()); bw.newLine(); } } } // VentanaPedido: GUI Swing que usa Pedido (asociación) class VentanaPedido extends JFrame { private Pedido pedidoActual; // asociación private JTextField txCodigo, txCantidad; private JTextArea areaResumen; public VentanaPedido() { pedidoActual = new Pedido("P001"); setTitle("Nuevo Pedido"); setSize(400,320); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLayout(new BorderLayout()); JPanel form = new JPanel(new GridLayout(3,2,5,5)); form.add(new JLabel("Código:")); txCodigo = new JTextField(); form.add(txCodigo); form.add(new JLabel("Cantidad:")); txCantidad = new JTextField(); form.add(txCantidad); JButton btnAgregar = new JButton("Agregar"); JButton btnGuardar = new JButton("Guardar Pedido"); form.add(btnAgregar); form.add(btnGuardar); add(form, BorderLayout.NORTH); areaResumen = new JTextArea(); add(new JScrollPane(areaResumen)); btnAgregar.addActionListener(e -> { Producto p = new ProductoNacional(txCodigo.getText(), "Prod", 100); int qty = Integer.parseInt(txCantidad.getText()); pedidoActual.agregarItem(new ItemPedido(p, qty)); areaResumen.append(p.getCodigo()+" x"+qty+"\n"); }); btnGuardar.addActionListener(e -> { try { pedidoActual.guardarEnArchivo("pedidos.txt"); areaResumen.append("Total: S/"+pedidoActual.calcularTotal()+"\n✅ Guardado!\n"); } catch(IOException ex) { JOptionPane.showMessageDialog(this,"Error E/S"); } }); setVisible(true); } public static void main(String[] a) { new VentanaPedido(); } }
6 de 8
Evaluación Quiz 1
📝

Quiz — Temas 1 y 2

10 preguntas sobre Herencia, Polimorfismo, Abstracción e Interfaces. Mezcla de opción múltiple y Verdadero/Falso.

🧬 Herencia🔷 Abstracción10 preguntas
1 / 10
Pregunta 1 de 10
0/10
7 de 8
Evaluación Quiz 2
🏆

Quiz — Temas 3 y 4

12 preguntas sobre GUI con Swing y Manejo de Archivos en Java. ¡Demuestra lo aprendido!

🖥️ Swing GUI📁 Archivos12 preguntas
1 / 12
Pregunta 1 de 12
0/12
🎓 ¡Has completado el Dashboard de POO en Java!

Repasaste herencia, polimorfismo, abstracción, interfaces, GUI con Swing, manejo de archivos y 5 proyectos completos UML → Java. ¡Sigue practicando con código!

  • 🧬 Tema 1: Herencia y Polimorfismo ✅
  • 🔷 Tema 2: Abstracción e Interfaces ✅
  • 🖥️ Tema 3: GUI con Swing ✅
  • 📁 Tema 4: Archivos en Java ✅
  • 📐 Tema 5: Casos Completos UML → Java ✅

Ref: Joyanes, L. & Zahonero, I. (2011). Programación en Java 6. Mc Graw Hill.

8 de 8