关于java字符串编译优化问题

情景一:不好的字符串拼接习惯   

起因是这样的:一个大牛在写了一篇关于java字符串优化问题的讲解,他提到:不要使用strObj+otherValue的方法将otherValue转换为字符串形式,因为底层操作会让你吓一跳的。那么底层的实质是怎么样的呢?他的意思是这样的:

 比如:    String s = "I have";

int  total = 12;

Dog dog = new Dog();     //假设Dog类重写了toString方法

String msg = s +  total+dog;

在运行时,msg的赋值语句会执行为: msg = new StringBuilder().append(s).append(total).append(dog.toString()).toString();

我们发现,运行时创建了一个匿名的StringBuilder对象,来拼接字符串,存储到缓冲区,最后调用toString方法返回拼接后的结果。显然,StringBuilder在这里小材大用了,而且消耗了内存资源,不可取。我发现很多人都喜欢写类似    ""+num的代码,我也是。所以以后需要注意了,特别是性能敏感的环境下。应该改用String.valueOf(value),或者包装类型的toString方法,比如Double.toString(value)。

 如果需要对字符串进行频繁的修改操作,那就使用StringBuilder,或者StringBuffer(线程安全的),从而避免大量的中间字符串垃圾碎片。

情景二:为什么一个是true,一个是false

一位网友提到了这样一个问题:

String str = "abc";
String str1 = "ab" + "c";

String str2 = "ab";
String str3 = str2 + "c";  //试试运行这段代码,就会发现第一个为true 第二个为false ,why?

下面是我的解释:

这是因为javac的编译优化造成的。str1是由2个字符串常量拼接的,常量一定是不会改变的量,那么在编译阶段,javac就有胆量将它给简化一下:str1就优化为:str1="abc",又因为str也是"abc",所以str和str1共享了内存据“abc”,所以str和str1都指向"abc"。由于==是浅比较,比较的是字符串的内存地址,所以他们相等。

而str3的拼接中包含了一个变量str2,而变量是在运行时动态确定的,所以javac不敢再编译时对它优化。也就是说,str3会在运行时在堆中new出字符串对象"abc",显然它的地址和前面的"abc"的地址不一样,所以是false。

我对上面的程序导出jar后进行反编译,验证了我的猜想,结果如图:

关于java字符串编译优化问题

为了进一步证实我的猜想,我把上面的代码改为:

String str = "abc"; 

final String str2 = "ab";
String str3 = str2 + "c"; System.out.println(str == str3); //打印出true

那么问题来了,不是说str2是变量的吗。怎么这次也打印是true呢?注意我给str2加上了修饰符final,声明str2位常量,那么同样的道理,javac也能保证str2不会改变,于是把str3优化为:str3="abc"。

反编译class文件截图:

关于java字符串编译优化问题

注意:不要使用 == 去比较字符串,因为字符串时引用类型,== 比较的是内存地址,而不是字符串内容。请使用equals() 或者equalsIgnoreCase()。

只有字符串常量是共享内存的,而字符串变量或者运行时动态生成的字符串,则非如此。

上一篇:PHP 文件与目录操作函数总结


下一篇:设计模式(二) 策略模式Strategy