Java 在循环内部或外部声明变量
为什么下面的工作很好Java 在循环内部或外部声明变量,java,optimization,while-loop,Java,Optimization,While Loop,为什么下面的工作很好 String str; while (condition) { str = calculateStr(); ..... } 但这一条据说是危险的/不正确的: while (condition) { String str = calculateStr(); ..... } 是否有必要在循环外部声明变量?在while循环外部声明字符串str允许在while循环内部和外部引用它。在while循环中声明字符串str只允许在while循环中被引用。
String str;
while (condition) {
str = calculateStr();
.....
}
但这一条据说是危险的/不正确的:
while (condition) {
String str = calculateStr();
.....
}
是否有必要在循环外部声明变量?在
while
循环外部声明字符串str
允许在while
循环内部和外部引用它。在while
循环中声明字符串str
只允许在while
循环中被引用。在内部,变量的可见范围越小越好。如果您还想在looop外部使用str
;在外面申报。否则,第二个版本就可以了。如果不需要在while循环(范围相关)之后使用str
,那么第二个条件就是
while(condition){
String str = calculateStr();
.....
}
更好,因为如果仅当条件
为真时才在堆栈上定义对象。也就是说,如果需要,请使用它。局部变量的范围应始终尽可能小。
在您的示例中,我假定str
不会在while
循环之外使用,否则您不会问这个问题,因为在while
循环内声明它将不是一个选项,因为它不会编译
因此,由于str
未在循环外使用,因此str
的最小可能范围在while循环内
因此,答案是强调str
绝对应该在while循环中声明。没有如果,没有结果,没有但是
此规则可能违反的唯一情况是,如果由于某种原因,每一个时钟周期都必须从代码中挤出,在这种情况下,您可能需要考虑在外部范围内实例化某个对象并重用它,而不是在内部范围的每一次迭代上重新实例化它。但是,这不适用于您的示例,因为java中字符串的不变性:str的新实例总是在循环开始时创建,并且必须在循环结束时丢弃,因此不可能在那里进行优化
编辑:(在下面的答案中插入我的评论)在任何情况下,正确的方法是正确地编写所有代码,为产品建立性能要求,根据此要求衡量最终产品,如果不满足,则进行优化。通常的结果是,你会找到一些方法,在几个地方提供一些好的、正式的算法优化,使我们的程序满足其性能要求,而不必在整个代码库中到处进行调整和修改,以压缩时钟周期。循环内部限制了相应变量的范围。这完全取决于项目对变量范围的要求。我比较了这两个(类似)示例的字节码: 让我们看看1。示例:
package inside;
public class Test {
public static void main(String[] args) {
while(true){
String str = String.valueOf(System.currentTimeMillis());
System.out.println(str);
}
}
}
package outside;
public class Test {
public static void main(String[] args) {
String str;
while(true){
str = String.valueOf(System.currentTimeMillis());
System.out.println(str);
}
}
}
在javac Test.java
之后,javap-c Test
您将得到:
public class inside.Test extends java.lang.Object{
public inside.Test();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: invokestatic #2; //Method java/lang/System.currentTimeMillis:()J
3: invokestatic #3; //Method java/lang/String.valueOf:(J)Ljava/lang/String;
6: astore_1
7: getstatic #4; //Field java/lang/System.out:Ljava/io/PrintStream;
10: aload_1
11: invokevirtual #5; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
14: goto 0
}
public class outside.Test extends java.lang.Object{
public outside.Test();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: invokestatic #2; //Method java/lang/System.currentTimeMillis:()J
3: invokestatic #3; //Method java/lang/String.valueOf:(J)Ljava/lang/String;
6: astore_1
7: getstatic #4; //Field java/lang/System.out:Ljava/io/PrintStream;
10: aload_1
11: invokevirtual #5; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
14: goto 0
}
在javac Test.java
之后,javap-c Test
您将得到:
public class inside.Test extends java.lang.Object{
public inside.Test();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: invokestatic #2; //Method java/lang/System.currentTimeMillis:()J
3: invokestatic #3; //Method java/lang/String.valueOf:(J)Ljava/lang/String;
6: astore_1
7: getstatic #4; //Field java/lang/System.out:Ljava/io/PrintStream;
10: aload_1
11: invokevirtual #5; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
14: goto 0
}
public class outside.Test extends java.lang.Object{
public outside.Test();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: invokestatic #2; //Method java/lang/System.currentTimeMillis:()J
3: invokestatic #3; //Method java/lang/String.valueOf:(J)Ljava/lang/String;
6: astore_1
7: getstatic #4; //Field java/lang/System.out:Ljava/io/PrintStream;
10: aload_1
11: invokevirtual #5; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
14: goto 0
}
外部公共类。Test扩展java.lang.Object{
公共外部测试();
代码:
0:aload_0
1:invokespecial#1;//方法java/lang/Object。“:()V
4:返回
公共静态void main(java.lang.String[]);
代码:
0:invokestatic#2;//方法java/lang/System.currentTimeMillis:()J
3:invokestatic#3;//方法java/lang/String.valueOf:(J)Ljava/lang/String;
6:astore_1
7:getstatic#4;//字段java/lang/System.out:Ljava/io/PrintStream;
10:aload_1
11:invokevirtual#5;//方法java/io/PrintStream.println:(Ljava/lang/String;)V
14:转到0
}
观察结果表明,这两个例子之间没有差异。这是JVM规范的结果
但以最佳编码实践的名义,建议在尽可能小的范围内声明变量(在本例中,变量位于循环内,因为这是唯一使用变量的地方)。确实,上述问题是编程问题。您希望如何编写代码?您需要在哪里访问“STR”?声明作为全局变量在本地使用的变量是没有用的。我相信编程的基础。在最小范围内声明对象可以提高可读性
对于今天的编译器来说,性能并不重要。(在这种情况下)
从维护角度来看,2nd选项更好。
在尽可能狭窄的范围内,在同一位置声明和初始化变量 正如Donald Ervin Knuth所说: “我们应该忘记小效率,比如说97%的时间: 过早优化是万恶之源“
i、 e)程序员让性能考虑因素影响代码设计的情况。这可能会导致设计不干净,或者代码不正确,因为优化会使代码变得复杂,程序员会因优化而分心。这两个示例产生相同的结果。但是,第一个选项为您提供了在while循环之外使用
str
变量的功能;第二个不是。变量的声明应尽可能接近它们的使用位置
这使RAII更容易
它使变量的范围保持紧密。这让优化器工作得更好。正如许多人所指出的
String str;
while(condition){
str = calculateStr();
.....
}
不是比这个好:
while(condition){
String str = calculateStr();
.....
}
因此,如果不重复使用,不要在变量的作用域之外声明变量…根据谷歌Android开发指南,变量的作用域应该是有限的。请检查此链接: <
String str;
while(condition){
str = calculateStr();
.....
}
while(condition){
String str = calculateStr();
.....
}
{
// all tmp loop variables here ....
// ....
String str;
while(condition){
str = calculateStr();
.....
}
}