注意,捕捉
Throwable
发生的次数比你可能意识到的要多。其中一些情况与Java语言特性紧密结合,这些特性可能会生成与您所示非常相似的字节码。
首先,因为没有依赖于
finally
在字节码级别,它是通过为
可抛出
将执行
最后
在重新旋转之前阻止
可抛出
如果代码流达到该点。在这一点上,你可能会做非常糟糕的事情:
try
{
throw new OutOfMemoryError();
}
finally
{
// highly discouraged, return from finally discards any throwable
return;
}
结果:
没有什么
try
{
throw new OutOfMemoryError();
}
finally
{
// highly discouraged too, throwing in finally shadows any throwable
throw new RuntimeException("has something happened?");
}
结果:
java.lang.RuntimeException: has something happened?
at Throwables.example2(Throwables.java:45)
at Throwables.main(Throwables.java:14)
当然,也有合法的使用案例
最后
,例如进行资源清理。使用类似字节码模式的相关构造是
synchronized
,将在重新抛出之前释放对象监视器:
Object lock = new Object();
try
{
synchronized(lock) {
System.out.println("holding lock: "+Thread.holdsLock(lock));
throw new OutOfMemoryError();
}
}
catch(Throwable t) // just for demonstration
{
System.out.println(t+" has been thrown, holding lock: "+Thread.holdsLock(lock));
}
结果:
holding lock: true
java.lang.OutOfMemoryError has been thrown, holding lock: false
这个
try-with-resource
声明更进一步;它可以通过记录
close()
操作:
try(AutoCloseable c = () -> { throw new Exception("and closing failed too"); }) {
throw new OutOfMemoryError();
}
结果:
java.lang.OutOfMemoryError
at Throwables.example4(Throwables.java:64)
at Throwables.main(Throwables.java:18)
Suppressed: java.lang.Exception: and closing failed too
at Throwables.lambda$example4$0(Throwables.java:63)
at Throwables.example4(Throwables.java:65)
... 1 more
此外,当您
submit
任务到
ExecutorService
,所有丢弃物将被捕获并记录在返回的未来:
ExecutorService es = Executors.newSingleThreadExecutor();
Future<Object> f = es.submit(() -> { throw new OutOfMemoryError(); });
try {
f.get();
}
catch(ExecutionException ex) {
System.out.println("caught and wrapped: "+ex.getCause());
}
finally { es.shutdown(); }
结果:
caught and wrapped: java.lang.OutOfMemoryError
对于JRE提供的执行人服务,责任在于
FutureTask
这是默认值
RunnableFuture
内部使用。我们可以直接演示该行为:
FutureTask<Object> f = new FutureTask<>(() -> { throw new OutOfMemoryError(); });
f.run(); // see, it has been caught
try {
f.get();
}
catch(ExecutionException ex) {
System.out.println("caught and wrapped: "+ex.getCause());
}
结果:
捕获并包装:java。lang.OutOfMemoryError
但是
CompletableFuture
表现出捕捉所有丢弃物的类似行为。
// using Runnable::run as Executor means we're executing it directly in our thread
CompletableFuture<Void> cf = CompletableFuture.runAsync(
() -> { throw new OutOfMemoryError(); }, Runnable::run);
System.out.println("if we reach this point, the throwable must have been caught");
cf.join();
结果:
if we reach this point, the throwable must have been caught
java.util.concurrent.CompletionException: java.lang.OutOfMemoryError
at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:314)
at java.base/java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:319)
at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1739)
at java.base/java.util.concurrent.CompletableFuture.asyncRunStage(CompletableFuture.java:1750)
at java.base/java.util.concurrent.CompletableFuture.runAsync(CompletableFuture.java:1959)
at Throwables.example7(Throwables.java:90)
at Throwables.main(Throwables.java:24)
Caused by: java.lang.OutOfMemoryError
at Throwables.lambda$example7$3(Throwables.java:91)
at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1736)
... 4 more
所以底线是,您不应该关注技术细节
可抛出
将在某个地方被捕获,但代码的语义。这是用于忽略异常(坏)还是用于在报告了严重环境错误的情况下尝试继续(坏)还是仅用于执行清理(好)?上面描述的大多数工具都可以用于好的和坏的方面