Las excepciones de Java son un mecanismo de consistencia proporcionado por Java para identificar y responder a los errores.
El mecanismo de excepción de Java puede separar el código de manejo de excepciones en el programa del código de negocio normal, asegurarse de que el código del programa sea más elegante y mejorar la robustez del programa. Cuando se usa excepciones de manera efectiva, la excepción puede responder claramente estas tres preguntas: el tipo de excepción responde "qué" y el rastreo de la pila de excepciones responde "donde" y la información de excepción responde "por qué" se lanza.
Varias palabras clave utilizadas en el mecanismo de excepción de Java: intente, atrapar, finalmente, lanzar, lanzar.
| Palabras clave | ilustrar |
|---|---|
| intentar | Utilizado para escuchar. Ponga el código que se escuche (código que puede lanzar una excepción) en el bloque de la declaración de prueba. Cuando se produce una excepción en el bloque de la declaración de prueba, se lanza la excepción. |
| atrapar | Utilizado para atrapar excepciones. La captura se usa para capturar excepciones que ocurren en los bloques de instrucciones Try. |
| Finalmente | Finalmente, los bloques de declaración siempre se ejecutarán. Se utiliza principalmente para reciclar recursos de material (como conexiones de bases de datos, conexiones de red y archivos de disco) abiertos en bloques de prueba. Solo después de la ejecución del bloque Finalmente regresará o arrojará declaraciones en el bloque de try o captura. Si se usan las declaraciones finales como el retorno o el lanzamiento, no volverán a la ejecución y se detendrán directamente. |
| tirar | Se usa para lanzar excepciones. |
| lanza | Se utiliza en firmas de métodos para declarar excepciones que pueden ser lanzadas por el método. |
public class demo1 {public static void main (string [] args) {try {int i = 10/0; System.out.println ("i ="+i); } Catch (arithmeticException e) {system.out.println ("excepción atrapada"); System.out.println ("E.GetMessage ():" + E.getMessage ()); System.out.println ("e.ToString ():" + e.ToString ()); System.out.println ("E.PrintStackTrace ():"); E.PrintStackTrace (); }}} Resultados de ejecución:
Exceptione.getMessage (): / by zeroe.ToString (): java.lang.arithmeticException: / por zeroe.printstacktrace (): java.lang.arithmeticException: / by cero en demo1.main (demo1.Java:6)
La descripción del resultado: hay una operación con un divisor de 0 en el bloque de la declaración de prueba, y la operación lanzará una excepción java.lang.arithmeticException. Por captura, la excepción es atrapada.
Observando los resultados que encontramos que System.out.println ("i ="+i) no fue ejecutado. Esto significa que después de que ocurra una excepción en el bloque de instrucción Prueba, el contenido restante en el bloque de la declaración de prueba ya no se ejecutará.
Ejemplo 2: Comprenda el uso básico de finalmente
Basado en "Ejemplo uno", agregamos finalmente declaraciones.
public class demo2 {public static void main (string [] args) {try {int i = 10/0; System.out.println ("i ="+i); } Catch (arithmeticException e) {system.out.println ("excepción atrapada"); System.out.println ("E.GetMessage ():" + E.getMessage ()); System.out.println ("e.ToString ():" + e.ToString ()); System.out.println ("E.PrintStackTrace ():"); E.PrintStackTrace (); } finalmente {System.out.println ("Ejecutar finalmente"); }}} Resultados de ejecución:
Capturado Exceptione.getMessage (): / por zeroe.ToString (): java.lang.arithmeticException: / by zeroe.printstackTrace (): java.lang.arithmeticException: / by cero en demo2.main (demo2.Java:6) Run finalmente
Resultados: Finalmente se ejecutó el bloque de la declaración.
Ejemplo 3: Comprenda el uso básico de lanzamientos y lanzamientos
Los lanzamientos se usan para declarar excepciones lanzadas, mientras que el lanzamiento se usa para lanzar excepciones.
Class MyException extiende la excepción {public myException () {} public myException (String msg) {super (msg); }} public class Demo3 {public static void main (string [] args) {try {test (); } catch (myException e) {system.out.println ("atrapar mi excepción"); E.PrintStackTrace (); }} public static void test () lanza myException {try {int i = 10/0; System.out.println ("i ="+i); } catch (arithmeticException e) {tire nueva myException ("esta es myException"); }}} Resultados de ejecución:
Atrapa My ExceptionMyException: Esta es MyException en demo3.test (demo3.java:24) en demo3.main (demo3.java:13)
Resultados: MyException es una subclase heredada de la excepción. La excepción de arithmeticException (el divisor es 0) se genera en el bloque de instrucción try de test (), y la excepción se atrapa en la captura; Entonces se lanza una excepción de MyException. El método Main () captura la MyException lanzada en Test ().
Marco de excepción de Java
Diagrama de arquitectura de excepción de Java:
1.
Throwable es una superclase de todos los errores o excepciones en el idioma Java.
Throwable contiene dos subclases: error y excepción. Por lo general, se usan para indicar que se ha producido una anormalidad.
Throwable contiene instantáneas del hilo que ejecuta la pila cuando se crea su hilo. Proporciona interfaces como PrintStackTrace () para obtener información como los datos de rastreo de pila.
2. Excepción
La excepción y sus subclases son una forma de lanzamiento que señala las condiciones que una aplicación razonable quiere capturar.
3. RuntimeException
RuntimeException es una superclase que puede arrojar excepciones durante el funcionamiento normal de la máquina virtual Java.
El compilador no verifica las excepciones de RuntimeException. Por ejemplo, cuando el divisor es cero, se lanza una excepción de ArithmeticException. RuntimeException es una superclase de arithmeticexception. Cuando el código tiene un divisor de cero, también se puede compilar si "no se arroja a través de la declaración de lanzamientos" o "no se maneja a través de la prueba ... Catch ...". ¡Esto es lo que decimos "El compilador no verificará las excepciones de RuntimeException"!
Si el código genera una excepción de RuntimeException, debe evitarse modificando el código. Por ejemplo, si el divisor es cero, ¡debe evitar esta situación a través del código!
4. Error
Al igual que la excepción, el error también es una subclase de lanzamiento. Se utiliza para indicar problemas serios que una aplicación razonable no debe intentar atrapar, y la mayoría de estos errores son condiciones excepcionales.
Al igual que RuntimeException, el compilador no verificará un error.
Java divide la estructura lanzable en tres tipos: excepción verificada (excepción verificada), excepción de tiempo de ejecución (runtimeException) y error (error).
(1) Excepción de tiempo de ejecución
Definición: RuntimeException y sus subclases se llaman excepciones de tiempo de ejecución.
Características: El compilador Java no lo verificará. Es decir, cuando tal excepción puede ocurrir en el programa, si no está "a través de la declaración de lanzamientos" o "no atrapado con la declaración de prueba de prueba", aún se compilará y pasará. Por ejemplo, la excepción de arithmeticException generada cuando el divisor es cero, la excepción del índice de excepción generada cuando la matriz está fuera de los límites, la excepción concurrente de EXCEPCIÓN generada por el mecanismo de falla, etc., son todas las excepciones de tiempo de ejecución.
Aunque el compilador de Java no verifica las excepciones de tiempo de ejecución, también podemos declararlo y lanzarlo a través de tiros, o capturarlo a través de la captura de prueba.
Si se genera una excepción de tiempo de ejecución, debe evitarse modificando el código. Por ejemplo, si el divisor es cero, ¡debe evitar esta situación a través del código!
(2) Excepción verificada
Definición: La clase de excepción en sí y otras subclases en las subclases de excepción, excepto la "excepción de tiempo de ejecución" se consideran excepciones verificadas.
Características: El compilador Java lo verificará. Tales excepciones se declaran y se arrojan a través de tiros o se capturan a través de la captura de try, de lo contrario no se pueden compilar. Por ejemplo, ClonenotsupportedException es una excepción verificada. Cuando un objeto se clona a través de la interfaz Clone (), y la clase correspondiente del objeto no implementa la interfaz clonable, se lanzará una ClonenotsupportedException.
Las excepciones que se están marcando generalmente se pueden recuperar.
(3) Error
Definición: clase de error y sus subclases.
Características: Al igual que las excepciones de tiempo de ejecución, el compilador no verificará los errores.
Se produce un error cuando no hay recursos insuficientes, falla de restricciones u otras condiciones que no pueden continuar administrando por otros programas. El programa en sí no puede corregir estos errores. Por ejemplo, VirtualMachineError es un error.
Según la Convención de Java, ¡no debemos implementar ninguna nueva subclases de error!
Para las tres estructuras anteriores, ¿cuál deberíamos lanzar una excepción o un error? La recomendación dada en "Java efectiva" es: usar excepciones verificadas para las condiciones que se pueden recuperar y usar excepciones de tiempo de ejecución para errores del programa.
Varias sugerencias sobre manejo de excepciones
Artículo 1: Use excepciones solo para situaciones anormales
Recomendación: las excepciones solo deben usarse para condiciones anormales, y nunca deben usarse para flujos de control normales.
La explicación se realiza comparando los dos códigos a continuación.
Código 1
intente {int i = 0; while (true) {arr [i] = 0; i ++; }} Catch (indexOuToFboundsexception e) {} código 2for (int i = 0; i <arr.length; i ++) {arr [i] = 0;} El propósito de ambos códigos es iterar sobre la matriz ARR y establecer el valor de cada elemento en la matriz a 0. El código 1 termina por excepción, lo que parece muy difícil de entender, el código 2 termina por límites de matriz. Debemos evitar usar el Código 1 por tres razones principales:
El diseño original de mecanismos de excepción es para situaciones anormales, por lo que pocas implementaciones de JVM intentan optimizar su rendimiento. Por lo tanto, la sobrecarga de crear, lanzar y atrapar excepciones es costosa.
Poner el código en las devoluciones de captura de prueba evita que el JVM implementa ciertas optimizaciones específicas que podrían haber sido funcionar.
El patrón estándar de atravesar matrices no conduce a controles redundantes, y algunas implementaciones de JVM modernas las optimizarán.
De hecho, los modos basados en excepción son mucho más lentos que los modos estándar. El código de prueba es el siguiente:
Public Class Advice1 {private static int [] arr = new int [] {1,2,3,4,5}; Tamaño privado estático int = 10000; public static void main (string [] args) {long s1 = system.currentTimemillis (); para (int i = 0; i <size; i ++) endbyRange (arr); long e1 = System.CurrentTimemillis (); System.out.println ("EndbyRange Time:"+(E1-S1)+"MS"); largo S2 = System.CurrentTimemillis (); para (int i = 0; i <size; i ++) endbyException (arr); long e2 = System.CurrentTimemillis (); System.out.println ("EndbyException Time:"+(e2-s2)+"ms"); } // atraviesa la matriz de arr: void static private endbyException (int [] arr) {try {int i = 0; while (true) {arr [i] = 0; i ++; //System.out.println("endByRange: arr ["+i+"] = "+arr [i]); }} Catch (indexOuToUfBoundsexception e) {}} // Transferir la matriz arr: privado void endbyRange (int [] arr) {for (int i = 0; i <arr.length; i ++) {arr [i] = 0; //System.out.println("endByException: arr ["+i+"] = "+arr [i]); }}} Resultados de ejecución:
EndbyRange Time: 8MSendByException Time: 16 ms
¡El resultado muestra que la velocidad de atravesar una excepción es mucho más lenta que atravesar una matriz de la manera ordinaria!
Artículo 2: Use excepciones verificadas para condiciones recuperables y use excepciones de tiempo de ejecución para errores del programa.
| anormal | ilustrar |
|---|---|
| Excepción de tiempo de ejecución | La clase RuntimeException y sus subclases se llaman excepciones de tiempo de ejecución. |
| La excepción verificada | La clase de excepción en sí, así como otras subclases en excepción, excepto la "excepción de tiempo de ejecución", todas las excepciones verificadas. |
La diferencia es que el compilador Java verifica las "excepciones verificadas" y no verifica las "excepciones de tiempo de ejecución".
Es decir, para la excepción verificada, se declara y se arroja a través de tiros, o se captura a través de la captura de try, de lo contrario no se puede compilar. Para las excepciones de tiempo de ejecución, si "no son lanzados a través de la declaración de lanzamientos" o "no atrapados con la declaración de captura de try", aún se compilarán y pasarán. Por supuesto, aunque el compilador Java no verifica las excepciones de tiempo de ejecución, también podemos explicar la excepción a través de los lanzamientos o atraparlo a través de la captura de try.
RithmeticException (por ejemplo, el divisor es 0), indexOutofboundsexception (por ejemplo, matriz fuera de los límites), etc. son todas las excepciones de tiempo de ejecución. Para esta excepción, debemos evitarlo modificando el código. Para la excepción verificada, el programa se puede restaurar para ejecutar el procesamiento. Por ejemplo, supongamos que debido a que un usuario no almacena un número suficiente de llamadas, fallará al intentar hacer una llamada en una llamada de pago; Lanzando así una excepción verificada.
Artículo 3: Evite el uso innecesario de excepciones verificadas
"Excepción censurada" es una buena característica de Java. A diferencia del código de retorno, las "excepciones verificadas" obligan al programador a tratar con condiciones de excepción, mejorando en gran medida la confiabilidad del programa.
Sin embargo, el uso excesivo de excepciones verificadas puede hacer que la API sea muy inconveniente. Si un método arroja una o más excepciones verificadas, el código que llama al método debe manejar estas excepciones en uno o más bloques de declaración de captura, o deben ser lanzados a través de la declaración de lanzamientos. Ya sea que se maneje a través de la captura o se lanza a través de declaraciones de lanzamientos, agrega una carga innegligible a los programadores.
Se deben cumplir dos condiciones para "excepciones verificadas": primero, incluso si la API se usa correctamente, no puede evitar la aparición de condiciones de excepción. En segundo lugar, una vez que se genera una excepción, los programadores que usan la API pueden tomar medidas útiles para procesar el programa.
Artículo 4: Intente usar excepciones estándar
La reutilización del código es digna de defensa, esta es una regla común, y las excepciones no son la excepción. Hay varios beneficios para reutilizar las excepciones existentes:
Primero, hace que su API sea más fácil de aprender y usar porque es consistente con los modismos con los que los programadores se han familiarizado.
En segundo lugar, para los programas que usan estas API, son mejor legibles porque no están llenos de excepciones que no son familiares para los programadores.
En tercer lugar, cuantas menos clases de excepción, menor sea el uso de la memoria y menor es el tiempo que pasó en reimprimir estas clases.
Varias de las excepciones estándar de Java a menudo se usan excepciones. La siguiente tabla:
| anormal | Usar ocasiones |
|---|---|
| IlegalargumentException | El valor del parámetro no es apropiado |
| IlegalstateException | Los parámetros no son inapropiados |
| NullpointerException | Cuando NULL está deshabilitado, el valor del parámetro es nulo |
| ÍndiceutOfboundSexception | El subíndice cruza el límite |
| ConcurrenteModificationException | Cuando se prohíbe la modificación concurrente, el objeto detecta la modificación concurrente |
| Operación no compatible. | Métodos que el objeto no admite las solicitudes de los clientes |
Aunque hasta la fecha son las excepciones más reutilizadas de las bibliotecas de la plataforma Java, otras excepciones también pueden reutilizarse en condiciones de licencia. Por ejemplo, si desea implementar objetos aritméticos, como números complejos o matrices, sería muy apropiado reutilizar ArithMeticException y NumberFormateException. Si una excepción satisface sus necesidades, no dude en usarlo, pero debe asegurarse de que la condición de lanzar la excepción sea consistente con las condiciones descritas en la documentación para la excepción. ¡Esta reutilización debe basarse en la semántica, no en el nombre!
Finalmente, asegúrese de tener claro que no hay una regla que deba seguir al elegir qué excepción reutilizar. Por ejemplo, considerando el caso de un objeto de tarjeta, suponga que hay un método para operaciones de trato, y su parámetro (Handsize) es el número de tarjetas que se darán en una mano. Supongamos que la persona que llama pasa un valor en este parámetro mayor que el número restante de cartas para todo el mazo. Entonces, esta situación puede interpretarse como una Excepción IlegalArgument (el valor de Handsize es demasiado grande) o una Excepción IlegalStateException (el objeto de la tarjeta tiene muy pocas tarjetas en relación con la solicitud del cliente).
Artículo 5: La excepción lanzada debe ser adecuada para la abstracción correspondiente
Esta situación puede ser abrumadora si una excepción lanzada por un método no tiene una correlación obvia con la tarea que realiza. Esto a menudo sucede cuando un método pasa una excepción lanzada por una abstracción de bajo nivel. Cuando esto sucede, no solo se confunde, sino también en las API de alto nivel "contaminas".
Para evitar este problema, la implementación de alto nivel debe captar la excepción de bajo nivel y lanzar una excepción que se puede introducir de acuerdo con la abstracción de alto nivel. Esta práctica se llama "traducción de excepciones".
Por ejemplo, el método Java Collection Framework AbstractSequeSeStentialList get () es el siguiente (basado en JDK1.7.0_40):
public e get (int index) {try {return listIterator (index) .next (); } Catch (nosuchelementException exc) {tirar nueva indexOutofBoundsexception ("índice:"+index); }}ListIterator (índice) devolverá el objeto ListIterator. Llamar al método Next () del objeto puede lanzar una excepción de nosuchelementException. En el método get (), lanzar una excepción de NosuchelementException será confusa. Entonces, Get () captura nosuchelementException y lanza una excepción de índice de información de la información. Es decir, es equivalente a convertir nosuchelementException en excepción de IndexOutOfBoundSException.
Artículo 6: Se debe documentar la excepción presentada por cada método
Para declarar la excepción verificada por separado y use la etiqueta @throws de Javadoc para registrar con precisión las condiciones para cada excepción a ser lanzada.
Si muchos métodos en una clase lanzan la misma excepción por la misma razón, es aceptable hacer documentación para esta excepción en los comentarios del documento de esa clase, en lugar de documentar individualmente para cada método.
Artículo 7: Incluya el mensaje de captura de mensajes de falla en detalle
En resumen, cuando personalizamos o organizamos una excepción, debemos incluir información relacionada con la falla.
Cuando un programa falla debido a una excepción no atrapada, el sistema imprimirá automáticamente el rastro de la apila de la excepción. Contiene una representación de cadena de la excepción en la pista de pila. Por lo general, contiene el nombre de clase de la clase de excepción, junto con el mensaje detallado que sigue.
Artículo 8: Esfuércese por mantener la atomicidad en el fracaso
Cuando un objeto lanza una excepción, siempre esperamos que el objeto permanezca en un estado disponible bien definido. Esto es especialmente importante para la excepción verificada, porque la persona que llama generalmente espera recuperarse de la excepción verificada.
En términos generales, una llamada de método fallido debería mantener el objeto en "su estado antes de que se llamara". Los métodos con tales atributos se denominan "falla atómica". Se puede entender que el fracaso aún mantiene la atomicidad. Hay varias formas de que un objeto mantenga "atomicidad fallida":
(1) Diseñe un objeto no mutable.
(2) Para los métodos que realizan operaciones en objetos mutables, la forma más común de obtener "atomicidad fallida" es verificar la validez de los parámetros antes de realizar la operación. Como sigue (método pop en stack.java):
Public Object Pop () {if (size == 0) Throw New VacentStackException (); Resultado del objeto = elementos [-tamaño]; elementos [tamaño] = nulo; Resultado de retorno;} (3) Similar al método anterior, el procesamiento de cálculo se puede ajustar de modo que se modifique cualquier posible falla de la parte de cálculo antes de que se modifique el estado del objeto.
(4) Escriba un código de recuperación para explicar las fallas durante la operación y hacer que el objeto regrese al estado antes de que comenzara la operación.
(5) Realice una operación en una copia temporal del objeto, y después de completar la operación, copie el resultado en la copia temporal al objeto original.
Si bien "mantener la atomicidad fallida de un objeto" es el objetivo deseado, no siempre es posible. Por ejemplo, si múltiples hilos intentan acceder a un objeto simultáneamente sin los mecanismos de sincronización adecuados, el objeto puede dejarse en un estado inconsistente.
Incluso en situaciones en las que se pueden lograr "atomicidad fallida", no siempre se espera. Para algunas operaciones, puede aumentar significativamente la sobrecarga o la complejidad.
La regla general es: como parte de la especificación del método, ninguna excepción debe cambiar el estado del objeto antes de llamar al método. Si se viola esta regla, la documentación de la API debe indicar claramente en qué estado se encuentre el objeto.
Artículo 9: No ignore las excepciones
Cuando un diseñador de API declara que un método organizará una excepción, está tratando de ilustrar algo. Entonces, ¡no lo ignores! El código para ignorar las excepciones es el siguiente:
Prueba {...} Catch (SomeException e) {} Un bloque de captura vacío hará que la excepción no pueda lograr su debido propósito. El propósito de la excepción es obligarlo a lidiar con condiciones anormales. Ignorar una excepción es como ignorar una señal de alarma de incendio: si la señal de alarma de incendio se apaga, nadie verá la señal de alarma de incendio cuando ocurra el fuego real. Entonces, al menos el bloque de captura debe contener una descripción que explique por qué es apropiado ignorar esta excepción.