当 catch 实际上没有捕获任何东西时
When catch doesn't actually catch anything
由于最近数据库中存储了错误数据,我遇到了程序崩溃。这让我很困惑,因为我认为我有办法防止这种情况发生。
以下代码的目的是比较员工徽章编号并对其进行排序。如果出现错误,return -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: Comparison method violates its general contract!" 错误,而不是 return 捕获中的 -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());
异常(无论是什么)被 捕获到catch (Exception e)
。你没有记录这个异常,所以你不知道它是什么。你应该以某种方式记录它,这样你就知道到底发生了什么。
当您 return -1
时出现问题。这允许排序不一致的可能性,Java 当前的排序算法有时会出现这种情况。简而言之,returning -1
错误意味着您断言 a < b
和 b < a
都为真,因为在这两种情况下都会捕获异常。这在逻辑上是不正确的。排序算法检测到这一点并抛出 IllegalArgumentException
。请注意,compare
方法在您的堆栈跟踪中是 而不是 ;这是对 Collections.sort
.
的调用
除了记录异常之外,在您进入程序中的比较步骤之前处理它。如果您必须将字符串解析为整数,请在创建 Employee
对象时执行此操作,以便在您进入程序中的排序步骤之前进行验证。 Comparator
不必验证数据;它应该只比较数据。
当您显式调用 Collections.sort()
时,内部调用 TimSort.mergeHi()
抛出异常:
at java.util.TimSort.mergeHi(TimSort.java:868)
您可以在 sort()
周围移动 catch 语句,但结果排序将不会执行或不会完成。所以这似乎不是一个好主意。
长话短说:不要违反 compareTo()
合同,您将不需要捕获任何不会再发生的异常。
说明
java.lang.IllegalArgumentException: Comparison method violates its general contract!
异常不是从您的 try
中抛出的。这就是它没有被抓住的原因。异常来自代码中的 NotifierHTML.java:363
,您在其中调用 Collection#sort
,它使用 TimSort
class。然后 TimSort#mergeHi
方法从 TimSort.java:868
抛出异常。
它告诉你,你对Comparator#compare
方法的实现是错误的。它违反了合同,如其 documentation:
中所述
Compares its two arguments for order. Returns a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second.
The implementor must ensure sgn(x.compareTo(y)) == -sgn(y.compareTo(x))
for all x
and y
. (This implies that x.compareTo(y)
must throw an exception iff y.compareTo(x)
throws an exception.)
The implementor must also ensure that the relation is transitive: (x.compareTo(y) > 0 && y.compareTo(z) > 0)
implies x.compareTo(z) > 0
.
Finally, the implementor must ensure that x.compareTo(y) == 0
implies that sgn(x.compareTo(z)) == sgn(y.compareTo(z))
, for all z
.
您的实施违反了其中一项要求,并且该方法检测到这一点。
问题的根源
问题是你return-1
如果出现错误。假设您有两个值 first
和 second
。并且至少其中之一会引发异常。
所以如果你想比较 first
和 second
,你会得到 -1
:
compare(first, second) -> -1
这意味着 first
比 second
小。但如果你用另一种方式比较它,你也会得到 -1
:
compare(second, first) -> -1
因为两个变体都抛出异常,导致你的return -1;
。但这意味着您的 compare
方法说:
first < second
second < first
两者同时出现,逻辑上不正确,违反了约定
解决方案
您需要正确定义订单中不可解析内容的放置位置。例如,让我们定义它总是小于任何数字。所以我们要
text < number
如果两者都无法解析怎么办?我们可以说它们是相等的,我们可以按词典顺序比较它们。让我们保持简单,假设任何两个文本都被认为是相等的:
text = text
我们通过检查哪些参数不可解析然后 returning 正确的值来实现:
@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
class 替换为以下生成相同比较器的语句:
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;
}
}
您在此处粘贴的 compare 方法中不会抛出该异常。检查堆栈跟踪。里面没有compare
调用。
尽管情况并非如此,但请记住,您可以抛出并接住 Throwable instances, and apart from Exceptions there are Errors。抓住它们是可能的,尽管当它们发生时不太可能进行任何进一步的工作。
所以你的 try-catch 不会捕获到 Error 或除 Exception 之外的任何 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");
}
}
这将导致:
Error caught in throwable catch
Exception in thread "main" java.lang.Error: test exception try-catch
at ...
由于最近数据库中存储了错误数据,我遇到了程序崩溃。这让我很困惑,因为我认为我有办法防止这种情况发生。
以下代码的目的是比较员工徽章编号并对其进行排序。如果出现错误,return -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: Comparison method violates its general contract!" 错误,而不是 return 捕获中的 -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());
异常(无论是什么)被 捕获到catch (Exception e)
。你没有记录这个异常,所以你不知道它是什么。你应该以某种方式记录它,这样你就知道到底发生了什么。
当您 return -1
时出现问题。这允许排序不一致的可能性,Java 当前的排序算法有时会出现这种情况。简而言之,returning -1
错误意味着您断言 a < b
和 b < a
都为真,因为在这两种情况下都会捕获异常。这在逻辑上是不正确的。排序算法检测到这一点并抛出 IllegalArgumentException
。请注意,compare
方法在您的堆栈跟踪中是 而不是 ;这是对 Collections.sort
.
除了记录异常之外,在您进入程序中的比较步骤之前处理它。如果您必须将字符串解析为整数,请在创建 Employee
对象时执行此操作,以便在您进入程序中的排序步骤之前进行验证。 Comparator
不必验证数据;它应该只比较数据。
当您显式调用 Collections.sort()
时,内部调用 TimSort.mergeHi()
抛出异常:
at java.util.TimSort.mergeHi(TimSort.java:868)
您可以在 sort()
周围移动 catch 语句,但结果排序将不会执行或不会完成。所以这似乎不是一个好主意。
长话短说:不要违反 compareTo()
合同,您将不需要捕获任何不会再发生的异常。
说明
java.lang.IllegalArgumentException: Comparison method violates its general contract!
异常不是从您的 try
中抛出的。这就是它没有被抓住的原因。异常来自代码中的 NotifierHTML.java:363
,您在其中调用 Collection#sort
,它使用 TimSort
class。然后 TimSort#mergeHi
方法从 TimSort.java:868
抛出异常。
它告诉你,你对Comparator#compare
方法的实现是错误的。它违反了合同,如其 documentation:
Compares its two arguments for order. Returns a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second.
The implementor must ensure
sgn(x.compareTo(y)) == -sgn(y.compareTo(x))
for allx
andy
. (This implies thatx.compareTo(y)
must throw an exception iffy.compareTo(x)
throws an exception.)The implementor must also ensure that the relation is transitive:
(x.compareTo(y) > 0 && y.compareTo(z) > 0)
impliesx.compareTo(z) > 0
.Finally, the implementor must ensure that
x.compareTo(y) == 0
implies thatsgn(x.compareTo(z)) == sgn(y.compareTo(z))
, for allz
.
您的实施违反了其中一项要求,并且该方法检测到这一点。
问题的根源
问题是你return-1
如果出现错误。假设您有两个值 first
和 second
。并且至少其中之一会引发异常。
所以如果你想比较 first
和 second
,你会得到 -1
:
compare(first, second) -> -1
这意味着 first
比 second
小。但如果你用另一种方式比较它,你也会得到 -1
:
compare(second, first) -> -1
因为两个变体都抛出异常,导致你的return -1;
。但这意味着您的 compare
方法说:
first < second
second < first
两者同时出现,逻辑上不正确,违反了约定
解决方案
您需要正确定义订单中不可解析内容的放置位置。例如,让我们定义它总是小于任何数字。所以我们要
text < number
如果两者都无法解析怎么办?我们可以说它们是相等的,我们可以按词典顺序比较它们。让我们保持简单,假设任何两个文本都被认为是相等的:
text = text
我们通过检查哪些参数不可解析然后 returning 正确的值来实现:
@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
class 替换为以下生成相同比较器的语句:
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;
}
}
您在此处粘贴的 compare 方法中不会抛出该异常。检查堆栈跟踪。里面没有compare
调用。
尽管情况并非如此,但请记住,您可以抛出并接住 Throwable instances, and apart from Exceptions there are Errors。抓住它们是可能的,尽管当它们发生时不太可能进行任何进一步的工作。
所以你的 try-catch 不会捕获到 Error 或除 Exception 之外的任何 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");
}
}
这将导致:
Error caught in throwable catch
Exception in thread "main" java.lang.Error: test exception try-catch
at ...