作为Java开发,俺想说的是:
!运算符放在那里就是要让人用的。能写 !cond 的地方写成 cond == false 是多此一举的事情。
但确实有些人习惯这样写,这俺也管不着——反正俺的代码里是坚决不会这样写的。
作为从事JVM的JIT编译器研发的人,俺想说的是:
就算生成的字节码形式不一样,如果一个JVM实现有JIT编译器,而那个JIT编译器不能把题主举例的那两种形式优化到完全一样的话,那么那个JIT编译器也是废了。
分析这种问题切记不要被Java字节码的样子给骗了。在带有JIT编译器的JVM实现中,一段代码的性能好坏跟其Java字节码的形态没啥关系,主要还是看JIT编译后的代码质量如何。
Java字节码的形态只会影响程序在解释器里跑的性能。好的JIT编译器生成的代码的性能跟解释器相比可以轻松拉开2-3x甚至10x的差距。所以在这种环境中比性能不要比解释器的性能。
不过——如果两段Java代码在字节码层面看就已经完全一样了的话,那就不用钻进JIT编译器里看了。题主的例子就是如此。
==========================================
具体看例子:
用JDK 1.7.0_45的javac来编译下面的代码:
public class Test {
public static int foo(boolean cond) {
if (!cond) return 123;
return 456;
}
public static int bar(boolean cond) {
if (cond == false) return 123;
return 456;
}
public static int baz(boolean cond) {
if (false == cond) return 123;
return 456;
}
}会得到这样的字节码:
public static int foo(boolean);
Code:
stack=1, locals=1, args_size=1
0: iload_0
1: ifne 7
4: bipush 123
6: ireturn
7: sipush 456
10: ireturn
LineNumberTable:
line 3: 0
line 4: 7
public static int bar(boolean);
Code:
stack=1, locals=1, args_size=1
0: iload_0
1: ifne 7
4: bipush 123
6: ireturn
7: sipush 456
10: ireturn
LineNumberTable:
line 8: 0
line 9: 7foo()和bar()的内容是不是完全一样?问题解决。
不过如果我们看baz()版,也就是把bar()里的实现改为 if (false == cond) 的话,javac就会有点犯傻:
public static int baz(boolean);
Code:
stack=2, locals=1, args_size=1
0: iconst_0 // load constant false
1: iload_0 // load argument 0
2: if_icmpne 8 // are they equal?
5: bipush 123 // yes, load constant 123
7: ireturn // and return it
8: sipush 456 // no, load constant 456
11: ireturn // and return it
LineNumberTable:
line 13: 0
line 14: 8此时不要担心,JIT编译器还是会把它优化到跟前面的写法一样的效果的。
用Oracle JDK 1.7.0_45的Client Compiler(C1)来演示给题主看:
foo():
# {method} 'foo' '(Z)I' in 'Test'
# parm0: rsi = boolean
# [sp+0x40] (sp of caller)
0x000000010eb43220: mov %eax,-0x14000(%rsp)
0x000000010eb43227: push %rbp
0x000000010eb43228: sub $0x30,%rsp ;*iload_0
; - Test::foo@0 (line 3)
0x000000010eb4322c: cmp $0x0,%esi
0x000000010eb4322f: jne 0x000000010eb43246 ;*ifne
; - Test::foo@1 (line 3)
0x000000010eb43235: mov $0x7b,%eax
0x000000010eb4323a: add $0x30,%rsp
0x000000010eb4323e: pop %rbp
0x000000010eb4323f: test %eax,-0x1375145(%rip) # 0x000000010d7ce100
; {poll_return}
0x000000010eb43245: retq ;*ireturn
; - Test::foo@6 (line 3)bar():
# {method} 'bar' '(Z)I' in 'Test'
# parm0: rsi = boolean
# [sp+0x40] (sp of caller)
0x000000010eb43560: mov %eax,-0x14000(%rsp)
0x000000010eb43567: push %rbp
0x000000010eb43568: sub $0x30,%rsp ;*iload_0
; - Test::bar@0 (line 8)
0x000000010eb4356c: cmp $0x0,%esi
0x000000010eb4356f: jne 0x000000010eb43586 ;*ifne
; - Test::bar@1 (line 8)
0x000000010eb43575: mov $0x7b,%eax
0x000000010eb4357a: add $0x30,%rsp
0x000000010eb4357e: pop %rbp
0x000000010eb4357f: test %eax,-0x1375485(%rip) # 0x000000010d7ce100
; {poll_return}
0x000000010eb43585: retq ;*ireturn
; - Test::bar@6 (line 8)baz():
# {method} 'baz' '(Z)I' in 'Test'
# parm0: rsi = boolean
# [sp+0x40] (sp of caller)
0x000000010eb438a0: mov %eax,-0x14000(%rsp)
0x000000010eb438a7: push %rbp
0x000000010eb438a8: sub $0x30,%rsp ;*iconst_0
; - Test::baz@0 (line 13)
0x000000010eb438ac: cmp $0x0,%esi
0x000000010eb438af: jne 0x000000010eb438c6 ;*if_icmpne
; - Test::baz@2 (line 13)
0x000000010eb438b5: mov $0x7b,%eax
0x000000010eb438ba: add $0x30,%rsp
0x000000010eb438be: pop %rbp
0x000000010eb438bf: test %eax,-0x13757c5(%rip) # 0x000000010d7ce100
; {poll_return}
0x000000010eb438c5: retq ;*ireturn
; - Test::baz@7 (line 13)三个版本内容完全一样对不对?
对C、C++、C#之类的类似的语言而言也是如此:这两种写法在一个有合理优化的编译器处理过后性能肯定是一样的。
如果性能不一样请换编译器 ;-)