抛出和捕获异常,还是使用instanceof?

28

我有一个变量中的异常(未被抛出)。

最好的选择是什么?

Exception exception = someObj.getExcp();
try {
    throw exception;
} catch (ExceptionExample1 e) {
    e.getSomeCustomViolations();
} catch (ExceptionExample2 e) {
    e.getSomeOtherCustomViolations(); 
}
或者
Exception exception = someObj.getExcp();
if (exception instanceof ExceptionExample1) {
    exception.getSomeCustomViolations();
} else if (exception instanceof ExceptionExample2) {
    exception.getSomeOtherCustomViolations();
}

第二个是提出的最不糟糕的选择。 - Elliott Frisch
@ElliottFrisch 的自定义异常类包含违规(业务、验证等),我需要处理这个。 - Ruslan
第二个是最好的选择,如果想要得到好的东西。 - Sir_Mr_Bman
我建议使用一个接口,可以叫做 Message - 或者直接使用 java.lang.String - Elliott Frisch
4个回答

15

我建议使用 instanceof,因为这样可能会更快。抛出异常是一项复杂而昂贵的操作。JVM优化的快速情况是当异常不会发生时。异常应该是个例外。

请注意,如果您的异常类型是已检查的异常,则throw技术可能无法按照所示编译,编译器将抱怨您必须捕获该类型或声明它被抛出(如果您使用instanceof技术,则相应于else { ... }子句),这可能有助于处理不是特定子类型之一的异常,也可能没有帮助。


谢谢,这正是我想听到的。 - Ruslan
3
instanceof 应该更快,但异常并不慢,可以通过JIT进行优化:https://dev59.com/n3VC5IYBdhLWcg3wbgdS#299214。 - Andrey Chaschev
1
@AndreyChaschev 无论如何,即使instanceof不更快,它也不可能更慢。最好的throw优化将导致相同的instanceof逻辑。 - Boann
如果您查看我的答案和代码,您会发现try/catch更快。我并不是说它通过了代码气味测试,但它比instanceof更快。 - disrvptor

14
抱歉打破大家的幻想,但使用try/catch更快。这并不是说它是“正确”的方法,但如果性能是关键因素,那么try/catch是赢家。以下是来自以下程序的结果: ... 折扣每个运行的预热子运行,instanceof方法最多只能实现与try/catch相同的性能。除去预热后的平均值,instanceof方法为98毫秒,try/catch为92毫秒。 ... 请注意,我没有改变测试每种方法的顺序。我总是先测试一段instanceof代码,然后再测试一段try/catch代码。我很想看到其他结果,以证明或否定这些发现。
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;
        }
    }
}

2
我得到了不一致的结果。有时一个比另一个快,但并非总是如此。如果您查看实际时间,您会发现instanceof在开始时较慢,但经过约3次迭代后,instanceof的性能优于try/catch。这可能是由于JIT优化造成的,并且可能不代表真实世界应用程序的情况。当运行时不断优化高度重复的操作时,在测试平台中很难甚至不可能获得准确的数字。 - disrvptor
事实上,如果我只在解释模式下运行 java -Xint,那么 instanceof 是明显的赢家。然而,如果我禁用后台编译 java -Xbatch,那么每次都是 try/catch 获胜。 - disrvptor
1
很有趣。大部分时间都花在了 Throwable.fillInStackTrace() 上。然后,在 HotSpot Server VM 上,它能够将简单的 try-catch 转换为类似于 if-instanceof 的东西(尽管我无法让它变得更)。在 HotSpot Client VM 上,instanceof 更快一些,或者如果禁用堆栈跟踪以将其排除在测试之外,则 instanceof 更快 10 倍。我总是被服务器 VM 的优化所打动,但它不是 32 位系统的默认值,因此不能假设它存在。无论如何,除非你正在抛出数百万个异常,否则似乎并没有什么影响。 - Boann

4

我强烈建议您使用一个普通对象来表示您的"约束条件"。无论是一个标记接口(例如 Message)还是一个 java.lang.String,都取决于您。异常并不意味着要按照您的意图使用,即使其中任何一个可以被制作出来(我预计第二个会更快,但是这是一种过早的优化...)。


1
这可能是一个存储的错误和逻辑,它在多个约束检查期间分析异常... - Andrey Chaschev
你说得完全正确。这是不好的做法。但不幸的是,我们并不生活在一个完美的世界中。 - Ruslan

3
您可以使用多态性,创建一个包含getCustomViolation()方法的自定义异常接口。然后每个自定义异常都将实现该接口和该方法。

如果我可以更改Obj类的代码,我就不需要这样的技巧。只需捕获异常即可。那么,在性能和代码质量方面,什么是最好的选择? - Ruslan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接