用Javap分析Java编译器对string常量表达式的处理和(2)

下面进一步探讨,什么样的string + 表达式会被编译器当成常量表达式?
String b = "a" + "b";
这个String + String被正式是ok的,那么string + 基本类型呢?

String a = "a1";
String b = "a" + 1;
System.out.println((a == b));  //result = true

String a = "atrue";
String b = "a" + true;
System.out.println((a == b));  //result = true

String a = "a3.4";
String b = "a" + 3.4;
System.out.println((a == b));  //result = true
  
可见编译器对string + 基本类型是当成常量表达式直接求值来优化的。

事实的真假还是用Javap看看:

0:   ldc     #16; //String a1

2:   astore_1

3:   ldc     #16; //String a1

5:   astore_2

6:   getstatic       #18; //Field java/lang/System.out:Ljava/io/PrintStream;

9:   aload_1

10:  aload_2

11:  if_acmpne       18

14:  iconst_1

15:  goto    19

18:  iconst_0

19:  invokevirtual   #24; //Method java/io/PrintStream.println:(Z)V

22:  return

看看  0:   ldc     #16; //String a1和   0:   ldc     #16; //String a1,知道作者的分时正确的。

接着作者又分析了下 string+string对象,作者分析如下:

String a = "ab";
String bb = "b";
String b = "a" + bb;
System.out.println((a == b));   //result = false
这个好理解,"a" + bb中的bb是变量,不能进行优化。这里很很好的解释了为什么3的观点不正确,如果String+String的操作是在运行时进行的,则会产生新的对象,而不是直接从jvm的string池中获取。

还是javap看看代码:

0:   ldc     #16; //String ab

2:   astore_1

3:   ldc     #18; //String b

5:   astore_2

6:   new     #20; //class java/lang/StringBuilder

9:   dup

10:  ldc     #22; //String a

12:  invokespecial   #24; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V

15:  aload_2

16:  invokevirtual   #27; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;

19:  invokevirtual   #31; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;

22:  astore_3

23:  getstatic       #35; //Field java/lang/System.out:Ljava/io/PrintStream;

26:  aload_1

27:  aload_3

28:  if_acmpne       35

31:  iconst_1

32:  goto    36

35:  iconst_0

36:  invokevirtual   #41; //Method java/io/PrintStream.println:(Z)V

39:  return

来看看   0:   ldc     #16; //String ab 说明 变量a直接从常量池中取出ab字符串,在看看

3:   ldc     #18; //String b

5:   astore_2

6:   new     #20; //class java/lang/StringBuilder

9:   dup

10:  ldc     #22; //String a

可以知道变量b是通过new出一个StringBuilder来赋值给变量b的。这进一步证明了作者的说法。

最后看看作者分析的最后一种情况,原文:

把bb作为常量变量:
String a = "ab";
final String bb = "b";
String b = "a" + bb;
System.out.println((a == b));   //result = true
竟然又是true,编译器的优化好厉害啊,呵呵,考虑下面这种情况:
String a = "ab";
final String bb = getBB();
String b = "a" + bb;
System.out.println((a == b));    //result = false
private static String getBB() {
return "b";
}

先看看常量变量的javap的结果:

0:   ldc     #16; //String ab

2:   astore_1

3:   ldc     #18; //String b

5:   astore_2

6:   ldc     #16; //String ab

8:   astore_3

9:   getstatic       #20; //Field java/lang/System.out:Ljava/io/PrintStream;

12:  aload_1

13:  aload_3

14:  if_acmpne       21

17:  iconst_1

18:  goto    22

21:  iconst_0

22:  invokevirtual   #26; //Method java/io/PrintStream.println:(Z)V

25:  return

看看 0:   ldc     #16; //String ab  和    6:   ldc     #16; //String ab 通过优化后变量a,b是指向相同的常量空间。

再用javap分析下通过静态方法获得的:

0:   ldc     #16; //String ab

2:   astore_1

3:   invokestatic    #18; //Method getBB:()Ljava/lang/String;

6:   astore_2

7:   new     #22; //class java/lang/StringBuilder

10:  dup

11:  ldc     #24; //String a

13:  invokespecial   #26; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V

16:  aload_2

17:  invokevirtual   #29; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;

20:  invokevirtual   #33; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;

23:  astore_3

24:  getstatic       #36; //Field java/lang/System.out:Ljava/io/PrintStream;

27:  aload_1

28:  aload_3

29:  if_acmpne       36

32:  iconst_1

33:  goto    37

36:  iconst_0

37:  invokevirtual   #42; //Method java/io/PrintStream.println:(Z)V

40:  return

分析:0:   ldc     #16; //String ab 变量a的值直接指向常量池中的‘ab’,

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wwsydd.html