内存
String 字符串的存储位置在JAVA7之后存储在堆空间中的字符串常量池中。而此前是存储在永久区之中。(java8之后jvm中的方法区的实现时元空间,而此前是永久区)
不同创建方式在内存的位置
String str = "this is str";
这种方式通过字面量的方式创建一个字符串,此时会直接在堆的常量池中创建此字符串(如果之前已经创建过了,由于字符串的不变性,会使用之前的那个)。String str2 = new String(“this is str”);
这种方式会在堆空间中创建一个String类型的对象,并且在字符串常量池中创建对应的字符串,即会创建两个对象。但是str保存的是所创建对象的引用,而非指向常量池,即调用 str2 == str ,结果为false。结果如下:- 通过字符串拼接的方式创建一个字符串:
3.1String str = "a"+"a";
这种方式属于字面量与字面量的拼接,此时会进行编译期优化,此时相当于str=“aa”,即str还是指向常量池中的aa,验证:
3.2String str = "a"; String str2= str+"a";
,这种方式属于字符串中存在变量的拼接方式,此时会创建一个新的String对象,然后创建一个StringBuilder 对象,然后调用StringBuilder的append方法把这两个字符串拼接到一起,最后,通过StringBuilder对象的toString方法返回了一个字符串对象,值得注意的是,toString方法中的内容是:
它是new了一个String对象,而且是通过char数组的方式创建的,也就是说不会在字符串常量池中创建对应的字符串,即这种方式创建方式在字符串常量池中不会有“aa”。验证如下:
结果表明他们不是一个对象。 String str3 = new String("a") + new String("a");
这种方式首先会分别创建两个String对象,然后字符串常量池中只会有一份“a”,验证:
随后,会创建一个StringBuilder对象,并通过它的toString方法new了一个String并返回,内容就是连个字符串的拼接,并且是字符数组方式创建的,因此在字符串常量池中不存在“aa”。
intern
如果不是用双引号声明的String对象,可以使用String提供的intern方法: intern方法会从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串放入常量池中。也就是说,如果在任意字符串上调用string.intern方法,那么其返回结果所指向的那个类实例,必须和直接以常量形式出现的字符串实例完全相同。
例如:
第一种方式以字面量的方式在常量池中创建了一个字符串,而第二种方式创建了一个String对象,并且由于常量池中已经有这个字面量,所以只会保证在其中只有一个字符串。此时比较二者发现不同:
而如果用intern方法创建:
此时结果:
即此时会在常量池中查看是否有该字符串,如果存在就会直接把该字符串引用赋值给a,否则就现在常量池中创建这个字符串再赋值引用给a。
两个jdk版本intern的区别
jdk1.6中,将这个字符串对象尝试放入串池。
- 如果串池中有,则并不会放入。返回已有的串池中的对象的地址
- 如果没有,会把此对象复制一份,放入串池,并返回串池中的对象地址
Jdk1.7起,将这个字符串对象尝试放入串池。
- 如果串池中有,则并不会放入。返回已有的串池中的对象的地址
- 如果没有,则会把对象的引用地址复制一份,放入串池,并返回串池中的引用地址,这可以节省堆中的内存(创建的对象数减少了)