返回变量和返回计算值的区别
Difference between returning a variable and returning the value of a calculation
在 Java 中创建一个 return 值的方法时,我总是可以 return 计算值或保存该值的变量。例如:
public int sum(int a, int b)
{
return a+b;
}
并且:
public int sum(int a, int b)
{
int res = a + b;
return res;
}
我的问题是,除了可读性之外,第一种方法和第二种方法有什么区别?
return
语句是如何工作的?它是否在 "returning" 之前创建一个变量到调用该方法的位置?
就节省内存而言,哪个版本最好?
我只是举了一个简单的例子,但这个问题代表了每一种 returning 方法。
I can always return a calculation or a variable that holds said calculation.
错了。您 return 是一个值,而不是计算。
除了你的措辞,没有什么不同。 Java 编译器可能会在这两种情况下生成相同的字节码,因为它会考虑 res
一个中间占位符以进行优化。
要了解 Java 的 return
语句如何工作,您必须查看 Java 字节码 ABI 的工作方式。您会注意到 JVM 是一个堆栈机器 - 因此,return 值将在函数调用结束时简单地位于堆栈顶部,如果我没记错的话,无论您之前做什么。
请注意,尽管有进一步的编译时优化 - 没有说 Javac 可能不是 "inline" 如果它没有副作用的方法调用。但是一旦我们谈论 "smart" 优化,询问特定语言概念如何工作可能是一个有争议的问题,因为它们可能会被优化掉。
第一个示例更具可读性,并且出现维护错误的可能性更小,因为您在 return 语句之前没有一些可能被意外破坏的变量。
在方法的数据类型、'res' 变量的数据类型和正在计算的表达式的数据类型之间还可能引入数据类型化问题。这在您的示例中不是问题,但是出于您所证明的其他原因引入该中间变量本质上比仅将表达式放在 return 语句中更具风险。
简答:它们是一样的,只有第一个 (IMO) 更具可读性。
更长的答案:
处理器对变量一无所知(最终由 JVM 编译的代码也会在处理器上 运行),它们只知道寄存器。当您 return 一个值时,该值必须存储在寄存器中(我们称该寄存器为 return)。所以,编译器不关心你是否给你的 return 值一个名字,它所知道的是为了 return 你的计算值它必须把它放入寄存器 return 这就是将要发生的事情。 a+b 将被计算并存储在寄存器中。
只有对于非常非常愚蠢的编译器,内存使用或速度才会有任何差异:可以想到将第二个示例存储在两个寄存器中的编译器(首先将结果计算为一个,然后复制结果进入 return 寄存器),而第一个只会在寄存器上使用,但实际上并没有发生 - 所有编译器都足够聪明,可以将结果直接存储在 return注册.
JVM 是一个堆栈机器,return 值总是在堆栈上。如果你编译这两个函数,你会发现生成的代码确实不同:
public int withVar(int a, int b);
Code:
0: iload_1 (int a)
1: iload_2 (int b)
2: iadd
3: istore_3 (int res)
4: iload_3 (int res)
5: ireturn
public int withoutVar(int a, int b);
Code:
0: iload_1 (int a)
1: iload_2 (int b)
2: iadd
3: ireturn
可以看出,较长的版本实际上填充了第三个局部变量槽 (istore_3
)。但是,由于从未使用过该值,因此任何理智的 JIT 都会快速优化多余的操作。
所以为了速度:别费心了。关于可读性?这取决于......我发现了像
这样的成语
public void func() {
final int res;
// do something
return res;
}
有时非常方便。
- 编译器会检查
res
的单个赋值,因此只有一个 return,您就可以保证代码中没有 fallthrough 路径。
- 在
return res
上设置断点并在命名变量中设置待 return 值通常非常方便。
不过,我不会为了单程而陷入困境。
在 Java 中创建一个 return 值的方法时,我总是可以 return 计算值或保存该值的变量。例如:
public int sum(int a, int b)
{
return a+b;
}
并且:
public int sum(int a, int b)
{
int res = a + b;
return res;
}
我的问题是,除了可读性之外,第一种方法和第二种方法有什么区别?
return
语句是如何工作的?它是否在 "returning" 之前创建一个变量到调用该方法的位置?
就节省内存而言,哪个版本最好? 我只是举了一个简单的例子,但这个问题代表了每一种 returning 方法。
I can always return a calculation or a variable that holds said calculation.
错了。您 return 是一个值,而不是计算。
除了你的措辞,没有什么不同。 Java 编译器可能会在这两种情况下生成相同的字节码,因为它会考虑 res
一个中间占位符以进行优化。
要了解 Java 的 return
语句如何工作,您必须查看 Java 字节码 ABI 的工作方式。您会注意到 JVM 是一个堆栈机器 - 因此,return 值将在函数调用结束时简单地位于堆栈顶部,如果我没记错的话,无论您之前做什么。
请注意,尽管有进一步的编译时优化 - 没有说 Javac 可能不是 "inline" 如果它没有副作用的方法调用。但是一旦我们谈论 "smart" 优化,询问特定语言概念如何工作可能是一个有争议的问题,因为它们可能会被优化掉。
第一个示例更具可读性,并且出现维护错误的可能性更小,因为您在 return 语句之前没有一些可能被意外破坏的变量。
在方法的数据类型、'res' 变量的数据类型和正在计算的表达式的数据类型之间还可能引入数据类型化问题。这在您的示例中不是问题,但是出于您所证明的其他原因引入该中间变量本质上比仅将表达式放在 return 语句中更具风险。
简答:它们是一样的,只有第一个 (IMO) 更具可读性。
更长的答案: 处理器对变量一无所知(最终由 JVM 编译的代码也会在处理器上 运行),它们只知道寄存器。当您 return 一个值时,该值必须存储在寄存器中(我们称该寄存器为 return)。所以,编译器不关心你是否给你的 return 值一个名字,它所知道的是为了 return 你的计算值它必须把它放入寄存器 return 这就是将要发生的事情。 a+b 将被计算并存储在寄存器中。
只有对于非常非常愚蠢的编译器,内存使用或速度才会有任何差异:可以想到将第二个示例存储在两个寄存器中的编译器(首先将结果计算为一个,然后复制结果进入 return 寄存器),而第一个只会在寄存器上使用,但实际上并没有发生 - 所有编译器都足够聪明,可以将结果直接存储在 return注册.
JVM 是一个堆栈机器,return 值总是在堆栈上。如果你编译这两个函数,你会发现生成的代码确实不同:
public int withVar(int a, int b);
Code:
0: iload_1 (int a)
1: iload_2 (int b)
2: iadd
3: istore_3 (int res)
4: iload_3 (int res)
5: ireturn
public int withoutVar(int a, int b);
Code:
0: iload_1 (int a)
1: iload_2 (int b)
2: iadd
3: ireturn
可以看出,较长的版本实际上填充了第三个局部变量槽 (istore_3
)。但是,由于从未使用过该值,因此任何理智的 JIT 都会快速优化多余的操作。
所以为了速度:别费心了。关于可读性?这取决于......我发现了像
这样的成语public void func() {
final int res;
// do something
return res;
}
有时非常方便。
- 编译器会检查
res
的单个赋值,因此只有一个 return,您就可以保证代码中没有 fallthrough 路径。 - 在
return res
上设置断点并在命名变量中设置待 return 值通常非常方便。
不过,我不会为了单程而陷入困境。