字符串不加 final 关键字拼接的情况
String s1 = "str";
String s2 = "ing";
String s3 = "str" + "ing";
String s4 = s1 + s2;
String s5 = "string";
System.out.println(s3 == s4);//false
System.out.println(s3 == s5);//true
System.out.println(s4 == s5);//false
注意:比较 String 字符串的值是否相等,可以使用 equals() 方法。 String 中的 equals 方法是被重写过的。 Object 的 equals 方法是比较的对象的内存地址,而 String 的 equals 方法比较的是字符串的值是否相等。
对于编译期可以确定值的字符串(常量字符串),jvm 会将其存入字符串常量池。并且,拼接得到的字符串常量在编译阶段就已经被存放到字符串常量池中,这个得益于编译器的优化。
在编译过程中,Javac 编译器会进行一个叫做 常量折叠(Constant Folding) 的代码优化。
常量折叠会把常量表达式的值求出来作为常量嵌在最终生成的代码中,这是 Javac 编译器会对源代码做的极少量优化措施之一。
对于 String s3 = "str" + "ing"
; 编译器会给你优化成 String s3 = "string"
;
不是所有的常量都会进行折叠,只有编译器在程序编译期就可以确定值的常量才可以:
- 基本数据类型( byte、boolean、short、char、int、float、long、double)以及字符串常量。
- final 修饰的基本数据类型和字符串变量
- 字符串通过 “+”拼接得到的字符串、基本数据类型之间算数运算(+、-、*、 /)、基本数据类型的位运算(<<、>>、>>> )
引用的值在程序编译期是无法确定的,编译器无法对其进行优化。
对象引用和“+”的字符串拼接方式,实际上是通过 StringBuilder 调用append() 方法实现的,拼接完成之后调用 toString() 得到一个 String 对象 。
String s4 = new StringBuilder().append(s1).append(s2).toString();
我们在平时写代码的时候,尽量避免多个字符串对象拼接,因为这样会重新创建对象。如果需要改变字符串的话,可以使用 StringBuilder 或者 StringBuffer。
字符串使用 final 关键字拼接的情况
final String s1 = "str";
final String s2 = "ing";
// 下面两个表达式其实是等价的
String m = "str" + "ing";// 常量池中的对象
String n = s1 + s2; // 常量池中的对象
System.out.println(m == n);// true
被 final 关键字修饰之后的字符串会被编译器当做常量来处理,编译器在程序编译期就可以确定它的值,其效果就相当于访问常量。
如果 ,编译器在运行时才能知道其确切值的话,就无法对其优化。
示例代码(s2 在运行时才能确定其值):
final String s1 = "str";
final String s2 = getStr();
String m = "str" + "ing";// 常量池中的对象
String n = s1 + s2; // 在堆上创建的新的对象
System.out.println(m == n);// false
public static String getStr() {
return "ing";
}