先上一段代码
public static void main(String[] args) {
String str1="abc";
String str2="a"+"b"+"c";
String str3=new String("abc");
String str4=str3+"";
String str5=new String("abc");
System.out.println("str1==str2:"+(str==str2));
System.out.println("str1==str3:"+(str==str3));
System.out.println("str1==str4:"+(str==str4));
System.out.println("str3==str5:"+(str3==str5));
}
结果是什么呢?
这种题目主要就是为了考察对字符串对象的常量池掌握与否
堆:用于存放所有的JAVA对象
栈:存放基本类型的变量数据和对象的引用
常量池:存放字符串常量和基本类型常量(public static final)。对于字符串常量池,为了减少在JVM中创建的字符串的数量,字符串类维护了一个字符串池,每当代码创建字符串常量时,JVM会首先检查字符串常量池。如果字符串已经存在池中,就返回池中的实例引用。如果字符串不在池中,就会实例化一个字符串并放到池中。
因此,以上代码分析如下:
下面再来看一个问题:
String s1 = new String("s1") ;
String s2 = new String("s1") ;
上面创建了几个对象?
答案:3个 ,编译期常量池中创建1个,运行期堆中创建2个.(用new创建的每new一次就在堆上创建一个对象,用引号创建的如果在常量池中已有就直接指向,不用创建)
总结:
1.单独使用"“引号创建的字符串都是常量,编译期就已经确定存储到常量池中;
2,使用new String(”")创建的对象会存储到堆中,是运行期新创建的;
3,使用只包含常量的字符串连接符如"aa" + “aa"创建的也是常量,编译期就能确定,已经确定存储到字符串常量池中,String pool中存有“aaaa”;但不会存有“aa”。
4,使用包含变量的字符串连接符如"aa” + s1创建的对象是运行期才创建的,存储在heap中;只要s1是变量,不论s1指向池中的字符串对象还是堆中的字符串对象,运行期s1 + “aa”操作实际上是编译器创建了StringBuilder对象进行了append操作后通过toString()
5 , String s2 = “aa” + s1; String s3 = “aa” + s1; 这种情况,虽然s2,s3都是指向了使用包含变量的字符串连接符如"aa" + s1创建的存在堆上的对象,并且都是s1 + “aa”。但是却指向两个不同的对象,两行代码实际上在堆上new出了两个StringBuilder对象来进行append操作。在Thinking in java一书中285页的例子也可以说明。
6,对于final String s2 = “111”。s2是一个用final修饰的变量,在编译期已知,在运行s2+"aa"时直接用常量“111”来代替s2。所以s2+"aa"等效于“111”+ “aa”。在编译期就已经生成的字符串对象“111aa”存放在常量池中。
参考博客:String之常量池小结