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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/50378884/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 23:36:00  来源:igfitidea点击:

When catch doesn't actually catch anything

javaexceptiontry-catchcomparator

提问by Bob Stout

I had a program crash because of bad data stored in a database recently. This confused me, because I thought I had a catch to prevent this.

由于最近存储在数据库中的数据错误,我的程序崩溃了。这让我很困惑,因为我认为我有办法防止这种情况发生。

The intent of the following code is to compare employee badge numbers and sort them. If there's an error, return -1 and soldier on -- don't stop because one of several thousand badge numbers is wrong:

以下代码的目的是比较员工徽章编号并对其进行排序。如果出现错误,则返回 -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;
}

When the bad badge number hit (as t in this case), I got an "java.lang.IllegalArgumentException: Comparison method violates its general contract!" error instead of returning the -1 in the catch.

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

What don't I understand about the catch here?

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

The full stacktrace:

完整的堆栈跟踪:

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)

The calling code:

调用代码:

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

采纳答案by rgettman

The exception (whatever it was) wascaught by catch (Exception e). You didn't log this exception, so you don't know what it was. You should log it somehow so you know what really happened.

唯一的例外(不管它是什么)通过抓catch (Exception e)。你没有记录这个异常,所以你不知道它是什么。你应该以某种方式记录它,以便你知道真正发生了什么。

The problem occurs when you return -1. This allows for the possibility of inconsistent ordering, which Java's current sorting algorithm sometimes catches. In short, returning -1on an error means that you are asserting that both a < band b < aare true, because the exception will be caught in both cases. This is logically incorrect. The sorting algorithm detects this and throws the IllegalArgumentException. Note that the comparemethod is notin your stack trace; it's the call to Collections.sort.

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

In addition to logging the exception, handle it before you even get to the comparison step in your program. If you have to parse the string as an integer, do that when creating the Employeeobjects, so that the validation occurs before you even get to the sorting step in your program. A Comparatorshouldn't have to validate data; it should only compare the data.

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

回答by davidxxx

The exception is thrown from TimSort.mergeHi()invoked internally as you explicitly invoked Collections.sort():

TimSort.mergeHi()当您显式调用时,从内部调用引发异常Collections.sort()

at java.util.TimSort.mergeHi(TimSort.java:868)

在 java.util.TimSort.mergeHi(TimSort.java:868)

You could move the catch statement around sort()but as a consequence the sort will not be performed or be no complete. So it seems not to be a good idea.
Long story short : don't violate the compareTo()contract and you would not need to catch any exception that will not happen any longer.

您可以移动 catch 语句,sort()但结果是排序不会执行或不完整。所以这似乎不是一个好主意。
长话短说:不要违反compareTo()合同,您将不需要捕获任何不会再发生的异常。

回答by Zabuzard

Explanation

解释

java.lang.IllegalArgumentException: Comparison method violatesits general contract!

java.lang.IllegalArgumentException:比较方法违反了它的一般约定

The exception is not thrown from within your try. That is why it is not caught. The exception comes from NotifierHTML.java:363in your code where you call Collection#sortwhich uses a TimSortclass. The exception is then thrown from TimSort.java:868by the TimSort#mergeHimethod.

异常不会从您的try. 这就是它不被捕获的原因。异常来自NotifierHTML.java:363您调用Collection#sort使用TimSort类的代码。然后TimSort.java:868TimSort#mergeHi方法抛出异常。

It tells you that your implementation of the Comparator#comparemethod is wrong. It violates the contract, as explained in its documentation:

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

Compares its two arguments for order. Returns a negativeinteger, zero, or a positiveinteger as the first argument is less than, equal to, or greater thanthe second.

The implementor must ensuresgn(x.compareTo(y)) == -sgn(y.compareTo(x))for all xand y. (This implies that x.compareTo(y)must throw an exception iff y.compareTo(x)throws an exception.)

The implementor must also ensurethat the relation is transitive: (x.compareTo(y) > 0 && y.compareTo(z) > 0)implies x.compareTo(z) > 0.

Finally, the implementor must ensurethat x.compareTo(y) == 0implies that sgn(x.compareTo(z)) == sgn(y.compareTo(z)), for all z.

比较它的两个参数的顺序。当第一个参数小于等于大于第二个参数时,返回一个整数、整数。

实现者必须确保sgn(x.compareTo(y)) == -sgn(y.compareTo(x))所有xy. (这意味着x.compareTo(y)如果y.compareTo(x)抛出异常,则必须抛出异常。)

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

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

Your implementation violates one of those requirements and the method detected that.

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



Source of the problem

问题的根源

The problem is that you return -1if an error occurs. Suppose you have two values firstand second. And that at least one of them will provoke the exception.

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

So if you want to compare firstwith second, you get -1:

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

compare(first, second) -> -1

Which means that firstis smallerthan second. But if you compare it the other way you get -1too:

这意味着firstsecond。但是如果你用另一种方式比较它,你-1也会得到:

compare(second, first) -> -1

Because the exception is thrown in both variants, which leads to your return -1;. But this means your comparemethod says:

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

first < second
second < first

Both at the same time, which is logically incorrect and violates the contract.

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



Solution

解决方案

You need to correctly define where in your ordering unparsable content is placed at. For example let us define that it is always smaller than any number. So we want

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

text < number

What do we do if both are unparsable? We could say they are equal, we could compare them lexicographical. Let's keep it simple and say that any two texts are considered equal:

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

text = text

We implement this by checking which of the arguments are unparseable and then returning the correct value:

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

@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);
}

As hinted in the comments you could replace your whole custom Comparatorclass by the following statement which generates the same Comparator:

正如评论中所暗示的,您可以用Comparator以下生成相同比较器的语句替换整个自定义类:

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

Together with a tryParseIntegermethod like this:

连同这样的tryParseInteger方法:

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

回答by Antoniossss

That exception is not thrown in comparemethod you pasted here. Check the stacktrace. There is no comparecall in it.

您在此处粘贴的比较方法中不会引发该异常。检查堆栈跟踪。里面没有compare电话。

回答by Dariusz

While this is not the case, remember that you can throw and catch Throwableinstances, and apart from Exceptions there are Errors. Catching them is possible, though when they occur its unlikely that any further work can be done.

虽然情况并非如此,但请记住,您可以抛出和捕获Throwable实例,除了 Exceptions 之外,还有Errors。抓住它们是可能的,但当它们发生时,就不太可能进行任何进一步的工作。

So your try-catch would not have caught an Error or any Throwable other than Exception.

因此,您的 try-catch 不会捕获错误或除异常以外的任何 Throwable。

public static void main(String[] args) {

    try {
        throw new Error("test exception try-catch");
    } catch (Throwable e) {
        System.out.println("Error caught in throwable catch");
    }

    try {
        throw new Error("test exception try-catch");
    } catch (Exception e) {
        System.out.println("Error caught in exception catch");
    }
}

Which will result in:

这将导致:

Error caught in throwable catch
Exception in thread "main" java.lang.Error: test exception try-catch
    at ...