web-dev-qa-db-fra.com

Lancer et intercepter une exception, ou utiliser instanceof?

J'ai une exception dans une variable (non levée).

Quelle est la meilleure option?

Exception exception = someObj.getExcp();
try {
    throw exception;
} catch (ExceptionExample1 e) {
    e.getSomeCustomViolations();
} catch (ExceptionExample2 e) {
    e.getSomeOtherCustomViolations(); 
}

ou

Exception exception = someObj.getExcp();
if (exception instanceof ExceptionExample1) {
    exception.getSomeCustomViolations();
} else if (exception instanceof ExceptionExample2) {
    exception.getSomeOtherCustomViolations();
}
21
Ruslan

Je vous conseille d'utiliser instanceof car ce sera probablement plus rapide. Lancer une exception est une opération compliquée et coûteuse. Les JVM sont optimisées pour être rapides dans le cas où des exceptions ne pas se produisent. Les exceptions doivent être exceptionnelles.

Notez que la technique throw ne se compilera probablement pas comme indiqué, si votre type d'exception est une exception vérifiée, le compilateur se plaindra que vous devez intercepter ce type ou le déclarer comme levé (correspondant à un else { ... } clause si vous utilisez la technique instanceof), qui peut être utile ou non, selon la façon dont vous souhaitez gérer les exceptions qui ne font pas partie des sous-types spécifiques.

9
Boann

Je déteste faire éclater la bulle de tout le monde, mais en utilisant try/catch est plus rapide. Cela ne veut pas dire que c'est la manière "correcte", mais si la performance est la clé, c'est le gagnant. Voici les résultats du programme suivant:

Exécuter 1

  • Sous-exécution 1: Instance de: 130 ms
  • Sous-run 1: Try/catch: 118 ms
  • Sous-exécution 2: Instance de: 96 ms
  • Sous-run 2: Try/catch: 93 ms
  • Sous-exécution 3: Instance de: 100 ms
  • Sous-run 3: Try/catch: 99 ms

Exécuter 2

  • Sous-exécution 1: Instance de: 140 ms
  • Sous-run 1: Try/catch: 111 ms
  • Sous-exécution 2: Instance de: 92 ms
  • Sous-run 2: Try/catch: 92 ms
  • Sous-exécution 3: Instance de: 105 ms
  • Sous-run 3: Try/catch: 95 ms

Exécuter

  • Sous-exécution 1: Instance de: 140 ms
  • Sous-run 1: Try/catch: 135 ms
  • Sous-exécution 2: Instance de: 107 ms
  • Sous-run 2: Try/catch: 88 ms
  • Sous-exécution 3: Instance de: 96 ms
  • Sous-run 3: Try/catch: 90 ms

Environnement de test

  • Java: 1.7.0_45
  • Mac OSX Mavericks

En actualisant les sous-exécutions de préchauffage de chaque exécution, la méthode instanceof n'atteint au mieux que les performances de try/catch. La moyenne (actualisation des échauffements) de la méthode instanceof est de 98 ms et la moyenne de try/catch est de 92 ms.

Veuillez noter que je n'ai pas modifié l'ordre dans lequel chaque méthode a été testée. J'ai toujours testé un bloc de instanceof puis un bloc de try/catch. J'aimerais voir d'autres résultats contredire ou confirmer ces résultats.

public class test {

    public static void main (String [] args) throws Exception {
        long start = 0L;
        int who_cares = 0; // Used to prevent compiler optimization
        int tests = 100000;

        for ( int i = 0; i < 3; ++i ) {
            System.out.println("Testing instanceof");
            start = System.currentTimeMillis();
            testInstanceOf(who_cares, tests);
            System.out.println("instanceof completed in "+(System.currentTimeMillis()-start)+" ms "+who_cares);

            System.out.println("Testing try/catch");
            start = System.currentTimeMillis();
            testTryCatch(who_cares, tests);
            System.out.println("try/catch completed in "+(System.currentTimeMillis()-start)+" ms"+who_cares);
        }
    }

    private static int testInstanceOf(int who_cares, int tests) {
        for ( int i = 0; i < tests; ++i ) {
            Exception ex = (new Tester()).getException();
            if ( ex instanceof Ex1 ) {
                who_cares = 1;
            } else if ( ex instanceof Ex2 ) {
                who_cares = 2;
            }
        }
        return who_cares;
    }

    private static int testTryCatch(int who_cares, int tests) {
        for ( int i = 0; i < tests; ++i ) {
            Exception ex = (new Tester()).getException();
            try {
                throw ex;
            } catch ( Ex1 ex1 ) {
                who_cares = 1;
            } catch ( Ex2 ex2 ) {
                who_cares = 2;
            } catch ( Exception e ) {}
        }
        return who_cares;
    }

    private static class Ex1 extends Exception {}

    private static class Ex2 extends Exception {}

    private static Java.util.Random Rand = new Java.util.Random();

    private static class Tester {
        private Exception ex;
        public Tester() {
            if ( Rand.nextBoolean() ) {
                ex = new Ex1();
            } else {
                ex = new Ex2();
            }
        }
        public Exception getException() {
            return ex;
        }
    }
}
10
disrvptor

Je vous exhorte fortement à réellement utiliser un objet simple pour représenter votre "contrainte". Que ce soit une interface de marquage (par exemple Message) ou un Java.lang.String c'est à vous de décider. Les exceptions ne sont pas censées être utilisées comme vous le souhaitez, même si l'une ou l'autre pourrait fonctionner (je m'attendrais à ce que la seconde soit plus rapide, mais une optimisation prématurée ...).

4
Elliott Frisch

Vous pouvez également utiliser le polymorphisme en créant une interface pour vos exceptions personnalisées qui contient la méthode getCustomViolation (). Ensuite, chaque exception personnalisée implémenterait cette interface et cette méthode.

2
Joe