在Java中始终抛出相同的异常实例

gia*_*olo 13 java scala exception-handling

总是告诉我Java异常处理非常昂贵.

我问的是,在程序开头创建特定类型的异常实例并且不创建新的异常实例是一个好习惯,总是抛出相同的异常对象.

我只想举个例子.常用代码:

if (!checkSomething(myObject))
   throw new CustomException("your object is invalid");
Run Code Online (Sandbox Code Playgroud)

替代方案:

static CustomException MYEXP = new CustomException("your object is invalid");

//somewhere else
if (!checkSomething(myObject))
    throw MYEXP;
Run Code Online (Sandbox Code Playgroud)

当然,我在这里做一些假设:

  1. MyCustomException 没有参数
  2. 客户端代码,无论何时是一种良好的实践,都是基于异常处理的重量级,并且重构不是一种选择.

所以问题是:

  1. 这是一个好习惯吗?
  2. 这会损坏一些JVM机制吗?
  3. 如果1为是,则有可能获得性能提升?(我想不是,但不确定)
  4. 如果1和3是肯定的,为什么不赞助作为练习?
  5. 如果1不是,为什么Martin Odersky在他对Scala的介绍中告诉我这是Scala在某些情况下的工作原理?(在28:30分,他告诉说实施休息已经抛出异常,观众说这很费时间并且他回复说每次都不会创建例外)Fosdem 2009

我希望这不是一个闲置/愚蠢的问题,我对此很好奇.我认为异常处理的实际成本是处理而不是创建.

编辑 添加了关于FOSDEM演示的精确讨论的参考

免责声明:我的代码都没有像提议的那样工作,我无意管理这样的异常,我只是在做一个"假设"问题,而这种好奇心是从视频的肯定中产生的.我想:如果在Scala中完成,为什么不在Java中呢?

Nat*_*hes 18

不,不要那样做.昂贵的部分不处理异常,它正在生成堆栈跟踪.不幸的是,堆栈跟踪也是有用的部分.如果你抛出一个保存的异常,你将传递一个误导性的堆栈跟踪.

可能是在Scala的实现中,有些情况下执行此操作是有意义的.(也许他们正在做一些递归的事情,并且想要预先生成一个异常对象,所以万一他们的内存耗尽,他们仍然会产生异常.)他们也有很多关于他们正在做什么的信息,所以他们有更好的机会做对了 但是JVM语言实现者所做的优化是一个非常特殊的情况.

因此,除非您认为提供误导性信息构成破损,否则您不会破坏任何东西.这对我来说似乎是一个很大的风险.

尝试Thomas Eding关于如何在没有堆栈跟踪的情况下创建异常的建议似乎有效:

groovy:000> class MyException extends Exception {
groovy:001>     public Throwable fillInStackTrace() {}}
===> true
groovy:000> e = new MyException()
===> MyException
groovy:000> Arrays.asList(e.stackTrace)
===> []
Run Code Online (Sandbox Code Playgroud)

还可以查看JLS:

由方法blowUp引发的NullPointerException(这是一种RuntimeException)不会被main中的try语句捕获,因为NullPointerException不能分配给BlewIt类型的变量.这会导致finally子句执行,之后执行main的线程(它是测试程序的唯一线程)因未捕获的异常而终止,这通常会导致打印异常名称和简单的回溯.但是,本规范不要求回溯.

强制回溯的问题在于,可以在程序中的某一点创建异常,然后抛出异常.将堆栈跟踪存储在异常中是非常昂贵的,除非它实际被抛出(在这种情况下,可以在展开堆栈时生成跟踪).因此,我们不会在每个例外中强制要求回溯.

  • @ MrSmith42:我不认为你可以在没有堆栈跟踪的情况下创建一个Exception.像Ruby这样的语言有一种方法可以抛出不是例外的东西.在Java-land中,我们留下了返回null之类的东西.迈克尔迈尔斯的评论提出了一个很好的观点,即异常应该是例外. (2认同)
  • @NathanHughes:重写 fillInStackTrace 会有多大作用吗? (2认同)