Excepciones en Java, Parte 1: Conceptos básicos sobre el manejo de excepciones

Las excepciones de Java son los tipos de bibliotecas y las características del lenguaje que se utilizan para representar y tratar las fallas del programa. Si ha querido comprender cómo se representa el error en el código fuente, ha venido al lugar correcto. Además de una descripción general de las excepciones de Java, lo ayudaré a comenzar con las funciones del lenguaje Java para lanzar objetos, probar código que puede fallar, detectar objetos lanzados y limpiar su código Java después de que se haya lanzado una excepción.

En la primera mitad de este tutorial, aprenderá sobre las características básicas del lenguaje y los tipos de bibliotecas que existen desde Java 1.0. En la segunda mitad, descubrirá capacidades avanzadas introducidas en versiones más recientes de Java.

Tenga en cuenta que los ejemplos de código de este tutorial son compatibles con JDK 12.

descargar Obtener el código Descargar el código fuente, por ejemplo, las aplicaciones de este tutorial. Creado por Jeff Friesen para JavaWorld.

¿Qué son las excepciones de Java?

La falla ocurre cuando el comportamiento normal de un programa Java es interrumpido por un comportamiento inesperado. Esta divergencia se conoce como excepción . Por ejemplo, un programa intenta abrir un archivo para leer su contenido, pero el archivo no existe. Java clasifica las excepciones en unos pocos tipos, así que consideremos cada una.

Excepciones marcadas

Java clasifica las excepciones que surgen de factores externos (como un archivo faltante) como excepciones comprobadas . El compilador de Java verifica que tales excepciones se manejen (corrijan) donde ocurren o que se documenten para manejarlas en otro lugar.

Controladores de excepciones

Un manejador de excepciones es una secuencia de código que maneja una excepción. Interroga el contexto, lo que significa que lee los valores guardados de las variables que estaban dentro del alcance en el momento en que ocurrió la excepción, luego usa lo que aprende para restaurar el programa Java a un flujo de comportamiento normal. Por ejemplo, un manejador de excepciones puede leer un nombre de archivo guardado y pedirle al usuario que reemplace el archivo que falta.

Excepciones de tiempo de ejecución (sin marcar)

Suponga que un programa intenta dividir un entero entre un entero 0. Esta imposibilidad ilustra otro tipo de excepción, a saber, una excepción en tiempo de ejecución . A diferencia de las excepciones comprobadas, las excepciones en tiempo de ejecución suelen surgir de un código fuente mal escrito y, por lo tanto, el programador debe corregirlas. Debido a que el compilador no verifica que las excepciones en tiempo de ejecución se manejen o documenten para que se manejen en otro lugar, puede pensar en una excepción en tiempo de ejecución como una excepción sin marcar .

Acerca de las excepciones en tiempo de ejecución

Puede modificar un programa para manejar una excepción de tiempo de ejecución, pero es mejor corregir el código fuente. Las excepciones en tiempo de ejecución a menudo surgen al pasar argumentos no válidos a los métodos de una biblioteca; el código de llamada defectuoso debería arreglarse.

Errores

Algunas excepciones son muy graves porque ponen en peligro la capacidad de un programa para continuar su ejecución. Por ejemplo, un programa intenta asignar memoria desde la JVM pero no hay suficiente memoria libre para satisfacer la solicitud. Otra situación grave ocurre cuando un programa intenta cargar un archivo de clase mediante una Class.forName()llamada a un método, pero el archivo de clase está dañado. Este tipo de excepción se conoce como error . Nunca debe intentar manejar los errores usted mismo porque es posible que la JVM no pueda recuperarse de ellos.

Excepciones en el código fuente

Una excepción puede representarse en el código fuente como un código de error o como un objeto . Presentaré ambos y te mostraré por qué los objetos son superiores.

Códigos de error versus objetos

Los lenguajes de programación como C usan códigos de error basados ​​en números enteros para representar fallas y razones de fallas, es decir, excepciones. Aquí hay un par de ejemplos:

if (chdir("C:\\temp")) printf("Unable to change to temp directory: %d\n", errno); FILE *fp = fopen("C:\\temp\\foo"); if (fp == NULL) printf("Unable to open foo: %d\n", errno);

La función de C chdir()(cambiar directorio) devuelve un entero: 0 en caso de éxito o -1 en caso de error. De manera similar, la función de C fopen()(archivo abierto) devuelve un puntero no nulo (dirección entera) a una FILEestructura en caso de éxito o un puntero nulo (0) (representado por constante NULL) en caso de falla. En cualquier caso, para identificar la excepción que causó el error, debe leer el errnocódigo de error basado en números enteros de la variable global .

Los códigos de error presentan algunos problemas:

  • Los números enteros no tienen sentido; no describen las excepciones que representan. Por ejemplo, ¿qué significa 6?
  • Asociar el contexto con un código de error es incómodo. Por ejemplo, es posible que desee generar el nombre del archivo que no se pudo abrir, pero ¿dónde va a almacenar el nombre del archivo?
  • Los enteros son arbitrarios, lo que puede generar confusión al leer el código fuente. Por ejemplo, especificar if (!chdir("C:\\temp"))( !significa NO) en lugar de if (chdir("C:\\temp"))probar la falla es más claro. Sin embargo, se eligió 0 para indicar el éxito y, por if (chdir("C:\\temp"))lo tanto, debe especificarse para probar el fracaso.
  • Los códigos de error son demasiado fáciles de ignorar, lo que puede provocar errores en el código. Por ejemplo, el programador podría especificar chdir("C:\\temp");e ignorar la if (fp == NULL)verificación. Además, el programador no necesita examinar errno. Al no realizar pruebas de falla, el programa se comporta de manera errática cuando cualquiera de las funciones devuelve un indicador de falla.

Para resolver estos problemas, Java adoptó un nuevo enfoque para el manejo de excepciones. En Java, combinamos objetos que describen excepciones con un mecanismo basado en lanzar y atrapar estos objetos. A continuación, se muestran algunas ventajas de utilizar objetos frente a códigos de error para indicar excepciones:

  • Se puede crear un objeto a partir de una clase con un nombre significativo. Por ejemplo, FileNotFoundException(en el java.iopaquete) es más significativo que 6.
  • Los objetos pueden almacenar contexto en varios campos. Por ejemplo, puede almacenar un mensaje, el nombre del archivo que no se pudo abrir, la posición más reciente en la que falló una operación de análisis y / u otros elementos en los campos de un objeto.
  • No usa ifdeclaraciones para probar el fracaso. En cambio, los objetos de excepción se lanzan a un controlador que está separado del código del programa. Como resultado, el código fuente es más fácil de leer y es menos probable que tenga errores.

Throwable y sus subclases

Java proporciona una jerarquía de clases que representan diferentes tipos de excepciones. Estas clases tienen sus raíces en el java.langde paquete de Throwableclase, junto con sus Exception, RuntimeExceptiony Errorsubclases.

Throwablees la última superclase en lo que respecta a las excepciones. Solo los objetos creados a partir de Throwablesus subclases se pueden lanzar (y posteriormente capturar). Estos objetos se conocen como objetos arrojadizos .

A Throwable object is associated with a detail message that describes an exception. Several constructors, including the pair described below, are provided to create a Throwable object with or without a detail message:

  • Throwable() creates a Throwable with no detail message. This constructor is appropriate for situations where there is no context. For example, you only want to know that a stack is empty or full.
  • Throwable(String message) creates a Throwable with message as the detail message. This message can be output to the user and/or logged.

Throwable provides the String getMessage() method to return the detail message. It also provides additional useful methods, which I'll introduce later.

The Exception class

Throwable has two direct subclasses. One of these subclasses is Exception, which describes an exception arising from an external factor (such as attempting to read from a nonexistent file). Exception declares the same constructors (with identical parameter lists) as Throwable, and each constructor invokes its Throwable counterpart. Exception inherits Throwable's methods; it declares no new methods.

Java provides many exception classes that directly subclass Exception. Here are three examples:

  • CloneNotSupportedException signals an attempt to clone an object whose class doesn't implement the Cloneable interface. Both types are in the java.lang package.
  • IOException signals that some kind of I/O failure has occurred. This type is located in the java.io package.
  • ParseException signals that a failure has occurred while parsing text. This type can be found in the java.text package.

Notice that each Exception subclass name ends with the word Exception. This convention makes it easy to identify the class's purpose.

You'll typically subclass Exception (or one of its subclasses) with your own exception classes (whose names should end with Exception). Here are a couple of custom subclass examples:

public class StackFullException extends Exception { } public class EmptyDirectoryException extends Exception { private String directoryName; public EmptyDirectoryException(String message, String directoryName) { super(message); this.directoryName = directoryName; } public String getDirectoryName() { return directoryName; } }

The first example describes an exception class that doesn't require a detail message. It's default noargument constructor invokes Exception(), which invokes Throwable().

The second example describes an exception class whose constructor requires a detail message and the name of the empty directory. The constructor invokes Exception(String message), which invokes Throwable(String message).

Objects instantiated from Exception or one of its subclasses (except for RuntimeException or one of its subclasses) are checked exceptions.

The RuntimeException class

Exception is directly subclassed by RuntimeException, which describes an exception most likely arising from poorly written code. RuntimeException declares the same constructors (with identical parameter lists) as Exception, and each constructor invokes its Exception counterpart. RuntimeException inherits Throwable's methods. It declares no new methods.

Java provides many exception classes that directly subclass RuntimeException. The following examples are all members of the java.lang package:

  • ArithmeticException signals an illegal arithmetic operation, such as attempting to divide an integer by 0.
  • IllegalArgumentException signals that an illegal or inappropriate argument has been passed to a method.
  • NullPointerException signals an attempt to invoke a method or access an instance field via the null reference.

Objects instantiated from RuntimeException or one of its subclasses are unchecked exceptions.

The Error class

Throwable's other direct subclass is Error, which describes a serious (even abnormal) problem that a reasonable application should not try to handle--such as running out of memory, overflowing the JVM's stack, or attempting to load a class that cannot be found. Like Exception, Error declares identical constructors to Throwable, inherits Throwable's methods, and doesn't declare any of its own methods.

You can identify Error subclasses from the convention that their class names end with Error. Examples include OutOfMemoryError, LinkageError, and StackOverflowError. All three types belong to the java.lang package.

Throwing exceptions

A C library function notifies calling code of an exception by setting the global errno variable to an error code and returning a failure code. In contrast, a Java method throws an object. Knowing how and when to throw exceptions is an essential aspect of effective Java programming. Throwing an exception involves two basic steps:

  1. Use the throw statement to throw an exception object.
  2. Use the throws clause to inform the compiler.

Later sections will focus on catching exceptions and cleaning up after them, but first let's learn more about throwables.

The throw statement

Java provides the throw statement to throw an object that describes an exception. Here's the syntax of the throw statement :

throw throwable;

The object identified by throwable is an instance of Throwable or any of its subclasses. However, you usually only throw objects instantiated from subclasses of Exception or RuntimeException. Here are a couple of examples:

throw new FileNotFoundException("unable to find file " + filename); throw new IllegalArgumentException("argument passed to count is less than zero");

The throwable is thrown from the current method to the JVM, which checks this method for a suitable handler. If not found, the JVM unwinds the method-call stack, looking for the closest calling method that can handle the exception described by the throwable. If it finds this method, it passes the throwable to the method's handler, whose code is executed to handle the exception. If no method is found to handle the exception, the JVM terminates with a suitable message.

The throws clause

You need to inform the compiler when you throw a checked exception out of a method. Do this by appending a throws clause to the method's header. This clause has the following syntax:

throws checkedExceptionClassName (, checkedExceptionClassName)*

A throws clause consists of keyword throws followed by a comma-separated list of the class names of checked exceptions thrown out of the method. Here is an example:

public static void main(String[] args) throws ClassNotFoundException { if (args.length != 1) { System.err.println("usage: java ... classfile"); return; } Class.forName(args[0]); }

This example attempts to load a classfile identified by a command-line argument. If Class.forName() cannot find the classfile, it throws a java.lang.ClassNotFoundException object, which is a checked exception.

Checked exception controversy

The throws clause and checked exceptions are controversial. Many developers hate being forced to specify throws or handle the checked exception(s). Learn more about this from my Are checked exceptions good or bad? blog post.