什么时候应该使用局部变量而不是方法?我使用规则,如果在特定代码块中使用两次方法,则应将其分配给局部变量以减少计算,但我不确定JVM是否不优化它,所以我'我希望得到真正的答案。让我们假设该方法每次都会为给定的上下文返回相同的结果。
例如:
private boolean someMethod() {
return true;
}
private boolean otherMethod() {
if (someMethod()) {
System.out.println(1);
}
// other logic
if (someMethod()) {
System.out.println(2);
}
}
我重构为:
private boolean someMethod() {
return true;
}
private boolean otherMethod() {
boolean localVar = someMethod();
if (localVar) {
System.out.println(1);
}
// other logic
if (localVar) {
System.out.println(2);
}
}
这是正确的方法吗?
答案 0 :(得分:4)
这取决于。如果方法在每次调用时返回不同的结果(例如时间戳),并且每次在otherMethod()
中都需要完全相同的值,则需要将其存储在局部变量中。
另一方面,如果someMethod()
始终返回相同的值,则可以根据需要随时调用该方法。但是,如果someMethod()
进行了一些广泛的计算(就成本而言 - >运行时间或内存使用量),如果应用程序逻辑允许,最好只调用一次方法。
答案 1 :(得分:2)
首先你应该考虑
然后是性能问题。对于这些微不足道的案例,它可能相同或效率较低。即使您使用变量1000次。
编译器可能会翻译if (someMethod())
- > if (true)
如果您只是返回常量或引用(请参阅Method inlining)。
<强>更新强>
仅仅是为了娱乐目的(与自制口袋基准愚弄)(没有像JMH那样严重)。
Inlining$2#execute
有3次调用calledStaticMethod
(因此编译时没有内联)。我必须遗漏一些东西(Eclipse有自己的编译器?选项?误解?)和虚拟机JIT优化器做得非常好。源代码(Inlining.java
)。包含几乎无害的基准类附加
// https://stackoverflow.com/questions/32500628/when-to-use-local-variable-instead-of-method
public class Inlining {
public static int calledStaticMethod() {
return 0;
}
public static void dumpValue(Object o) { // Fool compiler/JVM that value is going somewhere
}
public static void main(String[] args) {
float iterations = Benchmark.iterationsFor10sec(new Benchmark("cacheCall") {
@Override
public void execute(long iterations) {
int i1,i2,i3 = 0;
for (int i = 0; i < iterations; i++) {
int localVar = calledStaticMethod();
i1 = localVar;
i2 = i1 + localVar;
i3 = i2 + localVar;
}
dumpValue((Integer)i3);
}
});
System.out.printf("cacheCall: %10.0f\n", iterations);
iterations = Benchmark.iterationsFor10sec(new Benchmark("staticCall") {
@Override
public void execute(long iterations) {
int i1,i2,i3 = 0;
for (int i = 0; i < iterations; i++) {
i1 = calledStaticMethod();
i2 = i1 + calledStaticMethod();
i3 = i2 + calledStaticMethod();
}
dumpValue((Integer)i3);
}
});
System.out.printf("staticCall: %10.0f\n", iterations);
// borderline for inlining, as instance methods might be overridden.
iterations = Benchmark.iterationsFor10sec(new Benchmark("instanceCall") {
public int calledInstanceMethod() { return calledStaticMethod(); }
@Override
public void execute(long iterations) {
int i1,i2,i3 = 0;
for (int i = 0; i < iterations; i++) {
i1 = calledInstanceMethod();
i2 = i1 + calledInstanceMethod();
i3 = i2 + calledInstanceMethod();
}
dumpValue((Integer)i3);
}
});
System.out.printf("instanceCall: %10.0f\n", iterations);
}
}
abstract class Benchmark {
private String name;
public Benchmark(String s) { name = s; }
public String getName() { return name; }
public abstract void execute(long iterations);
public static float iterationsFor10sec(Benchmark bm) {
long t0 = System.nanoTime();
long ellapsed = 0L;
// Calibration. Run .5-1.0 seconds. Estimate iterations for .1 sec
final long runtimeCalibrate = (long)0.5e9; // half second
long iterations = 1L;
while (ellapsed < runtimeCalibrate) {
bm.execute(iterations);
iterations *= 2;
ellapsed = System.nanoTime() - t0;
}
iterations--; // Actually we executed 2^N - 1.
int innerIterations = (int) ((double)iterations * 1e8 /* nanos/inner */ / ellapsed);
if (innerIterations < 1) { innerIterations = 1; }
// run rest of the time
final long runtimeTotal = (long)1e10;
// outer loop
while (ellapsed < runtimeTotal) {
// delegate benchmark contains inner loop
bm.execute(innerIterations);
iterations += innerIterations;
ellapsed = System.nanoTime() - t0;
}
// execution time might exceed 10 seconds. rectify number of iterations
return (float)iterations * 1e10f /* nanos total */ / (float)ellapsed;
}
}
输出:
cacheCall: 21414115328
staticCall: 21423159296
instanceCall: 21357850624
答案 2 :(得分:1)
如果任何方法要返回相同的值,那么使用变量肯定比使用方法调用更好。
方法调用是一项昂贵的任务。但也要考虑对象大小的成本。 它总是在计算时间和计算量之间踩踏。我认为它不同于实例。
答案 3 :(得分:1)
仅供参考我制作了一个微型基准标记,如下所示:
public class Main {
public static void main(String[] args) {
Method method = new Method();
Variable variable = new Variable();
long startMethod;
long startVariable;
long methodDuration;
long variableDuration;
startMethod = System.currentTimeMillis();
method.someMethod();
methodDuration = System.currentTimeMillis() - startMethod;
startVariable = System.currentTimeMillis();
variable.someMethod();
variableDuration = System.currentTimeMillis() - startVariable;
System.out.println("methodDuration: " + methodDuration);
System.out.println("variableDuration: " + variableDuration);
System.exit(0);
}
}
public class Method {
public void someMethod() {
long x = otherMethod();
long y = otherMethod();
long z = otherMethod();
x--;
y--;
z--;
System.out.println(x);
System.out.println(y);
System.out.println(z);
}
private long otherMethod() {
long result = 0;
for (long i = 1; i < 36854775806L / 2; i++) {
i = i * 2;
i = i / 2;
result = i;
}
return result;
}
}
public class Variable {
public void someMethod() {
long x = otherMethod();
long y = x;
long z = x;
x--;
y--;
z--;
System.out.println(x);
System.out.println(y);
System.out.println(z);
}
private long otherMethod() {
long result = 0;
for (long i = 1; i < 36854775806L / 2; i++) {
i = i * 2;
i = i / 2;
result = i;
}
return result;
}
}
我跑了两次并获得了以下结果:
methodDuration: 116568
variableDuration: 37674
methodDuration: 116657
variableDuration: 37679
所以它更快(三次调用方法一次而不是三次),但我想用标准的小方法我们可以获得更好的性能,几十毫秒甚至更少。我可以接受上面三个答案,不知道我应该选择哪一个。