String的Equals()和==比较函数
常量池(Constant Pool):指的是在编译期被肯定,并被保存在已编译的.class文件中的一些数据。JVM虚拟机为每一个被装载的类型维护一个常量池。常量池就是该类型所用到常量的一个有序集和,包括直接常量(String,Integer和 Floating point常量)和对其余类型,字段和方法的符号引用。对于String常量,它的值是在常量池中的。而JVM中的常量池在内存当中是以表的形式存在的, 对于String类型,有一张固定长度的CONSTANT_String_info表用来存储文字字符串值,注意:该表只存储文字字符串值,不存储符号引用。 优化
一、String s = "abc";
建立过程分析:在class文件被JVM装载到内存中,JVM会建立一块String Pool(String缓冲池)。当执行String s = “abc”;时,JVM首先在String Pool中查看是否存在字符串对象“abc”(如何查看呢?用equals()方法判断),若是已存在该对象,则不用建立新的字符串对象“abc”,而直接使用String Pool中已存在的对象“abc”,而后将引用s指向该对象;若是不存在该对象,则先在String Pool中建立一个新的字符串对象“abc”,而后将引用s指向String Pool中建立的新对象。
注意:使用“字符串常量”引号建立的字符串对象时,在编译期就已经肯定将该对象存储到String Pool中了。所以,String s = “abc”只会在编译期,在String Pool中建立一个对象。 spa
二、String s = new String("abc");
建立过程分析:当执行String s = new String(“abc”);时,JVM首先在String Pool中查看是否存在字符串对象“abc”,若是不存在该对象,则先在String Pool中建立一个新的字符串对象“abc”,而后执行new String(“abc”)构造方法,在Heap里又建立一个新的字符串对象“abc”(new出来的对象都放在Heap里面),并将引用s指向Heap中建立的新对象;若是已存在该对象,则不用建立新的字符串对象“abc”,而直接使用String Pool中已存在的对象“abc”, 而后执行new String(“abc”)构造方法,在Heap里又建立一个新的字符串对象“abc”,并将引用s指向Heap中建立的新对象。
注意:使用new String(“”)建立的字符串对象时,会在运行期建立新对象存储到Heap中。所以,new String(“abc”)建立字符串对象时,会建立2个对象,编译期在String Pool中建立一个,运行时Heap中建立一个。 code
示例代码:对象
String a="hello"; //编译期间,以被存储字符串常量池 String b=new String("hello"); //运行期间才会建立 final String c="hello"; //编译期间已被存储字符串常量池 String d="hel"; String e="lo"; String f=new String("lo"); System.out.println(a==b); //false System.out.println(a.equals(b)); //true System.out.println(a==c); //true System.out.println(a.equals(c)); //true System.out.println(b==c); //false System.out.println(b.equals(c)); //true System.out.println("******split*****"); System.out.println(a==(d+f)); //false System.out.println(a.equals(d+f)); //true System.out.println(c==(d+f)); //false System.out.println(c.equals(d+f)); //true
注意代码:blog
一、对于有+号,而且有引用变量:内存
String s1 = "abc"; String s2 = "def"; String s3 = "abcdef"; String s4 = "abc"+"def"; String s5 = s1 + "def"; String s6 = "abc"+s2; String s7 = s1 + s2; System.out.println(s3 == s4); System.out.println(s3 == s5); System.out.println(s3 == s6); System.out.println(s3 == s7); 运行结果以下: true false false false
结果说明:JVM对于有字符串引用存在的字符串"+"链接中,而引用的值在程序编译期是没法肯定的,即s1 + “def”没法被编译器优化,只有在程序运行期来动态分配并将链接后的新地址赋给s5。 可是对于强制声明为final的除外,以下所示: final String s1 = "abc"; final String s2 = "def"; String s3 = "abcdef"; String s4 = "abc"+"def"; String s5 = s1 + "def"; String s6 = "abc"+s2; String s7 = s1 + s2; System.out.println(s3 == s4); System.out.println(s3 == s5); System.out.println(s3 == s6); System.out.println(s3 == s7);
运行结果以下:
true
true
true
true
结果说明:例程3和例程4与例程2的区别是,例程3在字符串s1前加了final修饰,例程4在字符串s1和s2前都加了final修饰。对于final修饰的变量,它在编译时被解析为常量值的一个本地拷贝存储到本身的常量池中或嵌入到它的字节码流中。因此此时的s1 + “def”和"abc" + "def"效果是同样的。接着后面两个含引用的字符串链接,JVM会进行相同的处理。故上面程序后面三个的结果为true。
二、对于方法调用的 public static void main(String args[]){ String s1 = "abc"; final String s2 = getDef(); String s3 = "abcdef"; String s4 = "abc"+s2; String s5 = s1 + s2; System.out.println(s3 == s4); System.out.println(s3 == s5); } private static String getDef(){ return "def"; } 程序运行结果以下: false false 结果说明:JVM对于方法调用给字符串引用赋值的状况,引用指向字符串的值在编译期是没法肯定的,只有在程序运行调用方法后,将方法的返回值“def”和“abc”动态链接并分配新地址赋值给s4,因此上述程序的结果都为false。
String s=new String("XYZ")+new String("XYZ");总共建立了四个对象字符串
对于第一个new String("XYZ"),须要在StringPool中存储XYZ建立一个对象,构造函数建立一个对象get
对于第二个new String("XYZ"),构造函数须要建立一个对象编译器
最后两个字符串相加会再StringPool中再建立一个对象
所以总共建立了4个对象