CodeGym /Blog Java /Random-ES /Excepciones: captura y manipulación.
Autor
Oleksandr Miadelets
Head of Developers Team at CodeGym

Excepciones: captura y manipulación.

Publicado en el grupo Random-ES
¡Hola! Odio mencionarlo, pero una gran parte del trabajo de un programador es lidiar con errores. La mayoría de las veces, la suya propia. Resulta que no hay gente que no cometa errores. Y tampoco existen tales programas. Excepciones: captura y manipulación - 1 Por supuesto, cuando se trata de un error, lo principal es entender su causa.. Y muchas cosas pueden causar errores en un programa. En algún momento, los creadores de Java se preguntaron qué hacer con los errores de programación más probables. Evitarlos por completo no es realista, los programadores son capaces de escribir cosas que ni siquiera puedes imaginar. :) Entonces, necesitamos darle al lenguaje un mecanismo para trabajar con errores. En otras palabras, si hay un error en su programa, necesita algún tipo de script para saber qué hacer a continuación. ¿Qué debe hacer exactamente un programa cuando ocurre un error? Hoy nos familiarizaremos con este mecanismo. Se llama " excepciones en Java ".

¿Qué es una excepción?

Una excepción es una situación excepcional no planificada que ocurre mientras se ejecuta un programa. Hay muchas excepciones. Por ejemplo, escribió un código que lee el texto de un archivo y muestra la primera línea.

public class Main {

   public static void main(String[] args) throws IOException {
       BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));
       String firstString = reader.readLine();
       System.out.println(firstString);
   }
}
¡Pero qué pasa si no existe tal archivo! El programa generará una excepción: FileNotFoundException. Salida: excepción en el subproceso "principal" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (El sistema no puede encontrar la ruta especificada) En Java, cada excepción está representada por una clase separada. Todas estas clases de excepción se derivan de un "ancestro" común: la Throwableclase principal. El nombre de una clase de excepción generalmente refleja de manera concisa por qué ocurrió la excepción:
  • FileNotFoundException(el archivo no fue encontrado)

  • ArithmeticException(se produjo una excepción al realizar una operación matemática)

  • ArrayIndexOutOfBoundsException(el índice está más allá de los límites de la matriz). Por ejemplo, esta excepción ocurre si intenta mostrar la posición 23 de una matriz que tiene solo 10 elementos.
¡En total, Java tiene casi 400 clases de este tipo! ¿Porqué tantos? Para hacerlos más convenientes para que los programadores trabajen con ellos. Imagínese esto: escribe un programa y, mientras se ejecuta, genera una excepción que se ve así:

Exception in thread "main"
Uhhhh. :/ Eso no ayuda mucho. No está claro qué significa el error o de dónde vino. No hay información útil aquí. Pero la gran variedad de clases de excepción en Java le da al programador lo que más importa: el tipo de error y su causa probable (incrustado en el nombre de la clase). Es otra cosa muy distinta de ver

Exception in thread "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (The system cannot find the specified path)
¡Está claro de inmediato cuál podría ser el problema y dónde comenzar a cavar para resolverlo! Las excepciones, como instancias de cualquier clase, son objetos.

Captura y manejo de excepciones

Java tiene bloques especiales de código para trabajar con excepciones: try, catchy finally. Excepciones: captura y manejo - 2 El código donde el programador cree que puede ocurrir una excepción se coloca en el trybloque. Eso no significa que ocurrirá una excepción aquí. Significa que podría ocurrir aquí, y el programador es consciente de esta posibilidad. El tipo de error que espera que ocurra se coloca en el catchbloque. Esto también contiene todo el código que debe ejecutarse si ocurre una excepción. Aquí hay un ejemplo:

public static void main(String[] args) throws IOException {
   try {
       BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));

       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (FileNotFoundException e) {

       System.out.println("Error! File not found!");
   }
}
Salida: ¡Error! ¡Archivo no encontrado! Ponemos nuestro código en dos bloques. En el primer bloque, te adelantamos que puede ocurrir un error de "Archivo no encontrado". Este es el trybloque. En el segundo, le decimos al programa qué hacer si ocurre un error. Y el tipo de error específico: FileNotFoundException. Si ponemos una clase de excepción diferente entre paréntesis del catchbloque, FileNotFoundExceptionno se detectará.

public static void main(String[] args) throws IOException {
   try {
       BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));
       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (ArithmeticException e) {

       System.out.println("Error! File not found!");
   }
}
Salida: excepción en el subproceso "principal" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (El sistema no puede encontrar la ruta especificada) El código en el catchbloque no se ejecutó porque "configuramos" este bloque para atrapar ArithmeticException, y el código en el trybloque arrojó un tipo diferente: FileNotFoundException. No escribimos ningún código para manejar FileNotFoundException, por lo que el programa muestra la información predeterminada para FileNotFoundException. Aquí debes prestar atención a tres cosas. Número uno. Una vez que ocurre una excepción en alguna línea del trybloque, el código que sigue no se ejecutará. La ejecución del programa "salta" inmediatamente al catchbloque. Por ejemplo:

public static void main(String[] args) {
   try {
       System.out.println("Divide by zero");
       System.out.println(366/0);// This line of code will throw an exception

       System.out.println("This");
       System.out.println("code");
       System.out.println("will not");
       System.out.println("be");
       System.out.println("executed!");

   } catch (ArithmeticException e) {

       System.out.println("The program jumped to the catch block!");
       System.out.println("Error! You can't divide by zero!");
   }
}
Salida: Dividir por cero ¡El programa saltó al bloque catch! ¡Error! ¡No puedes dividir por cero! En la segunda línea del trybloque, intentamos dividir por 0, lo que da como resultado un ArithmeticException. En consecuencia, las líneas 3-9 del trybloque no se ejecutarán. Como dijimos, el programa inmediatamente comienza a ejecutar el catchbloque. Número dos. Puede haber varios catchbloques. Si el código en el trybloque puede arrojar no uno, sino varios tipos diferentes de excepciones, puede escribir un catchbloque para cada uno de ellos.

public static void main(String[] args) throws IOException {
   try {
       BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));

       System.out.println(366/0);
       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (FileNotFoundException e) {
      
       System.out.println("Error! File not found!");
      
   } catch (ArithmeticException e) {

       System.out.println("Error! Division by 0!");
      
   }
}
En este ejemplo, hemos escrito dos catchbloques. Si FileNotFoundExceptionocurre un en el trybloque, entonces catchse ejecutará el primer bloque. Si ArithmeticExceptionocurre un, se ejecutará el segundo bloque. Podrías escribir 50 catchbloques si quisieras. Por supuesto, es mejor no escribir código que pueda arrojar 50 tipos diferentes de excepciones. :) Tercero. ¿Cómo sabe qué excepciones podría lanzar su código? Bueno, es posible que puedas adivinar algunos de ellos, pero es imposible que guardes todo en tu cabeza. Por lo tanto, el compilador de Java conoce las excepciones más comunes y las situaciones en las que pueden ocurrir. Por ejemplo, si escribe código que el compilador sabe que podría arrojar dos tipos de excepciones, su código no se compilará hasta que las maneje. Veremos ejemplos de esto a continuación. Ahora algunas palabras sobre el manejo de excepciones. Hay 2 formas de manejar las excepciones. Ya nos encontramos con el primero: el método puede manejar la excepción en sí mismo en un catch()bloque. Hay una segunda opción: el método puede volver a lanzar la excepción en la pila de llamadas. ¿Qué significa eso? Por ejemplo, tenemos una clase con el mismo printFirstString()método, que lee un archivo y muestra su primera línea:

public static void printFirstString(String filePath) {

   BufferedReader reader = new BufferedReader(new FileReader(filePath));
   String firstString = reader.readLine();
   System.out.println(firstString);
}
Actualmente, nuestro código no se compila porque tiene excepciones no controladas. En la línea 1, especifica la ruta al archivo. El compilador sabe que ese código podría producir fácilmente un archivo FileNotFoundException. En la línea 3, lee el texto del archivo. Este proceso podría resultar fácilmente en un IOException(error de entrada/salida). Ahora el compilador te dice: "Amigo, no aprobaré este código y no lo compilaré hasta que me digas qué debo hacer si ocurre una de estas excepciones. Y ciertamente podrían ocurrir según el código que escribiste". !" No hay forma de evitarlo: ¡debes manejar ambos! Ya conocemos el primer método de manejo de excepciones: necesitamos poner nuestro código en un trybloque y agregar dos catchbloques:

public static void printFirstString(String filePath) {

   try {
       BufferedReader reader = new BufferedReader(new FileReader(filePath));
       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (FileNotFoundException e) {
       System.out.println("Error, file not found!");
       e.printStackTrace();
   } catch (IOException e) {
       System.out.println("File input/output error!");
       e.printStackTrace();
   }
}
Pero esta no es la única opción. Podríamos simplemente lanzar la excepción más arriba en lugar de escribir código de manejo de errores dentro del método. Esto se hace usando la palabra clave throwsen la declaración del método:

public static void printFirstString(String filePath) throws FileNotFoundException, IOException {
   BufferedReader reader = new BufferedReader(new FileReader(filePath));
   String firstString = reader.readLine();
   System.out.println(firstString);
}
Después de la palabra clave throws, indicamos una lista separada por comas de todos los tipos de excepciones que puede arrojar el método. ¿Por qué? Ahora, si alguien quiere llamar al printFirstString()método en el programa, él o ella (no usted) tendrá que implementar el manejo de excepciones. Por ejemplo, suponga que en otra parte del programa uno de sus colegas escribió un método que llama a su printFirstString()método:

public static void yourColleagueMethod() {

   // Your colleague's method does something

   //...and then calls your printFirstString() method with the file it needs
   printFirstString("C:\\Users\\Henry\\Desktop\\testFile.txt");
}
¡Obtenemos un error! ¡Este código no compilará! No escribimos código de manejo de excepciones en el printFirstString()método. Como resultado, esta tarea ahora recae sobre los hombros de quienes usan el método. En otras palabras, el methodWrittenByYourColleague()método ahora tiene las mismas 2 opciones: debe usar un try-catchbloque para manejar ambas excepciones o volver a lanzarlas.

public static void yourColleagueMethod() throws FileNotFoundException, IOException {
   // The method does something

   //...and then calls your printFirstString() method with the file it needs
   printFirstString("C:\\Users\\Henry\\Desktop\\testFile.txt");
}
En el segundo caso, el siguiente método en la pila de llamadas, el que llama methodWrittenByYourColleague(), tendrá que manejar las excepciones. Es por eso que llamamos a esto "arrojar o pasar la excepción". Si lanza excepciones hacia arriba usando la palabra clave throws, su código se compilará. En este punto, el compilador parece estar diciendo: "Está bien, está bien. Su código contiene un montón de posibles excepciones, pero lo compilaré. ¡Pero volveremos a esta conversación!". Y cuando llama a cualquier método que tiene excepciones no controladas, el compilador cumple su promesa y se lo recuerda nuevamente. Finalmente, hablaremos sobre el finallybloque (perdón por el juego de palabras). Esta es la última parte del try-catch-finallytriunvirato de manejo de excepciones..

public static void main(String[] args) throws IOException {
   try {
       BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));

       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (FileNotFoundException e) {
       System.out.println("Error! File not found!");
       e.printStackTrace();
   } finally {
       System.out.println ("And here's the finally block!");
   }
}
En este ejemplo, el código dentro del finallybloque se ejecutará en ambos casos. Si el código en el trybloque se ejecuta en su totalidad sin generar ninguna excepción, el finallybloque se ejecutará al final. Si el código dentro del trybloque es interrumpido por una excepción y el programa salta al catchbloque, el finallybloque aún se ejecutará después del código dentro del catchbloque. ¿Por qué es esto necesario? Su objetivo principal es ejecutar código obligatorio: código que debe ejecutarse independientemente de las circunstancias. Por ejemplo, a menudo libera algunos recursos utilizados por el programa. En nuestro código, abrimos un flujo para leer información del archivo y pasarla al BufferedReaderobjeto. Debemos cerrar nuestro lector y liberar los recursos. Esto debe hacerse pase lo que pase, cuando el programa funcione como debería y cuando arroje una excepción. El finallybloque es un lugar muy conveniente para hacer esto:

public static void main(String[] args) throws IOException {

   BufferedReader reader = null;
   try {
       reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));

       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (FileNotFoundException e) {
       e.printStackTrace();
   } finally {
       System.out.println ("And here's the finally block!");
       if (reader != null) {
           reader.close();
       }
   }
}
Ahora estamos seguros de que cuidaremos los recursos, independientemente de lo que suceda cuando el programa se esté ejecutando. :) Eso no es todo lo que necesita saber acerca de las excepciones. El manejo de errores es un tema muy importante en la programación. Muchos artículos están dedicados a ello. En la próxima lección, descubriremos qué tipos de excepciones existen y cómo crear sus propias excepciones. :) ¡Hasta entonces!
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION