我编码如下,
for(int i=0 ; i<n; i++){
String a = someObject.getFirstName(); //may not come same value..
doManipulationon(a);
}
在代码审查期间,人们要求我删除字符串文字a
并someObject.getFirstName()
直接用于操作。他们担心每次迭代都会在堆中创建字符串对象。
这是正确的方法吗?
我认为将 getter 分配给字符串变量会使代码更具可读性。
首先,这里没有字符串文字。
字符串文字是用双引号括起来的字符串表达式,例如“dog”。
您的审阅者表示他们不喜欢a
用于保存您正在下一行操作的表达式的临时变量。
显然他们想让你说
doManipulationon(someObject.getFirstName());
删除临时变量会导致代码更紧凑。这通常是一个好主意,但并非总是如此。当您的“中间表达式”具有有趣的含义时,您可以使用临时变量,但在您的情况下,使用该名称a
没有帮助。你可以说
String firstname = someObejct.getFirstName();
特别是在长表达式中,将部分结果分配给具有有意义名称的变量确实增加了可读性。
哦,关于他们在您的示例中关于将新字符串对象添加到堆中的评论 --- 不,不会。
您是否在问是否可以将该循环编写为:
for(int i=0; i < n; i++) {
doManipulation( someObject.getFirstName() );
}
? 如果是这样,肯定是的。但是,a
没有字符串文字——它只是一个变量名。使用它只会将底层字符串(在堆上)的引用推送到堆栈上——不管你有没有变量a
都会发生这种情况(也就是说,它会发生在我的代码版本中)也写了)。即使没有,将引用推送到堆栈上也很便宜,这并不重要。但无论哪种方式,字符串都将位于堆上,因为字符串是对象,而对象位于堆上。
所以它只是风格,如果你发现将 String 分配给一个变量更具可读性,那么一定要这样做。它还可以使调试更容易,因为您可以在该doManipulation
行放置一个断点并在进入该函数之前查看输入。(即使没有分配,你也可以这样做,但它稍微不那么方便。)
他们担心每次迭代都会在堆中创建字符串对象。
如果那是他们真正关心的,他们不知道他们在说什么。当且仅当 创建一个新字符串时,才会在堆上someObject.getFirstName()
创建一个新字符串。创建一个临时变量来保存一个引用不会创建一个新的字符串。
临时变量花费一个堆栈槽。所有堆栈槽都在方法开始时分配,而不是在使用时分配。所以临时变量的空间成本是4-8个字节;时间成本是一次存储和一次加载;并且堆成本为零。
您可以使用以下命令来检查字节码:
>javap -c -classpath /path/to/your/class/file classname
不同之处在于您的代码有两个字节码指令:
astore_2 // store a reference into local variable 2
aload_2 // load a reference onto the stack from local variable 2
所以,你的代码没问题,它的作用并不像其他人说的那样
“他们担心每次迭代都会在堆中创建字符串对象。”