1. Tipos de excepciones

Tipos de excepciones

Todas las excepciones se dividen en 4 tipos, que en realidad son clases que se heredan entre sí.

Throwableclase

La clase base para todas las excepciones es la Throwableclase. La Throwableclase contiene el código que escribe la pila de llamadas actual (seguimiento de la pila del método actual) en una matriz. Aprenderemos qué es un seguimiento de pila un poco más tarde.

El operador throw solo puede aceptar un objeto que se deriva de la Throwableclase. Y aunque teóricamente puedes escribir código como throw new Throwable();, nadie suele hacer esto. El propósito principal de la Throwableclase es tener una sola clase principal para todas las excepciones.

Errorclase

La siguiente clase de excepción es la Errorclase, que hereda directamente la Throwableclase. La máquina Java crea objetos de la Errorclase (y sus descendientes) cuando se han producido problemas graves . Por ejemplo, un mal funcionamiento del hardware, memoria insuficiente, etc.

Por lo general, como programador, no hay nada que pueda hacer en una situación en la que Errorse ha producido un error de este tipo (el tipo por el cual se debe lanzar un) en el programa: estos errores son demasiado graves. Todo lo que puede hacer es notificar al usuario que el programa falla y/o escribir toda la información conocida sobre el error en el registro del programa.

Exceptionclase

Las clases Exceptiony RuntimeExceptionson para errores comunes que ocurren en la operación de muchos métodos. El objetivo de cada excepción lanzada es ser atrapado por un catchbloque que sepa cómo manejarlo correctamente.

Cuando un método no puede completar su trabajo por algún motivo, debe notificar de inmediato al método que llama lanzando una excepción del tipo apropiado.

En otras palabras, si una variable es igual a null, el método arrojará un NullPointerException. Si se pasaron los argumentos incorrectos al método, generará un InvalidArgumentException. Si el método divide accidentalmente por cero, arrojará un ArithmeticException.

RuntimeExceptionclase

RuntimeExceptionsson un subconjunto de Exceptions. Incluso podríamos decir que RuntimeExceptiones una versión ligera de las excepciones ordinarias ( Exception): se imponen menos requisitos y restricciones a tales excepciones

Aprenderás la diferencia entre Exceptiony RuntimeExceptionmás adelante.


2. Throws: excepciones marcadas

Lanzamientos: excepciones comprobadas

Todas las excepciones de Java se dividen en 2 categorías: marcadas y no marcadas .

Todas las excepciones que heredan RuntimeExceptiono Errorse consideran excepciones no verificadas . Todos los demás son excepciones marcadas .

¡Importante!

Veinte años después de que se introdujeran las excepciones verificadas, casi todos los programadores de Java piensan que esto es un error. En los marcos modernos populares, el 95% de todas las excepciones no están marcadas. El lenguaje C#, que casi copió Java exactamente, no agregó excepciones comprobadas .

¿ Cuál es la principal diferencia entre las excepciones marcadas y no marcadas ?

Hay requisitos adicionales impuestos a las excepciones marcadas . A grandes rasgos, son estos:

Requisito 1

Si un método arroja una excepción comprobada , debe indicar el tipo de excepción en su firma . De esa manera, cada método que lo llame es consciente de que esta "excepción significativa" podría ocurrir en él.

Indique las excepciones marcadas después de los parámetros del método después de la throwspalabra clave (no use la throwpalabra clave por error). Se ve algo como esto:

type method (parameters) throws exception

Ejemplo:

excepción comprobada excepción no verificada
public void calculate(int n) throws Exception
{
   if (n == 0)
      throw new Exception("n is null!");
}
public void calculate(n)
{
   if (n == 0)
      throw new RuntimeException("n is null!");
}

En el ejemplo de la derecha, nuestro código arroja una excepción no verificada : no se requiere ninguna acción adicional. En el ejemplo de la izquierda, el método arroja una excepción verificadathrows , por lo que la palabra clave se agrega a la firma del método junto con el tipo de excepción.

Si un método espera lanzar múltiples excepciones verificadas , todas ellas deben especificarse después de la throwspalabra clave, separadas por comas. El orden no es importante. Ejemplo:

public void calculate(int n) throws Exception, IOException
{
   if (n == 0)
      throw new Exception("n is null!");
   if (n == 1)
      throw new IOException("n is 1");
}

Requisito 2

Si llama a un método que ha verificado excepciones en su firma, no puede ignorar el hecho de que las arroja.

Debe capturar todas esas excepciones agregando catchbloques para cada una o agregándolas a una throwscláusula para su método.

Es como si estuviéramos diciendo: " Estas excepciones son tan importantes que debemos atraparlas. Y si no sabemos cómo manejarlas, entonces cualquiera que pueda llamar a nuestro método debe ser notificado de que tales excepciones pueden ocurrir en él".

Ejemplo:

Imagina que estamos escribiendo un método para crear un mundo poblado por humanos. El número inicial de personas se pasa como argumento. Por lo tanto, debemos agregar excepciones si hay muy pocas personas.

Creando la Tierra Nota
public void createWorld(int n) throws EmptyWorldException, LonelyWorldException
{
   if (n == 0)
      throw new EmptyWorldException("There are no people!");
   if (n == 1)
      throw new LonelyWorldException ("There aren't enough people!");
   System.out.println("A wonderful world was created. Population: " + n);
}
El método arroja potencialmente dos excepciones comprobadas :

  • ExcepciónMundoVacío
  • LonelyWorldException

Esta llamada de método se puede manejar de 3 maneras:

1. No atrape ninguna excepción

Esto se hace con mayor frecuencia cuando el método no sabe cómo manejar adecuadamente la situación.

Código Nota
public void createPopulatedWorld(int population)
throws EmptyWorldException, LonelyWorldException
{
   createWorld(population);
}
El método de llamada no detecta las excepciones y debe informar a otros sobre ellas: las agrega a su propia throwscláusula

2. Captura algunas de las excepciones

Manejamos los errores que podemos manejar. Pero los que no entendemos, los lanzamos al método de llamada. Para hacer esto, necesitamos agregar su nombre a la cláusula throws:

Código Nota
public void createNonEmptyWorld(int population)
throws EmptyWorldException
{
   try
   {
      createWorld(population);
   }
   catch (LonelyWorldException e)
   {
      e.printStackTrace();
   }
}
La persona que llama detecta solo una excepción marcadaLonelyWorldException : . La otra excepción debe agregarse a su firma, indicándola después de la throwspalabra clave

3. Atrapa todas las excepciones

Si el método no arroja excepciones al método de llamada, entonces el método de llamada siempre confía en que todo funcionó bien. Y no podrá tomar ninguna medida para solucionar situaciones excepcionales.

Código Nota
public void createAnyWorld(int population)
{
   try
   {
      createWorld(population);
   }
   catch (LonelyWorldException e)
   {
      e.printStackTrace();
   }
   catch (EmptyWorldException e)
   {
      e.printStackTrace();
   }
}
Todas las excepciones se capturan en este método. La persona que llama estará segura de que todo salió bien.


3. Envolviendo excepciones

Las excepciones marcadas parecían geniales en teoría, pero resultaron ser una gran frustración en la práctica.

Suponga que tiene un método súper popular en su proyecto. Se llama desde cientos de lugares en su programa. Y decide agregarle una nueva excepción marcada . Y bien puede ser que esta excepción marcada sea realmente importante y tan especial que solo el main()método sepa qué hacer si se detecta.

Eso significa que tendrá que agregar la excepción marcadathrows a la cláusula de cada método que llame a su método súper popular . Así como en la throwscláusula de todos los métodos que llaman a esos métodos. Y de los métodos que llaman a esos métodos.

Como resultado, las throwscláusulas de la mitad de los métodos del proyecto obtienen una nueva excepción comprobada . Y, por supuesto, su proyecto está cubierto por pruebas, y ahora las pruebas no se compilan. Y ahora también debe editar las cláusulas throws en sus pruebas.

Y luego todo su código (todos los cambios en cientos de archivos) tendrá que ser revisado por otros programadores. Y en este punto nos preguntamos por qué hicimos tantos cambios sangrientos al proyecto. Día(s) de trabajo y pruebas rotas, ¿todo por agregar una excepción verificada ?

Y, por supuesto, todavía hay problemas relacionados con la herencia y la anulación de métodos. Los problemas que surgen de las excepciones verificadas son mucho mayores que el beneficio. La conclusión es que ahora pocas personas los aman y pocas personas los usan.

Sin embargo, todavía hay mucho código (incluido el código de la biblioteca Java estándar) que contiene estas excepciones comprobadas . ¿Qué hay que hacer con ellos? No podemos ignorarlos, y no sabemos cómo manejarlos.

Los programadores de Java propusieron envolver las excepciones comprobadas en RuntimeException. En otras palabras, capture todas las excepciones verificadas y luego cree excepciones no verificadas (por ejemplo, RuntimeException) y tírelas en su lugar. Hacer eso se parece a esto:

try
{
   // Code where a checked exception might occur
}
catch(Exception exp)
{
   throw new RuntimeException(exp);
}

No es una solución muy bonita, pero no hay nada criminal aquí: la excepción simplemente se metió dentro de un archivo RuntimeException.

Si lo desea, puede recuperarlo fácilmente desde allí. Ejemplo:

Código Nota
try
{
   // Code where we wrap the checked exception
   // in a RuntimeException
}
catch(RuntimeException e)
{
   Throwable cause = e.getCause();
   if (cause instanceof Exception)
   {
      Exception exp = (Exception) cause;
      // Exception handling code goes here
   }
}







Obtenga la excepción almacenada dentro del RuntimeExceptionobjeto. La causevariable puede null

determinar su tipo y convertirla en un tipo de excepción verificada .


4. Captura de múltiples excepciones

Los programadores realmente odian duplicar el código. Incluso se les ocurrió un principio de desarrollo correspondiente: Don't Repeat Yourself (DRY) . Pero cuando se manejan excepciones, hay ocasiones frecuentes en las que un trybloque es seguido por varios catchbloques con el mismo código.

O puede haber 3 catchbloques con el mismo código y otros 2 catchbloques con otro código idéntico. Esta es una situación estándar cuando su proyecto maneja las excepciones de manera responsable.

A partir de la versión 7, en el lenguaje Java se agregó la capacidad de especificar múltiples tipos de excepciones en un solo catchbloque. Se ve algo como esto:

try
{
   // Code where an exception might occur
}
catch (ExceptionType1 | ExceptionType2 | ExceptionType3 name)
{
   // Exception handling code
}

Puedes tener tantos catchbloques como quieras. Sin embargo, un solo catchbloque no puede especificar excepciones que se hereden entre sí. En otras palabras, no puede escribir catch ( Exception| RuntimeExceptione), porque la RuntimeExceptionclase hereda Exception.



5. Excepciones personalizadas

Siempre puede crear su propia clase de excepción. Simplemente crea una clase que hereda la RuntimeExceptionclase. Se verá algo como esto:

class ClassName extends RuntimeException
{
}

Hablaremos de los detalles a medida que aprenda programación orientada a objetos, herencia, constructores y anulación de métodos.

Sin embargo, incluso si solo tiene una clase simple como esta (totalmente sin código), aún puede generar excepciones basadas en ella:

Código Nota
class Solution
{
   public static void main(String[] args)
   {
      throw new MyException();
   }
}

class MyException extends RuntimeException
{
}




Lanzar un desmarcado MyException .

En la misión Java Multithreading , profundizaremos en el trabajo con nuestras propias excepciones personalizadas.