不管是Java语言还是C/C++语言,String可以说是我们用的最多的类型吧!但是你是否真的了解它呢?那下面咱们一起聊聊Java String吧!
JDK版本不同,字符串常量所处的JVM位置不同,关于 虚拟机运行时数据区 ,后续专门找一个机会来分享;
备注:本文示例JDK环境: JDK 1.8 示例代码
返回标准表示的字符串对象。String类维护私有字符串池。 调用此方法时,如果字符串池已经包含等于此字符串对象的字符串(通过equals方法确定), 则返回池中的字符串。 否则,将此String对象添加到池中,并返回对此String对象的引用。 这个功能为String提供了字符串池,我们可以使用它来优化内存。但是,这有一个缺点:在OpenJDK中,String.intern()是本地方法,它实际上调用了JVM的相关方法来实现该功能。
public static void main(String args[]) { String s1 = new String("hello") + new String(" java"); s1.intern(); String s2 = "hello java"; System.out.println("s1.intern()==s2: " + (s1.intern()==s2)); System.out.println("s1==s2: " + (s1==s2)); String s3 = new String("hello"); s3.intern(); String s4 = "hello"; System.out.println("s3.intern()==s4: " + (s3.intern()==s4)); System.out.println("s3==s4: " + (s3==s4)); } 复制代码
输出结果:
s1.intern()==s2: true s1==s2: true s3.intern()==s4: true s3==s4: false 复制代码
说明:
第一行代码:共计创建五个对象:2个字符串常量对象,3个堆对象,其中"hello java"堆对象是最终的需要的对象;
第二行代码:执行intern,首先去常量池查找,如果没有找到,则原则上需要在常量池创建一份s1数据的拷贝,但是由于JDK8常量池并入了Heap上,因此,不会再创建一份s1数据的拷贝,直接创建一份引用指向s1,保存到常量池中。
第三行代码:显示声明一个字符串常量对象,由于常量池中已经存在,则直接和常量池一样,指向s1;
所以,无论s1,s1.intern()和s2都指向同一个对象,因此,s1=s2=s1.intern();
第六行代码:执行完,创建两个对象,一个在heap,一个在常量池;
第七行代码:执行s3.intern()时,由于常量池中已经创建了此对象(在执行第六行的时候创建的),因此执行s3.intern(),则不做任何处理;
第八行代码:显示声明一个字符串常量对象,由于常量池中已经存在,则直接指向常量池中的对象;
所以:s3.intern()指向常量池中的对象,s4则也指向常量池,s3.intern() = s4;s3指向堆中的对象,则s3 != s4;
具体的对象关系如下图:
)
String | StringBuffer | StringBuilder |
---|---|---|
String的值是不可变的,这就导致每次 对String的操作都会生成新的String对象, 不仅效率低下,而且浪费大量优先的内存空间 | StringBuffer是可变类,和线程安全的字符串操作类,任何对它指向的字符串的操作都不会产生新的对象。每个StringBuffer对象都有一定的缓冲区容量,当字符串大小没有超过容量时,不会分配新的容量,当字符串大小超过容量时,会自动增加容量 | 可变类,速度更快 |
线程安全 | 线程不安全 | |
多线程操作字符串 | 单线程操作字符串 |
在Java中的String类已经重载的"+",因此String支持“+”,通过反编译的字节码来看,其实是通过创建StringBuilder来完成字符串拼接;
这里具体说明:如果在for循环内部,不建议使用“+”的方式来进行字符串的拼接,会增加系统的开销。此处顺便回答了颠倒语序如何实现?中的一个遗留问题!
public static void test_string_builder() { long start = System.currentTimeMillis(); Random rand = new Random(); StringBuilder res = new StringBuilder(); for (int i = 0; i < 1000; i++) { res.append(rand.nextInt(10)); res.append(" "); } System.out.println(res.toString()); long end = System.currentTimeMillis(); System.out.println("test_string_builder cost: " + (end - start)); } 复制代码
时间成本: test_string_builder cost: 1 复制代码
public static void test_string_plus() { long start = System.currentTimeMillis(); Random rand = new Random(); String res = ""; for (int i = 0; i < 1000; i++) { res += rand.nextInt(10); res += " "; } System.out.println(res.toString()); long end = System.currentTimeMillis(); System.out.println("test_string_plus cost: " + (end - start)); } 复制代码
时间成本:test_string_plus cost: 6 复制代码