当前位置: 首页 > 知识库问答 >
问题:

如何在Java中发现潜在的未经检查的例外情况?

丌官博文
2023-03-14

根据Java规范,Java编译器根据“抛出”语句和方法签名自动验证是否捕获了所有检查的异常,并忽略未检查的异常。

然而,有时对于开发人员来说,找出可以抛出哪些未经检查的异常是很有用的,例如,某些第三方代码可能会在开发人员倾向于期望一个检查的异常的情况下抛出未经检查的异常(如long.parselong)。或者开发人员可能抛出未检查的异常作为将来检查的异常的占位符,而忘记替换它。

在这些例子中,理论上可以找到这些未捕获的未检查的异常。在第一种情况下,Long.Parselong的签名表明它抛出了NumberFormatException,在第二种情况下,源代码可用,因此编译器知道抛出了哪些未经检查的异常。

我的问题是:有没有一个工具可以报告这些案例?或者让Java编译器处理临时未检查异常的方法是检查异常?这对于手动验证和修复可能导致整个线程或应用程序在运行时崩溃的潜在bug非常有用。

编辑:在一些回答之后,我不得不强调,我的目标不是找到系统中可能存在的未检查异常的详尽列表,而是由于未检查异常而导致的潜在bug。我认为可以归结为两种情况:

  1. 方法的签名表示它抛出未经检查的异常,而调用方没有捕捉到它
  2. 方法的主体抛出显式未检查的异常,而调用方不捕获它们

共有2个答案

臧威
2023-03-14

有一个Intellij插件可以帮助您发现未经检查的异常。您可以自定义搜索过程,以便在搜索库时包括/排除库。

https://plugins.jetbrains.com/plugin/8157?pr=

陆飞龙
2023-03-14

是的,您可以编写一个静态分析来完成此操作。我自己也做过类似的事情,并且在一个叫做Atlas的程序分析工具中写了我的。这里:https://github.com/ensoftcorp/java-toolbox-commons/.../throwableanalysis.java是一些可能对您的需要有所帮助的代码,它静态地计算一个软件中抛出站点和潜在捕获站点的匹配(保守地说,它不考虑路径可行性)。对于您的情况,您感兴趣的是没有相应的catch块的抛出站点。

以下是分析的重要部分。

  1. 所有选中或未选中的异常都必须扩展Throwable。听起来您只对“未检查”的可抛物感兴趣,因此您应该考虑直接扩展或扩展Error或RuntimeException的类的子类的类。

在Atlas Shell中,您可以编写以下查询来查找所有未经检查的可抛物。

var supertypeEdges = Common.universe().edgesTaggedWithAny(XCSG.Supertype)
var errors = supertypeEdges.reverse(Common.typeSelect("java.lang", "Error"))
var uncheckedExceptions = supertypeEdges.reverse(Common.typeSelect("java.lang", "RuntimeException"))
show(errors.union(uncheckedExceptions))

任何可以在运行时捕获的异常(选中或未选中)都必须有相应的“抛出”站点。虽然抛出的检查异常必须在方法签名中声明,但对于抛出的未检查异常则不需要声明。但是,这并不是很重要,因为我们可以通过查看类型层次结构来检测所有抛出的未检查的异常,就像我们在步骤1中讨论的那样。

要将抛出站点与相应的catch块匹配起来,我们必须记住,抛出的异常会备份调用堆栈,直到它被捕获为止(或者当它没有被主方法或线程入口点捕获时会使程序崩溃)。要进行此分析,您需要一个调用图(调用图越精确,您的分析就越准确)。对于未检查的异常类型的每次抛出,沿着调用图向后返回到可能抛出未检查异常的方法的callsite。检查callsite是否包含在try块中(或者如果您正在分析字节码,则有一个陷阱区域)。如果是,则必须检查捕获块/捕获区域的兼容性,并确定是否将捕获异常。如果未捕获异常,则重复沿着调用图向后执行的过程,直到捕获异常或没有可能的catch块为止。

使用我前面分享的ThrowableAnalysis代码,您可以将其集合起来,找到每个未捕获的、抛出的、未检查的可抛出类型。

public class Analysis {

    // execute show(Analysis.run()) on the Atlas shell
    public static Q run(){
        Q supertypeEdges = Common.universe().edgesTaggedWithAny(XCSG.Supertype);
        Q errors = supertypeEdges.reverse(Common.typeSelect("java.lang", "Error"));
        Q uncheckedExceptions = supertypeEdges.reverse(Common.typeSelect("java.lang", "RuntimeException"));
        Q typeOfEdges = Common.universe().edgesTaggedWithAny(XCSG.TypeOf);
        Q thrownUncheckedThrowables = typeOfEdges.predecessors(errors.union(uncheckedExceptions)).nodesTaggedWithAny(XCSG.ThrownValue);

        AtlasSet<Node> uncaughtThrownUncheckedThrowables = new AtlasHashSet<Node>();
        for(Node thrownUncheckedThrowable : thrownUncheckedThrowables.eval().nodes()){
            if(ThrowableAnalysis.findCatchForThrows(Common.toQ(thrownUncheckedThrowable)).eval().nodes().isEmpty()){
                uncaughtThrownUncheckedThrowables.add(thrownUncheckedThrowable);
            }
        }

        Q uncaughtThrownUncheckedThrowableMethods = Common.toQ(uncaughtThrownUncheckedThrowables).containers().nodesTaggedWithAny(XCSG.Method);
        Q callEdges = Common.universe().edgesTaggedWithAny(XCSG.Call);
        Q rootMethods = callEdges.reverse(uncaughtThrownUncheckedThrowableMethods).roots();
        Q callChainToUncaughtThrowables = callEdges.between(rootMethods, uncaughtThrownUncheckedThrowableMethods);
        return callChainToUncaughtThrowables.union(Common.toQ(uncaughtThrownUncheckedThrowables));
    }

}

下面是在下面的测试用例上运行此代码的结果截图。

public class Test {

    public static void main(String[] args) {
        foo();
    }

    private static void foo(){
        throw new Pig("Pigs can fly!");
    }

    public static class Pig extends RuntimeException {
        public Pig(String message){
            super(message);
        }
    }

}

重要的注意事项:您必须考虑是否在这里进行整个程序分析。如果您只分析您的代码,而不分析完整的JDK(几百万行代码),那么您将只检测来自应用程序内部的未捕获的运行时异常。例如,您不会捕获“test”.substring(0,10),它在JDK中String类中声明的substring方法中抛出一个out of bounds异常。虽然Atlas支持使用Java源码或字节码的部分或全部程序分析,并且可以扩展到完整的JDK,但是如果计划包含完整的JDK,则需要分配大约一个小时的预处理时间和20GB的内存

 类似资料:
  • 问题内容: 我有以下代码行 哪里 但我收到有关未经检查的转化的警告。如何停止此警告? 问题答案: 之所以会这样,是因为getSpecialCharMap()返回的对象的类型不能由编译器验证为HashMap 。继续并提供getSpecialCharMap的原型。

  • 问题内容: 检查URL在Java中是否有效的最佳方法是什么? 如果试着拨打和赶上,但它似乎很乐意与任何开头。 我不关心建立连接,只是有效性。有办法吗?Hibernate Validator中的注释?我应该使用正则表达式吗? 编辑:和 URL的一些示例。 问题答案: 考虑使用Apache Commons UrlValidator类 有几个属性,您可以设置来控制如何类的行为,在默认情况下,和被接受。

  • 问题内容: 如何在不使用try语句的情况下检查文件是否存在? 问题答案: 如果您要检查的原因是可以执行类似的操作if file_exists: open_it(),则try尝试使用a来打开它会更安全。检查然后打开可能会导致文件被删除或移动,或者介于检查和尝试打开文件之间。 如果您不打算立即打开文件,则可以使用 os.path.isfile True如果path是现有的常规文件,则返回。这遵循符号链

  • 问题内容: 测试JavaScript中是否未定义变量的最合适方法是什么?我已经看到了几种可能的方法: 要么 要么 问题答案: 如果您想知道变量是否已声明而无论其值如何,那么使用运算符是最安全的方法。考虑以下示例: 但这在某些情况下可能不是预期的结果,因为已声明但未初始化变量或属性。使用运算符进行更强大的检查。 如果您想知道变量是否尚未声明或具有值,请使用运算符,该运算符可确保返回字符串: 直接比较

  • 问题内容: 我想检查变量是否已定义。例如,以下引发未定义的错误 如何捕获此错误? 问题答案: 在JavaScript中,是一个对象。不存在的事物还有另一个价值,。DOM 在几乎所有无法在文档中找到某些结构的情况下都会返回,但是在JavaScript本身中是所使用的值。 第二,不,没有直接对等的内容。如果您确实要专门检查,请执行以下操作: 如果要检查变量是否存在,那只能用/ 来完成,因为它将未声明的

  • 本文向大家介绍如何在JavaScript中检查未定义的变量?,包括了如何在JavaScript中检查未定义的变量?的使用技巧和注意事项,需要的朋友参考一下 要检查变量是否为“未定义”,您需要使用以下命令进行检查。如果结果为“ false”,则表示未定义变量。在这里,变量结果为“ True”- 示例