final
,表示不能被继承。serializable
接口,表示支持序列化;实现了Comparable
接口,表示可以做比较。final char[] value
用于存储数据,在JDK9改为byte[]
,节约了空间。String不可变的优点:
String类中的substring/replace/toLowerCase
等方法都是在内部再创建一个新的String类对象。
String s1 = "a"; //串池
String s2 = "b"; //串池
String s3 = "ab"; //串池
String s4 = s1 + s2; //堆区,new StringBuilder().append("a").append("b").toString() -> new String("ab")
String s5 = "a" + "b"; //编译期优化,直接转为“ab”
System.out.println(s3 == s4); //false
System.out.println(s3 == s5); //true
final String s1 = "a"; //串池
final String s2 = "b"; //串池
final String s3 = "ab"; //串池
String s4 = s1 + s2; //编译期优化,直接转为“ab”
String s5 = "a" + "b"; //编译期优化,直接转为“ab”
System.out.println(s3 == s4); //true
System.out.println(s3 == s5); //true
String s1 = "abc"; //串池
String s2 = new String("abc"); //String对象在堆区,指向串池
System.out.println(s1 == s2); //false
查看字节码:
0 new #2
3 dup
4 ldc #3
6 invokespecial #4 : (Ljava/lang/String;)V>
9 pop
10 return
会创建2个对象:
hello
字符串实例 0 new #2
3 dup
4 invokespecial #3 : ()V>
7 new #4
10 dup
11 ldc #5
13 invokespecial #6 : (Ljava/lang/String;)V>
16 invokevirtual #7
19 new #4
22 dup
23 ldc #8
25 invokespecial #6 : (Ljava/lang/String;)V>
28 invokevirtual #7
31 invokevirtual #9
34 astore_1
35 return
会创建6个 对象:
abc
字符串实例ABC
字符串实例注意:toString()
方法,本质是调用new String()
重新创建一个String对象,创建的abcABC
字符串不是常量,所以不会放入常量池,String类的本质是char数组。
String#intern()
方法会将字符串对象尝试放入串池。如果串池中存在,则不会放入,返回串池中的对象的地址;如果串池中不存在,则会将对象的引用地址复制一份,放入串池,并返回串池中的引用地址。
String s1 = new String("ab") + new String("c");
String s2 = s1.intern();
System.out.println(s1 == s2); //true
说明:s1指向堆中的new String("abc")
的地址,执行intern()
方法池化操作后,串池中的"abc"
指向堆区的new String("abc")
,并将地址返回给s2,所以s1和s2指向同一对象。
String s1 = new String("ab") + new String("c");
String s2 = "abc";
s1.intern();
System.out.println(s1 == s2); //false
说明:s1指向堆中的new String("abc")
,s2指向串池中的"abc"
,s1执行intern()
方法,尝试将“ab”字符串放入串池,但是串池已经存在所不会放入,所以s1仍然指向堆中,s2指向串池。
String s1 = new String("ab") + new String("c");
s1.intern();
String s2 = "abc";
System.out.println(s1 == s2); //true
说明:s1指向堆中的new String("abc")
,s1执行intern()
方法池化操作后,将"abc"
放入串池,串池中的"abc"
并指向堆中的地址,s2指向串池中的"abc"
的地址,所以s1和s2指向同一对象。
在JDK8下设置:-Xmx5m -XX:-UseGCOverheadLimit
List<String> list = new ArrayList<>();
int i = 0;
try {
for (int j = 0; j < 260000; j++) {
list.add(String.valueOf(j).intern());
i++;
}
} catch (Throwable e) {
e.printStackTrace();
} finally {
System.out.println(i);
}
//抛出异常:java.lang.OutOfMemoryError: Java heap space