1、String 常量池
为了减少在JVM中创建的字符串的数量,字符串类维护了一个字符串池,每当代码创建字符串常量时,JVM会首先检查字符串常量池。如果字符串已经存在池中,就返回池中的实例引用。如果字符串不在池中,就会实例化一个字符串并放到池中。
Note:常量池在java用于保存在编译期已确定的,已编译的class文件中的一份数据。它包括了关于类,方法,接口等中的常量,也包括字符串常量,如String s = "java"
这种申明方式。
2、True OR False
首先得看下 几个对象???
相信很多 玩java的都做过类似String s = new String("abc")
这个语句创建了几个对象的题目。 这种题目主要就是为了考察对字符串对象的常量池掌握与否。其实上述的语句中是创建了2个对象:
第一个对象是"abc"字符串存储在常量池中
第二个对象在JAVA Heap中的 String 对象。这里不要混淆了s是放在栈里面的指向了Heap堆中的String对象
下面再看:
String str1 = new String("abc");
String str2 = "abc";
这里是两种的形式来创建,第一种是用new()来新建对象的,它会在存放于堆中。每调用一次就会创建一个新的对象。运行时期创建
而第二种是先在栈中创建一个对String类的对象引用变量str2,然后通过符号引用去字符串常量池里找有没有"abc",如果没有,则将"abc"存放进字符串常量池,并令str2指向”abc”,如果已经有”abc” 则直接令str2指向“abc”。“abc”存于常量池在编译期间完成
还有几个经常考的面试题:
String s1 = new String(“s1”) ;
String s2 = new String(“s1”) ;
上面创建了几个String对象?
答案:3个 ,编译期Constant Pool中创建1个,运行期heap中创建2个.(用new创建的每new一次就在堆上创建一个对象,用引号创建的如果在常量池中已有就直接指向,不用创建)
比较类里面的数值是否相等时,用equals()方法;当测试两个包装类的引用是否指向同一个对象时,用==,下面用例子说明上面的理论。
String str1 = "abc";
String str2 = "abc";
System.out.println(str1==str2); //true
可以看出str1和str2是指向同一个对象的。
String str1 =new String ("abc");
String str2 =new String ("abc");
System.out.println(str1==str2); // false
用new的方式是生成不同的对象。每一次生成一个。
下面就是用几个例子说明来说明一下
例1
String s1 = "sss111";
String s2 = "sss111";
System.out.println(s1 == s2); //结果为true
例2
String s1 = new String("sss111");
String s2 = "sss111";
System.out.println(s1 == s2); //结果为false
例3
String s0 = "111"; //pool
String s1 = new String("111"); //heap
final String s2 = "111"; //pool
String s3 = "sss111"; //pool
String s4 = "sss" + "111"; //pool
String s5 = "sss" + s0; //heap
String s6 = "sss" + s1; //heap
String s7 = "sss" + s2; //pool
String s8 = "sss" + s0; //heap
System.out.println(s3 == s4); //true
System.out.println(s3 == s5); //false
System.out.println(s3 == s6); //false
System.out.println(s3 == s7); //true
System.out.println(s5 == s6); //false
System.out.println(s5 == s8); //false
结果上面分析,总结如下:
1.单独使用**""引号创建的字符串都是常量,编译期就已经确定存储到Constant Pool**中;
2,使用new String("")创建的对象会存储到heap中,是运行期新创建的;
3,使用只包含常量的字符串连接符如**“aa” + "aa"创建的也是常量,编译期就能确定,已经确定存储到String Pool中,String pool中存有“aaaa”**;
但不会存有“aa”。
4,使用包含变量的字符串连接符如**“aa” + s1创建的对象是运行期才创建的,存储在heap**中;只要s1是变量,不论s1指向池中的字符串对象还是堆中的字符串对象,运行期s1 + “aa”操作实际上是编译器创建了StringBuilder对象进行了append操作后通过toString()返回了一个字符串对象存在heap上。
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”存放在常量池**中。