@frank-shaw
2015-10-29T10:40:12.000000Z
字数 1415
阅读 1937
java.基础知识
String的一个很重要的特点:String是值不可变的常量,是线程安全的。接下来,String类使用了final修饰符,表明了String类的第二个特点:String类是不可继承的。
下面是String类的成员变量定义,从类的实现上阐明了String值是不可变的。
private final char value[];
private final int count;
那既然String变量是不可变的,那么对于String类中的concat方法(将另外字符串拼接到该字符串后面)该如何解释呢?实际上是生成了一个新的String类对象,复制了原来的数据信息而已。
“对String对象的任何改变都不影响到原对象,相关的任何change操作都会生成新的对象”。
问了解答这个问题,首先需要知道常量池的概念:在class文件中有一部分 来存储编译期间生成的 字面常量以及符号引用,这部分叫做class文件常量池,在运行期间对应着方法区的运行时常量池。
因此在上述代码中,String str1 = "hello world";和String str3 = "hello world"; 都在编译期间生成了 字面常量和符号引用,运行期间字面常量(确定的不变的值)"hello world"被存储在运行时常量池(当然只保存了一份)。通过这种方式来将String对象跟引用绑定的话,JVM执行引擎会先在运行时常量池查找是否存在相同的字面常量,如果存在,则直接将引用指向已经存在的字面常量;否则在运行时常量池开辟一个空间来存储该字面常量,并将引用指向该字面常量。
众所周知,通过new关键字来生成对象是在堆区进行的,而在堆区进行对象生成的过程是不会去检测该对象是否已经存在的。因此通过new来创建对象,创建出的一定是不同的对象,即使字符串的内容是相同的。
有了String,为什么还需要后面这两个家伙呢?其实呀,就是因为String不可变的特性,导致了StringBuffer、StringBuilder的产生。由于String每一次变化的时候都会产生一个新的对象,有些时候是必要的,有些时候就是不必要的而且会导致很大的内存浪费,这个时候就要使用StringBuffer、StringBuilder来帮忙弥补啦。
那么StringBuffer、StringBuilder之间有什么区别呢?
1.StringBuffer是线程安全的,前面有synchronized关键字,吊得很不跟他玩;相比之下,StringBuilder就不是线程安全的啦。
运行时常量池相对于Class文件常量池的另外一个重要特征就是具备动态性,Java语言并不要求常量一定只有编译期才能产生,也就是并非预置入Class文件中常量池的内容才能进入方法区运行时常量池,运行期间也可能将新的变量放入池中。一个非常典型的例子就是String类的intern()方法:
String.valueOf(500).intern();
既然运行时常量池是方法区的一部分,自然受到方法区内存的限制,当常量池无法再申请到内存时会抛出OutOfMemoryError异常。