我一直假设每次调用 Java 中的方法时,该方法都会再次执行。我假设返回值不会自动存储,除非我将其存储在变量中。
然后我在 Princeton 的 algs4.BST 类中遇到了这段代码,其中他们每个方法调用了两次:
private boolean check() {
if (!isBST()) StdOut.println("Not in symmetric order");
if (!isSizeConsistent()) StdOut.println("Subtree counts not consistent");
if (!isRankConsistent()) StdOut.println("Ranks not consistent");
return isBST() && isSizeConsistent() && isRankConsistent();
}
他们根本不关心绩效吗?或者编译器是否足够聪明,保留每个方法的第一个返回值以在 return 语句中使用?
抱歉,如果这是重复的,似乎这个答案应该存在,但我在这里或 Java 文档中找不到它。我找到了这些(和其他),但他们没有回答我的问题:
Java 语言规范明确且无条件地声明方法调用表达式的求值涉及执行指定方法的代码。 JLS 8 是这样说的:
在运行时,方法调用需要五个步骤。首先,一个目标 可以计算参考值。其次,参数表达式是 评价。第三,要调用的方法的可访问性是 检查过。第四,该方法实际执行的代码是 位于。五、创建新的激活帧,同步 如有必要,执行,并且控制权转移到方法代码。
(JLS 8, 15.12.4;已添加强调)
因此,无论是否可以预期计算出相同的值,第二次调用一个方法都会产生第二次成本。 可以想象,JIT 编译可以对此进行优化,但是除了是否计算相同的结果之外,还有更多的考虑因素,并且您无论如何都不太可能看到仅两次调用给定方法而触发的任何 JIT 操作。
底线:是的,代码的作者根本不关心性能。 他们可能认为所提供的实现比避免冗余方法调用的实现更清晰,或者他们可能有其他一些个人原因的选择。 这种对实用性的漠视在服务于学术目的的代码中并不罕见,例如本文所介绍的。
该代码中的
check
函数仅在上下文中调用:
assert check();
生产代码中通常不会启用断言,如果未启用断言,则
assert
语句绝对不会执行任何操作。因此 check
函数只会在调试运行中运行。这一事实并不意味着可以任意低效,但通常不尝试优化此类代码。作为断言运行的代码的要点是,在验证不变量、前置条件或后置条件时,它显然是无可争议的正确性,而优化——即使是像将结果保存在局部变量中这样的琐碎优化——也无助于实现该目标。
断言是包含布尔表达式的断言语句。断言要么被启用,要么被禁用。如果启用断言,则断言的执行会导致布尔表达式的计算,如果表达式的计算结果为 false,则会报告错误。如果断言被禁用,则断言的执行没有任何效果。