当捕获实际上没有捕获任何东西时

2022-08-31 19:40:50

由于最近数据库中存储的数据不正确,我遇到了程序崩溃。这让我感到困惑,因为我认为我有一个陷阱来防止这种情况。

以下代码的目的是比较员工徽章编号并对其进行排序。如果出现错误,请返回 -1 并打开士兵 -- 不要因为几千个徽章编号中的一个错误而停止:

public int compare(Employee t, Employee t1) {
    Integer returnValue = -1;
    try {
        Integer tb = Integer.parseInt(t.getBadgeNumber());
        Integer t1b = Integer.parseInt(t1.getBadgeNumber());
        returnValue = tb.compareTo(t1b);
    } catch (Exception e) {
        returnValue = -1;//useless statement, I know.
    }
    return returnValue;
}

当错误的徽章编号命中时(在本例中为t),我得到了一个“java.lang.IllegalArgumentException:比较方法违反了其总协定!”错误,而不是在捕获中返回-1。

我对这里的捕获有什么不明白的?

完整的堆栈跟踪:

16-May-2018 14:28:53.496 SEVERE [http-nio-8084-exec-601] org.apache.catalina.core.StandardWrapperValve.invoke Servlet.service() for servlet [RequestServlet] in context with path [/AppearanceRequest] threw exception
 java.lang.IllegalArgumentException: Comparison method violates its general contract!
at java.util.TimSort.mergeHi(TimSort.java:868)
at java.util.TimSort.mergeAt(TimSort.java:485)
at java.util.TimSort.mergeForceCollapse(TimSort.java:426)
at java.util.TimSort.sort(TimSort.java:223)
at java.util.TimSort.sort(TimSort.java:173)
at java.util.Arrays.sort(Arrays.java:659)
at java.util.Collections.sort(Collections.java:217)
at org.bcso.com.appearancerequest.html.NotifierHTML.getHTML(NotifierHTML.java:363)
at org.bcso.com.appearancerequest.AppearanceRequestServlet.processRequest(AppearanceRequestServlet.java:96)
at org.bcso.com.appearancerequest.AppearanceRequestServlet.doGet(AppearanceRequestServlet.java:565)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:618)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:725)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:301)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.netbeans.modules.web.monitor.server.MonitorFilter.doFilter(MonitorFilter.java:393)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:503)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:136)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:74)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:610)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:516)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1015)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:652)
at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:222)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1575)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1533)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)

调用代码:

    List<Employee> employeeList = DatabaseUtil.getEmployees();
    Collections.sort(employeeList, new BadgeComparator());

答案 1

异常(无论它是什么) 捕获。您没有记录此异常,因此您不知道它是什么。你应该以某种方式记录它,以便你知道到底发生了什么。catch (Exception e)

返回 时出现问题。这允许不一致的排序的可能性,Java当前的排序算法有时会捕获。简而言之,在错误时返回意味着您断言两者都是正确的,因为在这两种情况下都会捕获异常。这在逻辑上是不正确的。排序算法检测到此情况并抛出 .请注意,该方法在您的堆栈跟踪中;这是对 .-1-1a < bb < aIllegalArgumentExceptioncompareCollections.sort

除了记录异常之外,还要在进入程序中的比较步骤之前处理它。如果必须将字符串解析为整数,请在创建对象时执行此操作,以便在进入程序中的排序步骤之前进行验证。A 不应该必须验证数据;它应该只比较数据。EmployeeComparator


答案 2

解释

java.lang.IllegalArgumentException:比较方法违反了其总合同

不会从 中引发异常。这就是为什么它没有被抓住的原因。该异常来自您调用的代码中使用类。然后,该方法从中引发异常。tryNotifierHTML.java:363Collection#sortTimSortTimSort.java:868TimSort#mergeHi

它告诉您该方法的实现是错误的。它违反了合同,如其文档中所述:Comparator#compare

比较其两个参数的顺序。返回整数、整数,因为第一个参数小于等于大于第二个参数。

实现者必须确保所有人和 。(这意味着必须引发异常,否则会引发异常。sgn(x.compareTo(y)) == -sgn(y.compareTo(x))xyx.compareTo(y)y.compareTo(x)

实现者还必须确保关系是可传递的:implies 。(x.compareTo(y) > 0 && y.compareTo(z) > 0)x.compareTo(z) > 0

最后,实现者必须确保 ,对于所有 。x.compareTo(y) == 0sgn(x.compareTo(z)) == sgn(y.compareTo(z))z

您的实现违反了其中一个要求,并且检测到该方法。


问题的根源

问题是如果发生错误,您将返回。假设您有两个值和 .而且其中至少有一个会引起异常。-1firstsecond

所以如果你想比较,你会得到:firstsecond-1

compare(first, second) -> -1

这意味着 它小于 。但是,如果您以另一种方式进行比较,您也会得到:firstsecond-1

compare(second, first) -> -1

因为异常在两个变体中都引发,这会导致 .但这意味着您的方法说:return -1;compare

first < second
second < first

两者同时存在,这在逻辑上是不正确的,并且违反了合同。


溶液

您需要正确定义不可解析内容在订购中的位置。例如,让我们定义它总是小于任何数字。所以我们想要

text < number

如果两者都不可解析,我们该怎么办?我们可以说它们是相等的,我们可以在字典上比较它们。让我们保持简单,并说任何两个文本都被认为是相等的:

text = text

我们通过检查哪些参数是不可解析的,然后返回正确的值来实现这一点:

@Override
public int compare(Employee first, Employee second) {
    Integer firstValue;
    Integer secondValue;
    try {
        firstValue = Integer.parseInt(first.getBadgeNumber());
    } catch (NumberFormatException e) {
        // Could not parse, set null as indicator
        firstValue = null;
    }
    try {
        secondValue = Integer.parseInt(second.getBadgeNumber());
    } catch (NumberFormatException e) {
        // Could not parse, set null as indicator
        secondValue = null;
    }

    if (firstValue == null && secondValue != null) {
        // text < number
        return -1;
    }
    if (firstValue != null && secondValue == null) {
        // number > text
        return 1;
    }
    if (firstValue == null && secondValue == null) {
        // text = text
        return 0;
    }

    // Both are numbers
    return Integer.compare(firstValue, secondValue);
}

如注释中所述,您可以将整个自定义类替换为以下语句,该语句将生成相同的比较器:Comparator

Comparator<Employee> comp = Comparator.nullsLast(
    Comparator.comparing(e -> tryParseInteger(e.getBadgeNumber())));

结合使用如下方法:tryParseInteger

public static Integer tryParseInteger(String text) {
    try {
        return Integer.parseInt(text);
    } catch (NumberFormatException e) {
        return null;
    }
}