web-dev-qa-db-fra.com

Java if vs overhead try / catch

Y a-t-il des frais généraux dans Java pour l'utilisation d'un bloc try/catch, par opposition à un bloc if block (en supposant que le code inclus autrement ne le demande pas)?

Par exemple, prenez les deux implémentations simples suivantes d'une méthode de "découpage sécurisé" pour les chaînes:

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 l'entrée raw n'est que rarement null, y a-t-il une différence de performances entre les deux méthodes?

De plus, est-ce un bon modèle de programmation pour utiliser l'approche tryTrim() pour simplifier la mise en page du code, surtout quand plusieurs si blocs la vérification de conditions d'erreur rares peut être évité en enfermant le code dans un bloc try/catch?

Par exemple, il est courant d'avoir une méthode avec N parameters, Qui en utilise M <= N Près de son début, échouant rapidement et de manière déterministe si l'un de ces paramètres est "invalide" (par exemple, un chaîne nulle ou vide), sans affecter le reste du code.

Dans de tels cas, au lieu d'avoir à écrire k * Msi blocs (où k est le nombre moyen de contrôles par paramètre, par exemple k = 2 Pour null ou des chaînes vides), un bloc try/catch raccourcirait considérablement le code et un commentaire de 1-2 lignes pourrait être utilisé pour noter explicitement la logique "non conventionnelle".

Un tel schéma accélérerait également la méthode, surtout si les conditions d'erreur se produisent rarement, et cela sans compromettre la sécurité du programme (en supposant que les conditions d'erreur sont "normales", par exemple comme dans une méthode de traitement de chaîne où les valeurs nulles ou vides sont acceptables, bien que rarement présents).

54
PNS

Je sais que vous posez des questions sur la surcharge de performances, mais vous ne devriez vraiment pas utiliser try/catch et if de manière interchangeable.

try/catch est pour les choses qui vont mal hors de votre contrôle et pas dans le flux de programme normal. Par exemple, vous essayez d'écrire dans un fichier et le système de fichiers est plein? Cette situation doit généralement être gérée avec try/catch.

Les instructions if doivent être un flux normal et une vérification d'erreur ordinaire. Ainsi, par exemple, l'utilisateur ne remplit pas un champ de saisie obligatoire? Utilisez if pour cela, pas try/catch.

Il me semble que votre exemple de code suggère fortement que la bonne approche est une instruction if et non une try/catch.

Pour répondre à votre question, je suppose qu'il y a généralement plus de frais généraux dans un try/catch qu'un if. Pour en être sûr, procurez-vous un Java profiler et recherchez le code spécifique qui vous intéresse. Il est possible que la réponse varie en fonction de la situation.

63
Trott

Cette question a presque été "répondue à mort", mais je pense qu'il y a quelques autres points qui pourraient être utilement avancés:

  • Utiliser try / catch Pour un flux de contrôle non exceptionnel est un mauvais style (en Java). (Il y a souvent un débat sur ce que signifie "non exceptionnel" ... mais c'est un sujet différent.)

  • Une partie de la raison pour laquelle c'est un mauvais style est que try / catch Est des ordres de grandeur plus cher qu'une déclaration de flux de contrôle régulière1. La différence réelle dépend du programme et de la plate-forme, mais je m'attends à ce qu'elle soit 1000 fois ou plus plus chère. Entre autres choses, la création l'objet d'exception capture une trace de pile, recherchant et copiant des informations sur chaque trame de la pile. Plus la pile est profonde, plus elle doit être copiée.

  • Une autre raison de son mauvais style est que le code est plus difficile à lire.

1 - Le JIT dans les versions récentes de Java 7 peut optimiser la gestion des exceptions pour réduire considérablement les frais généraux, dans certains cas. Cependant, ces optimisations ne sont pas activées par défaut.

Il y a aussi des problèmes avec la façon dont vous avez écrit l'exemple:

  • Attraper Exception est une très mauvaise pratique, car il est possible que vous interceptiez d'autres exceptions non contrôlées par accident. Par exemple, si vous faisiez cela autour d'un appel à raw.substring(1) vous attraperiez également les potentiels StringIndexOutOfBoundsExceptions ... et hide Bugs.

  • Ce que votre exemple essaie de faire est (probablement) le résultat d'une mauvaise pratique dans le traitement des chaînes null. En règle générale, vous devez essayer de minimiser l'utilisation des chaînes null et tenter de limiter leur propagation (intentionnelle). Dans la mesure du possible, utilisez une chaîne vide au lieu de null pour signifier "aucune valeur". Et lorsque vous avez un cas où vous avez besoin de passer ou de renvoyer une chaîne null, documentez-la clairement dans votre méthode javadocs. Si vos méthodes sont appelées avec un null alors qu'elles ne devraient pas ... c'est un bug. Laissez-le lever une exception. N'essayez pas de compenser le bogue en renvoyant (dans cet exemple) null.


SUIVI

Ma question était plus générale, pas seulement pour les valeurs nulles.

... et la plupart des points de ma réponse ne concernent pas les valeurs nulles!

Mais gardez à l'esprit qu'il existe de nombreux cas où vous souhaitez autoriser la valeur nulle occasionnelle, ou toute autre valeur qui pourrait produire une exception, et simplement les ignorer. C'est le cas, par exemple, lorsque vous lisez des valeurs de clé/paire quelque part et que vous les passez à une méthode comme tryTrim () ci-dessus.

Oui, il existe des situations où des valeurs null sont attendues, et vous devez les gérer.

Mais je dirais que ce que tryTrim() fait est (typiquement) la mauvaise façon de traiter null. Comparez ces trois bits de code:

// 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 fin de compte, vous devez traiter le null différemment d'une chaîne régulière, et c'est généralement une bonne idée de le faire dès que possible . Plus le null est autorisé à se propager à partir de son point d'origine, plus il est probable que le programmeur oublie qu'un null value est une possibilité, et écrivez du code buggy qui suppose une valeur non nulle. Et oublier qu'un paramètre de requête HTTP pourrait être manquant (c'est-à-dire param == null) Est un cas classique où cela se produit.

Je ne dis pas que tryTrim() est intrinsèquement mauvais. Mais le fait qu'un programmeur ressente le besoin de méthodes d'écriture comme celle-ci est probablement indicatif d'une gestion nulle moins qu'idéale.

33
Stephen C

Utilisez la deuxième version. N'utilisez jamais d'exceptions pour le flux de contrôle lorsque d'autres alternatives sont disponibles, car ce n'est pas leur raison d'être. Les exceptions concernent des circonstances exceptionnelles.

Sur le sujet, n'attrapez pas Exception ici, et surtout ne l'avalez pas. Dans votre cas, vous vous attendez à un NullPointerException. Si vous deviez attraper quelque chose, c'est ce que vous attraperiez (mais revenez au paragraphe un, faites pas faites cela ). Lorsque vous attrapez (et avalez!) Exception, vous dites "peu importe ce qui ne va pas, je peux le gérer. Je m'en fiche de ce que c'est." Votre programme est peut-être dans un état irrévocable! Ne saisissez que ce que vous êtes prêt à gérer, laissez tout le reste se propager vers un calque qui peut le traiter, même si ce calque est le calque supérieur et il ne fait que consigner l'exception, puis appuyer sur le bouton d'éjection.

10
Anthony Pegram

Sinon, les exceptions sont rapides à lancer et à attraper (bien qu'un if soit probablement encore plus rapide), mais la chose lente crée la trace de la pile de l'exception, car elle doit parcourir toute la pile actuelle. (En général, il est mauvais d'utiliser des exceptions pour le flux de contrôle, mais lorsque cela est vraiment nécessaire et que les exceptions doivent être rapides, il est possible d'ignorer la création de la trace de pile en remplaçant la méthode Throwable.fillInStackTrace(), ou pour enregistrer une exception instance et la lancer à plusieurs reprises au lieu de toujours créer une nouvelle instance d'exception.)

4
Esko Luontola