Este artículo se centra en algunos malentendidos en la selección y el uso de excepciones de Java. Espero que los lectores puedan dominar algunos de los puntos y principios de manejo de excepciones, y prestar atención al resumen e inducción. Solo al manejar las excepciones podemos mejorar las cualidades básicas de los desarrolladores, mejorar la robustez del sistema, mejorar la experiencia del usuario y mejorar el valor del producto.
Malentendido 1. Elección anormal
Figura 1. Clasificación de anomalías
La Figura 1 describe la estructura de la excepción. De hecho, todos sabemos que las anormalidades se dividen en detectar anormalidades y anormalidades no detectadas, pero en la práctica, la aplicación de estas dos anomalías está confundida. Debido a que las excepciones no detectadas son fáciles de usar, muchos desarrolladores piensan que la detección de excepciones es inútil. De hecho, los escenarios de aplicación anormales se pueden resumir de la siguiente manera:
1. El código de llamada no puede continuar siendo ejecutado y necesita ser terminado de inmediato. Hay demasiadas posibilidades para esta situación, como el servidor no está conectado, los parámetros son incorrectos, etc. Las excepciones no detectadas son aplicables en estos momentos, y no hay necesidad de llamar a la captura y procesamiento explícito del código, y el código es conciso y claro.
2. El código de llamadas necesita procesamiento y recuperación adicionales. Si SQLException se define como excepciones no detectadas, los desarrolladores naturalmente creerán que SQLException no requiere llamar capturado y procesamiento explícito del código, lo que conducirá a situaciones graves como no cerrar la conexión, no volver a hacer la transacción y los datos sucios en el DB. Se debe precisamente a que SQLException se define como detectar excepciones de que los desarrolladores se verán impulsados a capturar y limpiar recursos explícitamente después de que el código genera una excepción. Por supuesto, después de limpiar los recursos, puede continuar lanzando excepciones no detectadas para evitar la ejecución del programa. Según la observación y la comprensión, la detección de excepciones se puede aplicar principalmente a las clases de herramientas. Java Learning Group 669823128
Malentendido 2: Muestre excepciones directamente en la página o cliente.
Es común imprimir excepciones directamente en el lado del cliente. Tomando JSP como ejemplo, una vez que se ejecuta el código, el contenedor imprime la información de la pila de excepciones directamente en la página de forma predeterminada. De hecho, desde la perspectiva del cliente, cualquier excepción no tiene un significado práctico, y la mayoría de los clientes no pueden entender la información de excepción. El desarrollo de software también debe intentar evitar presentar la excepción directamente a los usuarios.
Listado 1
<strong> paquete </strong> com.ibm.dw.sample.exception;/*** Custom RuntimeException* Agregar atributo de código de error*/<strong> public </strong> <strong> class </strong> <strong> runtimeException </strong> <strong> extiende </strong> <strong> java </strong>. <strong> lang </strong>. <strong> runexception </strong> ° //////code uplet <strong> público </strong> <strong> static </strong> <strong> final </strong> entero genérico = 1000000; // código de error <strong> privado </strong> código de error entero; <strong> public </strong> <strong> runtimeException </strong> (código de error entero, causa de lanzamiento) {<strong> this </strong> (error código, <strong> nulo </strong>, causa); } <strong> public </strong> <strong> runtimeException </strong> (mensaje de cadena, causa de lanzamiento) {// use el código de error general <strong> this </strong> (genérico, mensaje, causa); } <strong> public </strong> <strong> runtimeException </strong> (código de error entero, mensaje de cadena, causa de lanzamiento) {<strong> super </strong> (mensaje, causa); <strong> this </strong> .errrocode = errorcode; } <strong> public </strong> entero <strong> getErrorCode </strong> () {<strong> return </strong> errorcode; }}Como muestra el código de ejemplo, introduzca los códigos de error en excepciones. Una vez que ocurre una excepción, simplemente presentamos el código de error de la excepción al usuario, o convertimos el código de error en un mensaje más comprensible. De hecho, el código de error aquí también contiene otra función, y los desarrolladores también pueden saber con precisión qué tipo de excepción ocurrió en función del código de error.
Concepto erróneo 3: Contaminación de la jerarquía de códigos
A menudo dividimos el código en diferentes jerarquías, como el servicio, la lógica de negocios, DAO, etc. La capa DAO contendrá métodos para lanzar excepciones, como se muestra en el Listado 2:
Listado 2
<strong> public </strong> Customer <strong> RECONTRIECUSTOMERBYID </strong> (Long Id) <strong> Throw </strong> sqlexception {// consulta la base de datos basada en ID}A primera vista, no hay ningún problema con el código anterior, pero si piensa cuidadosamente desde la perspectiva del acoplamiento de diseño, el SQLException aquí contamina el código de llamadas superior. La capa de llamadas debe usar explícitamente la captura de try para capturar, o lanzarla aún más al nivel superior. De acuerdo con el principio de aislamiento de diseño, podemos modificarlo adecuadamente para:
Listado 3
<strong> public </strong> Customer <strong> RECONTRIBUSTOMERBYID </strong> (Long Id) {<strong> try </strong> {// consulta la base de datos basada en id} <strong> capt </strong> (sqlexception e) {// use encapsulación de excepción no detectada para excepcionar no detectado para la encapsulación no detectada para detectar la excepción de la hierda, reducir el acoplamiento <strong> shrow> <strong> <strong> strong> <strong> strong> strong> RuntimeException (sqlerrorCode, e); } <strong> finalmente </strong> {// cierre la conexión y limpia los recursos}}Concepto erróneo 4: Ignorar excepciones
El siguiente manejo de excepciones es simplemente generar la excepción a la consola, y no tiene sentido. Además, aparece una excepción aquí y el programa no interrumpe, y el código de llamada continúa ejecutándose, lo que resulta en más excepciones.
Listado 4
<strong> public </strong> <strong> void </strong> <strong> RemieveObjectByid </strong> (Long Id) {<strong> try </strong> {//..- Los códigos que lanza SQLException} <strong> Catch </strong> (Sqlexception ex) {/***cualquiera que sabe que sabe que la excepción de la excepción no tiene sentido, solo emite el error de error a. * En el entorno de producción, la pila de errores debe emitirse al registro. * Y el programa continúa ejecutándose después del proceso de captura, lo que conducirá a otros problemas*/ Ex.PrintStackTrace (); }}Se puede reconstruir:
Listado 5
<strong>public</strong> <strong>void</strong> <strong>retrieveObjectById</strong>(Long id){ <strong>try</strong>{ //..some code that throws SQLException } <strong>catch</strong>(SQLException ex){ <strong>throw</strong> <strong>new</strong> RuntimeException("Exception <strong>in</strong> retrieveObjectById", ex); } <strong> Finalmente </strong> {// limpia los resultados de resultados, declaración, conexión, etc.}}Este malentendido es relativamente básico, y en circunstancias normales, no cometerá este error de bajo nivel.
Concepto erróneo 5: Incluya excepciones en los bloques de la declaración de bucle
Como se muestra en el siguiente código, la excepción está contenida en el bloque de instrucciones For Loop.
Listado 6
<strong> para </strong> (<strong> int </strong> i = 0; i <100; i ++) {<strong> try </strong> {} <strong> catch </strong> (xxxxexception e) {// ... }}Todos sabemos que el manejo de excepciones ocupa los recursos del sistema. A primera vista, todos pensaron que no cometerían tal error. Desde otra perspectiva, se ejecuta un bucle en la clase A, y el método de clase B se llama en el bucle, pero el método llamado en la clase B contiene bloques de declaración como Try-Catch. La jerarquía de la clase se ha desvanecido, y el código es exactamente el mismo que el anterior.
Concepto erróneo 6: Use la excepción para capturar todas las excepciones potenciales
Se lanzan varios tipos diferentes de excepciones durante la ejecución de un método. En aras de la simplicidad del código, la excepción de la clase base se usa para atrapar todas las excepciones potenciales, como se muestra en el siguiente ejemplo:
Listado 7
<strong> public </strong> <strong> void </strong> <strong> RemieveObjectByid </strong> (Long Id) {<strong> try </strong> {// ... Code llame que lanza IOException // ... Código Calls que lanza Sqlexception} <strong> Catch </strong> (excepción e) {// aquí todas las excepciones potenciales Caded por la excepción de clases de base. Si múltiples niveles atrapan esto, la información válida de la excepción original se perderá <strong> throw </strong> <strong> new </strong> runtimeException ("excepción <strong> en </strong> RomEbjectByid", e); }}Se puede reconstruir
Listado 8
<strong> public </strong> <strong> void </strong> <strong> RemieveObjectByid </strong> (Long Id) {<strong> try </strong> {//..some Code que lanza runtimeException, ioexception, sqlexception} <strong> capt </strong> (iooexception e) {// solo captura ioexception <strong> shrow> <strong> <strong> <strong> "Strong>" Strong> "Strong>" Strong> "Strong>" Strong> "Strong>" Strong> "Strong> NEUN RuntimeException (/*Especifique el código de error correspondiente a ioException aquí*/código, "Excepción <strong> en </strong> RomEbjectByid", e); } <strong> Catch </strong> (sqlexception e) {// simplemente atrapar sqlexception <strong> throw </strong> <strong> nuevo </strong> runtimeException (/*Especifique el código de error correspondiente a sqlexception aquí*/code, "excepción <strong> en </strong> recuperieBjectByID", e); }}Concepto erróneo 7: La encapsulación de varios niveles arroja excepciones no detectadas
Si siempre insistimos en que diferentes tipos de excepciones deben usar diferentes declaraciones de captura, entonces la mayoría de los ejemplos pueden evitar esta sección. Sin embargo, si solo una pieza de código de código lanza más de una excepción, a menudo no es necesario escribir una declaración de captura para cada tipo diferente de excepción. Para el desarrollo, cualquier excepción es suficiente para explicar el problema específico del programa.
Listado 9
<strong> try </strong> {// El RuntimeException, IoExeption u otros pueden ser lanzados; // Tenga en cuenta la diferencia entre aquí y el malentendido seis, aquí hay un código que arroja múltiples excepciones. Los anteriores son múltiples segmentos de código, cada uno lanzando diferentes excepciones} <strong> catch </strong> (<strong> excepción </strong> e) {// Como siempre, convierta la excepción a runtimeException, pero la e aquí es en realidad una instancia de runtimeException, y se ha encapsulado en el código anterior. RuntimeException (/**/code,/**/, e);}Si convertimos todas las excepciones en runtimeException como se muestra en el ejemplo anterior, cuando el tipo de excepción ya es runtimeException, hacemos otra encapsulación. La RuntimeException se volvió a colocar nuevamente, y se perdió la información válida que llevó a cabo la RuntimeException original.
La solución es que podemos agregar verificaciones relevantes a la clase RuntimeException para confirmar que el parámetro que se puede ver no es una instancia de RuntimeException. Si es así, copie el atributo correspondiente a la instancia recién creada. O use diferentes bloques de declaración de captura para atrapar RuntimeException y otras excepciones. Método de preferencia personal 1, los beneficios son evidentes.
Concepto erróneo 8: Excepción de impresión de varios niveles
Primero veamos el siguiente ejemplo, que define las 2 clases A y B. El código de clase B se llama en la Clase A, y las excepciones de captura e imprimen de clase A y Clase B.
Listado 10
<strong> public </strong> <strong> class </strong> <strong> a </strong> {<strong> privado </strong> <strong> static </strong> logger logger = loggerFactory.getLogger (A.Class); <strong> public </strong> <strong> void </strong> <strong> proceso </strong> () {<strong> try </strong> {// Instanciar la clase B, puede cambiar a otros métodos de inyección como b b = <strong> new </strong> b (); B.Process (); // Otro código puede causar excepción} <strong> Catch </strong> (xxxException e) {// Si el método de proceso de clase B arroja una excepción, la excepción estará en B la clase se imprime, y también se imprimirá aquí, por lo que logger.error (e); <strong> Throw </strong> <strong> new </strong> runtimeException (/*código de error*/errorcode,/*información de excepción*/msg, e); }}} <strong> public </strong> <strong> class </strong> <strong> b </strong> {<strong> privado </strong> <strong> static </strong> logger logger = loggerFactory.getLogger (B.Class); <strong> public </strong> <strong> void </strong> <strong> proceso </strong> () {<strong> try </strong> {// código que puede lanzar excepción} <strong> capt </strong> (xxxexception e) {logger.error (e); <strong> Throw </strong> <strong> new </strong> runtimeException (/*código de error*/errorcode,/*información de excepción*/msg, e); }}}La misma excepción se imprimirá 2 veces. Si el nivel es un poco más complicado, es un dolor de cabeza no considerar el rendimiento del sistema de los registros de impresión y simplemente localizar problemas específicos en registros de excepción.
De hecho, los registros de impresión solo requieren capturar e imprimir en la capa más externa del código. La impresión de excepciones también se puede escribir como AOP y entretejerse en la capa más externa del marco.
Concepto erróneo 9: El problema de que la información contenida en excepciones no puede ubicarse completamente
Las excepciones no solo permiten a los desarrolladores saber qué está mal, sino que los desarrolladores también necesitan saber qué causa el problema. Sabemos que Java .lang.Exception tiene un constructor de parámetros de tipo de cadena, y esta cadena se puede personalizar en información rápida fácil de entender.
Los desarrolladores de información personalizada simple solo pueden saber dónde aparece la excepción, pero en muchos casos, los desarrolladores deben saber más sobre qué parámetros están causando tal excepción. En este momento, necesitamos agregar la información de parámetros de la llamada de método a la información personalizada. El siguiente ejemplo solo enumera el caso de un parámetro. En el caso de múltiples parámetros, puede escribir una clase de herramientas para organizar dicha cadena.
Listado 11
public <strong>void</strong> retrieveObjectById(Long id){ <strong>try</strong>{ //..some code that throws SQLException }<strong>catch</strong>(SQLException ex){ //Add parameter information to exception information <strong>throw</strong> <strong>new</strong> RuntimeException("Exception <strong>in</strong> retrieveObjectById <strong> con </strong> ID de objeto: "+ id, ex); }}Concepto erróneo 10: no se puede predecir anormalidades potenciales
Durante el proceso de escritura del código, debido a la falta de una comprensión profunda del código de llamadas, es imposible determinar con precisión si el código llamado producirá excepciones, por lo que se ignora el procesamiento. Después de generar un error de producción, recordé que debería agregar recuperación de excepción a un cierto código, y ni siquiera podía señalar con precisión la causa de la excepción. Esto requiere que los desarrolladores no solo saben lo que están haciendo, sino también para saber lo más posible lo que otros han hecho y qué posibles resultados pueden ser causados, y considerar el proceso de procesamiento de toda la aplicación desde una perspectiva global. Estas ideas afectarán nuestra escritura y procesamiento del código.
Concepto erróneo 11: Uso mixto de múltiples bibliotecas de registro de terceros
Hoy en día, hay más y más bibliotecas de registro de terceros Java. Se introducirán varios marcos en un proyecto grande, y estos marcos dependerán de la implementación de diferentes bibliotecas de registro. El problema más problemático es no introducir todas las bibliotecas de registro necesarias, el problema es que las bibliotecas de registro introducidas son incompatibles en sí mismas. Si puede ser fácil de resolver en la etapa inicial del proyecto, puede reintroducir todas las bibliotecas de registro en su código según sea necesario, o cambiar a un marco. Pero este tipo de costo no es asequible para cada proyecto, y cuanto mayor sea el riesgo a medida que avanza el proyecto.
¿Cómo podemos evitar efectivamente problemas similares? La mayoría de los marcos ahora han tenido en cuenta problemas similares. Pueden configurar propiedades o archivos XML, parámetros o clases de implementación de registro de escaneo de tiempo de ejecución en la biblioteca LIB, y solo cuando la aplicación se ejecuta podemos determinar qué biblioteca de registro específica aplicar.
De hecho, según el principio de no requerir múltiples niveles de impresión de registro, podemos simplificar muchas clases que originalmente llamaron código de impresión de registro. En muchos casos, podemos usar interceptores o filtros para imprimir registros para reducir el costo del mantenimiento y la migración del código.
Conclusión
Lo anterior es la experiencia y el resumen puramente personal. Las cosas son dialécticas, y no hay principios absolutos. El principio más efectivo es adecuado para usted. Espero que la explicación y el análisis anteriores puedan ser útiles para usted.