众所周知string对象在java里面是不可变(immutable)的,任
何的连接,修改,其实都是在heap里面创建了一个新的string对象,而对原来的string对象是没有任何影响的,它们还是在那里,不曾改变,你只
是用它们的字面值去创造了新的strings而已。
string的不可变特性是非常有用的,至少这使得string是绝对的多线程安全的,那么java是怎么对待string对象的呢?是怎么利用string的这个不可变特性去优化内存的使用的呢?
我们从string的两种构造方式说起
String s1 = new String("tubie");
String s2 = "tubie";
有什么两样?两者最后都返回了一个指向字面值为tubie的一个引用,从这个结果来看是一样的,但其实过程和内存的变化都是大大的不同的
前者因为使用了new关键字,这迫使JVM在heap上创建了一个新的string对象,然后把该对象的引用返回,任务结束;
后者因为是"常量"赋值,所以其任务其实多了很多。JVM会首先到一个内部的string
pool中找是否有存在相同值的string对象,这个string
pool是java.lang.String内部维护的。如果有,就返回池里那个对象的引用,没有就在堆上分配个新的,然后把引用返回给用户的同时把这个
新的string,以前没有的string加到池里。
注意第一种方法虽然创建了一个string对象但是并没有放到池里,哪个对内存的利用率高清晰可辨,哪个在调用时的消耗大,做的事情多也清晰可见(哈希做的string pool搜索?)。空间和时间的trade off啊。
所以咯,如果之前string pool中没有"tubie"这个string,那么毫无疑问之前的s1 !=
s2,当然,equls的值比较是相等的,而这时如果有String s3 = "tubie";那么 S2 ==
S3是成立的了,因为池里已经加入了s2,并且在创建s3时搜到了。
前面说到用new关键字创建的string对象不会自己加入到string pool里面(在池里的string,有种叫法叫intern strings),那就要显示的由用户来加入
String sa = new String("Cao Dui");
sa = sa.intern();
这时候Cao Dui就加入到字符串池里面了。
这个intern到底做了哪些事?
首先查看了string pool,有没有一个"Cao Dui"已经加入过了,加入的那个直接返回给sa
不存在就在堆上另外分配一个"Cao Dui"的string对象,把sa指向这个,再把sa放入到池里。
无论是哪种情况,可以肯定的是,第一句的sa和第二句结束时的sa肯定是两个引用了。在这里第一个Cao Dui因为唯一的sa已经不指向它了,所以会很快被GC给回收掉。
我们另外从刚才的过程,intern函数的调用里可以得到一个结论,就是如果s1.equls(s2);则必定有s1.intern() == s2.intern();这时候s1和s2其实已经在这句比较结束后指向同一个已经在string池里面的对象了。
JVM这么做就是因为string的不可变特性。intern函数是一个native函数。
值得一说的是string的连接是创造了一个新的对象的。所以把sa和s1连接下,创建的就是一个完全新的string对象:
Cao Dui tubie
String的设计是一个典型的单一模式
String str1="AAAA";
String str2="AAAA";
这生成两个对象吗?
不是。在内存中,这是同一个对象
所以
if(str1==str2){} 的结果应该是 true
如果要生成不同的对象,就必须
String str1=new String("AAAA");
String str2=new String("AAAA");
if(str1==str2){} 的结果应该是 false
用第一种做法虽然变量名变来变去,但内存中对象仍只有一个,这种方法可以有效地节省内存空间和提高运行效率。
由于String具有不变的性质。所以对一长串String中的每一个字符进行操作是比较浪费时间的,对String进行加减删除替换等工作比较耗时,所以后来Sun又推出来StringBuffer方法来做这些事情。
StringBuffer类似于一个char[],
所以对数组无素做遍历,删除,增加,修改,查找等工作是比较快速的,完成后再把StringBuffer可以转化成String
例:
String a="请输入帐号:";
String b="NNNN";
String c="请输入密码:";
String d="XXXX";
............
一般的做法 String result=a+b+c+d;
结果是正确的,但不是高效的代码!
改良的做法:
StringBuffer buffer=new StringBuffer();
buffer.append(a);
buffer.append(b);
buffer.append(c);
buffer.append(d);
String result=buffer.toString();