返回语句之前的局部变量,这有关系吗?

SJ1*_*J19 21 java memory string int helper

很抱歉,如果这是一个新手问题,但我找不到答案.这样做更好:

int result = number/number2;
return result;
Run Code Online (Sandbox Code Playgroud)

要么:

return number/number2;
Run Code Online (Sandbox Code Playgroud)

我知道整数使用内存所以我猜它会略微降低性能?但另一方面它使事情变得更清楚,特别是当int/string是一个很长的计算时.

Fre*_*ula 22

实际上有一个从PMD继承的SonarQube规则称为Unnecessary Local Before Return,它讨论了这一点.它说:

避免不必要地创建局部变量.

此规则后来被 SSLR规则替换.不应声明变量,然后立即返回或抛出,保持相同的位置:

声明一个变量只是为了立即返回或抛出它是一种不好的做法.一些开发人员认为这种做法提高了代码的可读性,因为它使他们能够明确地命名返回的内容.但是,此变量是一个内部实现细节,不会向方法的调用者公开.方法名称应足以让调用者准确知道将返回的内容.

我完全同意这一点.

IntelliJ(或至少Android Studio)也有针对这种情况的警告:

变量仅用于以下返回,可以内联

此检查报告仅在下一次返回时使用的局部变量或其他变量的精确副本.在这两种情况下,最好内联这样的变量.


在这种情况下,我认为表演根本不用担心.话虽如此,正如@Clashsoft在他的评论中所提到的,JIT很可能会内联变量,你最终会得到相同的结果.

  • 我实际上不同意SSLR规则.这通常会使方法更容易调试,因为在从方法返回之前调试器中有一个步骤,可以观察到结果.它的成本是堆栈上的局部变量....基本上可以免费清理. (32认同)
  • 在使用这里提供的解决方案http://stackoverflow.com/questions/5010362/can-i-find-out-the-return-value-before-returning-while-debugging-in-intellij之后,实际上非常清楚复杂表达式的返回值调试并不是很好用.因此,分配返回变量可以使调试变得更加容易.特别是如果您必须从多个返回链中追踪某些内容. (12认同)
  • 你不一定需要在`return`语句之前执行一个步骤,以便在调试时返回它之前检查它.我认为拥有一个更简洁的代码而不是更容易调试代码更为重要.否则我们总会有很多局部变量来帮助我们调试每个操作结果. (5认同)
  • 同意调试的好处。另一个好处是命名返回值。如果遵循 demeter 法则(不深入到对象中)并使用清晰的变量名称,程序员应该能够从上下文(包含方法名称、方法名称或要返回其结果的操作)中得出想法,但这并不总是正确的,因为跨上下文的变量命名很困难。将“Long.parseLong(price.getTotalValue())”放入像“finalCostAfterAdjusting”这样的变量中可以为程序员添加一些有用的上下文。 (3认同)

Zho*_*gYu 7

选择认为更具可读性的版本.

存在命名变量提高可读性的合法情况.例如

public String encrypt(String plainString)
{
    byte[] plainBytes      = plainString.getBytes(StandardCharsets.UTF_8);
    byte[] hashPlainBytes  = enhash( plainBytes, 4 );
    byte[] encryptedBytes  = doAes128(Cipher.ENCRYPT_MODE , hashPlainBytes );
    String encryptedBase64 = Base64.getEncoder().withoutPadding().encodeToString(encryptedBytes);
    return encryptedBase64;
}

public String decrypt(String encryptedBase64)
{
    byte[] encryptedBytes = Base64.getDecoder().decode(encryptedBase64);
    byte[] hashPlainBytes = doAes128(Cipher.DECRYPT_MODE , encryptedBytes );
    byte[] plainBytes     = dehash( hashPlainBytes, 4 );
    String plainString = new String(plainBytes, StandardCharsets.UTF_8);
    return plainString;
}
Run Code Online (Sandbox Code Playgroud)

在某些情况下,我们需要一个与返回类型不同的变量.这会影响类型转换和推理,从而产生显着的语义差异.

Foo foo()            vs.        Foo foo()
{                               {
                                    Bar bar = expr;
    return expr;                    return bar;
}                               }
Run Code Online (Sandbox Code Playgroud)


Vla*_*lad 5

编译器通常足够聪明,可以适当地优化这种事情.请参阅Wikipedia上的数据流优化.

在这种情况下,即使您没有自己指定结果,也可能需要分配一个临时变量来存储结果.

编辑:Clashsoft对字节码编译器是正确的:

$ cat a.java
class a {
   public static int a(int x, int y) {
     return x / y;
   }

   public static int b(int x, int y) {
     int r = x/y;
     return r;
   }

   public static int c(int x, int y) {
     final int r = x/y;
     return r;
   }
}
$ javap -c a
Compiled from "a.java"
class a {
  a();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static int a(int, int);
    Code:
       0: iload_0
       1: iload_1
       2: idiv
       3: ireturn

  public static int b(int, int);
    Code:
       0: iload_0
       1: iload_1
       2: idiv
       3: istore_2
       4: iload_2
       5: ireturn

  public static int c(int, int);
    Code:
       0: iload_0
       1: iload_1
       2: idiv
       3: istore_2
       4: iload_2
       5: ireturn
}
Run Code Online (Sandbox Code Playgroud)

  • Java 字节码编译器本身可能不会捕获这个,除非你将变量声明为“final”,但我非常怀疑 JIT 编译器不会内联该变量。 (2认同)