Excepciones y Errores comunes en Java

Buenas tardes, en esta entrada discutiremos los aspectos de las excepciones que fueron añadidas en Java SE6. La intención de esta sección es estar familiarizados con algúnas de las excepciones mas comunes y errores que encontraremos al programar en Java.

  • De donde vienen las Excepciones

    Es importante saber lo que causa una excepción o un error, y de donde vienen. Para este propósito, vamos a definir 2 categorías de excepciones y errores:

    • Excepciones de la JVM: Estas escepciones o errores son exclusivamente o mas lógicamente lanzadas por la JVM.
    • Excepciones Programáticas: Estas excepciones son lanzadas explícitamente por la aplicación y/o programadores de una API.
  • Excepciones lanzadas por la JVM

    Vamos a empezar con una ecepción muy común, llamada NullPointerException. Esta excepción ocurre cuando estamos intentando tener acceso a un objeto usando una variable de referencia cuyo valor actual es null. No hay manera en la que el compilador pueda encontrarla antes de tiempo de ejecución. Vamos a echar un vistazo a lo siguiente:

    		class NPE {
    			static String s;
    			public static void main(String[] args){
    				System.out.println(s.length());
    			}
    		}
    		

    Seguramente, el compilador pueda encontrar el problema con este pequeño programa. No, esto va por tu cuenta. El codigo compilará bien, y la JVM lanzará una NullPointerException cuando intenta invocar el método length().

    En la entrada anterior discutimos sobre el call stack o la pila de llamadas. Vamos a volver a crear un ejemplo en el que el método main() está en la parte de abajo de la pila, y este main() invoca otro método, que volverá a llamar a otro método, y así, la pila empieza a crecer hacia arriba. Por supuesta el stack reside en la memoria, y incluso aunque nuestro OS nos dé un gigabyte de RAM para nuestro programa, hay una cantidad finita. Es posible que el stack crezca tanto que el OS se quede sin espacio para almacenar la pila de llamadas. Cuando esto ocurra tendremos un StackOverflowError. El método mas común para crear esto es crear un método recursivo. Un método recursivo es un método que se invoca así mismo en el cuerpo del método. Puede que suene un poco extraño, pero es una técnica muy común y muy útil para cosas como algoritmos de búsqueda o de ordenación. Vamos a ver un ejemplo:

    		void go() {
    			go();
    		}
    		

    Como podemos ver, si alguna vez cometemos el error de invocar el método go(), nuestro programa caerá en un agujero negro: go() invoca a go() que invoca a go(), hasta que, sin importar la memoria que tengamos, tendremos un StackOverflowError. De nuevo, la JVM solo sabe cuando esto ocurrirá, y será la fuente de este error.

  • Excepciones lanzadas programáticamente

    Ahora vamos a ver excepciones lanzadas programáticamente. Recordemos que hemos definido programáticamente como algo así:

    Creado por el desarrollador de una API o una aplicación.

    Por ejemplo, algunas clases en la API de Java tienen métodos que cogen un String como argumento, y convierten este String en un primitivo numérico. Un buen ejemplo de estas clases son las llamadas “wrapper classes” que vimos en una entrada anterior.

    En algún momento, algún programador escribiría métodos como el parseInt() o valueOf(). Este programador decidió sabiamente que si uno de estos métodos recibía un String que no podía ser convertido en un número, el método debería lanzar un NumberFormatException. El código implementado parcialmente podía parecer a lo siguiente:

    		int parseInt(String s) throws NumberFormatException{
    			boolean parseSuccess = false;
    			int result = 0;
    			// Cosas complicadas de parseo
    			if (!parseSuccess)	// Si el parseo ha fallado
    				throw new NumberFormatException();
    			return result;
    		}
    		

    Otros ejemplos de excepciones programáticas incluyen las AssertionError (aunque no es una excepción, es lanzada programáticamente) y IllegalArgumentException. De hecho, nuestro desarrollador mítico de la API podría haber usado IllegalArgumentException para el método parseInt(). El caso es que NumberFormatException hereda de IllegalArgumentException, y es un poco mas precisa, por lo que en este caso, usar NumberFormatException soporta la noción que hemos discutido anteriormente: que cuando tenemos una jerarquía de excepciones, usaremos la excepción mas precisa que podamos.

    Por supuesto, como discutimos antes, tambien podemos hacer nuestra propia y especial excepcion, y lanzarla allí donde queramos. Estas excepciones tambien caerían en la categoría de “Excepciones lanzadas programáticamente”.

A continuación vemos una tabla con las excepciones mas comunes:

Excepcion Descripcion Lanzado por
ArrayIndexOutOfBoundsException Es lanzada cuando intentamos acceder a un array con un index inválido (menor que 0 o mayor que la longitud del array). Por la JVM
ClassCastException Lanzado cuando intentamos hacer cast a una variable de referencia que no pasa el test IS-A Por la JVM
IllegalArgumentException Lanzado cuando un método recibe un argumento formateado de diferente manera del que el método espera. Programáticamente
IllegalStateException Lanzado cuando el estado del entorno no coincide con la operación que se intenta llevar a cabo. (Usar un Scaner que no ha sido cerrado) Programáticamente
NullPointerException Lanzado cuando intentamos acceder a un objeto con una variable de referencia cuyo valor actual es null. Por la JVM
NumberFormatException Lanzado cuando un método convierte un String a un número y ese String no puede ser convertido. Programáticamente
AssertionError Lanzado cuando una sentencia que comprueba un booleano devuelve false. Programáticamente
ExceptionInInitializerError Lanzado cuando intentamos inicializar una variable static en un bloque de inicialización. Por la JVM
StackOverflowError Típicamente lanzado cuando un método es muy recursivo y cada llamada se va añadiendo al stack. Por la JVM
NoClassDefFoundError Lanzado cuandl la JVM no puede encontrar la clase que se necesita, porque hay un error en la linea de comandos, o un problema con el classpath, o un archivo .class que no se encuentra. Por la JVM


En esta breve entrada hemos visto las categorías de las excepciones, y como las dividimos, y una tabla que nos muestra las excepciones mas comunes que podemos obtener en el día a día.

Sin mas, cualquier aporte o corrección es bienvenido.

Saludos!!!