首先给出一个思考题:
有如下一段代码,请选择其运行结果()
1
2
3
4
5
6
7
8
9
10
|
public
class
StringDemo{
private
static
final
String MESSAGE=
"taobao"
;
public
static
void
main(String [] args) {
String a =
"tao"
+
"bao"
;
String b=
"tao"
;
String c=
"bao"
;
System.out.println(a==MESSAGE);
System.out.println((b+c)==MESSAGE);
}
}
|
Java中的变量和基本类型的值存放于栈内存,对象的引用还是存放在栈内存,而new出来的对象本身存放于堆内存。例如如下的代码:
int i=1;
String s = new String( "Hello World" );
变量i和s以及1存放在栈内存,而s指向的对象”Hello World”存放于堆内存。
栈内存的一个特点是数据共享,这样设计是为了减小内存消耗,前面定义了i=1,i和1都在栈内存内,如果再定义一个j=1,此时将j放入栈内存,然后查找栈内存中是否有1,如果有则j指向1。如果再给j赋值2,则在栈内存中查找是否有2,如果没有就在栈内存中放一个2,然后j指向2。也就是如果常量在栈内存中,就将变量指向该常量,如果没有就在该栈内存增加一个该常量,并将变量指向该常量。
对于字符串常量的相加,在编译时直接将字符串合并,而不是等到运行时再合并。也就是说
String a = "tao" + "bao" ;和String a = "taobao" ;编译出的字节码是一样的。所以等到运行时,根据上面说的栈内存是数据共享原则,a和MESSAGE指向的是同一个字符串。
再把变量b和c的定义改一下,
final String b = "tao" ;
final String c = "bao" ;
System. out .println( (b+c)== MESSAGE );
现在b和c不可能再次赋值了,所以编译器将b+c编译成了”taobao”。因此,这时的结果是true。
在字符串相加中,只要有一个是非final类型的变量,编译器就不会优化,因为这样的变量可能发生改变,所以编译器不可能将这样的变量替换成常量。例如将变量b的final去掉,结果又变成了false。这也就意味着会用到StringBuffer对象,计算的结果在堆内存中
String a = "tao"+"bao";
String b = new String("taobao");
System.out.println(a==MESSAGE); //true
System.out.println(b==MESSAGE); //false
b = b.intern();
System.out.println(b==MESSAGE); //true
System. out .println(a==a.intern()); //true