一、堆内存和栈内存
在JVM中,堆内存是内存空间存放的是对象实例化的内容(程序的数据),栈内存存放的是对象的名称,其内容是指向对应堆的地址。
也可以这么说:所有的对象名称保存在栈内存中,对象具体的内容则保持在堆内存中,引用类型数据必须使用new关键字来在堆内存中开辟空间。
String有一个特殊之处:构造String对象时可以使用new构造也可以使用"hello"直接构造。
②
1:在栈内存中定义了一个a对象引用,指向堆内存的值“hello”内存地址。最终开辟了一个内存空间
2:在栈内存重定义了一个a对象引用,先指向堆内存值为“hello”内存地址,然后又指向new之后堆内存为“hello”的地址。最终开辟了两个空间,第一个空间没有对象引用,会被JVM垃圾回收。
附代码实例:
public class TestString {
public static void main(String[] args){
String a = "hello";
String b = "hello";
String c = new String("hello");
String d = new String();
d = "hello";
String e = c;
String f=new String("hello");
System.out.println("a==b ? " + (a== b));
System.out.println("a==c ? " + (c== b));
System.out.println("a==d ? " + (a== d));
System.out.println("a==e ? " + (a== e));
System.out.println("c==d ? " + (c== d));
System.out.println("c==e ? " + (c== e));
System.out.println("c==f ? " + (c== f));
}
}
输出:
其中只有a==b==d 、c=e。( ==比较的是2个对象的地址,而equals比较的是2个对象的内容)
1、String每new一次堆内存都不相等,而d在new分配完新地址之后,又放弃new之后的地址,指向a对应的内存地址,所以他们是相同的。
对于已存在的内容时,会将对象指向已经实例的空间地址。
3、e直接指向C的内存空间。
4、所以在使用String时,建议使用直接赋值方式,减小内存空间,提高性能。
二、String、StringBuffer和StringBuilder的区别
1、 String、StringBuffer、StringBuilder都是被final修饰的,是不能够被继承改写的。
同之处在于StringBuilder不是线程同步,因此操作起来必然比StringBuffer更加高效。
这时会有人想:
str += "andy";
str的值不是也改变了吗?
其实上述代码在内存中已经开辟了3个空间,分别是:”hello“, ”andy“, ”helloandy“,他们的堆内存大小是固定的,最终str指向了”helloandy“的堆地址。如下图所示:
而StringBuffer使用时,只会开辟一块内存空间,可以使用append添加delete等操作内容。
String 每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了以后, JVM 的 GC 就会开始工作,那速度是一定会相当慢的。 而如果是使用 StringBuffer/StringBuilder 类则结果就不一样了,每次结果都会对 StringBuffer/StringBuilder 对象本身进行操作,而不是生成 新的对象,再改变对象引用。
因而在对一个字符串循环赋值时,最好使用StringBuffer(线程安全)或StringBuilder,这样可以节约内存,提高性能,切记。