1. String创建的两种方式
String str = "mcdull";
String str1 = str;
String str2 = new String("mcdull");
System.out.println(str == str1);
System.out.println(str == str2);
输出结果:true、false。
为什么会这样?原因是 str2 使用 new String 时一定会在堆中重新创建一个内存区域,而 str1 则会直接使用了 str 的引用,所以得到的结果也完全不同。
2. String 对象在 JVM 如何存储提高性能
字符串的分配和其他的对象分配一样,需要耗费高昂的时间和空间为代价,如果需要大量频繁的创建字符串,会极大程度地影响程序的性能,因此 JVM 为了提高性能和减少内存开销引入了字符串常量池(Constant Pool Table)的概念。
字符串常量池相当于给字符串开辟一个常量池空间类似于缓存区,对于直接赋值的字符串(String s=“xxx”)来说,在每次创建字符串时优先使用已经存在字符串常量池的字符串,如果字符串常量池没有相关的字符串,会先在字符串常量池中创建该字符串,然后将引用地址返回变量,如下图所示:
可通过以下代码验证:
String s1 = "java";
String s2 = "java";
System.out.println(s1 == s2);
输出结果:true,说明变量 s1 和变量 s2 指向的是同一个地址。
3. String 创建时会创建几个对象
先来看一个测试:
String s1 = new String("java");
String s2 = new String("java");
String s3 = "java";
String s4 = "java";
System.out.println(s1 == s2);
System.out.println(s3 == s4);
输出结果:false , true。
既然 new String 会在常量池中创建字符串,那么执行的结果不应该是 true 么?其实并不是,这里对比的变量 s1 和 s2 堆上地址,因为堆上的地址是不同的,所以结果一定是 false,如下图所示:
从图中可以看出 s1 和 s2 的引用一定是相同的,而 s3 和 s4 的引用是不同的。
也就是说 new String 的方式会首先去判断字符串常量池,如果没有就会新建字符串那么就会创建 2 个对象,如果已经存在就只会在堆中创建一个对象指向字符串常量池中的字符串。
4. String intern
intern方法的实现底层是一个native方法,在Hotspot JVM里字符串常量池它的逻辑在注释里写得很清楚:如果常量池中有这个字符串常量,就直接返回字符串的引用,否则将该字符串对象的值存入常量池,再返回其引用。善用 String.intern() 方法可以有效的节约内存并提升字符串的运行效率。
现实开发中使用并不是太多,举个简单例子,等有最新发现再补上别的,
String s1 = new String("javaString");
String s2 = s1.intern();
String s3 = "javaString";
System.out.println(s1 == s2);
System.out.println(s3 == s2);
返回结果 false, true.
证实了String intern() 返回的是常量池中字符串的引用。
5. 后续
常量池的内存布局,由于这一块只有简单了解,等学习完JVM再补上详细。
参考:java中文社区