java的常量区是个有意思的部分。首先解释下堆、栈和常量区分别存放的数据。
堆:存放所有new出来的对象;
栈:存放基本类型的变量数据和对象的应用,对象(new出来的对象)本身并不存在栈中,而是存放在堆中或者常量池中(字符串常量对象存放在常量池中);
常量池:存放基本类型常量和字符串常量。
字符串的创建分两种,一种是静态的创建,是指在编译期就创建了,用双引号括起来的。这种创建的句柄在栈区,对象在常量池中。而如果是动态的创建,即在运行期创建,也就是用new创建的字符串,则是首先去常量池中找,有没有相同的,有就直接拷贝一个放到堆中,没有的话,就在常量池中先创建一个,然后再拷贝到堆中。而当引用传递的时候,传递的究竟是对象还是句柄呢?我做了如下一些实验。
1 若是作为“对象.属性”进行传递。
<span style="font-family:Microsoft YaHei;font-size:14px;">class A{
public static void main(String[] args){
B b=new B();
String a=b.temp;
fun(b);
System.out.println(b.temp);
}
public static void fun(B b){
b.temp="world";}
}
class B{
String temp="hello";
}</span>
<span style="font-family:Microsoft YaHei;font-size:14px;"></span>
编译运行之后,产生的结果是world。由此可以判断,当“对象.属性”代表了句柄。所以这个例子里,当运行到方法fun时,先在常量区里创建一个对象“world”,然后把句柄b.temp指向“world”
2 对上述内容稍作修改,把打印内容从b.temp改成a,会是怎样?
编译运行之后,产生的结果是hello。这就说明,b.temp把指向“hello”的句柄给了a,从此a就指向了“hello”
3 继续玩儿,之前,我们在1中,往方法里传递的是“对象.属性”,这次直接传递属性试试?
<span style="font-family:Microsoft YaHei;font-size:14px;">class A{
public static void main(String[] args){
B b=new B();
String a=b.temp;
fun(b.temp);
System.out.println(b.temp);
}
public static void fun(String c){
c="world";}
}
class B{
String temp="hello";
}
</span>
编译运行之后,产生的结果是hello。想想这个过程,temp其实只是一个指向的句柄。它传递给了c之后,c就指向“hello”了。但是后面的这句c=“world”就代表了在常量区生成一个“world”然后让c指向它。但这个时候,对temp并不会造成任何的影响。
总的来说,String是一种很特别的类型。它创建之后不能被我们主动地销毁。我们只能改变句柄,让句柄指向另一处的string。