String s =new String()分析堆与栈,是先定义S,还是先new string()

本文详细解析了Java中String类型的创建及比较方式,包括直接赋值与new关键字的区别,以及不同运算符对String对象的影响。
先定义S

1. String str1 = "abc"; 
  System.out.println(str1 == "abc"); 

步骤: 
1) 栈中开辟一块空间存放引用str1, 
2) String池中开辟一块空间,存放String常量"abc", 
3) 引用str1指向池中String常量"abc", 
4) str1所指代的地址即常量"abc"所在地址,输出为true 

2. String str2 = new String("abc"); 
  System.out.println(str2 == "abc"); 

步骤: 
1) 栈中开辟一块空间存放引用str2, 
2) 堆中开辟一块空间存放一个新建的String对象"abc", 
3) 引用str2指向堆中的新建的String对象"abc", 
4) str2所指代的对象地址为堆中地址,而常量"abc"地址在池中,输出为false 

3. String str3 = new String("abc"); 
  System.out.println(str3 == str2); 

步骤: 
1) 栈中开辟一块空间存放引用str3, 
2) 堆中开辟一块新空间存放另外一个(不同于str2所指)新建的String对象, 
3) 引用str3指向另外新建的那个String对象 
4) str3和str2指向堆中不同的String对象,地址也不相同,输出为false 

4. String str4 = "a" + "b"; 
  System.out.println(str4 == "ab"); 

步骤: 
1) 栈中开辟一块空间存放引用str4, 
2) 根据编译器合并已知量的优化功能,池中开辟一块空间,存放合并后的String常量"ab", 
3) 引用str4指向池中常量"ab", 
4) str4所指即池中常量"ab",输出为true 

5. final String s = "a"; 
  String str5 = s + "b"; 
  System.out.println(str5 == "ab"); 

步骤: 
同4 

6. String s1 = "a"; 
  String s2 = "b"; 
  String str6 = s1 + s2; 
  System.out.println(str6 == "ab"); 

步骤: 
1) 栈中开辟一块中间存放引用s1,s1指向池中String常量"a", 
2) 栈中开辟一块中间存放引用s2,s2指向池中String常量"b", 
3) 栈中开辟一块中间存放引用str5, 
4) s1 + s2通过StringBuilder的最后一步toString()方法还原一个新的String对象"ab",因此堆中开辟一块空间存放此对象, 
5) 引用str6指向堆中(s1 + s2)所还原的新String对象, 
6) str6指向的对象在堆中,而常量"ab"在池中,输出为false 

7. String str7 = "abc".substring(0, 2); 
  
步骤: 
1) 栈中开辟一块空间存放引用str7, 
2) substring()方法还原一个新的String对象"ab"(不同于str6所指),堆中开辟一块空间存放此对象, 
3) 引用str7指向堆中的新String对象, 

8. String str8 = "abc".toUpperCase(); 
  
步骤: 
1) 栈中开辟一块空间存放引用str6, 
2) toUpperCase()方法还原一个新的String对象"ABC",池中并未开辟新的空间存放String常量"ABC", 
3) 引用str8指向堆中的新String对象
### Java 中 `String` 对象的创建机制及内存分配 在 Java 中,`String` 是一种特殊的对象类型,其不可变性和内部实现决定了它的行为和内存管理方式。以下是关于 `String s = new String("xyz");` 的具体分析: #### 基本概念 1. **字符串常量池** 字符串常量池是一个特殊区域,在 JVM 加载类时被初始化。当通过双引号定义字符串字面量(如 `"xyz"`),JVM 首会在字符串常量池中查找是否存在相同的值。如果存在,则返回该引用;否则将其加入到常量池并返回新引用。 2. **`new` 关键字的作用** 使用 `new` 创建对象会强制在上生成一个新的实例,即使该值已经存在于字符串常量池中。这意味着每次调用 `new String()` 都会产生新的对象,而不会重用已有对象。 --- #### 代码解析:`String s = new String("xyz");` 这段代码涉及以下几个部分: - **字面量 `"xyz"`**: 这是字符串的一个字面量形式。首次遇到此字面量时,它会被放入字符串常量池[^2]。 - **`new String("xyz")`**: 此操作会在区创建一个新的 `String` 对象,并将字符串常量池中的 `"xyz"` 复制为其内容[^3]。 因此,执行上述语句后,总共会有两个对象: 1. 存在于字符串常量池中的 `"xyz"`; 2. 上的由 `new String("xyz")` 创建的新对象。 需要注意的是,虽然这两个对象的内容相同,但由于它们位于不同的存储区域,所以实际上是独立的对象。 --- #### 特殊情况讨论 1. 如果字符串常量池中已存在 `"xyz"`,则不会再重复创建另一个常量池条目,仅会在上新增一个对象。 2. 若未使用 `new` 而直接赋值给变量(如 `String s = "xyz";`),此时只会有一个对象——即字符串常量池中的 `"xyz"`[^4]。 --- #### 示例代码验证 以下是一段用于测试对象数量的代码片段: ```java public class Test { public static void main(String[] args) { String str1 = "xyz"; String str2 = new String("xyz"); System.out.println(str1 == str2); // false (比较地址不同) System.out.println(str1.equals(str2)); // true (比较内容相同) // 查看哈希码是否一致 System.out.println(System.identityHashCode(str1)); System.out.println(System.identityHashCode(str2)); } } ``` 运行结果表明,尽管两者的值相等,但它们指向不同的内存位置。 --- #### 达性分析垃圾回收 对于相互引用的情况,可达性分析能够有效解决循环依赖问题,而简单的引用计数法则可能失效[^1]。然而,这种现象通常不适用于普通的 `String` 对象,因为一旦没有任何强引用指向某个对象,GC 就可以安全地回收它。 --- ### 总结 在 `String s = new String("xyz");` 的情况下,最终会创建两个对象:一个是存放在字符串常量池中的 `"xyz"`,另一个是由 `new` 操作符显式创建于上的对象。理解这一过程有助于优化程序性能,减少不必要的内存开销。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值