char[] chs = {'h','e','l','l','o'};
String a = new String(chs);
String b = "hello";
- 上述代码中 a 和 b有什么区别?
- 内存分配不同:
- a会创建一个新的字符串对象,该对象会在堆内存中分配空间
- b是一个字符串常量,会分配在字符串常量池
- 内存分配不同:
- 字符串常量池
- JDK 7及之前的版本,字符串常量池被存放在永久代(Permanent Generation)内存区域中的方法区(Method Area)。
- JDK 8及以后的版本,由于永久代被移除,取而代之的是元空间(Metaspace),字符串常量池也被存放在元空间中。
- 元空间不属于传统的堆、栈或方法区,它是一个独立于这些区域的内存区域,通常位于本地内存中。
- 我们来看一道题
public static void main(String[] args) {
String s1 = "abc";
String s2 = "abc";
String s3 = "ABC";
String s4 = "a";
String s5 = s4 + "bc";
String s6 = "a" + "bc";
//1
System.out.println(s1.equals(s2)+" "+s1.equalsIgnoreCase(s3)+" "+s2.equals(s5)+" "+s3.equalsIgnoreCase(s5));
//2
System.out.println((s1==s2)+" "+(s1==s3)+" "+(s2==s5)+" "+(s2==s6));
}
结果:
true true true true
true false false true
s1直接新建一个字符串常量abc放到常量池中
s2只需要指向s1创建好的字符串常量即可
s3直接新建一个字符串常量ABC放到常量池中
s4直接新建一个字符串常量a放到常量池中
s5这里涉及到字符串拼接,由于 s4 是一个变量,编译器无法在编译时确定其值,因此会在运行时进行拼接。所以,s5 最终的值是 “abc”。注意,s5 的拼接并不会使用字符串池,而是在堆内存中创建一个新的对象。
s6这是一个编译时常量表达式,因此在编译时会被优化成一个字符串 “abc”,而不会在运行时进行拼接。所以,s6 会直接引用字符串池中已经存在的 “abc”。