被抑制的异常在新的Java 7 try-with-resources语句 (也称为自动资源管理 [ ARM ])的执行中起着重要作用。 为这种新的资源管理功能提供API支持似乎是Throwable类上新的构造函数和方法的主要驱动程序,它们提供对抑制的异常的访问,但是API支持在try-with-resources语句之外使用抑制的异常。 。
遇到受抑制的异常的最常见用例是,当try-with-resources语句(这是一种尝试类型,根据Java语言规范Java SE 7 Edition 14.20 ,它不需要catch或finally子句时)遇到一个异常。 try块中的异常,然后在隐式尝试关闭相关资源时遇到另一个异常。
为了说明这种情况,我组成了自己的“资源”,可以在try-with-resource语句中使用它,因为它实现了java.lang.AutoCloseable接口。 当使用它的代码尝试使用它时,此“顽皮资源”会故意引发异常,然后在调用重写的close方法(由AutoCloseable接口指定的唯一方法)时引发另一个异常,从而继续其不良形式。 接下来显示NaughtyResource的代码清单。
NaughtyResource.java
package dustin.examples;
/**
* Resource that throws exceptions both in its use and its closure and is only
* intended for use in demonstrating Java 7's suppressed exceptions APIs. This
* is not a well-behaved class.
*
* @author Dustin
*/
public class NaughtyResource implements AutoCloseable
{
/**
* Method that intentionally throws an exception.
*
* @throws RuntimeException Thrown no matter how you call me.
*/
public void doNothingGood()
{
throw new RuntimeException("Nothing good can come of this.");
}
/**
* The overridden closure method from AutoCloseable interface.
*
* @throws Exception Exception that might be thrown during closure of this
* resource.
*/
@Override
public void close() throws Exception
{
throw new UnsupportedOperationException("Not supported yet.");
}
}
现在有了顽皮的资源,是时候使用顽皮的资源并演示抑制异常API。 下一张图片描述了如果尝试使用此资源而不捕获close方法隐式抛出的Exception且未声明该方法抛出该异常的情况,将发生什么情况。
这是javac提供的错误消息,如以下屏幕快照所示。
我已经显示了前两个屏幕快照,以强调对资源执行的隐式关闭调用。 NetBeans编辑器和控制台中显示的错误消息清楚地表明了这种情况。
下一个代码清单包含编译的SuppressedExceptions类的第一个版本。
SuppressedExceptions.java(版本1)
package dustin.examples;
/**
* Demonstrate JDK 7's Suppressed Exceptions API support.
*
* @author Dustin
*/
public class SuppressedExceptions
{
/**
* Executable function demonstrating suppressed exceptions.
*
* @param arguments The command line arguments; none expected.
*/
public static void main(String[] arguments) throws Exception
{
try (NaughtyResource naughty = new NaughtyResource())
{
naughty.doNothingGood();
}
}
}
尽管在执行上述代码时确实遇到了两个异常(一个在NaughtyResource.doNothingGood()调用的try块中,一个在隐式调用close方法的调用中),但只有一个渗透到顶部,并在应用程序显示时显示运行。 下一个屏幕快照对此进行了演示。
如最后一个图像所示,仅显示在try-with-resources语句的try try块中遇到的异常。 在这里,可以向Throwable(在这种情况下为Exception)询问其受抑制的异常的功能非常有用。 为了证明这一点,接下来显示SuppressedExceptions类的版本2(使用Throwable.getSuppressed() )。
SuppressedExceptions.java(版本2)
package dustin.examples;
import static java.lang.System.err;
/**
* Demonstrate JDK 7's Suppressed Exceptions API support.
*
* @author Dustin
*/
public class SuppressedExceptions
{
/**
* Method that uses NaughtyResource with try-with-resource statement.
*
* @throws Exception Expected exception for try-with-resource used on the
* NaughtyResource.
*/
public static void performTryWithResource() throws Exception
{
try (NaughtyResource naughty = new NaughtyResource())
{
naughty.doNothingGood();
}
}
/**
* Executable function demonstrating suppressed exceptions.
*
* @param arguments The command line arguments; none expected.
*/
public static void main(String[] arguments)
{
try
{
performTryWithResource();
}
catch (Exception ex)
{
err.println("Exception encountered: " + ex.toString());
final Throwable[] suppressedExceptions = ex.getSuppressed();
final int numSuppressed = suppressedExceptions.length;
if (numSuppressed > 0)
{
err.println("\tThere are " + numSuppressed + " suppressed exceptions:");
for (final Throwable exception : suppressedExceptions)
{
err.println("\t\t" + exception.toString());
}
}
}
}
}
上面的应用程序将打印出所有抑制的异常。 在这种情况下,尝试关闭NaughtyResource时遇到的异常现在显示为被抑制的异常之一。 下一个屏幕快照对此进行了演示。
如Java教程中所述,我们看到唯一抛出的异常是在try-with-resources语句的try块中遇到的异常,并且在资源关闭期间遇到的第二个异常被“抑制”(但仍可从实际抛出异常)。
不需要使用try-with-resources来处理抑制的异常。 下一个代码清单将SuppressedExceptions改编为使用更传统的try-finally。
SuppressedExceptions.java(版本3)
package dustin.examples;
import static java.lang.System.err;
/**
* Demonstrate JDK 7's Suppressed Exceptions API support.
*
* @author Dustin
*/
public class SuppressedExceptions
{
/**
* Method that uses NaughtyResource with JDK 7 try-with-resource statement.
*
* @throws Exception Expected exception for try-with-resource used on the
* NaughtyResource.
*/
public static void performTryWithResource() throws Exception
{
try (NaughtyResource naughty = new NaughtyResource())
{
naughty.doNothingGood();
}
}
/**
* Method that uses NaughtyResource with traditional try-finally statement.
*
* @throws Exception Exception thrown during use of NaughtyResource.
*/
public static void performTryFinally() throws Exception
{
final NaughtyResource naughty = new NaughtyResource();
try
{
naughty.doNothingGood();
}
finally
{
naughty.close();
}
}
/**
* Executable function demonstrating suppressed exceptions.
*
* @param arguments The command line arguments; none expected.
*/
public static void main(String[] arguments)
{
try
{
// performTryWithResource();
performTryFinally();
}
catch (Exception ex)
{
err.println("Exception encountered: " + ex.toString());
final Throwable[] suppressedExceptions = ex.getSuppressed();
final int numSuppressed = suppressedExceptions.length;
if (numSuppressed > 0)
{
err.println("\tThere are " + numSuppressed + " suppressed exceptions:");
for (final Throwable exception : suppressedExceptions)
{
err.println("\t\t" + exception.toString());
}
}
else
{
err.println("\tNo Suppressed Exceptions.");
}
}
}
}
上面的代码调用了一种使用try-finally的方法,其行为与try-with-resources示例的行为不同,如下面的屏幕快照所示。
try-finally显示尝试关闭资源时遇到的异常,而不是使用资源时遇到的异常(上面显示的try-with-resources相反)。 另一个不同之处是,即使在try-finally情况下,作为抑制的异常,其他异常也不可用。 这是一个广为宣传的 “ 丢失的异常 ”问题的示例 ,该问题可能包含潜在的“琐碎”异常(关闭资源),而隐藏了潜在的更重要的异常(实际上是在使用资源)。
如果我想同时查看NaughtyResource的使用和关闭过程中引发的异常以及try-finally,可以使用Throwable.addSuppressed(Throwable) ,如下面的代码清单所示。 在该清单中,对try和finally子句进行了增强,以捕获使用NaughtyResource期间引发的异常,并将捕获的异常添加到实际抛出的异常中作为抑制的异常。
SuppressedExceptions.java(版本4)
package dustin.examples;
import static java.lang.System.err;
/**
* Demonstrate JDK 7's Suppressed Exceptions API support.
*
* @author Dustin
*/
public class SuppressedExceptions
{
/**
* Method that uses NaughtyResource with JDK 7 try-with-resource statement.
*
* @throws Exception Expected exception for try-with-resource used on the
* NaughtyResource.
*/
public static void performTryWithResource() throws Exception
{
try (NaughtyResource naughty = new NaughtyResource())
{
naughty.doNothingGood();
}
}
/**
* Method that uses NaughtyResource with traditional try-finally statement.
*
* @throws Exception Exception thrown during use of NaughtyResource.
*/
public static void performTryFinally() throws Exception
{
final NaughtyResource naughty = new NaughtyResource();
Throwable throwable = null;
try
{
naughty.doNothingGood();
}
catch (Exception usingEx)
{
throwable = usingEx;
}
finally
{
try
{
naughty.close();
}
catch (Exception closingEx)
{
if (throwable != null)
{
closingEx.addSuppressed(throwable);
throw closingEx;
}
}
}
}
/**
* Executable function demonstrating suppressed exceptions.
*
* @param arguments The command line arguments; none expected.
*/
public static void main(String[] arguments)
{
try
{
// performTryWithResource();
performTryFinally();
}
catch (Exception ex)
{
err.println("Exception encountered: " + ex.toString());
final Throwable[] suppressedExceptions = ex.getSuppressed();
final int numSuppressed = suppressedExceptions.length;
if (numSuppressed > 0)
{
err.println("\tThere are " + numSuppressed + " suppressed exceptions:");
for (final Throwable exception : suppressedExceptions)
{
err.println("\t\t" + exception.toString());
}
}
else
{
err.println("\tNo Suppressed Exceptions.");
}
}
}
}
修改后的类的输出如下所示。 注意,由于使用资源本身而导致的异常现在与作为抑制的异常而引发的异常相关联。 尽管此处未显示,但Throwable现在还提供了一个构造函数,该构造函数允许通过布尔参数指定新实例化的Throwable是否允许(启用)抑制的异常(禁止)。
查看抑制异常的另一个角度是完整堆栈跟踪。 以下三幅图像是使用try-with-resources(显式显示假定的异常),使用传统的try-finally语句而未显式添加抑制的异常(因此在full stack跟踪中没有抑制的异常)导致的完整堆栈跟踪的屏幕快照。 ,并使用传统的try-finally方法,并明确添加抑制的异常(因此确实会出现在完整堆栈跟踪中)。 我在每个图像上都添加了一条红线,以单独显示整个堆栈跟踪结束的位置,并在适用的情况下,圈出了对抑制异常的显式引用。
抑制的异常与链接的异常
禁止的异常与链接的异常不同 。 链接异常是JDK 1.4引入的,旨在使轻松跟踪异常之间的因果关系成为可能。 通常,链接的异常是由于将新引发的异常与已捕获并导致引发新异常的异常相关联而导致的。 例如,可能会引发未检查的异常,从而“包装”已捕获的已检查的异常,并且可以将它们链接在一起。
JDK 7引入了抑制的异常,它与因果关系无关,而与在单个抛出的异常中表示可能相关但不一定因果的多个例外条件有关。 在上面的我的顽皮资源示例中,顽皮资源在其唯一方法被调用时的异常并不是导致它在调用其close方法时引发异常的原因。 因此,将两个异常关联起来(通过抑制的异常机制)比强迫一个异常似乎是造成连锁关系中另一个异常的原因更有意义。
通过比较Throwable访问每种类型的方法,最快速地了解链式异常与抑制型异常之间的区别可能是最容易的。 对于链式异常,将调用特定的Exception(Throwable)方法。 此方法返回导致Throwable的单个实例。 可以询问返回的Throwable的原因,并在导致Throwables的整个过程中重复该过程。 重要的观察结果是,每个给定的Throwable都有一个导致Throwable的原因。 这可以与Throwable在调用其getSuppressed()方法时提供多个抑制的Throwable(在数组中)的能力进行对比。
NetBeans推荐尝试资源
在此值得注意的是,NetBeans会警告您尝试使用try-finally,并建议使用try-with-resources替换它,如我在我的文章《 用于现代化Java代码的七个NetBeans提示》和下面显示的屏幕快照中所讨论的。
结论
我相信很明显,为什么NetBeans建议开发人员将对资源处理的最后尝试更改为try-with-resources语句。 通常最好先了解资源上的哪个操作导致了异常,但是如果需要的话,也能够在关闭时访问异常是很不错的。 如果必须选择,我通常会对执行期间的资源问题更感兴趣,因为关闭问题可能是该问题的衍生形式。 但是,两者都更好。 传统的try-finally最终仅列出关闭时的异常,而无需付出额外的努力来中继这两者。 try-with-resource语句不仅更简洁; 由于内置支持包含抑制的异常,它也更加有用。
参考: JCG合作伙伴 Dustin Marx在Inspired by Actual Events博客上提供了Java 7对抑制异常的支持 。
翻译自: https://www.javacodegeeks.com/2012/04/java-7s-support-for-suppressed.html