32.String str=aaa与 String str=new String(aaa)一样吗?new String(“aaa”);创建了几个字符串对象
32.String str="aaa"与 String str=new String(“aaa”)一样吗?new String(“aaa”);创建了几个字符串对象?
-
String str = “aaa” 会先在字符串常量池里寻找有没有这个字符串,如果没有,就会在常量池里新建一个对象,然后把对象地址赋给str。
-
String str=new String(“aaa”) 是会在堆中创建一个存储空间,然后去常量池里寻找有没有这个字符串,如果没有就会再新建一个对象,然后再把对象的地址赋给这个字符串。相当于创建了两个对象
String str = "aaa"
与 String str = new String("aaa")
不完全相同,它们在内存分配和对象引用上存在关键区别。以下是详细分析:
1. 内存分配的区别
String str = "aaa"
- 创建对象数:1个对象。
- 机制:Java 的字符串常量池(String Pool)会缓存所有字面量字符串(如"aaa")。当使用String str = "aaa"时:
- JVM 会先检查常量池中是否已存在值为
"aaa"
的字符串对象。 - 如果存在,直接返回该对象的引用(
str
指向常量池中的对象)。 - 如果不存在,JVM 会在常量池中创建
"aaa"
并返回引用。
- JVM 会先检查常量池中是否已存在值为
String str = new String("aaa")
- 创建对象数:2个对象。
- 机制:
- 常量池中的对象:如果常量池中没有
"aaa"
,JVM 会先创建它(同上)。 - 堆内存中的对象:
new String("aaa")
会通过构造函数在堆内存中显式创建一个新的字符串对象,内容与常量池中的"aaa"
相同。 - 最终,
str
引用的是堆中的对象,而不是常量池中的对象。
- 常量池中的对象:如果常量池中没有
2. 对象引用的区别
-
String str = "aaa"
:-
str
指向常量池中的对象。 -
示例:
String s1 = "aaa"; String s2 = "aaa"; System.out.println(s1 == s2); // true(引用同一个常量池对象)
-
String str = new String("aaa")
:
-
str
指向堆中的对象。 -
示例:
String s1 = new String("aaa"); String s2 = new String("aaa"); System.out.println(s1 == s2); // false(堆中是两个不同对象) System.out.println(s1.equals(s2)); // true(内容相同)
3. new String("aaa")
创建了几个对象?
- 答案:2个对象。
- 常量池中的对象:
"aaa"
(如果之前未创建过)。 - 堆内存中的对象:通过
new String("aaa")
显式创建的新对象。
- 常量池中的对象:
4. 为什么推荐使用字符串字面量?
- 性能优势:
- 字符串常量池可以复用相同值的对象,减少内存开销。
- 使用
new String("aaa")
会导致堆中冗余对象,增加 GC 压力。
- 线程安全:
- 常量池中的字符串是不可变的(
immutable
),天然线程安全。 new String("aaa")
创建的对象虽然是不可变的,但多线程环境下仍需注意引用一致性。
- 常量池中的字符串是不可变的(
5. 特殊场景下使用 new String()
- 需要独立对象:
当需要确保字符串对象具有独立身份(如基于引用的比较==
)时。 - 安全考虑:
处理不可信数据时,避免共享敏感信息(如密码)。 - 框架需求:
某些框架(如 Hibernate)可能依赖对象引用的唯一性。
总结
特性 | String str = "aaa" | String str = new String("aaa") |
---|---|---|
对象数 | 1(常量池) | 2(常量池 + 堆) |
引用对象 | 常量池对象 | 堆中的新对象 |
== 比较结果 | 同值对象引用相同 | 同值对象引用不同 |
推荐使用场景 | 通用场景 | 特殊需求(独立对象、安全等) |
如果不需要特殊需求,优先使用字符串字面量(String str = "aaa"
)以优化性能和内存。
String 是最基本的数据类型吗
不是,String是引用数据类型。Java里有8个基本数据类型 byte, short, int, long, float, double, char, blooean。
String 有什么特性
-
不可变性
-
对一个String字符串进行任何的修改都是会新建一个对象,再把引用指向该对象,原的字符串是不会做任何改变。在多线程共享访问时,可以保证数据一致性。
-
定义:一旦创建,
String
对象的内容和长度无法被修改。 -
实现机制:
String
的底层通过final char[] value
存储字符数据,且value
被声明为final
,禁止外部修改。- 所有修改操作(如拼接、替换)都会生成新的
String
对象,原对象保持不变。
-
示例:
String s = "Hello"; s = s + " World"; // 创建新对象 "Hello World",s 指向新对象,原 "Hello" 未被修改
-
-
常量池优化
-
String 创建之后,会先在字符串常量池里寻找,如果没有,就会新建一个,如果有,就会把常量池的对象的引用拿出来。提高了性能和减少了内存的开销
-
机制:
- 使用字面量(如String s = “abc”)创建时,JVM 会检查常量池是否存在相同值的对象:
- 存在:直接返回池中对象的引用。
- 不存在:创建新对象并放入池中。
- 使用 new String(“abc”)时:
- 先检查常量池是否存在
"abc"
(若无则创建),再在堆中创建新对象。
- 先检查常量池是否存在
- 使用字面量(如String s = “abc”)创建时,JVM 会检查常量池是否存在相同值的对象:
-
示例:
String s1 = "abc"; // 池中对象 String s2 = "abc"; // s1 == s2 → true String s3 = new String("abc"); // 堆中新对象 System.out.println(s1 == s3); // false
-
-
final 修饰:String 底层还是用数组来实现的,用final来修饰的。
在使用 HashMap 的时候,用 String 做 key 有什么好处?
HashMap会先计算hashCode值,如果是String的话,因为String不可变,String也会去常量池缓存,这样就不会每一次都要计算hashCode值。而是先去在常量池里去匹配,没有再去创建。