Errores Checked vs Unchecked
Parte de la sección Programación Orientada a Objetos del Journey de Java de Coddy — lección 61 de 87.
Has visto que la jerarquía de excepciones de Java se divide en diferentes ramas. La distinción clave que debes entender es entre las excepciones checked (verificadas) y unchecked (no verificadas); esto afecta a cómo debes manejarlas en tu código.
Las excepciones verificadas son excepciones que el compilador te obliga a manejar. Extienden Exception directamente (no a través de RuntimeException). Debes capturarlas o declararlas con throws:
// Debe manejar IOException - es una excepción comprobada
public void readFile(String path) throws IOException {
FileReader reader = new FileReader(path); // Puede lanzar IOException
}
// O capturarla
public void readFileSafe(String path) {
try {
FileReader reader = new FileReader(path);
} catch (IOException e) {
System.out.println("File error: " + e.getMessage());
}
}Excepciones no verificadas extienden RuntimeException. El compilador no requiere que las manejes; normalmente indican errores de programación como referencias nulas o accesos a arreglos inválidos:
// No se necesita la declaración throws
public int divide(int a, int b) {
return a / b; // Puede lanzar ArithmeticException (unchecked)
}
String name = null;
name.length(); // NullPointerException (unchecked)Al crear excepciones personalizadas, elija la clase padre en función de cómo desea que quienes las llaman las manejen. Utilice excepciones verificadas para condiciones recuperables que el llamador debería anticipar. Utilice excepciones no verificadas para errores de programación que no deberían ocurrir en un código correcto.
Desafío
Fácil¡Vamos a construir un sistema de verificación de edad que demuestre la diferencia clave entre las excepciones verificadas (checked) y no verificadas (unchecked)! Crearás excepciones personalizadas de ambos tipos y verás cómo el compilador las trata de manera diferente.
Organizarás tu código en cuatro archivos:
InvalidAgeException.java: Crea una excepción verificada para fallos de validación de edad. Esta excepción debe extender directamente deException, lo que significa que los llamadores estarán obligados a manejarla. Incluye un constructor que acepte un mensaje y lo pase a la clase padre.NegativeAgeException.java: Crea una excepción no verificada para cuando alguien proporcione una edad negativa (un error de programación que no debería ocurrir con una entrada correcta). Esta debe extender deRuntimeException. Incluye un constructor que acepte y pase un mensaje al padre.AgeVerifier.java: Crea una clase que valide edades utilizando ambos tipos de excepciones. Incluye dos métodos estáticos:verifyAge(int age)- Este método debe lanzarNegativeAgeException(no verificada) con el mensaje"Age cannot be negative: " + agesi la edad es menor que 0. Si la edad es menor que 18, lanzaInvalidAgeException(verificada) con el mensaje"Must be 18 or older: " + age. De lo contrario, imprime"Age " + age + " verified successfully". Dado que este método lanza una excepción verificada, debes declararlo conthrows InvalidAgeException.verifyAgeUncheckedOnly(int age)- Este método solo utiliza excepciones no verificadas. Si la edad es negativa, lanzaNegativeAgeExceptioncon el mismo formato de mensaje. Si la edad es menor que 18, lanza unaIllegalArgumentExceptioncon el mensaje"Too young: " + age. De lo contrario, imprime"Age " + age + " verified (unchecked method)". ¡Nota que este método no necesita una declaraciónthrows!Main.java: ¡Une tu sistema de verificación! Recibirás una entrada: una edad (entero).Primero, imprime
"=== Checked Exception Method ==="y llama averifyAgecon la edad de entrada. Dado que lanza una excepción verificada, debes envolverla en un try-catch. CapturaInvalidAgeExceptione imprime"Checked exception caught: " + e.getMessage(). También capturaNegativeAgeExceptione imprime"Unchecked exception caught: " + e.getMessage().Luego imprime una línea en blanco y
"=== Unchecked Exception Method ===". Llama averifyAgeUncheckedOnlycon la misma edad. Aunque este método puede lanzar excepciones, no estás obligado a capturarlas. Sin embargo, envuélvelo en un try-catch de todos modos para manejar los errores con elegancia. CapturaNegativeAgeExceptione imprime"Runtime exception caught: " + e.getMessage(). CapturaIllegalArgumentExceptione imprime"Illegal argument caught: " + e.getMessage().
Recibirás una entrada: un número entero que representa la edad a verificar.
Observa la diferencia clave: el método con la excepción verificada requiere una declaración throws y obliga a los llamadores a manejarla, mientras que el método no verificado compila perfectamente sin ningún manejo de excepciones. ¡Esta es la distinción fundamental entre las excepciones verificadas y no verificadas en Java!
Hoja de referencia
Las excepciones en Java se dividen en excepciones verificadas (checked) y no verificadas (unchecked), lo que determina cómo el compilador obliga a manejarlas.
Las excepciones verificadas extienden de Exception directamente (no a través de RuntimeException). El compilador te obliga a capturarlas o a declararlas con throws:
// Debe declararse con throws
public void readFile(String path) throws IOException {
FileReader reader = new FileReader(path);
}
// O capturarla
public void readFileSafe(String path) {
try {
FileReader reader = new FileReader(path);
} catch (IOException e) {
System.out.println("File error: " + e.getMessage());
}
}Las excepciones no verificadas extienden de RuntimeException. El compilador no requiere que las manejes. Normalmente indican errores de programación:
// No se necesita declaración throws
public int divide(int a, int b) {
return a / b; // Puede lanzar ArithmeticException (no verificada)
}
String name = null;
name.length(); // NullPointerException (no verificada)Al crear excepciones personalizadas, elige la clase padre basándote en cómo deben manejarlas quienes llaman al método:
- Usa excepciones verificadas (extiende
Exception) para condiciones recuperables que el llamador debería anticipar - Usa excepciones no verificadas (extiende
RuntimeException) para errores de programación que no deberían ocurrir en un código correcto
Pruébalo tú mismo
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int age = scanner.nextInt();
// TODO: Probar el método de excepción verificada
// Imprimir: "=== Checked Exception Method ==="
// Llamar a AgeVerifier.verifyAge(age) en un try-catch
// - Capturar InvalidAgeException: imprimir "Checked exception caught: [message]"
// - Capturar NegativeAgeException: imprimir "Unchecked exception caught: [message]"
// TODO: Imprimir una línea en blanco, luego "=== Unchecked Exception Method ==="
// Llamar a AgeVerifier.verifyAgeUncheckedOnly(age) en un try-catch
// - Capturar NegativeAgeException: imprimir "Runtime exception caught: [message]"
// - Capturar IllegalArgumentException: imprimir "Illegal argument caught: [message]"
}
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de OOP
Archivos externosIntroducción a la OOPClases vs. ObjetosLa palabra clave thisMétodosCampos (Atributos)Método constructorSobrecarga de constructoresResumen - Calculadora simple4Herencia
Herencia básica (extends)La palabra clave superSobrescritura de métodos (@Override)Encadenamiento de constructoresLa clase ObjectHerencia simple y multinivelPor qué no hay herencia múltiple de clasesResumen - Jerarquía de empleados7Métodos especiales y la clase Object
Método toString()equals() y hashCode()Método clone()compareTo() y ComparableInterfaz ComparatorResumen - Ordenamiento personalizado10Manejo de excepciones en POO
Jerarquía de clases de excepcionesExcepciones personalizadasErrores Checked vs UncheckedPatrón Try With ResourcesResumen - Usuario validado2Modificadores de acceso y encapsulamiento
Visión general de los niveles de accesoMétodos Getter y SetterOcultamiento de informaciónLa palabra clave finalResumen - Gestor de cuentas bancarias5Polimorfismo
Fundamentos de sobrecarga de métodosSobrescritura de métodos (tiempo de ejecución)Upcasting y DowncastingEl operador instanceofClases y métodos abstractosResumen - Calculadora de figuras8Conceptos avanzados de POO
Composición vs. HerenciaAgregación vs. ComposiciónClases internas, anidadas y anónimasEnums y métodos de EnumRecords (Java 16+)Clases selladas (Java 17+)11Patrones de diseño - Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón BuilderPatrón ObserverPatrón Strategy3Propiedades de clase y miembros estáticos
Variables de instancia vs. estáticasMétodos estáticosBloques estáticosConstantes (static final)Repaso - Contador y utilidad6Interfaces y Clases Abstractas
Introducción a las InterfacesImplementación de InterfacesImplementación de Múltiples InterfacesMétodos Default y Static en InterfacesClases Abstractas vs InterfacesInterfaces FuncionalesResumen - Sistema de Pagos9Genéricos
Introducción a los genéricosClases genéricasMétodos genéricosParámetros de tipo delimitadosComodines (?, extends, super)Resumen - Contenedor genérico12Patrones de diseño Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositePatrón Iterator