Java si vs. try / catch overhead


Answers

Esta pregunta casi ha sido "respondida hasta la muerte", pero creo que hay algunos puntos más que podrían ser útiles:

  • El uso de try / catch para un flujo de control no excepcional es un mal estilo (en Java). (A menudo hay debate sobre lo que significa "no excepcional" ... pero ese es un tema diferente).

  • Parte de la razón por la cual es un estilo malo es que try / catch es mucho más caro que una declaración de flujo de control regular 1 . La diferencia real depende del programa y la plataforma, pero espero que sea 1000 o más veces más costoso. Entre otras cosas, la creación del objeto de excepción captura un seguimiento de pila, buscando y copiando información sobre cada cuadro en la pila. Cuanto más profunda es la pila, más necesita ser copiada.

  • Otra parte de la razón por la cual es un estilo malo es que el código es más difícil de leer.

1 - El JIT en versiones recientes de Java 7 puede optimizar el manejo de excepciones para reducir drásticamente los gastos generales, en algunos casos. Sin embargo, estas optimizaciones no están habilitadas por defecto.

También hay problemas con la forma en que ha escrito el ejemplo:

  • La Exception captura es una práctica muy mala, porque existe la posibilidad de que atrape otras excepciones no verificadas por accidente. Por ejemplo, si lo hiciera con una llamada a raw.substring(1) , también podría detectar posibles StringIndexOutOfBoundsException s ... y ocultar errores.

  • Lo que intenta hacer tu ejemplo es (probablemente) el resultado de una práctica deficiente al tratar con cadenas null . Como principio general, debe intentar minimizar el uso de cadenas null e intentar limitar su propagación (intencional). Siempre que sea posible, use una cadena vacía en lugar de null para indicar "sin valor". Y cuando tenga un caso donde necesite pasar o devolver una cadena vacía, documente claramente en su método javadocs. Si sus métodos se llaman con un null cuando no deberían ... es un error. Deja que arroje una excepción. No intente compensar el error por (en este ejemplo) devolviendo null .

SEGUIR

Mi pregunta era más general, no solo para valores nulos.

... y la mayoría de los puntos en mi respuesta no son sobre valores nulos!

Pero tenga en cuenta que hay muchos casos en los que desea permitir el valor nulo ocasional, o cualquier otro valor que pueda producir una excepción, y simplemente ignórelos. Este es el caso, por ejemplo, al leer valores de clave / par de algún lugar y pasarlos a un método como el tryTrim () anterior.

Sí, hay situaciones en null que se esperan valores null , y debe lidiar con ellos.

Pero yo diría que lo que tryTrim() está haciendo es (típicamente) la forma incorrecta de lidiar con null . Compara estos tres bits de código:

// Version 1
String param = httpRequest.getParameter("foo");
String trimmed = tryTrim(param);
if (trimmed == null) {
    // deal with case of 'foo' parameter absent
} else {
    // deal with case of 'foo' parameter present
}

// Version 2
String param = httpRequest.getParameter("foo");
if (param == null) {
    // deal with case of 'foo' parameter absent
} else {
    String trimmed = param.trim();
    // deal with case of 'foo' parameter present
}

// Version 3
String param = httpRequest.getParameter("foo");
if (param == null) {
    // treat missing and empty parameters the same
    param = "";
}
String trimmed = param.trim();

En definitiva, tiene que tratar el null diferente a una cadena normal, y por lo general es una buena idea hacerlo lo antes posible. Cuanto más se permita que el null se propague desde su origen de punto, más probable es que el programador olvide que un valor null es una posibilidad, y escriba un código con errores que asuma un valor no nulo. Y olvidar que un parámetro de solicitud HTTP podría faltar (es decir, param == null ) es un caso clásico en el que esto sucede.

No estoy diciendo que tryTrim() sea ​​intrínsecamente malo. Pero el hecho de que un programador sienta la necesidad de escribir métodos como este es probablemente indicativo de un manejo nulo menos que ideal.

Question

¿Hay alguna sobrecarga en Java para usar un bloque try / catch , en oposición a un bloque if (suponiendo que el código adjunto no lo solicita)?

Por ejemplo, tome las siguientes dos implementaciones simples de un método de "ajuste seguro" para cadenas:

public String tryTrim(String raw) {
    try {
        return raw.trim();
    } catch (Exception e) {
    }
    return null;
}

public String ifTrim(String raw) {
    if (raw == null) {
        return null;
    }
    return raw.trim();
}

Si la entrada en raw rara vez es null , ¿hay alguna diferencia de rendimiento entre los dos métodos?

Además, ¿ es un buen patrón de programación utilizar el enfoque tryTrim() para simplificar el diseño del código, especialmente cuando se pueden evitar muchos bloques que comprueban las condiciones de error raro encerrando el código en un bloque try / catch?

Por ejemplo, es un caso común tener un método con N parameters , que usa M <= N de ellos cerca de su inicio, fallando rápidamente y de manera determinista si dicho parámetro es "inválido" (por ejemplo, una cadena vacía o nula), sin afectar el resto del código.

En tales casos, en lugar de tener que escribir k * M si los bloques (donde k es el número promedio de verificaciones por parámetro, p. Ej. k = 2 para cadenas vacías o nulas), un bloque try / catch acortaría significativamente el código y 1 -2 línea de comentario podría utilizarse para señalar explícitamente la lógica "no convencional".

Tal patrón también aceleraría el método, especialmente si las condiciones de error ocurren raramente, y lo haría sin comprometer la seguridad del programa (asumiendo que las condiciones de error son "normales", por ejemplo, como en un método de procesamiento de cadena donde valores nulos o vacíos son aceptables, aunque raramente en presencia).




De lo contrario, las excepciones son rápidas de lanzar y atrapar (aunque un if es probablemente aún más rápido), pero lo lento está creando la traza de la pila de la excepción, porque necesita recorrer toda la pila actual. (En general, es malo usar excepciones para el flujo de control, pero cuando eso realmente se necesita y las excepciones deben ser rápidas, es posible omitir la creación del rastreo de pila anulando el método Throwable.fillInStackTrace() , o para guardar una instancia de excepción y lanzarlo repetidamente en lugar de crear siempre una nueva instancia de excepción).




Links