在Java中,Java的设计者对String对象进行了大量的优化,主要有三个特点:
1.不变性:
不变性是指String对象一旦生成,则不能再对它进行改变。String的这个特点可以泛化成不变(immutable)模式,一个对象的状态在对象被创建之后就不再发生变化。不变模式的主要作用在于,当一个对象需要被多线程共享,并且访问频繁时,可以省略同步和锁等待的时间,从而大幅提升系统性能。
不变性可以提高多线程访问的性能。因为对象不可变,因此对于所有线程都是只读的,多线程访问时,即使不加同步也不会产生数据的不一致,故减少了系统的开销。
由于不变性,一些看起来像是修改的操作,实际上都是依靠产生新的字符串实现的。比如 String.substring(),String.concat()方法,他们都没有修改原始字符串,而是产生了一个新的字符串。
如果需要一个可以修改的字符串,那么需要使用StringBuffer或者StringBuilder对象。
2.针对常量池的优化
针对常量池的优化指当两个String对象拥有相同的值时,他们只引用常量池中的同一个拷贝。当同一个字符串反复出现时,这个技术可以大幅节省内存空间。
String str1 = new String("abc"); String str2 = new String("abc"); System.out.println(str1==str2);//false System.out.println(str1==str2.intern());//false System.out.println("abc"==str2.intern());//true System.out.println(str1.intern()==str2.intern());//true
以上代码str1和str2都开辟了一块堆空间存放String实例。虽然str1和str2内容相同,但是在堆空间中的引用是不同的。String.intern()返回字符串在常量池中的引用,显然它和str1也是不相同的,String.intern()始终和常量字符串相等。
3.类的final定义
final类型定义也是String对象的重要特点。作为final类的String对象在系统中不可能有任何子类,这是对系统安全性的保护。
有关String的内存泄漏
内存泄漏就是程序未能释放已经不再使用的内存的情况,他并不是说物理内存消失了,而是指由于不再使用的对象占据内存不被释放,而导致可用内存不断减小,最终有可能导致内存溢出。
以String.substring()方法为例,说明内存泄漏问题:
java.lang.String主要由3部分组成:代表字符数组的value、偏移量offset和长度count.这个结构为内存泄漏埋下伏笔,字符串的实际内容有value,offset,count三者共同决定,而非value一项。如果字符串value数组包含100个字符,而count长度只有1个字节,那么这个String实际上只要1个字符,却占据了至少100个字节,那剩余的99个就属于泄漏的部分,他们不会被使用,不会被释放,却长期占用内存,直到字符串本身被回收。
这种情况在JDK1.6中非常容易出现。String.substring()中,新生成的String并没有从value中获取自己需要的那部分,而是简单的使用了相同的value引用,只是修改了offset和count,以此类确定新的String对象的值。当原始字符串没有被回收时,这种情况是没有问题的,并且通过共用value,还可以节省一部分内存,但是一旦原始字符串被回收,value中多余的部分就造成了空间浪费。
综上所述,如果使用了String.substing()将一个大字符串切割为小字符串,当大字符串被回收时,小字符串的存在就会引起内存泄漏。
在JDK1.7中,去掉了offset和count两项,而String的实质性内容仅由value决定,而value数组本身就代表了这个String实际的取值。