Java 对于基本类型和 String 采用池化的享元模式,将其放在常量池中,提高运行程序速度,更节省内存
String 有两种方式放入常量池中String ab = "ab" 定义字面字符串
ab.intern() 该方法的解释, 若在常量池中,取引用,不在就加入常量池取引用;* When the intern method is invoked, if the pool already contains a
* string equal to this {@code String} object as determined by
* the {@link #equals(Object)} method, then the string from the pool is
* returned. Otherwise, this {@code String} object is added to the
* pool and a reference to this {@code String} object is returned.
实验1:
String c = new String("ceudcnfsalncfw");
c.intern();
String d = "ceudcnfsalncfw";
System.out.println(c==d);//false 第一步
String ab = new String("dnsdfnjew")+new String("dnjsfne");
ab.intern();
String cd = "dnsdfnjewdnjsfne";
System.out.println(ab==cd);//true 第二步
实验1分析:new String 首先会在堆里创建一个 String 对象,然后常量池没有就会添加进去;所以 c 是 new 的引用,d 是常量池里的引用
ab 是堆里的一份引用,此时 "dnsdfnjewdnjsfne" 是不在常量池里的 (第一步的 new 会直接创建在常量池里, 而这里是由 StringBuilder 拼接而成的, 不会直接添加到常量池),另外 java 8 里常量池不用单独再存储一份对象,可以直接存储堆中的引用,所以 ab 和 cd 引用的是同一个对象;
实验2:
运行了一段代码,然后出现了?? (不认识 python??)
String str3 = new StringBuffer("jav").append("a").toString();
System.out.println(str3.intern() == str3);//false 1 String str4 = new StringBuffer("pyth").append("on").toString();
System.out.println(str4.intern() == str4);//true 2
然后换了 StringBuilder 实现??
String str1 = new StringBuilder("ja").append("va").toString();
System.out.println(str1.intern() == str1);//false 3 String str4 = new StringBuilder("pyth").append("on").toString();
System.out.println(str4.intern() == str4);//true 4
再然后??
String ab = new String("dnsdfnjew")+new String("dnjsfne");
ab.intern();
String cd = "dnsdfnjewdnjsfne";
System.out.println(ab==cd);// true 5
String ab2 = new String("ja")+new String("va");
ab2.intern();
String cd2 = "java";
System.out.println(ab2==cd2);//false 6
推测: “java” 默认加在常量池里?
2 和 4 的分析应该和实验 1 的第二步一样的; 常量池里存储着堆里的引用
1,3,6 都是 false; 推测是根加载器主动将 "java" 字符串添加到常量池里,跟堆里的“java”内存地址是不一样的,所以引用指向堆和常量池是不相同的;
那么哪些字符串会被 JVM 主动添加到常量池?
JDK 8 跑的实验,可以试试;
参考