何时使用局部变量而不是方法?

时间:2015-09-10 11:26:50

标签: java

什么时候应该使用局部变量而不是方法?我使用规则,如果在特定代码块中使用两次方法,则应将其分配给局部变量以减少计算,但我不确定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);
    }
}

这是正确的方法吗?

4 个答案:

答案 0 :(得分:4)

这取决于。如果方法在每次调用时返回不同的结果(例如时间戳),并且每次在otherMethod()中都需要完全相同的值,则需要将其存储在局部变量中。

另一方面,如果someMethod()始终返回相同的值,则可以根据需要随时调用该方法。但是,如果someMethod()进行了一些广泛的计算(就成本而言 - >运行时间或内存使用量),如果应用程序逻辑允许,最好只调用一次方法。

答案 1 :(得分:2)

首先你应该考虑

  • 可读性(哪种方式更容易理解和维护)。
  • 价值一致性。如果您习惯于将值缓存在变量中,并且在某些情况下值会发生变化,那么您可能会遇到很难发现的错误。

然后是性能问题。对于这些微不足道的案例,它可能相同或效率较低。即使您使用变量1000次。

编译器可能会翻译if (someMethod()) - > if (true)如果您只是返回常量或引用(请参阅Method inlining)。

<强>更新

仅仅是为了娱乐目的(与自制口袋基准愚弄)(没有像JMH那样严重)。

  • 表示对于简单情况,创建变量以缓存返回常量的方法的结果没有任何好处。
  • 奇怪的是我检查了生成的字节码(jdk 1.8.0_31),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

所以它更快(三次调用方法一次而不是三次),但我想用标准的小方法我们可以获得更好的性能,几十毫秒甚至更少。我可以接受上面三个答案,不知道我应该选择哪一个。