String 变量只要被赋值就等于新new一个String对象

原文出处:https://blog.csdn.net/theblackbeard/article/details/52770048

引言

        字符串只要赋值就相当于新new一个对象,字符串变量指向这个新new的对象,之前的对象就成了没有引用指向的对象了。


看下面代码:

[java]  view plain  copy
  1. public class Example {  
  2.   
  3.     String str = new String("good");  
  4.   
  5.     char[] ch = { 'a''b''c' };  
  6.   
  7.     public static void main(String args[]) {  
  8.   
  9.         Example ex = new Example();  
  10.   
  11.         ex.change(ex.str, ex.ch);  
  12.   
  13.         System.out.print(ex.str + " and ");  
  14.   
  15.         System.out.print(ex.ch);  
  16.   
  17.     }  
  18.   
  19.     public void change(String str, char ch[]) {  
  20.   
  21.         str = "test ok";  
  22.   
  23.         ch[0] = 'g';  
  24.   
  25.     }  
  26. }  

        结果输出是什么?

        我以为会是good and abc,因为形参无法改变实参的值嘛(值传递的时候应该是这样的),但是正确结果是good and gbc,后来明白,引用传递给形参(查了一些资料,有的说java当中只有值传递,没有引用传递;有的又说两种传递都有,我也弄不太清楚),然后在形参里面做改变的时候,实参也是会改变的,类比C语言的指针。原来如此,所以ch[0]的值会改变,可为什么字符串str的值又不变呢?我觉得有必要检查一下对象的hash码。

看下面的代码:

[java]  view plain  copy
  1. public class Example {  
  2.   
  3.     String str = new String("good");  
  4.   
  5.     char[] ch = { 'a''b''c' };  
  6.   
  7.     public static void main(String args[]) {  
  8.   
  9.         Example ex = new Example();  
  10.   
  11.         ex.change(ex.str, ex.ch);  
  12.           
  13.         System.out.println("ex str hash"+ex.str.hashCode());//获得ex.str的hashcode  
  14.     System.out.println("ex ch hash"+ex.ch.hashCode());//获得ex.ch的hashcode  
  15.   
  16.         System.out.print(ex.str + " and ");  
  17.   
  18.         System.out.print(ex.ch);  
  19.           
  20.           
  21.   
  22.     }  
  23.   
  24.     public void change(String str, char ch[]) {  
  25.   
  26.         str = "test ok";  
  27.           
  28.     System.out.println("change str hash"+str.hashCode());//获得change 方法中str的hashcode  
  29.           
  30.         ch[0] = 'g';  
  31.                   
  32.         System.out.println("ch hash"+ch.hashCode());//获得change 方法中ch的hashcode  
  33.     }  
  34. }  

就加了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的对象的内容。

        那么问题来了,既然都是引用传递给形参,为什么数组的就是指向堆上的同一个对象,而字符串就不是指向同一个堆上的对象呢?

上面的代码稍作修改,如下:

[java]  view plain  copy
  1. public class Example {  
  2.   
  3.     String str = new String("good");  
  4.   
  5.     char[] ch = { 'a''b''c' };  
  6.   
  7.     public static void main(String args[]) {  
  8.   
  9.         Example ex = new Example();  
  10.   
  11.         ex.change(ex.str, ex.ch);  
  12.           
  13.         System.out.println("ex str hash"+ex.str.hashCode());//获得ex.str的hashcode  
  14.     System.out.println("ex ch hash"+ex.ch.hashCode());//获得ex.ch的hashcode  
  15.   
  16.         System.out.print(ex.str + " and ");  
  17.   
  18.         System.out.print(ex.ch);  
  19.           
  20.           
  21.   
  22.     }  
  23.   
  24.     public void change(String str, char ch[]) {  
  25.           
  26.         System.out.println("change str hash"+str.hashCode());//获得change 方法中str的hashcode  
  27.                                                             //<span style="color:#ff0000;">在str重新赋值之前就打印change方法中的形参str的对象的hashcode</span>  
  28.         str = "test ok";  
  29.       
  30.         ch[0] = 'g';  
  31.           
  32.           
  33.         System.out.println("ch hash"+ch.hashCode());//获得change 方法中ch的hashcode  
  34.     }  
  35. }  

输出结果为下:

      这时hashcode一样了。

      换句话说,在传递参数的时候,确实是引用传递过来,但是当重新给字符串赋值的时候,新赋值的字符串变量(引用)就不再指向原来的堆上的对象!

看下面代码:

[cpp]  view plain  copy
  1. String s = "hello";  
  2. System.out.println(s.hashCode());  
  3. s = "world";  
  4. System.out.println(s.hashCode());  

输出结果:


      只要一赋值,对象的hashcode就改变,也就是说只要一赋值,就新生成一个对象,而不是在原对象上做更改。

      执行完这几行代码以后,在堆上面有两个对象!!一个是“hello”,一个是“world”。也就是说,在改变字符串的内容时,它没有在原来的堆内存上重写内容,而是新划出一块堆内存保存新赋的字符串内容!这个机制像什么?对,就是新new一个对象,所以我认为下面第2行和第3行代码是等价的:

[java]  view plain  copy
  1. String s = "hello";  
  2. s = "world";  
  3. s = new String("world");  
      s先指向new出的“hello”对象,当对s重新赋值的时候(执行第2行代码),s这个引用变量会指向新的一块堆内存所保存的对象“world”,而之前的”hello“对象会成为没有引用指向的对象,我认为不久就会被GC。

      至此,得出一个结论:字符串只要赋值就相当于新new一个对象,字符串变量指向这个新new的对象,之前的对象就成了没有引用指向的对象了。


猜你喜欢

转载自blog.csdn.net/qq_31301961/article/details/80752721