原文出处:https://blog.csdn.net/theblackbeard/article/details/52770048
引言
字符串只要赋值就相当于新new一个对象,字符串变量指向这个新new的对象,之前的对象就成了没有引用指向的对象了。
看下面代码:
- public class Example {
- String str = new String("good");
- char[] ch = { 'a', 'b', 'c' };
- public static void main(String args[]) {
- Example ex = new Example();
- ex.change(ex.str, ex.ch);
- System.out.print(ex.str + " and ");
- System.out.print(ex.ch);
- }
- public void change(String str, char ch[]) {
- str = "test ok";
- ch[0] = 'g';
- }
- }
结果输出是什么?
我以为会是good and abc,因为形参无法改变实参的值嘛(值传递的时候应该是这样的),但是正确结果是good and gbc,后来明白,引用传递给形参(查了一些资料,有的说java当中只有值传递,没有引用传递;有的又说两种传递都有,我也弄不太清楚),然后在形参里面做改变的时候,实参也是会改变的,类比C语言的指针。原来如此,所以ch[0]的值会改变,可为什么字符串str的值又不变呢?我觉得有必要检查一下对象的hash码。
看下面的代码:
- public class Example {
- String str = new String("good");
- char[] ch = { 'a', 'b', 'c' };
- public static void main(String args[]) {
- Example ex = new Example();
- ex.change(ex.str, ex.ch);
- System.out.println("ex str hash"+ex.str.hashCode());//获得ex.str的hashcode
- System.out.println("ex ch hash"+ex.ch.hashCode());//获得ex.ch的hashcode
- System.out.print(ex.str + " and ");
- System.out.print(ex.ch);
- }
- public void change(String str, char ch[]) {
- str = "test ok";
- System.out.println("change str hash"+str.hashCode());//获得change 方法中str的hashcode
- ch[0] = 'g';
- System.out.println("ch hash"+ch.hashCode());//获得change 方法中ch的hashcode
- }
- }
就加了4行打印hash码,hash码输出如下:
看到结果,发现在change方法中的数组和在main中的数组是同样的hashcode,说明修改的是同一个对象(该对象的实体在堆heap上),所以在change方法中修改了数组以后,main当中的ex.ch肯定也会被修改,因为是在堆上的同一个对象。
但是,在change方法中的str和main当中的ex.str的hashcode居然是不一样的!!这说明在方法change中的形参str指向的并不是main当中的ex.str指向的对象,所以在change中做修改,当然不会影响main中的ex.str的对象的内容。
那么问题来了,既然都是引用传递给形参,为什么数组的就是指向堆上的同一个对象,而字符串就不是指向同一个堆上的对象呢?
上面的代码稍作修改,如下:
- public class Example {
- String str = new String("good");
- char[] ch = { 'a', 'b', 'c' };
- public static void main(String args[]) {
- Example ex = new Example();
- ex.change(ex.str, ex.ch);
- System.out.println("ex str hash"+ex.str.hashCode());//获得ex.str的hashcode
- System.out.println("ex ch hash"+ex.ch.hashCode());//获得ex.ch的hashcode
- System.out.print(ex.str + " and ");
- System.out.print(ex.ch);
- }
- public void change(String str, char ch[]) {
- System.out.println("change str hash"+str.hashCode());//获得change 方法中str的hashcode
- //<span style="color:#ff0000;">在str重新赋值之前就打印change方法中的形参str的对象的hashcode</span>
- str = "test ok";
- ch[0] = 'g';
- System.out.println("ch hash"+ch.hashCode());//获得change 方法中ch的hashcode
- }
- }
输出结果为下:
这时hashcode一样了。
换句话说,在传递参数的时候,确实是引用传递过来,但是当重新给字符串赋值的时候,新赋值的字符串变量(引用)就不再指向原来的堆上的对象!
看下面代码:
- String s = "hello";
- System.out.println(s.hashCode());
- s = "world";
- System.out.println(s.hashCode());
输出结果:
只要一赋值,对象的hashcode就改变,也就是说只要一赋值,就新生成一个对象,而不是在原对象上做更改。
执行完这几行代码以后,在堆上面有两个对象!!一个是“hello”,一个是“world”。也就是说,在改变字符串的内容时,它没有在原来的堆内存上重写内容,而是新划出一块堆内存保存新赋的字符串内容!这个机制像什么?对,就是新new一个对象,所以我认为下面第2行和第3行代码是等价的:
- String s = "hello";
- s = "world";
- s = new String("world");
至此,得出一个结论:字符串只要赋值就相当于新new一个对象,字符串变量指向这个新new的对象,之前的对象就成了没有引用指向的对象了。