String在java中算是一个比较特殊的类,既能作为基本数据类型使用(String str="123"),也能作为对象使用(String str = new String("123"))。我们先看几组简单的测试。
第一组:
String str1 = new String("123"); String str2 = new String("123"); System.out.println(str1==str2);//false System.out.println(str1.equals(str2));//true
第二组:
String str1 = new String("123"); String str2 = str1; System.out.println(str1==str2);//true System.out.println(str1.equals(str2));//true
第三组:
String str1 = "123"; String str2 = "123"; System.out.println(str1==str2);//true System.out.println(str1.equals(str2));//true
首先我们得知道==和equals的区别,==比较的是引用(即是否指向同一个对象)是否相等,equals比较的是字面量是否相等。
第一组结果很好理解,str1和str2作为对象使用,通过new关键字创建各自对立的内存堆(这只是其一,后面会再介绍)。
第二组也好理解,str1通过new创建自己对立的内存堆,然后将引用赋值给str2,两者指向的是同一片内存区域。
第三组为什么==比较的结果是true?我们发现第三组str1和str2是当作基本类型使用的,没有new关键字,也就是说JVM不会在堆中给两者分配空间,那它们存在哪里?其实在java中有一个String池这么个东西。我们知道String str = new String("123")这行代码其实是创建了两个对象,一个对象在String池中,一个在堆中。
小结:当作为对象使用时,首先会在String池中找是否存在该对象,如果有则不在String池中创建该对象,直接copy一份到堆中,如果没有,现在池中创建,也是copy一份到堆中,返回的是堆中对应的引用。
当作为基本数据使用时,只会在String池中创建,如果有不创建,返回该对象的引用,没有则创建,返回该对象的引用。
了解了这两种用法的原理后,我们扩展下。
String s1 = "s";
String s2 = "a";
String str = new String("123"+"456");该行代码会产生两个对象,等价于new String("123456"),java编译器有合并已知量的优化功能。
String str = s1+s2;该行代码会产生三个对象,等价与new String(new StringBuilder(s1).append(s2).toString()),String是常量不能相加,java会转换成等价写法。之所以不用StringBuffer(线程安全)可能是StringBuilder速度快把(非线程安全),个人猜想,未验证。
小结:
String str="s";//1个对象
String str=new String("s");//两个对象
String str="123"+"123";//一个对象
String s= str+"123";//三个对象
特例:final String s1="s";
final String s2="a";
s1+s2等价于"s"+"a"
最后再说一个null,String a 和String a=null在作为类变量时是相同的,但在局部变量中是不一样的,在局部变量中必须采后后者写法,必须初始化。这就是为什么局部中String 编译不通过的原因。
本人是java菜鸟,希望各位高手批评指正。
原文地址:http://liangzan.blog.51cto.com/3711181/1413012