字符串常量池
- 前言
- 1. 直接赋值法,默认从常量池中取对象
- 2. new一个对象
- 3. 字符串常量池、字符串对象、内部的value引用、具体的字符数组之间的关系
- 4. 手动入池方法:intern方法
前言
Java使用 “ ” 称为字符串常量,为了提高程序的运行速度,节省空间,JVM会维护一个字符串常量池。当字符串常量第一次出现,则产生新对象并将该对象置入常量池中,后续如果再出现该字符串常量,不会产生新对象,直接复用常量池中的已有对象。
1. 直接赋值法,默认从常量池中取对象
- “ ” 内容都是字符串常量,都是字符串对象
- 字符串对象不可变,无法修改value的内容
//第一次出现,产生新对象,入池
String s1="hello";
//第二次出现,不会产生新对象
String s2="hello";
以上代码的内存示意图s1==s2:
2. new一个对象
- new一个对象,需要调用构造方法进行新对象的创建。
- 每调用一次new,都会在堆上开辟新空间,返回新创建的对象。
- 总之:看见new就会产生新空间!!!
对如下代码进行分析:
//直接赋值法
String s1="hello";
String s2="hello";
//构造方法产生新对象
String s3=new String("hello");
String s4=new String("hello");
分析:对于s1和s2来说,属于直接赋值类型,产生字符串对象放入字符串常量池,s2将会直接从字符串常量池取出已有对象,则s1==s2。s3属于构造方法new一个新对象,需要在堆区开辟一个新的空间,其中value值保存的是字符数组的地址。
上述代码共产生三个对象,一个在常量池中,两个在堆上;产生了4个引用,其中s1和s2指向的是常量池中的对象,s3和s4分别指向的是堆中的对象。
3. 字符串常量池、字符串对象、内部的value引用、具体的字符数组之间的关系
- 字符串常量池中保存的都是字符串对象;
- 字符串对象内部的value引用是指向字符数组的引用;
举例:
假如将CSDN的博客内容相当于字符数组的内容;value引用保存了这块内容的地址;字符串对象相当于博客链接;CSDN相当于字符串常量池,保存了许多字符串对象,即链接!
new出来的字符串对象中的value引用,只是一个名字,指向字符数组,不是实体!!!
4. 手动入池方法:intern方法
将手动创建的字符串对象置入常量池,并返回置入常量池之后的地址。
看如下的代码:
char[] ch={'a','b','c'};
String s1=new String(ch);
String s2="abc";
System.out.println(s1==s2);
s1:通过new的方式产生的字符串仍然在堆中存储,不会置入常量池
s2:字符串常量产生后直接置入常量池
s1 != s2
char[] ch={'a','b','c'};
String s1=new String(ch);
s1.intern();
String s2="abc";
System.out.println(s1==s2);
此时,将堆上new出来的新对象通过intern()方式手动置入常量池,当“abc”出现时,常量池中已经包含,因此,s2直接返回常量池中存储的地址。
s1 == s2
String s2="abc";
char[] ch={'a','b','c'};
String s1=new String(ch);
s1.intern();
System.out.println(s1==s2);
此时,s2属于直接赋值法将产生的字符串对象直接放入字符串常量池中;
s1属于new出来的对象放置在堆区,s1.intern()方法执行后的返回值无人接收,s1依旧指向的是堆区的字符串对象的地址。
s1 != s2
补充介绍intern()方法
尝试将当前字符串对象置入常量池;
若常量池中不存在该对象内部保存的内容“abc”,则将当前对象置入常量池;
若常量池中已经存在该对象保存的内容,则该方法直接返回常量池中的字符串对象地址!
intern()方法调用后会返回常量池中的字符串地址,调用之后进行接收即可!
如果让s1去接收intern()方法的返回值,此时由于字符串常量池中包含字符串“abc“,因此直接返回常量池中的字符串对象地址,则s1==s2