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菜鸟,希望各位高手批评指正。