浅析Java中字符串初始化new String()和直接赋值的区别、数组初始化时用new与不用new的区别
首先明白一个事,Java存在一个常量池,可以用来存储字符串常量。
一、创建的字符串变量在内存中的区别
对于字符串:其对象的引用都是存储在栈中的,如果是编译期已经创建好(直接用双引号定义的)的就存储在常量池中,如果是运行期(new出来的)才能确定的就存储在堆中。对于equals相等的字符串,在常量池中永远只有一份,在堆中有多份。
例如:String str1=”ABC”; 和String str2 = new String(“ABC”); 两者看似都是创建了一个字符串对象,但在内存中确是各有各的想法。
1、String str1=”ABC” :可能创建一个对象或者不创建对象。
如果”ABC”这个字符串在 Java String 池里不存在,会在 Java String 池创建一个String对象(“ABC”)。如果已经存在,str1直接reference to 这个String池里的对象。
在编译期,JVM会去常量池来查找是否存在“ABC”,如果不存在,就在常量池中开辟一个空间来存储“ABC”;如果存在,就不用新开辟空间。然后在栈内存中开辟一个名字为str1的空间,来存储“ABC”在常量池中的地址值。
2、String str2 = new String(“ABC”) :至少创建一个对象,也可能两个。
因为用到 new 关键字,会在heap堆中创建一个 str2 的String 对象,它的value 是 “ABC”。同时,如果”ABC”这个字符串在 Java String 池里不存在,也会在 Java String 池创建一个String对象(“ABC”)。
在编译阶段JVM先去常量池中查找是否存在“ABC”,如果不存在,则在常量池中开辟一个空间存储“ABC”。在运行时期,通过String类的构造器在堆内存中new了一个空间,然后将String池中的“ABC”复制一份存放到该堆空间中,在栈中开辟名字为str2的空间,存放堆中new出来的这个String对象的地址值。
也就是说,前者在初始化的时候可能创建了一个对象,也可能一个对象也没有创建;后者因为new关键字,至少在内存中创建了一个对象,也有可能是两个对象。
二、String类的特性
String类 是final修饰的,不可以被继承。
String类的底层是基于char数组的。
三、intern() 方法
String 有一个intern() 方法,用来检测在String pool是否已经有这个String存在。
public String intern() // 返回字符串对象的规范化表示形式