while和if的区别 java_Java 中while(true)和for(;;)两种循环的比较

背景

最近有接触很多的中间件源码,注意到部分的循环都写成了for(;;),说实话以前也有注意过。在平常自己写业务代码很多死循环用的是while(true),有点沿袭写C语言时候用的while(1)。查找资料后发现这两种在某些情况下是有区别的,下面将详细分析。

验证

通过javap命令查看编译后的指令,来分析两种实现的不同。

源码

//LoopTestFor.java

public class LoopTestFor {

public static void main(String[] args) {

for(;;) {

System.out.println("OK");

}

}

}

//LoopTestWhile.java

public class LoopTestWhile {

public static void main(String[] args) {

while(true) {

System.out.println("OK");

}

}

}

反编译后的结果

$javap -c -v -l LoopTestWhile.class

Classfile /export/code/LoopTestWhile.class

Last modified 2021-2-24; size 445 bytes

MD5 checksum 754de9c534c56e5928411a5f3a804375

Compiled from "LoopTestWhile.java"

public class LoopTestWhile

minor version: 0

major version: 52

flags: ACC_PUBLIC, ACC_SUPER

Constant pool:

#1 = Methodref #6.#16 // java/lang/Object."":()V

#2 = Fieldref #17.#18 // java/lang/System.out:Ljava/io/PrintStream;

#3 = String #19 // OK

#4 = Methodref #20.#21 // java/io/PrintStream.println:(Ljava/lang/String;)V

#5 = Class #22 // LoopTestWhile

#6 = Class #23 // java/lang/Object

#7 = Utf8

#8 = Utf8 ()V

#9 = Utf8 Code

#10 = Utf8 LineNumberTable

#11 = Utf8 main

#12 = Utf8 ([Ljava/lang/String;)V

#13 = Utf8 StackMapTable

#14 = Utf8 SourceFile

#15 = Utf8 LoopTestWhile.java

#16 = NameAndType #7:#8 // "":()V

#17 = Class #24 // java/lang/System

#18 = NameAndType #25:#26 // out:Ljava/io/PrintStream;

#19 = Utf8 OK

#20 = Class #27 // java/io/PrintStream

#21 = NameAndType #28:#29 // println:(Ljava/lang/String;)V

#22 = Utf8 LoopTestWhile

#23 = Utf8 java/lang/Object

#24 = Utf8 java/lang/System

#25 = Utf8 out

#26 = Utf8 Ljava/io/PrintStream;

#27 = Utf8 java/io/PrintStream

#28 = Utf8 println

#29 = Utf8 (Ljava/lang/String;)V

{

public LoopTestWhile();

descriptor: ()V

flags: ACC_PUBLIC

Code:

stack=1, locals=1, args_size=1

0: aload_0

1: invokespecial #1 // Method java/lang/Object."":()V

4: return

LineNumberTable:

line 2: 0

public static void main(java.lang.String[]);

descriptor: ([Ljava/lang/String;)V

flags: ACC_PUBLIC, ACC_STATIC

Code:

stack=2, locals=1, args_size=1

0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;

3: ldc #3 // String OK

5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V

8: goto 0

LineNumberTable:

line 6: 0

StackMapTable: number_of_entries = 1

frame_type = 0 /* same */

}

SourceFile: "LoopTestWhile.java"

$javap -c -v LoopTestFor.class

Classfile /export/code/LoopTestFor.class

Last modified 2021-2-24; size 441 bytes

MD5 checksum 02520f26cdea541889c75365f4504060

Compiled from "LoopTestFor.java"

public class LoopTestFor

minor version: 0

major version: 52

flags: ACC_PUBLIC, ACC_SUPER

Constant pool:

#1 = Methodref #6.#16 // java/lang/Object."":()V

#2 = Fieldref #17.#18 // java/lang/System.out:Ljava/io/PrintStream;

#3 = String #19 // OK

#4 = Methodref #20.#21 // java/io/PrintStream.println:(Ljava/lang/String;)V

#5 = Class #22 // LoopTestFor

#6 = Class #23 // java/lang/Object

#7 = Utf8

#8 = Utf8 ()V

#9 = Utf8 Code

#10 = Utf8 LineNumberTable

#11 = Utf8 main

#12 = Utf8 ([Ljava/lang/String;)V

#13 = Utf8 StackMapTable

#14 = Utf8 SourceFile

#15 = Utf8 LoopTestFor.java

#16 = NameAndType #7:#8 // "":()V

#17 = Class #24 // java/lang/System

#18 = NameAndType #25:#26 // out:Ljava/io/PrintStream;

#19 = Utf8 OK

#20 = Class #27 // java/io/PrintStream

#21 = NameAndType #28:#29 // println:(Ljava/lang/String;)V

#22 = Utf8 LoopTestFor

#23 = Utf8 java/lang/Object

#24 = Utf8 java/lang/System

#25 = Utf8 out

#26 = Utf8 Ljava/io/PrintStream;

#27 = Utf8 java/io/PrintStream

#28 = Utf8 println

#29 = Utf8 (Ljava/lang/String;)V

{

public LoopTestFor();

descriptor: ()V

flags: ACC_PUBLIC

Code:

stack=1, locals=1, args_size=1

0: aload_0

1: invokespecial #1 // Method java/lang/Object."":()V

4: return

LineNumberTable:

line 2: 0

public static void main(java.lang.String[]);

descriptor: ([Ljava/lang/String;)V

flags: ACC_PUBLIC, ACC_STATIC

Code:

stack=2, locals=1, args_size=1

0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;

3: ldc #3 // String OK

5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V

8: goto 0

LineNumberTable:

line 6: 0

StackMapTable: number_of_entries = 1

frame_type = 0 /* same */

}

SourceFile: "LoopTestFor.java"

环境 jdk_1.8.0_201, javac 1.8.0_201

重点在如下部分:

public static void main(java.lang.String[]);

descriptor: ([Ljava/lang/String;)V

flags: ACC_PUBLIC, ACC_STATIC

Code:

stack=2, locals=1, args_size=1

0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;

3: ldc #3 // String OK

5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V

8: goto 0

LineNumberTable:

其中8: goto 0即表示循环,回到第0行代码 0: getstatic。可以看到两种循环最终实现是一致的。这是为什么呢?因为编译器对此进行了优化,所以最终效果是一样的。

结论

可以看到默认在HotSpot下经过优化,两种循环的实现是一致的。但是功能组件可能有跨平台的情况,包括JUC中java.util.concurrent.ConcurrentMap#compute,保持兼容性更强的for(;;)是比较好的选择。根据解释while(true),在没有编译优化的情况下,会认为是一个表达式,而进行一次计算,导致实际操作指令比for(;;)多。当然我们从上也看到,一般场景,二者具体实现是一样的,没有区别。

以上就是本期的内容,以做备忘。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值