前言
Jdk1.6及之前
: JVM存在永久代, 运行时常量池在永久代,运行时常量池包含字符串常量池
Jdk1.7:有永久代
,但已经逐步“去永久代”,字符串常量池从永久代里的运行时常量池分离到堆里
Jdk1.8及之后
: 无永久代,变成了元空间,运行时常量池在元空间,字符串常量池依然在堆里
字符串创建各种状态如下:
- 只在常量池上创建常量
String a1 = “AA”; - 只在堆上创建对象:调用toString方法或者+
String a2 = new String(“A”) + new String(“A”); - 在堆上创建对象,在常量池上创建常量
String a3 = new String(“AA”); - 在堆上创建对象,在常量池上创建引用
String a4 = new String(“A”) + new String(“A”);//只在堆上创建对象AA
a4.intern();//将该对象AA的引用保存到常量池上
代码举例说明
package base;
public class StringInnerTest {
private String some = "hello";
private final String some2 = new String("hello");
private static final String some3 = new String("hello");
public static void main(String[] args) {
// null 和null是可以相等的
if (null == null) {
System.out.println("ok"); // 这里会输出ok
}
/**
* 定义在类中的成员变量some没有进行实例化,会先去从常量池里边去拿,如果拿不到才会实例化然后放入到常量池
*/
System.out.println("StringInnerTest.new1.some == StringInnerTest.new2.some:" +
(new StringInnerTest().some == new StringInnerTest().some)); // true
// 类初始化的时候会创建新的对象,记住只要两个String不是同一个对象就不会相等
System.out.println("StringInnerTest.new1.some2 == StringInnerTest.new2.some2:" +
(new StringInnerTest().some2 == new StringInnerTest().some2)); // false
// 静态变量在类被引用的时候会被初始化,
// 只会被初始化一次,所以不管实例1还是实例2读取的some3都是同一个对象
System.out.println("StringInnerTest.new1.some3 == StringInnerTest.new2.some3:" +
(new StringInnerTest().some3 == new StringInnerTest().some3)); // true
// 如果常量池中没有"h"则创建新实例放入到常量池
String c = "h";
// 常量池中已经有了"h"直接引用过来
String e = "h";
System.out.println("e==c:" + (e == c)); // 这里会输出位true
// 最终JAVA虚拟机会将这种情况处理为 String a = new String(c + "w");
String a = c + "w";
// 同上
String b = c + "w";
// 这样a和b就是不同对象了,此时肯定不会相等
System.out.println("a==b:" + (a == b)); // 结果必定为false
// 同上String.format 也会创建一个新String对象
String f = String.format("%s%s", c, "w");
System.out.println("f 对象地址:" + Integer.toHexString(f.hashCode()));
System.out.println("a 对象地址:" + Integer.toHexString(a.hashCode()));
System.out.println("f.equals(a):" + (f.equals(a))); // 结果true
System.out.println("f==a:" + (f == a)); // 结果为false
// 超过一定数量的string对象想加会被编译器优化变成StringBuilder处理
// (new StringBuilder()).append(c).append(a).append(b).append(f).toString();
String d = c + a + b + f;
// 这种情况相当于指针直接指向到a指向的内存块
String g = a;
// 同上
String h = a;
System.out.println("g==h:" + (g == h)); // 这里g和h指向的是相同的内存地址所以这里是true
String a3 = "world";
final String a1 = "hello" + a3;
String a2 = "hello" + a;
// String a2 = new String("hello" + a);
System.out.println("a1 == a2:" + (a1 == a2)); // false
String b1 = "helloworld";
// 编译器会把b2优化为String b2 = "helloworld";这样只要常量池有实力就回去常量池去拿
String b2 = "hello" + "world";
String b3 = a1 + "world";
System.out.println("b1 == b2:" + (b1 == b2)); // true
// 不解释了,上述有说明
System.out.println("b1 == b2:" + (a1 == b2)); // false
final String a4 = "hello";
String a6 = "";
final String a5 = a4 + "world" + a6;
System.out.println("a5 == b2:" + (a5 == b2));
String a7 = a5.intern();
// intern 如果常量池中没有"helloword"会将a5实例String放入到常量池中,
// 常量池中有"helloword的话会a5会直接指向常量池中"helloword内存地址"",
// a7引用a5在常量池中内存地址,这个时候a7和b2就相等了
System.out.println("a7 == b2:" + (a7 == b2));
StringBuilder sb = new StringBuilder("helloworld");
// sb.toString 等于创建一个新的String对象所以这里也不会相等
// new String(value, 0, count)
System.out.println("sb == a7:" + (sb.toString() == a7));
}
}