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

来源:互联网 发布:网络在线测速 编辑:程序博客网 时间:2024/05/20 11:36

引言

        字符串只要赋值就相当于新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的hashcodeSystem.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的hashcodeSystem.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");
      s先指向new出的“hello”对象,当对s重新赋值的时候(执行第2行代码),s这个引用变量会指向新的一块堆内存所保存的对象“world”,而之前的”hello“对象会成为没有引用指向的对象,我认为不久就会被GC。

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


0 0
原创粉丝点击