从堆和栈的角度看String对象

String对象问题曾困扰我很长时间,总是不太明白它与基本数据类型之间的区别,看了java内存分配,得到了一点启发,下面从堆和栈的角度看一下String对象在内存中是如何操作的。

首先看一下堆和栈的区别

栈:栈中存放的是基本类型的变量或者引用类型的变量。其中局部变量是通过数组的索引来访问,操作数栈通过栈操作才实现。局部变量有利于内存空间的高效利用, 方法运行完就回收,而对于类成员变量,只要对象还在,即使方法运行结束,成员变量还是存在。

堆:存放通过new关键字新建的对象和数组。声明的对象是在堆内存中初始化,对象的数据存放在堆中,不可以直接访问。引用变量保存了堆中对象的地址,它保存在栈中,用来引用堆中的对象。

栈代表了运行的逻辑,堆代表了运行时的数据。

我们对堆和栈有了基本的理解,现在我们步入正题,String对象。
首先我们从一个例子来看。
String s1 = "hello";
String s2 = new String("hello");
String s3 = "hello";
String s4 = new String("hello");
System.out.println(s1 == s2);
System.out.println(s1 == s3);
System.out.println(s2 == s4);


答案是:false, true, false
首先我们看一下 String s2 = new String("hello"), 和正常的创建对象一样,通过new String("hello") 在堆内存中创建了一个String对象,堆给这个对象分配内存空间,对象的内容是”hello“,记住对象保存在堆中。唯一不同的是JVM为string设置了一个常量池,用new关键字的同时,在常量池中也会产生一个“hello”对象,也就是说 new String("hello")会在堆和字符串常量池中分别创建一个对象。s2就是所谓的引用变量,s2是保存在栈中,它里面的内容是这个对象在堆中的内存地址,正如上面所说对象是不能直接调用的,只能通过引用变量来调用。并且new一次,堆就会给这个对象分配内存空间。对于==操作符,只有当两个引用变量指向同一个对象时才返回true,因此s2 == s4返回false。

对于String s1 = "hello", 它首先会在字符串常量池中寻找是否有‘’hello”,如果存在,就把指向“hello”的引用变量里面的值赋值给新的引用变量,在这个例子中就是把s1里面的内容赋值给s3,他们实际上是指向的同一个对象,因此s1 == s3返回true。如果字符串常量池中不存在“hello”,此时首先在常量池中创建一个对象,然后让当然的引用变量指向这个地址。对于这个例子,s1指向的是常量池里的“hello”,s2指向的是堆中的“hello”,因此s1 == s2返回false。JVM是为了提高效率才设计了这样的操作。如果每次都用new关键字,会造成内存的浪费。

个人的一点体会,如有错误的地方希望大家指出!

猜你喜欢

转载自kickcode.iteye.com/blog/2260030