java 中String到底是值传递还是引用传递的问题 您所在的位置:网站首页 引用传递和值传递的区别java java 中String到底是值传递还是引用传递的问题

java 中String到底是值传递还是引用传递的问题

#java 中String到底是值传递还是引用传递的问题| 来源: 网络整理| 查看: 265

为什么String是不可变的:

Java中的String为什么是不可变的? -- String源码分析_昨夜星辰的博客-CSDN博客_string为什么是不可变的什么是不可变对象?众所周知, 在Java中, String类是不可变的。那么到底什么是不可变的对象呢? 可以这样认为:如果一个对象,在它创建完成之后,不能再改变它的状态,那么这个对象就是不可变的。不能改变状态的意思是,不能改变对象内的成员变量,包括基本数据类型的值不能改变,引用类型的变量不能指向其他的对象,引用类型指向的对象的状态也不能改变。区分对象和对象的引用对于Java初学https://blog.csdn.net/zhangjg_blog/article/details/18319521

下面原博客的例子有点问题,予以补充:      java中都是值传递,没有引用传递,比如下面str传给change方法的s变量的是"bea"这个字符串的地址,而不是srt这个变量自己的地址,所以在方法里修改s后str的值不变 1.String中的坑

  最近看到一道关于string的面试题,就是下面这个例子,体验下:

1 2 3 4 5 6 7 8 9 10 11 12 public class demo{     public static void main(string[] args) {         demo d = new demo();         string str = "bea";         d.change(str);         system.out.println(str);     }     void change(string s){         s= s.replace('a', 'e');         s = s.tolowercase();     } }

  当时一看到这个题目,我第一反应就是输出”bee“,因为string是引用类型,其参数传递的方式就是引用传递,传递的是string的地址。可是答案让我的大吃一惊,“bea”,str根本就没有发生变化!!

  难道string是值传递?难道string是基本类型?

  其实都不是,后来通过查阅相应资料发现,jvm在实例化字符串时会使用字符串常量池,把str作为参数传入change()方法。jvm复制了一份str变量,为了便于理解我们叫它str'。这个时候str和str'都指向字符串常量池中的“abc”。

  当我们执行s = s.replace('a', 'e'); 其实相当于执行了s = new string(s.replace('a', 'e'));

 要理解上面这两段话,就要从java的底层结构说起了。java的内存模型大体分为 堆 和 栈 (细分还有方法区,和程序计数器等)。

1.基本类型的变量放在栈里; 2.封装类型中,对象放在堆里,对象的引用放在栈里。

java在方法传递参数时,是将变量复制一份,然后传入方法体去执行。

根据这些再细分一下jvm的执行过程

1.虚拟机在堆中开辟一块内存,并存值”bea”。 2.虚拟机在栈中分配给str一个内存,内存中存的是1中的地址。(1指第一步) 3.虚拟机复制一份str,我们叫str’,str和str’内存不同,但存的值都是1的地址。 4.将str’传入方法体 5.方法体在堆中开辟一块内存,并存值”bee”。 6.方法体在堆中再次开辟一块内存,并存值”bee”。 7.方法体将str’的值改变,存入5的内存地址。 8.方法结束,方法外打印str,由于str存的是1的地址,所有打印结果是”bea”。 string的底层是一个不可变数据,所以每次给他赋新的值的时候都相当于新建了一个string对象(如果string常量池里没有该字符串的话),我们可以验证一下。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class demo{     public static void main(string[] args) {         demo d = new demo();         //通过比较str的hashcode来比较两个对象是否为同一对象         string str = "bea";         system.out.println("第一次string的hashcode:"+str.hashcode());         str = "bee";         system.out.println("第二次string的hashcode:"+str.hashcode());         //stringbuilder来试一次         stringbuilder s = new stringbuilder("bea");         system.out.println("第一次stringbuilder的hashcode:"+s.hashcode());         s.append('t');         system.out.println("第二次stringbuilder的hashcode:"+s.hashcode());         system.out.println("调用方法前的stringbuilder对象的值:"+s);         d.change(s);         system.out.println("调用方法后的stringbuilder对象的值:"+s);     }     void change(stringbuilder s){         s = s.append('s');     } }

  看看执行的结果~

file

  tips: hashcode并不能判断是否为同一个对象,但是hashcode不同的话肯定不是同一个对象,hashcode相同的不一定是同一个对象。

2.String str = "" 和 new String()的区别

首先明白一个事,java存在一个常量池,可以用来存储字符串常量。

1 创建的字符串变量在内存中的区别

两者看似都是创建了一个字符串对象,但在内存中确是各有各的想法。

String str1= “abc”; 在编译期,JVM会去常量池来查找是否存在“abc”,如果不存在,就在常量池中开辟一个空间来存储“abc”;如果存在,就不用新开辟空间。然后在栈内存中开辟一个名字为str1的空间,来存储“abc”在常量池中的地址值。

String str2 = new String("abc") ;当使用String str=new String("abc");时,不管事先是否存在"abc",每次都会创建其新的对象。

2 String类的特性

String类 是final修饰的,不可以被继承。

String类的底层是基于char数组的。

3 两个方面

1)性能效率

String类被设计成不可变(immutable)类,所以它的所有对象都是不可变对象。例如:

String str = “hello";

str = str + "world“;

所以当上文str指向了一个String对象(内容为“hello”),然后对str进行“+”操作,str原来指向的对象并没有变,而是str又指向了另外一个对象(“hello world”),原来的对象还在内存中。

由此也可以看出,频繁的对String对象进行修改,会造成很大的内存开销。此时应该用StringBuffer或StringBuilder来代替String。

而new String()更加不适合,因为每一次创建对象都会调用构造器在堆中产生新的对象,性能低下且内存更加浪费。

2)安全性

对象都是只读的,所以多线程并发访问也不会有任何问题。

由于不可变,用来存储数据也是极为安全的。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有