blog.csdn.net/u011541946/…
www.cnblogs.com/xuxinstyle/…
Java的String涉及到几种形式
- 堆上字符串, 用new String(), StringBuilder/StringBuffer.toString 创建的字符串
- 常量字符串(字符串字面量), 用""直接声明的字符串
对c++比较了解的可能有这样一个概念 "C++鼓励使用常量(字面量是常量的一种),是因为编译器对常量有比较大的优化"。对于 java也一样。
字符串有以下形式
- 字面量字符串拼接,
"aaa" + "bbb" + "ccc"会触发编译器优化,成一个字面量串"aaabbbccc" - 堆上字符串和其他字符串的拼接,
"aaa" + new String("bbb")本质上是一个语法糖,编译器会将其替换成StringBuilder - 字符串intern(), 本质上是获取常量池里的字符串,具有"没有则创建"语义
- new String() 创建的字符串, 位于堆上
- "aaa" 字面量字符串,位于常量池。
- 对一个字面量字符串调用intern() 算啥? e.g. "aaa".intern() java会给出一个warning, 因为如此举动如同脱裤子放屁。我们对intern()的需求是因为线上可能有一些用户传入的字符串,这些是运行时的内容,我们希望把运行时的字符串缓存在常量池,而不是每个对象都占用堆上的空间。尽管"aaa".intern()没有啥意义,但注意: 由于调用intern()后,这个对象就无法被常量优化,实际上对c++,我们可以将其声明为constexpr让其享受编译器对常量的优化,但java显然做不到。因此
"aa".intern() + "b"就不等同于"aab"个人理解是因为编译器不会把"aa".intern() 视为常量,因此其 + 操作是用stringbuilder做的,从而导致最终"aab"位于堆上(待考证)
字符串的比较
== 比较字符串的地址是否相同 equals() 比较两个串 per-char 是否相同。如果字符串只涉及到常量池,那么"equals()相同的所有串"都是同一个副本
因此,我们对上面的字符串分个类
- 在堆上的字符串 {2, 4}
- 在常量池的字符串{1, 3, 5}
- 尽管是常量, 但编译器不认为是常量 {6}
如果两个字符串都在常量池,那么equals() 返回true, == 就返回true。{22, 24, 44}
如果一个在堆, 一个在常量池. 那么equals() 返回true, == 就不一定返回true。{12, 32, 52, 14, 34, 54}
如果一个在堆, 一个也在堆. 那么equals() 返回true, == 就不一定返回true。{11, 33, 55, 13, 15, 35}
一共有15种组合。
唯一需要注意的是,对字面量intern()后的对象调用 + ,不走编译器对字符串常量加法的优化。。。
字符串拼接
- 字面量 + 字面量 == 编译期优化,拼接后的字符串在常量池
- 字面量.intern() + 字面量,走stringbuilder,拼接后的字符串在堆上
- new String() + 字面量,走stringbuilder,拼接后的字符串在堆上
- new String() + new String() 走stringbuilder,拼接后的字符串在堆上